Files

254 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ESP32-C3 CO₂ & Environment Sensor Board
A small environmental monitoring board: **ESP32-C3** module with a **Sensirion S8** NDIR CO₂ sensor
(UART) and a **BME280** temperature / humidity / pressure sensor (I²C). Flashed with ESPHome and
integrated into Home Assistant.
**Status:** prototype v1 — designed in KiCad, fabricated at PCBWay, hand assembled, powered up and
running.
| | |
|---|---|
| Board size | 50.05 × 40.05 mm |
| Layers | 2 |
| Assembly | through-hole / pin-header plug-in modules |
| Firmware | ESPHome (see [`firmware/`](firmware/)) |
| Schematic | [`docs/esp-sensair-schematic.pdf`](docs/esp-sensair-schematic.pdf) |
| Fabrication data | Gerber X2 + Excellon + job file — attached to the [latest release](https://git.honney333.work/honney/esp_sensair/releases/latest) (`esp-sensair-v1.0.0-gerbers.zip`) |
![3D render](docs/esp-sensair-3d-top.png)
## Photos
Photos of the prototype live in [`photos/`](photos/), are embedded below, and are shown
in the [v1.0.0 release notes](https://git.honney333.work/honney/esp_sensair/releases/tag/v1.0.0).
Bare fabricated board, straight from the fab (front and back):
| | |
|---|---|
| ![Bare fabricated board, front side](photos/frontside.jpg) | ![Bare fabricated board, back side](photos/backside.jpg) |
| Front side | Back side |
Assembled and soldered (front and back):
| | |
|---|---|
| ![Assembled board, front side, soldered](photos/frontside_soldered.jpg) | ![Assembled board, back side, soldered](photos/backside_soldered.jpg) |
| Front side, soldered | Back side, soldered |
Running, with an active CO₂ measurement:
![Active CO₂ measurement](photos/active_co2_measurement.jpg)
Live values on the Home Assistant dashboard:
![Home Assistant dashboard](photos/sensor_current_homeassistant.png)
## Block diagram
```
+5 V
|
+-------------------------+
| |
| +----------v----------+
| | ESP32-C3 module |--- 3.3 V out --+
| | (onboard 3V3 LDO) | |
+--------------+ | |
| UART 9600 +----------+----------+ |
| | |
+-----v-------+ +------v-------+ v
| Sensirion S8| | BME280 |<--- I2C (SDA/SCL) -+
| NDIR CO2 | | T / RH / P |
+-------------+ +--------------+
SW1 (calibration button) -- RC (1 k / 100 n) -- Sensirion S8 bCal_in (pin 8)
5 V enters at the ESP32-C3 devkit's USB-C connector and is passed to the S8 via the 5 V net.
```
## Connections
| Signal | ESP32-C3 module | Sensirion S8 | BME280 |
|---|---|---|---|
| `+5V` | pad 11 | pad 0 (heater supply) | — |
| `GND` | pad 12 | pad 1 | pads 0, 4 |
| `+3V3` | pad 13 (module output) | — | pads 1, 5 |
| `/TX` | pad 21 | pad 6 (`UART_RxD`) | — |
| `/RX` | pad 20 | pad 5 (`UART_TxD`) | — |
| `/SDA` | pad 7 | — | pad 2 |
| `/SCL` | pad 8 | — | pad 3 |
| `bCal_in` | — | pad 8 (`bCal_in`) | — |
The calibration button SW1 is wired to the **S8's own `bCal_in` input**, not to the MCU: pulling
`bCal_in` low starts the sensor's background calibration. C1 (100 nF) and R1 (1 kΩ to GND) form the
debounce/RC network on that line.
Sensirion S8 pins `Alarm_OC`, `PWM_1kHz`, `DVCC_out` and `UART_R/T` are left unconnected.
## Bill of materials
| Ref | Part | Package | Function |
|---|---|---|---|
| U1 | ESP32-C3 Wi-Fi/BLE module (16 pads) | custom `esp_sensair_stuff:ESP32-C3` | MCU, radio, 3.3 V regulator on module |
| U2 | Sensirion S8 NDIR CO₂ sensor | custom `esp_sensair_stuff:Sensair S8` | CO₂ measurement, UART, 5 V heater |
| U3 | BME280 (plug-in breakout) | custom `esp_sensair_stuff:BME280` | temperature, relative humidity, pressure, I²C |
| SW1 | Tactile push button 6 mm | `Button_Switch_THT:SW_PUSH_6mm` | S8 manual (background) calibration trigger |
| R1 | 1 kΩ | `Resistor_THT:R_Axial_DIN0207_L6.3mm_D2.5mm_P7.62mm_Horizontal` | button debounce, low-pass to GND |
| C1 | 100 nF | `Capacitor_THT:C_Disc_D10.5mm_W5.0mm_P5.00mm` | button debounce / supply decoupling |
Machine-readable BOM: [`bom/esp-sensair-bom.csv`](bom/esp-sensair-bom.csv)
## Fabrication
Order data used for the v1 prototype:
| Parameter | Value |
|---|---|
| Fabricator | PCBWay |
| Layers | 2 |
| Board size (ordered) | 40 × 50 mm |
| Board size (design) | 40.05 × 50.05 mm |
| Board material | FR-4 |
| Board thickness | 1.6 mm |
| Finished copper weight | 1 oz (35 µm) |
| Surface finish | HASL with lead |
| Design rules used | 0.15 mm track / 0.15 mm clearance, via 0.7 mm / 0.3 mm drill |
| Drill sizes present | 0.8 mm, 1.0 mm, 1.1 mm (39 plated holes, 4 vias) |
| Non-plated holes | none |
| Solder mask / silkscreen | as ordered (see `gerber/`) |
| Stencil | not ordered (THT assembly) |
| Qty / lead time / cost | 5 pcs; 24 h build + ~2 weeks shipping; $10.36 incl. shipping & tax |
Fabrication files are **derived artifacts** and are not committed to the tree. They are
published as assets on the matching release:
- `esp-sensair-v1.0.0-gerbers.zip` — Gerber X2 plot + Excellon drill files + job file, ready to
upload (attached to the [latest release](https://git.honney333.work/honney/esp_sensair/releases/latest))
To regenerate them from the design: open `esp-sensair.kicad_pro` in KiCad, plot Gerber X2 +
drills with the project's stored export settings, and zip the result.
> **Note on the job file:** the v1 project was started from a 4-layer KiCad stackup, so
> `esp-sensair-job.gbrjob` declares `LayerNumber: 4` and labels the copper layers `L1`/`L4` even
> though only the outer layers are used. The board was fabricated correctly as a 2-layer board.
> For the next revision: set the stackup to 2 layers in *Board Setup → Stackup* before exporting,
> so the job file is self-consistent for anyone re-ordering the board.
## Assembly
All parts are through-hole or plug-in modules on pin headers; hand soldered with a soldering iron.
The Sensirion S8 requires a 5 V supply (its NDIR heater draws current pulses of up to ~150 mA) and
about 30 s of preheat time before it reports valid CO₂ values.
## Grounding and copper
Both copper layers carry a **GND copper pour** (one zone on `F.Cu` + `B.Cu`, filled, thermal-relief
connection to pads with 0.5 mm gap, 0.5 mm thermal bridge). Four rule-area keep-outs (pads and
footprints excluded, copper pour allowed) mark the module areas, including the ESP32-C3 antenna
region.
## Firmware
ESPHome on `esp32-c3-devkitm-1`, framework **esp-idf**. Config template:
[`firmware/esp-sensair.yaml.example`](firmware/esp-sensair.yaml.example), secrets template:
[`firmware/secrets.yaml.example`](firmware/secrets.yaml.example). The flashed config and
`secrets.yaml` are git-ignored — **never commit live keys.**
| Item | Value |
|---|---|
| I²C | `SDA = GPIO7`, `SCL = GPIO8`, bus scan enabled |
| BME280 | I²C address `0x76`, temperature / pressure / humidity, 60 s interval |
| Sensirion S8 | UART `TX = GPIO21`, `RX = GPIO20`, 9600 baud, 60 s interval |
| Home Assistant | native ESPHome API with noise encryption, OTA updates |
| Network | static IP, `fast_connect`, fallback hotspot + captive portal |
## Known issues and lessons learned (v1 → v2)
1. **Power comes in through the ESP32-C3 devkit's USB-C 5 V pin.** The Sensirion S8 heater draws
current pulses of up to ~150 mA, and that current travels through the module's USB connector, its
5 V rail and its onboard regulator path. v2: a **dedicated 5 V power input** (2.54 mm header,
screw terminal or DC barrel jack) wired straight to the 5 V net, independent of the module's
USB-C, which then stays a programming/debug connection. Add to that: 220–470 µF bulk plus 100 nF
at the S8 supply pins, reverse-polarity protection (series Schottky or P-MOSFET), a jumper to
back-feed 5 V from the module's USB for programming-only setups, and a power-good LED. Before
changing anything: measure the ripple and droop on the 5 V rail with an oscilloscope while the S8
heater fires, so v2 fixes a quantified problem.
2. **Single 100 nF decoupling capacitor** for the whole board. v2: 100 nF per supply pin, bulk
capacitance at each sensor, and supply stitching vias into the ground pour.
3. **No mounting holes.** The board is held by its connectors only. v2: four M2.5 holes.
4. **No I²C pull-ups populated.** The design relies on the pull-ups of the BME280 breakout.
v2: add 4.7 kΩ pull-up footprints on `SDA`/`SCL`.
5. **Schematic is not annotated** (references `U`, `U`, `U`), so KiCad BOM export is unusable.
v2: annotate, set MPNs, and export the BOM from the schematic.
6. **Leaded HASL** is not RoHS-compliant. v2: HASL lead-free, and ENIG for any impedance-controlled
design.
7. **Project name contained `+`.** It propagated into the Gerber file names
(`esp+sensair-Front.gbr`), which some fabricator upload portals and drill parsers reject.
Fixed: the project and all fabrication files are now named `esp-sensair`.
8. **Antenna keep-out** for the ESP32-C3 module is defined as a rule area (pads and footprints
excluded, copper pour allowed). v2: verify it against the module datasheet's antenna keep-out
figure, and check the pour's clearance to the antenna region specifically.
## Repository layout
```
.
├── esp-sensair.kicad_pro KiCad project
├── esp-sensair.kicad_sch schematic
├── esp-sensair.kicad_pcb board layout
├── esp_sensair_stuff.kicad_sym custom symbols (ESP32-C3, Sensirion S8, BME280)
├── esp_sensair_stuff.pretty/ custom footprints, drawn from component datasheets
├── fp-lib-table project-local footprint library table (${KIPRJMOD})
├── sym-lib-table project-local symbol library table (${KIPRJMOD})
├── bom/ bill of materials (CSV)
├── docs/ schematic PDF, layer plots, 3D render
├── firmware/ ESPHome config templates, secrets templates, notes
├── photos/ photographs of the assembled board (also release assets)
├── CHANGELOG.md release history
├── LICENSE CERN-OHL-S-2.0 (hardware)
└── LICENSE.MIT MIT (firmware config, BOM, docs)
(Fabrication outputs — Gerber X2, Excellon drills, job file, upload zip — are generated
from the design and published as release assets, not committed to the tree.)
```
## Rebuilding the board
1. Open `esp-sensair.kicad_pro` in KiCad 10.
2. Run DRC; fix anything you change.
3. Export Gerbers/drill (the settings are stored in the project) and upload the resulting zip to
your fabricator with the parameters from the fabrication table.
4. Order parts per BOM, solder through-hole parts and pin headers.
5. Flash the ESPHome configuration and add the device to Home Assistant.
## License
Dual licensed, SPDX identifiers:
| Scope | Licence | File |
|---|---|---|
| Hardware: schematic, layout, symbols, footprints, fabrication data | **CERN-OHL-S-2.0** (CERN Open Hardware Licence v2, strongly reciprocal) | [`LICENSE`](LICENSE) |
| Firmware configuration, BOM, scripts, documentation | **MIT** | [`LICENSE.MIT`](LICENSE.MIT) |
SPDX notices to keep in derived files:
```
SPDX-License-Identifier: CERN-OHL-S-2.0
SPDX-FileCopyrightText: 2026 Hannes Gehrmann
```
Making a board from these files and distributing it is explicitly allowed; if you modify the design
and convey it, CERN-OHL-S requires you to publish the modified design under the same licence and to
mark what you changed (sections 3.2 and 3.3).
## Acknowledgements
The hardware design — schematic, layout, symbols, footprints, fabrication data — and the firmware
configuration are by **Hannes Gehrmann**.
The documentation and repository tooling in the v1.0.0 release — this README, `CHANGELOG.md`, the
project-local library tables (`fp-lib-table`, `sym-lib-table`), the `.gitignore`, and the release
packaging (project rename, moving fabrication outputs to release assets) — were drafted with
**OpenCode** running the **Qwen3.8 Flash Next** model, and reviewed by the maintainer.