H₂S Gas Sensor
STM32G431KB · Firmware v0.1.0
Testing

UART Command Interface

JSON protocol, all commands, response format, and error codes.

Connection

SettingValue
Port (TX)PA9
Port (RX)PA10
Baud rate9600
Format8N1
Flow controlNone

Connect a USB-UART adapter: PA9 → RX, PA10 → TX, GND → GND.

Protocol

All messages are JSON objects terminated by \n. Max command length: 127 bytes.

Command:   {"cmd": "GAS", "data": ""}
Response:  {"cmd": "GAS", "data": "12.50"}

On power-on the firmware sends:

{"cmd": "FW", "data": "0.1.0"}

Command Reference

Readings

CommandResponse dataNotes
{"cmd":"GAS","data":""}"12.50" (ppm)Stability mean if available, else raw code
{"cmd":"TEMP","data":""}"23.4" (°C)SHT45
{"cmd":"HUM","data":""}"52.1" (%RH)SHT45
{"cmd":"FW","data":""}"0.1.0"Returns ACK cmd

Status & Diagnostics

CommandResponse dataNotes
{"cmd":"STATUS","data":""}"2048:CALIBRATED"raw_adc:state
{"cmd":"STABILITY","data":""}"1300:30:1"mean_mv:sample_count:is_stable

Calibration states: UNCALIBRATED, ZERO_CALIBRATED, CALIBRATED.

Calibration

CommandResponse dataNotes
{"cmd":"ZERO","data":""}"1010" (baseline ADC)Auto-zero. Requires is_stable=1.
{"cmd":"ZERO","data":"1010"}"1010"Manual zero at given ADC code.
{"cmd":"SPAN","data":"25"}"25.0:71%"Auto-span. Requires ZERO first.
{"cmd":"SPAN","data":"25:1420"}"25.0:71%"Manual span at given ADC code.

SPAN response format: "<ppm>:<cha_percent>%"

Error Responses

All errors use {"cmd":"ERR","data":"<code>"}:

DataCause
NOT_STABLEZERO attempted before sensor is stable
ZERO_FIRSTSPAN attempted without prior zero
INVALID_PPMPPM value ≤ 0 in SPAN command
JSON_PARSEMalformed JSON received
UTF8Non-UTF8 bytes received
UNKNOWN_CMDUnrecognised cmd field

Example Session

← {"cmd":"FW","data":"0.1.0"}

→ {"cmd":"STATUS","data":""}
← {"cmd":"STATUS","data":"1253:UNCALIBRATED"}

→ {"cmd":"STABILITY","data":""}
← {"cmd":"STABILITY","data":"1253:12:0"}

(wait ~20 more seconds)

→ {"cmd":"STABILITY","data":""}
← {"cmd":"STABILITY","data":"1250:30:1"}

→ {"cmd":"ZERO","data":""}
← {"cmd":"ZERO","data":"1250"}

(apply 25 ppm cylinder, wait T90 ~35 s)

→ {"cmd":"SPAN","data":"25"}
← {"cmd":"SPAN","data":"25.0:71%"}

→ {"cmd":"GAS","data":""}
← {"cmd":"GAS","data":"24.87"}

→ {"cmd":"TEMP","data":""}
← {"cmd":"TEMP","data":"23.6"}

Testing with Python

import serial, json, time

ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
time.sleep(1.5)  # wait for MCU boot

def cmd(command, data=""):
    msg = json.dumps({"cmd": command, "data": data}) + "\n"
    ser.reset_input_buffer()
    ser.write(msg.encode())
    ser.flush()
    raw = ser.readline()
    return json.loads(raw.decode().strip()) if raw else None

print(cmd("FW"))           # {'cmd': 'ACK', 'data': '0.1.0'}
print(cmd("STABILITY"))    # {'cmd': 'STABILITY', 'data': '1250:30:1'}
print(cmd("GAS"))          # {'cmd': 'GAS', 'data': '24.87'}
print(cmd("ZERO"))         # {'cmd': 'ZERO', 'data': '1250'}
print(cmd("SPAN", "25"))   # {'cmd': 'SPAN', 'data': '25.0:71%'}

Last updated: March 2026