Architecture
Repo Shape
The repo is organized around one canonical library root: src/.
Inside src/:
ezo.h,ezo.c: shared public results, timing hints, and numeric parsingezo_calibration_transfer.h,ezo_calibration_transfer.c: shared calibration export/import helpersezo_common.h,ezo_common.c: internal shared formatting and parsing helpersezo_control.h,ezo_control.c: shared control-plane helper moduleezo_do.h,ezo_do.c: typed DO product moduleezo_ec.h,ezo_ec.c: typed EC product moduleezo_hum.h,ezo_hum.c: typed HUM product moduleezo_i2c.h,ezo_i2c.c: I2C C coreezo_i2c.hpp: thin I2C C++ wrapperezo_i2c_arduino_wire.h,ezo_i2c_arduino_wire.cpp: ArduinoTwoWireI2C adapterezo_i2c_linux_i2c.h: Linux I2C adapter public headerezo_linux_device.h: Linux host device facade over the I2C and UART adaptersezo_orp.h,ezo_orp.c: typed ORP product moduleezo_parse.h,ezo_parse.c: shared query, CSV, and UART-sequence helpersezo_ph.h,ezo_ph.c: typed pH product moduleezo_product.h,ezo_product.c: product identity and metadata layerezo_rtd.h,ezo_rtd.c: typed RTD product moduleezo_schema.h,ezo_schema.c: canonical output schemas and typed reading structsezo_uart.h,ezo_uart.c: UART C coreezo_uart_posix_serial.h: POSIX UART adapter public headerezo_uart_arduino_stream.h,ezo_uart_arduino_stream.cpp: ArduinoStreamUART adapter
Host-only implementation code lives outside src/:
platform/linux/: Linux host adapter implementations
Everything else is supporting material:
platform/: non-Arduino platform codeexamples/: small integration examplestests/: host-side tests and fakesdocs/: tracked implementation docs and curated EZO product/protocol notes
Rules
- The core stays C99 and platform-agnostic.
- The C++ layer stays thin and convenience-only.
- Platform transport code stays out of the core implementation.
- The core does not allocate dynamically.
- The core does not sleep internally.
- Callers own buffers and timing behavior.
- I2C and UART stay separate driver families.
Layers
- Shared public layer
- result enum
- command kinds
- timing hints
- numeric parsing helper
- Shared internal helper layer
- fixed-point command formatting
- transport-neutral helper code
- I2C core
- addressed transaction send flow
- status-byte response decoding
- text and raw response handling
- Product metadata layer
- static registry for known products
- device-info parsing and normalized identification
- support tiers, capabilities, defaults, timing lookup, and timing fallback
- UART core
- line-oriented send flow
- CR-terminated single-line read primitive
- response classification and small sequence-oriented helpers
- Shared product-facing parse/schema layer
- borrowed text spans and CSV/query parsing
- canonical field-order descriptors per product
- typed scalar and multi-output reading structs
- transport-neutral sequence state for higher-level UART workflows
- Shared control and transfer modules
- transport-explicit identity, status, LED, UART response-code, sleep, factory-reset, protocol-lock, and mode-switch helpers
- transport-explicit calibration export/import helpers, including I2C import-result handling for the documented pending-reboot state
- Typed product modules
- pH read, temperature, calibration, slope, and extended-range helpers
- ORP read, calibration, and extended-scale helpers
- RTD read, scale, calibration, logger, and sequential or bulk memory helpers
- EC multi-output read, output-config, calibration, temperature, probe-K, and TDS-factor helpers
- DO multi-output read, output-config, calibration, temperature, salinity, and pressure helpers
- HUM multi-output read, output-config, and temperature-calibration helpers
- explicit I2C and UART entry points with no fake unified device
- C++ wrapper
- thin convenience layer over the I2C C API
- no separate protocol logic
- Platform integrations
- convert platform APIs into the transport callback contracts
- current integrations: Arduino
TwoWire, ArduinoStream, Linux file-descriptor I2C, Linux host POSIX serial, and a Linux host device facade for binding-friendly lifecycle management
Transport Boundary
The cores talk to the outside world through injected transport callbacks plus caller-owned context.
The transport contracts are deliberately different by mode:
- I2C uses one addressed
write_then_read(...)transaction callback - UART uses
write_bytes(...),read_bytes(...), and optionaldiscard_input(...)
That separation is intentional. The repo does not pretend I2C and UART are the same transport model.
API Direction
The public API is split into:
- shared public helpers in
src/ezo.h - shared calibration-transfer helpers in
src/ezo_calibration_transfer.h - shared control helpers in
src/ezo_control.h - multi-output product APIs in
src/ezo_ec.h,src/ezo_do.h, andsrc/ezo_hum.h - shared parse helpers in
src/ezo_parse.h - I2C API in
src/ezo_i2c.handsrc/ezo_i2c.hpp - scalar product APIs in
src/ezo_ph.h,src/ezo_orp.h, andsrc/ezo_rtd.h - product metadata API in
src/ezo_product.h - shared schema API in
src/ezo_schema.h - UART API in
src/ezo_uart.h - POSIX UART adapter API in
src/ezo_uart_posix_serial.h - Arduino integration headers in
src/ezo_i2c_arduino_wire.handsrc/ezo_uart_arduino_stream.h
Current surface:
- shared timing and numeric parsing helpers
- shared control-plane helpers for the common admin/protocol families, including UART response-code mode
- shared calibration-transfer helpers for supported products
- shared query parsing, sequence-state, and schema helpers
- I2C device init, command send helpers, text reads, raw reads
- product IDs, metadata lookup, device-info parsing, timing lookup, timing fallback
- typed read/query helpers over both transports for pH, ORP, RTD, EC, DO, and HUM
- UART device init, command send helpers, line reads, discard hook
- Arduino I2C and UART adapter shims
- Linux I2C and Linux host POSIX UART adapters
Explicit non-goals for the current baseline:
- async/state-machine APIs
- hidden reconnect or resynchronization flows around rebooting, sleep, or mode switching
- hidden retries or hidden sleeps
- UART C++ wrapper
Validation
Validation is split across:
- host-side C and C++ tests against fake transports
- Linux I2C and Linux host POSIX UART adapter behavior tests on host builds
- PlatformIO Arduino compile coverage for both I2C and UART examples
- manual Arduino IDE validation
Public Surface Guidance
The public starting-point docs are:
docs/public-api-layers.mddocs/support-matrix.mddocs/product-onboarding.md
Packaging
Primary development flow:
- CMake for host builds and tests
Packaging/distribution surfaces:
library.propertiesfor Arduino toolinglibrary.jsonfor PlatformIOcmake --installfor host-side header and static-library installation
Arduino packaging now covers the full public header surface. Host-only Linux support remains a CMake-side concern, and the CMake install path exports installable targets plus public headers.
Handoff Notes
A new developer should treat these files as the main entry points:
README.mdfor first-contact orientationdocs/canonical-library-direction.mdfor the long-term product-aware direction beyond the current transport baselinedocs/product-onboarding.mdfor the maintained checklist and validation bar for future product familiesdocs/ezo/README.mdfor product-family and protocol contextsrc/ezo.hfor shared types and helperssrc/ezo_calibration_transfer.hfor shared export/import helperssrc/ezo_control.hfor shared control-plane helperssrc/ezo_do.hfor typed DO helperssrc/ezo_ec.hfor typed EC helperssrc/ezo_hum.hfor typed HUM helperssrc/ezo_parse.hfor shared text, query, and sequence helperssrc/ezo_i2c.hfor the I2C C APIsrc/ezo_i2c.hppfor the I2C C++ wrappersrc/ezo_i2c_arduino_wire.hfor Arduino I2C integrationsrc/ezo_i2c_linux_i2c.hfor Linux I2C integrationsrc/ezo_linux_device.hfor Linux host device lifecyclesrc/ezo_orp.hfor typed ORP helperssrc/ezo_ph.hfor typed pH helperssrc/ezo_product.hfor product IDs, metadata, and device-info parsingsrc/ezo_rtd.hfor typed RTD helperssrc/ezo_schema.hfor canonical field-order and typed reading helperssrc/ezo_uart.hfor the UART C APIsrc/ezo_uart_posix_serial.hfor POSIX UART integrationsrc/ezo_uart_arduino_stream.hfor Arduino UART integrationsrc/ezo_common.cfor shared helper behaviorsrc/ezo_calibration_transfer.cfor shared export/import behaviorsrc/ezo_control.cfor shared control-plane behaviorsrc/ezo_do.cfor typed DO helper behaviorsrc/ezo_ec.cfor typed EC helper behaviorsrc/ezo_hum.cfor typed HUM helper behaviorsrc/ezo_i2c.cfor I2C-specific core behaviorsrc/ezo_orp.cfor typed ORP helper behaviorsrc/ezo_ph.cfor typed pH helper behaviorsrc/ezo_parse.cfor shared query/CSV parsing and UART sequence statesrc/ezo_product.cfor the static product registry and lookup logicsrc/ezo_rtd.cfor typed RTD helper behaviorsrc/ezo_schema.cfor canonical product schemas and typed reading helperssrc/ezo_uart.cfor UART-specific core behaviorplatform/linux/ezo_uart_posix_serial.cfor POSIX UART adapter behavior