{"id":51634924,"url":"https://github.com/ManfredFr/CO2-Sensor-Zigbee-Rust","last_synced_at":"2026-08-01T16:00:43.546Z","repository":{"id":369436425,"uuid":"1289576464","full_name":"ManfredFr/CO2-Sensor-Zigbee-Rust","owner":"ManfredFr","description":"Zigbee CO2 sensor in Rust (v2.0) — ESP32-H2 + Senseair S8, Home Assistant via Zigbee2MQTT, Espressif Zigbee SDK driven through esp-idf-sys FFI","archived":false,"fork":false,"pushed_at":"2026-07-05T09:33:45.000Z","size":258,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-05T11:11:44.477Z","etag":null,"topics":["co2-sensor","esp-idf","esp32-h2","home-assistant","rust","zigbee","zigbee2mqtt"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ManfredFr.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-04T23:49:37.000Z","updated_at":"2026-07-05T09:33:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ManfredFr/CO2-Sensor-Zigbee-Rust","commit_stats":null,"previous_names":["manfredfr/co2-sensor-zigbee-rust"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/ManfredFr/CO2-Sensor-Zigbee-Rust","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManfredFr%2FCO2-Sensor-Zigbee-Rust","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManfredFr%2FCO2-Sensor-Zigbee-Rust/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManfredFr%2FCO2-Sensor-Zigbee-Rust/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManfredFr%2FCO2-Sensor-Zigbee-Rust/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ManfredFr","download_url":"https://codeload.github.com/ManfredFr/CO2-Sensor-Zigbee-Rust/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ManfredFr%2FCO2-Sensor-Zigbee-Rust/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":36161704,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-08-01T02:00:05.789Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["co2-sensor","esp-idf","esp32-h2","home-assistant","rust","zigbee","zigbee2mqtt"],"created_at":"2026-07-13T13:00:40.280Z","updated_at":"2026-08-01T16:00:43.539Z","avatar_url":"https://github.com/ManfredFr.png","language":"Rust","funding_links":[],"categories":["Projects"],"sub_categories":["`std`"],"readme":"# ESP32-H2 Zigbee CO2 Sensor (Rust)\n\nA battery-free, WiFi-free indoor air-quality sensor: an **ESP32-H2** reads CO2 from a **Senseair S8** and reports it over **Zigbee** into Home Assistant via Zigbee2MQTT. The onboard RGB LED gives an at-a-glance air-quality indication. Firmware is written in **Rust** (std/ESP-IDF with Espressif's Zigbee SDK via FFI).\n\n## Features\n- **CO2 measurement** every 10–300 s (interval adjustable live from Home Assistant) using the S8's Modbus RTU interface\n- **Zigbee End Device** — no WiFi credentials, no MQTT config on the device; it joins the Zigbee mesh like any commercial sensor\n- **LED air-quality indicator** — brightness adjustable from HA, 0 % = off\n- **Settings survive in HA**: report interval and LED brightness are Zigbee attributes, exposed as number sliders in Home Assistant\n\n### LED colors\n\n| CO2 | LED |\n|---|---|\n| ≤ 1000 ppm | green — good |\n| 1001–2000 ppm | orange — fair/poor |\n| 2001–5000 ppm | red — bad |\n| \u003e 5000 ppm | flashing red — dangerous |\n\n## How it works\n\nThe device exposes three Zigbee endpoints, decoded by the external converter [`Co2-Sensor.js`](Co2-Sensor.js):\n\n| EP | ZCL Cluster | Purpose |\n|---|---|---|\n| 1 | Temperature Measurement | **CO2 carrier**: ppm is stored as ppm/100 °C, so the ZCL INT16 `measuredValue` equals ppm directly. (The standard CO2 cluster isn't reportable in the SDK's attribute tables; temperature is.) |\n| 2 | Analog Output | Report interval in seconds, read/write |\n| 3 | Analog Output | LED brightness 0–100 %, read/write |\n\nCO2 reporting uses ZCL *configured reporting*: on join, the converter's `configure` step binds endpoint 1 to the coordinator and configures reporting; from then on the firmware only updates the attribute value and the stack reports changes automatically.\n\n## Hardware\n\n| Component | Details |\n|---|---|\n| Microcontroller | ESP32-H2-DevKit-N4 (4 MB flash, USB-C) |\n| CO2 sensor | Senseair S8 004-0-0053 (10 000 ppm, Modbus RTU @ 9600 8N1) |\n| Zigbee coordinator | Sonoff Zigbee 3.0 USB Dongle Plus |\n| Integration | Zigbee2MQTT + Home Assistant |\n\n## Wiring\n\nOnly four wires are needed, all on the **left header (J1)** of the DevKit:\n\n| S8 Pin | ESP32-H2 Pin | J1 position | Purpose |\n|---|---|---|---|\n| G+ | 5V0 | pin 14 | Power (S8 needs 4.5–5.25 V) |\n| G0 | GND | pin 13 | Ground |\n| UART_TxD | GPIO4 | pin 9 | Sensor → ESP data |\n| UART_RxD | GPIO5 | pin 10 | ESP → Sensor data |\n\n\u003e The S8 is powered from 5 V but its UART is 3.3 V logic — it connects to the ESP32-H2 directly, **no level shifter needed**.\n\u003e\n\u003e The RGB status LED is the onboard WS2812 on **GPIO8** (this board uses RGB byte order, not the usual GRB) — no wiring required.\n\n### ESP32-H2-DevKit-N4 pinout\n\n![ESP32-H2 DevKit N4 pinout](Specs/ESP32-H2-DEV-KIT-N4-pinout.webp)\n\n### Senseair S8 pinout\n\n![Senseair S8 pinout](Specs/Senseair-S8-Pinout-Diagram.jpg)\n\nThe S8 talks Modbus RTU; the firmware polls input register 3 (CO2 ppm) with the request `FE 04 00 03 00 01 D5 C5` and parses the 7-byte response.\n\n## Firmware\n\nThe firmware (**v2.0**) lives in [`Rust/`](Rust/) — see [`Rust/README.md`](Rust/README.md) for toolchain setup, build \u0026 flash instructions, serial monitoring, and the hard-won ESP32-H2 lessons (esp-idf-hal bugs, linker workarounds, Zigbee stack pitfalls).\n\n| Component | Version |\n|---|---|\n| Rust | nightly, std on `riscv32imac-esp-espidf` |\n| esp-idf-svc | 0.51 |\n| ESP-IDF | v5.3.3 |\n| esp-zigbee-lib / esp-zboss-lib | 1.6 (ESP-IDF remote components, FFI via bindgen) |\n| Zigbee2MQTT | via Home Assistant add-on |\n\n\u003e The original Arduino-based firmware (v1.3) was removed after the Rust port was verified end-to-end; it remains available in git history.\n\n## Zigbee2MQTT setup\n\n1. Copy `Co2-Sensor.js` into the Zigbee2MQTT external converters folder (`config/zigbee2mqtt/external_converters/` or add via the Z2M frontend under Settings → External converters).\n2. Enable **Permit join** and power the device. It joins, the interview discovers the three endpoints, and the converter exposes: `co2` (ppm), `report_interval` (s), `led_brightness` (%), `firmware_version`.\n3. Optional: give it a friendly name and a custom icon (below).\n\n## Project Files\n\n| File | Description |\n|---|---|\n| `Rust/` | Rust firmware (current) |\n| `Co2-Sensor.js` | Zigbee2MQTT external converter |\n| `Specs/` | Pinout diagrams (ESP32-H2 DevKit, Senseair S8) |\n| `Logo/` | Device icon for Zigbee2MQTT |\n| `CHANGELOG.md` | Version history and key lessons learned |\n\n## Adding a Custom Device Icon in Zigbee2MQTT\n\nTo display a custom logo or image for your device in the Zigbee2MQTT frontend:\n\n**1. Prepare the Image Directory**\n\n* Navigate to your Zigbee2MQTT configuration folder (typically `config/zigbee2mqtt/`).\n* Create a new folder named `device_icons` (if it does not already exist).\n* Place your custom image file (e.g., `co2-sensor.png`) inside this folder. The recommended format is a square PNG.\n\n**2. Update the Configuration**\n\n* Open your `zigbee2mqtt/configuration.yaml` file.\n* Locate your device under the `devices:` section using its IEEE address.\n* Add the `icon` property. **Important:** The path must explicitly start with `device_icons/` followed by your filename.\n\n**Example `configuration.yaml` entry:**\n\n```yaml\ndevices:\n  '0xYOUR_DEVICE_IEEE_ADDRESS':\n    friendly_name: Your_Device_Name\n    icon: device_icons/co2-sensor.png\n```\n\n**3. Apply Changes**\n\n* Restart the Zigbee2MQTT add-on to apply the configuration. Your custom icon will now appear in the Z2M dashboard.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FManfredFr%2FCO2-Sensor-Zigbee-Rust","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FManfredFr%2FCO2-Sensor-Zigbee-Rust","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FManfredFr%2FCO2-Sensor-Zigbee-Rust/lists"}