drivers.Semeatech.Semeatech.Semeatech¶
- class drivers.Semeatech.Semeatech.Semeatech(sm_port: str = '/dev/ttyAMA1', baud: int = 115200)¶
Driver for the Semeatech electrochemical gas sensor over UART.
Sends pre-computed binary commands to a Semeatech OGS module and parses the multi-field response into gas concentration (
ug/m^3andppb), on-board temperature (degrees Celsius), relative humidity (percent), and a raw ADC reading. The protocol uses a0x3Aheader with 10-byte command frames; responses are variable-length packets whose payload bytes are decoded as big-endian signed integers.- What it measures:
Concentration of a single target gas (the species depends on the cell installed - e.g. O3, NO2, SO2, CO, H2S). The primary reading is in parts per billion (ppb); ug/m^3 is also reported by the sensor and cached for downstream conversion.
- Variables:
SM_GET (
ClassVar[list[int]]) – 10-byte command frame that requests a full multi-field readout (gas + temperature + humidity + raw).SM_RAW (
ClassVar[list[int]]) – 10-byte command frame for the raw ADC channel only.SM_TEMP (
ClassVar[list[int]]) – 10-byte command frame for the on-board temperature channel.SM_HUM (
ClassVar[list[int]]) – 10-byte command frame for the on-board humidity channel.DEBUG (
bool) – WhenTrue, verbose TX/RX traces are emitted viautils.oizom_logger.OizomLogger.TIMEOUT (
int) – Per-transaction response timeout in milliseconds (derived from_timeout_seconds).COMMAND_DELAY (
int) – Inter-command idle period in seconds.RESPONSE_DELAY (
float) – Mandatory post-transmit settle delay in seconds. Do not change - the bus is timing-sensitive.MAX_BUFFER_SIZE (
int) – Upper bound on bytes read in a single response.data (
ClassVar[dict]) – Cache of the most recent decoded values with keysug,ppb,temp,hum, andraw.
Example
Read the gas concentration and cached environmental fields:
sm = Semeatech("/dev/ttyAMA1") # doctest: +SKIP ppb = sm.getSensorData(0) # doctest: +SKIP raw_adc = sm.getSensorData(1) # doctest: +SKIP temp_c = sm.getSensorData(2) # doctest: +SKIP hum_rh = sm.getSensorData(3) # doctest: +SKIP
See also
SensorBase.SensorBase.GenericSensor: Reference base contractfor Oizom sensor drivers.
Semeatechdoes not inherit from it but mirrors thegetSensorData(channel)access pattern so it can be used interchangeably by higher-level acquisition loops.
Configure the UART port used to talk to the Semeatech module.
Creates the
serial.Serialinstance with 8N1 framing and a 5-second read/write timeout, then immediately closes the handle so the port is only held while a transaction is in flight.- Parameters:
- Returns:
The constructor has no return value.
- Return type:
None
- Raises:
serial.SerialException – If the underlying
pyserialdriver fails to acquire the requested device (e.g. permissions, missing device node, or port already in use).
Example
Construct a driver bound to the default UART:
sm = Semeatech() # doctest: +SKIP sm2 = Semeatech("/dev/ttyUSB0", baud=115200) # doctest: +SKIP
Note
The port is left closed after construction. Subsequent calls to
sendCommand()will open and close it on demand.- getSensorData(channel: int = 0) float¶
Return one decoded measurement from the sensor.
Channel
0issues a freshSM_GETtransaction, parses the full response into thedatacache, and returns the gas concentration in ppb. Channels1-3are non-blocking and return the cached value populated by the most recent channel-0 read:1-> raw ADC count (divided by 10.0)2-> on-board temperature in degrees Celsius3-> on-board relative humidity in percent
- Parameters:
channel (
int) – Logical data channel.0for ppb (full read),1for raw ADC,2for temperature,3for humidity.- Returns:
The requested measurement, or
Noneif a hardware or parsing error occurred (the exception is logged, not re-raised).- Return type:
- Raises:
Exception – Any error raised inside the read path is caught and logged via
context_logger.error_with_context; this method does not propagate exceptions to the caller.
Example
Sample a full frame and then fetch cached environmental fields:
sm = Semeatech("/dev/ttyAMA1") # doctest: +SKIP ppb = sm.getSensorData(0) # doctest: +SKIP temp_c = sm.getSensorData(2) # doctest: +SKIP
Note
Channels
1-3will return0until at least one channel-0read has succeeded, because the cache is seeded with zeros at class definition time.
- micros() int¶
Return the current wall-clock time in microseconds.
Thin wrapper around
time.time()provided so the rest of the driver can match the microsecond-based timing idiom used elsewhere in the Oizom firmware.- Parameters:
None – This method takes no arguments other than
self.- Returns:
Microseconds since the Unix epoch.
- Return type:
- Raises:
None – This method does not raise.
Example
Measure how long a transaction takes in microseconds:
sm = Semeatech("/dev/ttyAMA1") # doctest: +SKIP t0 = sm.micros() # doctest: +SKIP sm.getSensorData(0) # doctest: +SKIP elapsed_us = sm.micros() - t0 # doctest: +SKIP
Note
Resolution is bounded by the underlying OS clock; on Linux this is typically nanosecond-accurate but is reported here as an integer microsecond value.
- millis() int¶
Return the current wall-clock time in milliseconds.
Used internally by
sendCommand()to enforce theTIMEOUTbudget while waiting for a sensor response.- Parameters:
None – This method takes no arguments other than
self.- Returns:
Milliseconds since the Unix epoch.
- Return type:
- Raises:
None – This method does not raise.
Example
Build a simple millisecond stopwatch:
sm = Semeatech("/dev/ttyAMA1") # doctest: +SKIP start = sm.millis() # doctest: +SKIP sm.getSensorData(0) # doctest: +SKIP duration_ms = sm.millis() - start # doctest: +SKIP
Note
The returned value is a monotonic-looking integer but it is actually wall-clock time, so it can step backwards if the system clock is adjusted (e.g. by NTP).
- sendCommand(command: list[int], recieve: bool = False) list¶
Send a binary command frame and return the raw response.
Opens the serial port (if not already open), writes each byte of
commandindividually, waitsRESPONSE_DELAYseconds for the sensor to begin replying, then pollsinWaitinguntil either data is available orTIMEOUTmilliseconds elapse. Up to three retry passes are performed when an empty response is received. The port is closed at the end of the transaction unlessrecieveisTrue.- Parameters:
command (
list[int]) – Sequence of byte values (0-255) constituting a full Semeatech command frame, typically one of theSM_*class constants.recieve (
bool) – IfTrue, the serial port is left open after reading so a follow-up call can re-use the same handle. IfFalse(default) the port is closed before returning.
- Returns:
Response bytes as hex strings (e.g.
["0x3a", "0x10", ...]). An empty list is returned if every retry pass observed no data and no exception was raised.- Return type:
- Raises:
Exception – If all three retry passes time out with no bytes received, an
Exception("Timeout Semeatech")is raised inside the method, caught locally, logged, and an empty list is returned to the caller.
Example
Issue the full-readout frame and inspect the raw payload:
sm = Semeatech("/dev/ttyAMA1") # doctest: +SKIP rx = sm.sendCommand(Semeatech.SM_GET) # doctest: +SKIP print(rx[:4]) # doctest: +SKIP
Note
The method is byte-oriented and intentionally writes one byte at a time to remain compatible with the sensor’s input buffer handling. Do not parallelize calls on the same port.
- COMMAND_DELAY = 1¶
- DEBUG = True¶
- MAX_BUFFER_SIZE = 51¶
- RESPONSE_DELAY = 0.001¶
- TIMEOUT = 8000¶
- __sm_port¶
- _timeout_ = 8¶