Quickstart¶
Read oxygen from your MCU: wire four pins, write a small port class, call three driver functions.
1. Wire it¶
| MCU | FDO2-G2 connector | Notes |
|---|---|---|
| UART TX | pin 3, RXD | Sensor input, 3.0 V levels, 3.3 V maximum |
| UART RX | pin 2, TXD | Sensor output, 3.0 V levels |
| GND | pin 1, GND | Common ground |
| 3.3 V to 5.0 V | pin 4, VCC | 40 mA peak during a measurement |
Set the UART to 19200 baud, 8 data bits, no parity, 1 stop bit, no flow control. A 5 V MCU needs a level shifter on its TX line. The Hardware page has the connector, the timing and the electrical limits.
2. Add the driver¶
Add the repository as a submodule and link the hf::fdo2 CMake target (or add inc/ to your include path and
build with C++17). Installation covers CMake and ESP-IDF.
3. Write the port¶
The port is the only platform code. It writes bytes, returns one complete reply line and flushes the receive
buffer. Replace the five platform_* calls with your SDK's.
class MyUart : public fdo2::UartInterface<MyUart> {
public:
// Send one command line (the driver adds the CR).
void write(const uint8_t* data, std::size_t length) noexcept { platform_uart_write(data, length); }
// Return one complete reply line, CR included, or 0 if none arrived within timeout_ms.
// timeout_ms == 0 means "only if a whole line is already here".
std::size_t read(uint8_t* out, std::size_t max, uint32_t timeout_ms) noexcept {
const uint32_t start = platform_millis();
for (;;) {
uint8_t byte = 0;
while (platform_uart_read_byte(byte)) {
if (len_ < sizeof(line_)) {
line_[len_++] = byte;
}
if (byte == '\r') { // the module ends every reply with one CR
const std::size_t n = (len_ < max) ? len_ : max;
std::memcpy(out, line_, n);
len_ = 0;
return n;
}
}
if (platform_millis() - start >= timeout_ms) {
return 0; // keep a partial line for the next call
}
platform_delay_ms(1);
}
}
void flush_rx() noexcept {
platform_uart_flush();
len_ = 0;
}
// Optional: lets the driver and your code wait through UartInterface::delay_ms().
void delay_ms_impl(uint32_t ms) noexcept { platform_delay_ms(ms); }
private:
uint8_t line_[192]{}; // longest reply: #MRAW, under 120 characters
std::size_t len_ = 0;
};
read() must return a whole line ending in CR, or nothing. Porting explains why and lists the
rules for each method.
4. Read oxygen¶
#include "fdo2.hpp"
#include <cstdio>
int read_oxygen(MyUart& uart) {
fdo2::Driver<MyUart> sensor(uart);
uart.delay_ms(fdo2::kFdo2G2PowerUpSettleMs); // 1. the module needs about 1 s after power-up
const auto version = sensor.ReadVersion(); // 2. identify: D = 8 for an FDO2-G2
if (!version || version.value.device_id != 8) {
return -1; // wiring, baud rate or supply
}
const auto m = sensor.MeasureMoxy(); // 3. one measurement, up to 150 ms
if (!m || !fdo2::MoxyStatusOkForOxygen(m.value.status_u32)) {
return -2; // timeout, #ERRO or a fault bit in S
}
std::printf("pO2 %.3f hPa at %.3f C\n", m.value.p_o2_hpa, m.value.temp_c); // 4. use it
return 0;
}
What each step does:
- The module answers commands about 1 s after power-up.
kFdo2G2PowerUpSettleMsis 1100 ms. ReadVersion()sends#VERSand checks that a module answers. An FDO2-G2 reports device id 8.MeasureMoxy()sends#MOXY, waits for the reply and scales it.MoxyStatusOkForOxygen()accepts the status values 0 and 1 only: any other value means the oxygen or temperature reading can be wrong.p_o2_hpais the oxygen partial pressure in hPa andtemp_cthe sensor temperature in °C.
Allow 3 minutes of warm-up for full accuracy. This example is built by the host CI, so it compiles against the current headers.
If it does not work¶
| Symptom | First thing to check |
|---|---|
ReadVersion() returns Timeout |
TX and RX crossed (MCU TX to sensor RXD), 19200 baud, supply, 1 s boot delay |
ProtocolError on every call |
Baud rate, or broadcast mode left on by another tool (Troubleshooting) |
DeviceError |
Read LastDeviceErrorCode(); the codes are in UART protocol |
| Status not 0 or 1 | Ambient light, condensation on the membrane, a sensor fault (Troubleshooting) |
Next¶
- API guide: raw data, volume percent, non-blocking measurement, errors.
- Examples: two complete ESP32-S3 applications.
- UART protocol: every command and field the driver uses.