Karbon-Assure Edge AI Node: An edge-AI field device for soil-health monitoring and digital MRV (Measurement, Reporting, and Verification). It senses, reasons, and runs inference entirely on-device on an Arduino UNO Q, then syncs compact, self-explaining evidence records to the cloud instead of streaming raw telemetry.
Video Tutorial
Component List
| ID | Name | Designator | Quantity |
| 1 | Arduino UNO Q (ABX00162) 2GB RAM / 16GB eMMC | MAIN-BOARD | 1 |
| 2 | DFRobot SEN0193 Capacitive Soil Moisture Sensor | SOIL-A0 / SOIL-A1 | 2 |
| 3 | SparkFun Qwiic BME688 Environmental Sensor | BME 0x76 / BME 0x77 | 2 |
| 4 | DFRobot SEN0097 BH1750 Ambient Light Sensor | BH1750 0x5C / 0x23 | 2 |
Project Explanation Step By Step
Karbon-Assure is an edge-AI field device for soil-health monitoring and digital MRV (Measurement, Reporting, and Verification). Two potted plants (Pot A control, Pot B deliberately drought-stressed) are each instrumented with a capacitive soil-moisture probe, a BME688 environmental/gas sensor, and a BH1750 ambient-light sensor. The pipeline runs in four on-device stages on an Arduino UNO Q.
Sense: the UNO Q’s STM32U585 real-time domain polls all sensors at 1 Hz and streams readings to the Linux domain over Bridge RPC, while also logging continuously to a microSD card.
Reason: the QRB2210 Linux domain converts raw readings into soil-type-agnostic relative features – a relative soil-moisture index (RSMI), light-delta, and gas-index trend – so the same model works across different soil types without per-site recalibration.
Detect: a compact trained transformer classifier (1 self-attention block, 1,155 parameters, exported to dependency-free pure Python) classifies soil-moisture state with a confidence score, trained on 1.27 million real readings from the controlled two-pot trial (98.9% train / 88.9% test accuracy, chronological split).
Synchronise: HMAC-signed evidence records – sensor values, derived features, the inference result, a timestamp, and the model version – are queued in a durable local store and pushed to the Karbon-Assure ingestion API, with automatic retry on failure.
The differentiator is not the sensors themselves (soil-moisture/gas/light proxies are well known) but wiring a continuous edge-inference node directly into an insurable MRV pipeline: every reading becomes part of an auditable evidence chain an insurer or carbon-credit verifier can trust, not just a number on a chart.
Coding Explanation and Configuration
The board runs the official Arduino App SDK for the UNO Q, split across two processors. STM32 real-time firmware (KA-Edge-source/sketch/sketch.ino) polls each sensor over I2C/analog and exposes read functions to the Linux side over Arduino_RouterBridge RPC – no display or touchscreen library is involved; the on-device UI is a Flask dashboard used for local demoing, described below. On the Linux side, KA-Edge-source/python/main.py receives readings over Bridge, computes features (RSMI, gas-index trend, light-delta), runs a validity gate for sensor-fault detection, and hands off to the trained model.
The classifier itself (source/edge_node/tiny_transformer.py) is a hand-written, dependency-free transformer: one self-attention block over four feature tokens plus a learned [CLS] token, 1,155 parameters total, trained via gradient descent (Adam) with a hand-written forward/backward pass gradient-checked against numerical differentiation (max relative error ~1e-6 across every parameter tensor). It’s exported (source/edge_node/export_transformer.py) to a pure-Python module using only bisect/math from the standard library – zero numpy/scipy at runtime – and verified to reproduce the in-memory model’s predictions exactly on 2,000 held-out rows (0 mismatches). Retraining uses scripts/build_training_set.py (raw readings → a labeled training set) and scripts/train_transformer.py (train + export); see docs/model-choice-jury-justification.md for the full methodology and why a transformer was chosen over this project’s earlier decision-tree baseline.
Local Demo: Dashboard (not the production integration)
The project’s actual production integration is the cloud sync path: HMAC-signed evidence records (sensor values, derived features, inference result, confidence, model version, timestamp) are queued locally and pushed to the Karbon-Assure ingestion API over WiFi, with automatic retry/backoff on failure – see
KA-Edge-source/python/push_to_karbon_assure.py and sync_queue.py. That’s the interface a real deployment relies on; there is no companion mobile app in this project. For local demoing and development, a Flask web dashboard (source/dashboard/) reads the same on-device data and inference results, and renders live/raw sensor charts plus the model’s flag/confidence history per pot, with selectable time ranges (5 minutes through the full trial). It’s a read-only viewer – scripts/inference_service.py computes inference in a standalone background process, the dashboard never runs the model itself – used purely to demonstrate the pipeline working end to end during development and judging.
Connection Explanation
Both BME688 units share the Qwiic I2C chain (BME688 #1 at address 0x76, BME688 #2 daisy-chained off it at 0x77 – plug-and-play 4-pin JST, no breadboard). The two BH1750 light sensors share a separate I2C bus wired on a breadboard (BH1750 #1 at 0x5C with ADDR tied to VCC, BH1750 #2 at 0x23 with ADDR tied to GND). Each capacitive soil-moisture sensor is powered at 3.3V with its SIG line wired directly to an analog pin – A1 for Pot A’s sensor, A0 for Pot B’s – with no voltage-divider network; sketch.ino’s calibration constants (SOIL_DRY_ADC=1020, SOIL_WET_ADC=350) are set against that raw, undivided 3.3V signal. The microSD card breakout is the one component on the 5V rail, wired over a dedicated SPI bus (CS/MOSI/MISO/SCK on D10–D13) independent of the Qwiic/I2C chains. Full pin-level detail is in the wiring schematic below.

Testing
The pipeline was validated end to end against real data from a controlled two-pot trial (Pot A watered normally, Pot B’s watering deliberately stopped to induce drought stress), continuously logged since 2026-08-14. The trained transformer was evaluated on a chronological holdout – trained on the earlier 80% of the trial, tested on the later 20%, so reported accuracy reflects classifying readings later than anything trained on, not interpolation between near-duplicate rows – reaching 98.9% train / 88.9% test accuracy. Software correctness is covered by an automated test suite (.venv/bin/python -m pytest source/tests and KA-Edge-source/python, 123 tests as of this writing): feature computation, the validity gate, the exported transformer’s round-trip fidelity against the in-memory model (0 mismatches on 2,000 held-out rows), the SQLite storage/retention layer, and the dashboard’s chart-decimation logic are all independently covered.
Schematics




