Add bit-probe results, decimal point/percentage bit map, and working temperature+humidity display
- bitprobe.c: interactive single-bit probe that discovered the display glyph layout and the decimal point / percent sign bit positions - temphummon.c: render top line '188.8°C' and bottom '88.8%' using the verified decimal points (f[4], f[8] bit5) and percent sign (f[10] bit5) - PROJECT_MEMORY.md: document full frame layout + empirical bit map - README: add wiring table and project memory pointer - add Waveshare reference zip and flake.lock
This commit is contained in:
parent
ffe390c782
commit
ebbeffa432
8 changed files with 809 additions and 4 deletions
292
PROJECT_MEMORY.md
Normal file
292
PROJECT_MEMORY.md
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
# Project Memory — temphummon
|
||||
|
||||
Temperature and humidity monitor using Matter over Thread.
|
||||
Based on the **ESP32-H2 Zero** and **Waveshare 1.9" Segment E-Paper** display.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Item | Value |
|
||||
|---|---|
|
||||
| MCU | ESP32-H2 (RISC-V, IEEE 802.15.4 / Thread) |
|
||||
| IDF version | v6.0.1 |
|
||||
| Chip target | `esp32h2` |
|
||||
| Build tool | `idf.py` (via Nix dev shell) |
|
||||
| Main source | `firmware/main/temphummon.c` |
|
||||
| sdkconfig | `firmware/sdkconfig` |
|
||||
| Datasheets | `datasheets/` |
|
||||
| CAD files | `cad/` |
|
||||
| BOM | See `README.md` |
|
||||
| Reference code | `E-Paper-Segment-Code.zip` (Waveshare, in project root) |
|
||||
|
||||
---
|
||||
|
||||
## Wiring (ESP32-H2-Zero ↔ E-Paper Module)
|
||||
|
||||
Fixed and tested — display does NOT overheat with correct wiring.
|
||||
|
||||
| ESP32-H2-Zero | E-Paper Module | Wire color |
|
||||
|:---|:---|:---:|
|
||||
| GPIO4 | SDA | 🟢 Green |
|
||||
| GPIO5 | SCL | 🟡 Yellow |
|
||||
| GPIO10 | RST | 🟠 Orange |
|
||||
| GPIO11 | BUSY | 🔵 Blue |
|
||||
| 3V3 | VCC | 🔴 Red |
|
||||
| GND | GND | ⚫ Black |
|
||||
|
||||
**Note:** The module version has its own level shifters and LDO — safe to connect directly. The bare panel (without PCB) has different pinout.
|
||||
|
||||
---
|
||||
|
||||
## Important: Display Interface = I²C, not SPI
|
||||
|
||||
The IST7134 driver IC on the 1.9" Segment E-Paper uses **I²C** communication, NOT SPI. The Waveshare reference code confirms this.
|
||||
|
||||
### I²C Protocol Details
|
||||
|
||||
| Parameter | Value |
|
||||
|:---|:---:|
|
||||
| Command address | `0x3C` (A0=0, 7-bit) |
|
||||
| Data address | `0x3D` (A0=1, 7-bit) |
|
||||
| Speed | 100 kHz |
|
||||
| Pull-ups | Internal (enabled in firmware) |
|
||||
|
||||
The A0 bit is encoded in the I²C address itself: `0x3C` for commands, `0x3D` for data. This is handled by using two separate `i2c_master_dev_handle_t` handles in the firmware.
|
||||
|
||||
### Display Data Format
|
||||
|
||||
- 15 bytes per frame
|
||||
- 91 segment outputs + 2 background + 1 VCOM (94 bits ≈ 12 bytes, padded to 15)
|
||||
- Digit patterns (0-9) from Waveshare reference code
|
||||
- Two SRAM banks used: first for image data, second for control
|
||||
|
||||
### Frame Layout (EMPIRICALLY VERIFIED with bit-probe)
|
||||
|
||||
The digits are **dot-matrix style** (each bit = one dot / short stroke of a column),
|
||||
NOT classic one-bit-per-7-segment. Each digit glyph maps to a **byte pair**. The two
|
||||
lines interleave: top-tens, top-ones, bottom-tens, bottom-ones, bottom-tenths, top-tenths.
|
||||
|
||||
| Frame byte(s) | Display location | Position label |
|
||||
|:---|:---|:---|
|
||||
| `f[0]` bits 0-4 | Top line, **leading "1"** (hundreds) | — |
|
||||
| `f[1], f[2]` | Top line, **tens digit** | pos 1 |
|
||||
| `f[3], f[4]` | Top line, **ones digit** | pos 2 |
|
||||
| `f[5], f[6]` | **Bottom** line, **tens digit** | pos 3 |
|
||||
| `f[7], f[8]` | Bottom line, **ones digit** | pos 4 |
|
||||
| `f[9], f[10]` | Bottom line, **tenths digit** | pos 5 |
|
||||
| `f[11], f[12]` | Top line, **tenths digit** | pos 6 |
|
||||
| `f[13]` | °C / °F letter (0x05 = °C) | — |
|
||||
| `f[14]` | Padding / unknown | — |
|
||||
|
||||
**→ Top line renders as `[1][tens][ones].[tenths] °C`** e.g. `188.8 °C`
|
||||
**→ Bottom line renders as `[tens][ones].[tenths] %`** e.g. `88.8 %`
|
||||
|
||||
### Decimal point & % (THE missing piece — now found! 🎯)
|
||||
|
||||
All three are **bit 5 (0x20) of the SECOND byte of the pair**, matching the manual:
|
||||
"decimal point and % are the 5th place in the 4th, 8th, and 10th positions."
|
||||
|
||||
| Symbol | Frame byte | Bit | Where it appears |
|
||||
|:---|:---|:---|:---|
|
||||
| **Decimal point (top)** | `f[4]` | bit 5 (0x20) | after top-line **ones** → `188.8` |
|
||||
| **Decimal point (bottom)** | `f[8]` | bit 5 (0x20) | after bottom-line **ones** → `88.8` |
|
||||
| **% sign (humidity)** | `f[10]` | bit 5 (0x20) | after bottom-line **tenths** → `88.8%` |
|
||||
|
||||
### Verified glyph segment map (from probing)
|
||||
|
||||
For a digit byte-pair, the **first byte** = left column + horizontal bars, the
|
||||
**second byte** = right column (bits 0-3) + decimal/control (bit 5+).
|
||||
Example (pos 1, top tens): byte1 bit5 = top bar, bit6 = middle bar, bit7 = bottom
|
||||
bar; byte2 bits 0-3 = right-side dots/bars.
|
||||
|
||||
⚠️ **Control bits** (probed): second-byte bit 4 of some pairs and byte13 bit1
|
||||
flip the whole panel BLACK and wedge the driver until a hard re-init (RST pulse).
|
||||
Avoid setting them, or re-init after. (bitprobe had to power-cycle the board.)
|
||||
|
||||
### Initialization Sequence
|
||||
|
||||
```
|
||||
1. Hardware reset (RST pin: high → low → high with delays)
|
||||
2. POWER_ON command (0x2B)
|
||||
3. Boost + TSON (0xA7, 0xE0)
|
||||
4. Temperature compensation based on ambient temp
|
||||
```
|
||||
|
||||
### Display Refresh Sequence
|
||||
|
||||
```
|
||||
1. Wake from sleep (0xAC)
|
||||
2. Power on (0x2B)
|
||||
3. Set RAM address (0x40)
|
||||
4. Open first SRAM (0xA9), close (0xA8)
|
||||
5. Write 15 bytes of image data
|
||||
6. Write extra byte (0x00 or 0x03)
|
||||
7. Open second SRAM (0xAB), close (0xAA)
|
||||
8. Display on (0xAF)
|
||||
9. Wait for BUSY pin
|
||||
10. Display off (0xAE), HV off (0x28), sleep (0xAD)
|
||||
```
|
||||
|
||||
### Waveform LUTs
|
||||
|
||||
| Function | Bytes sent | Use case |
|
||||
|:---|:---|:---|
|
||||
| `epd_lut_DU_WB()` | `82 80 00 C0 80 80 62` | Partial update (white extinction + black out) |
|
||||
| `epd_lut_GC()` | `82 20 00 A0 80 40 63` | Full global refresh |
|
||||
| `epd_lut_5S()` | `82 28 20 A8 A0 50 65` | Boot waveform (better ghosting) |
|
||||
|
||||
---
|
||||
|
||||
## Nix Development Shell
|
||||
|
||||
The project uses **Nix flakes** to provision the ESP-IDF toolchain.
|
||||
|
||||
### How to enter the dev shell
|
||||
|
||||
```bash
|
||||
export PATH="/root/.nix-profile/bin:$PATH"
|
||||
cd /src && nix develop --command bash
|
||||
```
|
||||
|
||||
### One-off commands (no interactive shell needed)
|
||||
|
||||
```bash
|
||||
cd /src/firmware && nix develop /src --no-write-lock-file --command idf.py build
|
||||
```
|
||||
|
||||
### Nix details
|
||||
|
||||
- **Flake**: `/src/flake.nix`
|
||||
- **Lock**: `/src/flake.lock`
|
||||
- **Nix binary**: `/root/.nix-profile/bin/nix` (also at `/nix/store/irfrbndi76zhkvqsfhmsn4a99iafck29-nix-2.35.2/bin/nix`)
|
||||
- **Nix config**: `/etc/nix/nix.conf` (has `experimental-features = nix-command flakes`)
|
||||
- **Dev shell inputs**: `esp-idf`, `git`, `minicom`, `usbutils`, `gawk`, `coreutils`, `cmake`, `ninja`
|
||||
|
||||
### ⚠️ Note: Custom nixpkgs-esp-dev fork
|
||||
|
||||
The flake uses a **custom fork** of `nixpkgs-esp-dev`:
|
||||
```
|
||||
nixpkgs-esp-dev.url = "github:dvdvgt/nixpkgs-esp-dev/update-v6.0.1";
|
||||
```
|
||||
|
||||
This fork has **few/no cached binary substitutes**, so `nix develop` compiles the entire ESP-IDF toolchain from source. This takes **a long time** on first run but we're keeping it for now since mainline `mirrexagon/nixpkgs-esp-dev` doesn't support ESP-IDF v6.0.1 yet.
|
||||
|
||||
---
|
||||
|
||||
## Build State
|
||||
|
||||
### ✅ Successful build
|
||||
|
||||
The firmware compiles and links successfully for ESP32-H2 (RISC-V target). Size: `0x2b1b0` bytes (83% free).
|
||||
|
||||
### Firmware behavior
|
||||
|
||||
Current firmware (`main/temphummon.c`):
|
||||
- Initializes GPIOs (RST output, BUSY input) and I²C bus
|
||||
- Initializes display (power on, boost, temperature compensation)
|
||||
- Clears display (full refresh cycle)
|
||||
- Reads internal CPU temperature, renders on top line as `188.8°C` with
|
||||
correct **decimal point** (`f[4]` bit5) and leading "1" (`f[0]`)
|
||||
- Renders fixed humidity sample on bottom line as `88.8%` (tens/ones/tenths
|
||||
+ decimal `f[8]` bit5 + **percent sign** `f[10]` bit5), updates every 1s
|
||||
- Prints status via ESP_LOG
|
||||
|
||||
### ✅ Hardware bringup (confirmed working)
|
||||
|
||||
- **ESP32-H2 Zero** detected as rev v1.2 at 96 MHz
|
||||
- **Flashing** via USB-Serial/JTAG works at 460800 baud
|
||||
- **Display driver** (IST7134 via I²C at 0x3C/0x3D) works:
|
||||
- I²C communication succeeds, BUSY handshake works
|
||||
- Display clears properly
|
||||
- Digits 0–9 show correctly in sequence
|
||||
- All-black font pattern renders
|
||||
- Deep sleep mode works without errors
|
||||
- **No hardware issues** — wiring, level shifting, LDO all correct
|
||||
|
||||
### Flashing (requires serial connected)
|
||||
|
||||
```bash
|
||||
cd /src/firmware && nix develop /src --no-write-lock-file --command idf.py -p <PORT> flash monitor
|
||||
```
|
||||
|
||||
Or via the flake app:
|
||||
```bash
|
||||
nix run .#flash
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hardware Info
|
||||
|
||||
### ESP32-H2 Zero (Waveshare)
|
||||
|
||||
- **SoC**: ESP32-H2 (single-core RISC-V 32-bit, up to 96 MHz)
|
||||
- **Radio**: IEEE 802.15.4 (Thread, Zigbee) + Bluetooth 5 (LE)
|
||||
- **Flash**: 4 MB
|
||||
- **PSRAM**: Not present on this board
|
||||
- **Schematic**: `datasheets/ESP32-H2-Zero-Schematic.pdf`
|
||||
- **Datasheet**: `datasheets/Esp32-h2_datasheet_en.pdf`
|
||||
- **TRM**: `datasheets/Esp32-h2_technical_reference_manual_en.pdf`
|
||||
- **Wiki**: https://docs.waveshare.com/ESP32-H2-Zero
|
||||
|
||||
### 1.9" Segment E-Paper Module (Waveshare)
|
||||
|
||||
- **Driver IC**: IST7134 (datasheet: `datasheets/IST7134.pdf`)
|
||||
- **Interface**: I²C
|
||||
- **Module schematic**: `datasheets/1.9inch_Segment_e-Paper_Module01.pdf`
|
||||
- **Display manual**: `datasheets/1.9inch Segment e-Paper V1.1.pdf`
|
||||
- **Module wiki**: https://www.waveshare.com/wiki/1.9inch_Segment_e-Paper_Module_Manual
|
||||
- **Reference code**: `E-Paper-Segment-Code.zip` (in project root)
|
||||
|
||||
#### Module board features
|
||||
|
||||
- **NDC7002N** dual N-channel MOSFETs for level shifting
|
||||
- **RT9193-33** LDO (3.3V regulator)
|
||||
- **AO3401** P-MOSFET power switch (on by default without external EN)
|
||||
- Two **6-pin male headers**: SDA, SCL, RST, BUSY, VCC, GND
|
||||
- Supports 3.3V or 5V input
|
||||
|
||||
### Other components
|
||||
|
||||
- **WS2812B RGB LED**: `datasheets/XL-0807RGBC-WS2812B.pdf` (on-board, on GPIO8)
|
||||
|
||||
---
|
||||
|
||||
## Next Steps (TODO)
|
||||
|
||||
- [x] **Board bringup** — flashed and tested the display driver on real hardware
|
||||
- [x] **Port Waveshare digit lookup table** — all 10 digits (0–9) verified working on the display
|
||||
- [x] **Internal temperature sensor** — reads ESP32-H2 internal temp sensor, shows on display line 1, updates every 1s
|
||||
- [x] **Find decimal point & % bits** — probing session mapped them: `f[4]/f[8]` bit5 = decimals, `f[10]` bit5 = %
|
||||
- [ ] **Add humidity sensor** — wire up an external I²C sensor (SHT40, BME280, etc.) and show on display line 2
|
||||
- [ ] **Configure Matter/Thread** — enable OpenThread / Matter components in `sdkconfig`
|
||||
- [ ] **Implement Matter cluster** — temperature and humidity measurement clusters
|
||||
- [ ] **Implement low-power operation** — deep sleep between measurements
|
||||
|
||||
---
|
||||
|
||||
## Flake Nix Apps
|
||||
|
||||
The flake defines these convenience apps (`nix run .#<name>`):
|
||||
|
||||
| App | Description |
|
||||
|---|---|
|
||||
| `build` | Build firmware (`idf.py build`) |
|
||||
| `flash` | Flash to auto-detected serial port |
|
||||
| `monitor` | Open serial monitor |
|
||||
| `menuconfig` | Open `idf.py menuconfig` |
|
||||
| `clean` | `idf.py clean` |
|
||||
| `full` | Build → Flash → Monitor pipeline |
|
||||
| `run-agent` | Run pi agent container |
|
||||
| `build-agent` | Build pi agent container image |
|
||||
|
||||
---
|
||||
|
||||
## Environment Notes
|
||||
|
||||
- Container: Debian-based (from `Containerfile`), runs as root
|
||||
- Working directory: `/src` (mounted volume)
|
||||
- Nix is installed but NOT in default PATH — must use `/root/.nix-profile/bin/nix`
|
||||
- `.envrc` contains `use flake` (for direnv, not active in this environment)
|
||||
- `direnv` state: `.direnv/` directory exists
|
||||
Loading…
Reference in a new issue