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
BIN
E-Paper-Segment-Code.zip
Normal file
BIN
E-Paper-Segment-Code.zip
Normal file
Binary file not shown.
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
|
||||||
14
README.md
14
README.md
|
|
@ -12,3 +12,17 @@ Based on the ESP32-H2 and Waveshare Segment E-Paper display.
|
||||||
| [1.9inch Segment E-Paper Module](https://www.waveshare.com/product/displays/e-paper/1.9inch-segment-e-paper-module.htm) | 9.47 € | [wiki](https://www.waveshare.com/wiki/1.9inch_Segment_e-Paper_Module_Manual) |
|
| [1.9inch Segment E-Paper Module](https://www.waveshare.com/product/displays/e-paper/1.9inch-segment-e-paper-module.htm) | 9.47 € | [wiki](https://www.waveshare.com/wiki/1.9inch_Segment_e-Paper_Module_Manual) |
|
||||||
|
|
||||||
Matter over Thread Temperature and humidity monitor
|
Matter over Thread Temperature and humidity monitor
|
||||||
|
|
||||||
|
## 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 |
|
||||||
|
|
||||||
|
> 📖 **Project Memory**: See [`PROJECT_MEMORY.md`](PROJECT_MEMORY.md) for environment setup,
|
||||||
|
> Nix dev shell usage, build status, hardware details, and next steps.
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,2 +1,3 @@
|
||||||
idf_component_register(SRCS "temphummon.c"
|
idf_component_register(SRCS "temphummon.c"
|
||||||
INCLUDE_DIRS ".")
|
INCLUDE_DIRS "."
|
||||||
|
PRIV_REQUIRES esp_driver_tsens esp_driver_i2c esp_driver_gpio)
|
||||||
|
|
|
||||||
226
firmware/main/bitprobe.c
Normal file
226
firmware/main/bitprobe.c
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
/**
|
||||||
|
* dpfinder — ESP32-H2 Zero + 1.9" Segment E-Paper (IST7134 via I²C)
|
||||||
|
*
|
||||||
|
* Targeted decimal-point / % tester, guided by the manual:
|
||||||
|
* "The decimal point and % are the 5th place in the 4th, 8th, and 10th
|
||||||
|
* positions respectively." (positions = vertical columns, L→R, T→B)
|
||||||
|
*
|
||||||
|
* We don't trust our model for exactly which BYTE is column 4/8/10, so we
|
||||||
|
* just test bit-5 (0x20) at a handful of candidate bytes on a clear display
|
||||||
|
* (a few "8" digits for context) and the human reports which shows a decimal
|
||||||
|
* point or %.
|
||||||
|
*
|
||||||
|
* Wiring (GPIO4→SDA, GPIO5→SCL, GPIO10→RST, GPIO11→BUSY, 3V3→VCC, GND→GND)
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
|
||||||
|
#define PIN_SDA GPIO_NUM_4
|
||||||
|
#define PIN_SCL GPIO_NUM_5
|
||||||
|
#define PIN_RST GPIO_NUM_10
|
||||||
|
#define PIN_BUSY GPIO_NUM_11
|
||||||
|
|
||||||
|
#define PIN_BOOT GPIO_NUM_9
|
||||||
|
|
||||||
|
#define FRAME_LEN 15
|
||||||
|
|
||||||
|
#define ADVANCE_MODE 1
|
||||||
|
#define AUTO_HOLD_MS 3000
|
||||||
|
#define BOOT_TIMEOUT_MS 0
|
||||||
|
|
||||||
|
/* Digit "1" = minimal glyph (0x1f sets bits 0-4 only; bit 5 stays DARK so
|
||||||
|
* an added bit-5 decimal point is clearly visible). */
|
||||||
|
static const uint8_t DIG1[2] = {0x1f, 0x00};
|
||||||
|
|
||||||
|
/* The 6 digit positions (byte pair). */
|
||||||
|
static const uint8_t POS[6][2] = {
|
||||||
|
{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12},
|
||||||
|
};
|
||||||
|
|
||||||
|
static i2c_master_bus_handle_t bus;
|
||||||
|
static i2c_master_dev_handle_t cmd_dev, dat_dev;
|
||||||
|
|
||||||
|
static void epd_reset(void)
|
||||||
|
{
|
||||||
|
gpio_set_level(PIN_RST, 1); vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
gpio_set_level(PIN_RST, 0); vTaskDelay(pdMS_TO_TICKS(20));
|
||||||
|
gpio_set_level(PIN_RST, 1); vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void epd_wait_busy(void)
|
||||||
|
{
|
||||||
|
while (gpio_get_level(PIN_BUSY) == 0) vTaskDelay(pdMS_TO_TICKS(1));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void epd_lut_GC(void)
|
||||||
|
{
|
||||||
|
uint8_t lut[] = {0x82, 0x20, 0x00, 0xA0, 0x80, 0x40, 0x63};
|
||||||
|
i2c_master_transmit(cmd_dev, lut, sizeof(lut), pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
static void epd_lut_DU_WB(void)
|
||||||
|
{
|
||||||
|
uint8_t lut[] = {0x82, 0x80, 0x00, 0xC0, 0x80, 0x80, 0x62};
|
||||||
|
i2c_master_transmit(cmd_dev, lut, sizeof(lut), pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void epd_temperature(void)
|
||||||
|
{
|
||||||
|
uint8_t a[] = {0x7E, 0x81, 0xB4};
|
||||||
|
i2c_master_transmit(cmd_dev, a, sizeof(a), pdMS_TO_TICKS(100));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
uint8_t b[] = {0xE7, 0x0E};
|
||||||
|
i2c_master_transmit(cmd_dev, b, sizeof(b), pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void epd_init(void)
|
||||||
|
{
|
||||||
|
epd_reset();
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
uint8_t p[] = {0x2B, 0xA7, 0xE0};
|
||||||
|
i2c_master_transmit(cmd_dev, p, sizeof(p), pdMS_TO_TICKS(100));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
epd_temperature();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void epd_write(const uint8_t *frame, uint8_t extra_byte)
|
||||||
|
{
|
||||||
|
uint8_t w[] = {0xAC, 0x2B, 0x40, 0xA9, 0xA8};
|
||||||
|
i2c_master_transmit(cmd_dev, w, sizeof(w), pdMS_TO_TICKS(100));
|
||||||
|
i2c_master_transmit(dat_dev, frame, FRAME_LEN, pdMS_TO_TICKS(100));
|
||||||
|
i2c_master_transmit(dat_dev, &extra_byte, 1, pdMS_TO_TICKS(100));
|
||||||
|
uint8_t d[] = {0xAB, 0xAA, 0xAF};
|
||||||
|
i2c_master_transmit(cmd_dev, d, sizeof(d), pdMS_TO_TICKS(100));
|
||||||
|
epd_wait_busy();
|
||||||
|
uint8_t s[] = {0xAE, 0x28, 0xAD};
|
||||||
|
i2c_master_transmit(cmd_dev, s, sizeof(s), pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show a readout of "1"s (one in each position) + one extra bit.
|
||||||
|
* Base "1" leaves bit5 dark, so a decimal/% bit at bit5 is clearly seen. */
|
||||||
|
static void show_1s_plus(uint8_t frame_byte, uint8_t phys_bit, int tag)
|
||||||
|
{
|
||||||
|
uint8_t f[FRAME_LEN];
|
||||||
|
memset(f, 0, FRAME_LEN);
|
||||||
|
for (int p = 0; p < 6; p++) {
|
||||||
|
f[POS[p][0]] = DIG1[0];
|
||||||
|
f[POS[p][1]] = DIG1[1];
|
||||||
|
}
|
||||||
|
f[frame_byte] |= (uint8_t)(1u << phys_bit);
|
||||||
|
ESP_LOGI("dpfinder", "TEST %d: byte %d bit %d", tag, frame_byte, phys_bit);
|
||||||
|
|
||||||
|
uint8_t off[FRAME_LEN];
|
||||||
|
memset(off, 0, FRAME_LEN);
|
||||||
|
epd_lut_GC();
|
||||||
|
epd_write(off, 0x00);
|
||||||
|
epd_lut_DU_WB();
|
||||||
|
epd_write(f, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
static void boot_init(void)
|
||||||
|
{
|
||||||
|
gpio_config_t bc = {
|
||||||
|
.pin_bit_mask = (1ULL << PIN_BOOT),
|
||||||
|
.mode = GPIO_MODE_INPUT,
|
||||||
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||||
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
|
.intr_type = GPIO_INTR_DISABLE,
|
||||||
|
};
|
||||||
|
gpio_config(&bc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wait_for_advance(void)
|
||||||
|
{
|
||||||
|
if (ADVANCE_MODE == 0) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(AUTO_HOLD_MS));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGI("dpfinder", "press BOOT to advance next...");
|
||||||
|
int64_t t_start = esp_timer_get_time();
|
||||||
|
for (;;) {
|
||||||
|
if (gpio_get_level(PIN_BOOT) == 0) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(30));
|
||||||
|
while (gpio_get_level(PIN_BOOT) == 0) vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(30));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (BOOT_TIMEOUT_MS > 0 &&
|
||||||
|
esp_timer_get_time() - t_start > BOOT_TIMEOUT_MS * 1000LL) {
|
||||||
|
ESP_LOGW("dpfinder", "no BOOT press in %d ms, auto-advancing", BOOT_TIMEOUT_MS);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(20));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
i2c_master_bus_config_t bc = {
|
||||||
|
.i2c_port = -1, .sda_io_num = PIN_SDA, .scl_io_num = PIN_SCL,
|
||||||
|
.clk_source = I2C_CLK_SRC_DEFAULT, .glitch_ignore_cnt = 7,
|
||||||
|
.flags.enable_internal_pullup = 1,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(i2c_new_master_bus(&bc, &bus));
|
||||||
|
i2c_device_config_t cc = {
|
||||||
|
.dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = 0x3C, .scl_speed_hz = 100000,
|
||||||
|
};
|
||||||
|
i2c_device_config_t dc = {
|
||||||
|
.dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = 0x3D, .scl_speed_hz = 100000,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus, &cc, &cmd_dev));
|
||||||
|
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus, &dc, &dat_dev));
|
||||||
|
|
||||||
|
gpio_config_t rc = {
|
||||||
|
.pin_bit_mask = (1ULL << PIN_RST), .mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
|
.intr_type = GPIO_INTR_DISABLE,
|
||||||
|
};
|
||||||
|
gpio_config(&rc); gpio_set_level(PIN_RST, 1);
|
||||||
|
gpio_config_t busy_cfg = {
|
||||||
|
.pin_bit_mask = (1ULL << PIN_BUSY), .mode = GPIO_MODE_INPUT,
|
||||||
|
.pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
|
.intr_type = GPIO_INTR_DISABLE,
|
||||||
|
};
|
||||||
|
gpio_config(&busy_cfg);
|
||||||
|
|
||||||
|
boot_init();
|
||||||
|
epd_init();
|
||||||
|
|
||||||
|
ESP_LOGI("dpfinder", "== Test decimal/% at bit5 (0x20) of candidate bytes ==");
|
||||||
|
|
||||||
|
/* Phase 0: plain all 1s, no extra bit (reference). */
|
||||||
|
{
|
||||||
|
uint8_t f[FRAME_LEN];
|
||||||
|
memset(f, 0, FRAME_LEN);
|
||||||
|
for (int p = 0; p < 6; p++) {
|
||||||
|
f[POS[p][0]] = DIG1[0];
|
||||||
|
f[POS[p][1]] = DIG1[1];
|
||||||
|
}
|
||||||
|
uint8_t off[FRAME_LEN];
|
||||||
|
memset(off, 0, FRAME_LEN);
|
||||||
|
epd_lut_GC(); epd_write(off, 0x00);
|
||||||
|
epd_lut_DU_WB(); epd_write(f, 0x00);
|
||||||
|
ESP_LOGI("dpfinder", "REF: all 1s (no extras)");
|
||||||
|
wait_for_advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bit 5 (=0x20) at each candidate byte, 0-indexed: 3,4,7,8,9,10 */
|
||||||
|
const int cand_byte[] = {3, 4, 7, 8, 9, 10};
|
||||||
|
for (int i = 0; i < (int)(sizeof(cand_byte)/sizeof(cand_byte[0])); i++) {
|
||||||
|
show_1s_plus(cand_byte[i], 5, i + 1);
|
||||||
|
wait_for_advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI("dpfinder", "== Done. ==");
|
||||||
|
uint8_t off[FRAME_LEN];
|
||||||
|
memset(off, 0, FRAME_LEN);
|
||||||
|
epd_lut_DU_WB();
|
||||||
|
epd_write(off, 0x00);
|
||||||
|
while (1) vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,192 @@
|
||||||
#include <stdio.h>
|
/**
|
||||||
|
* temphummon — ESP32-H2 + 1.9" Segment E-Paper (IST7134 via I²C)
|
||||||
|
* Shows internal CPU temperature on top line + fixed humidity on bottom.
|
||||||
|
*
|
||||||
|
* Frame layout (empirically verified, see PROJECT_MEMORY.md):
|
||||||
|
* f[0] = leading "1" glyph (bits 0-4)
|
||||||
|
* f[1],f[2] = top-line tens digit
|
||||||
|
* f[3],f[4] = top-line ones digit
|
||||||
|
* f[5],f[6] = bottom-line tens digit
|
||||||
|
* f[7],f[8] = bottom-line ones digit
|
||||||
|
* f[9],f[10] = bottom-line tenths digit
|
||||||
|
* f[11],f[12] = top-line tenths digit
|
||||||
|
* f[13] = °C (0x05) / °F (0x06) letter
|
||||||
|
*
|
||||||
|
* DECIMAL POINT / % (the hard-won bits!):
|
||||||
|
* f[4] bit5 = top decimal (after top ones) -> "188.8"
|
||||||
|
* f[8] bit5 = bottom decimal (after bottom ones) -> "88.8"
|
||||||
|
* f[10] bit5 = percent sign (after bottom tenths) -> "88.8%"
|
||||||
|
*
|
||||||
|
* Wiring: GPIO4→SDA, GPIO5→SCL, GPIO10→RST, GPIO11→BUSY, 3V3→VCC, GND→GND
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/temperature_sensor.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
void app_main(void)
|
#define PIN_SDA GPIO_NUM_4
|
||||||
{
|
#define PIN_SCL GPIO_NUM_5
|
||||||
|
#define PIN_RST GPIO_NUM_10
|
||||||
|
#define PIN_BUSY GPIO_NUM_11
|
||||||
|
|
||||||
|
static i2c_master_bus_handle_t bus;
|
||||||
|
static i2c_master_dev_handle_t cmd_dev, dat_dev;
|
||||||
|
static temperature_sensor_handle_t tsens;
|
||||||
|
|
||||||
|
/* Digit glyphs from Waveshare reference (byte pair per digit). */
|
||||||
|
static const uint8_t DIG[10][2] = {
|
||||||
|
{0xbf,0x1f},{0x1f,0x00},{0xfd,0x17},{0xf5,0x1f},{0x47,0x1f},
|
||||||
|
{0xf7,0x1d},{0xff,0x1d},{0x21,0x1f},{0xff,0x1f},{0xf7,0x1f},
|
||||||
|
};
|
||||||
|
static const uint8_t DIG_L[10][2] = {
|
||||||
|
{0xbf,0x1f},{0x1f,0x00},{0xfd,0x37},{0xf5,0x1f},{0x47,0x1f},
|
||||||
|
{0xf7,0x1d},{0xff,0x3d},{0x21,0x1f},{0xff,0x3f},{0xf7,0x1f},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Decimal point & % bits (0x20) */
|
||||||
|
#define BIT_DP_TOP 0x20 /* f[4] */
|
||||||
|
#define BIT_DP_BOT 0x20 /* f[8] */
|
||||||
|
#define BIT_PERCENT 0x20 /* f[10] */
|
||||||
|
|
||||||
|
static void epd_wait_busy(void) {
|
||||||
|
/* BUSY_N: LOW = busy, HIGH = ready */
|
||||||
|
while (gpio_get_level(PIN_BUSY) == 0) vTaskDelay(1);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void epd_lut_GC(void) {
|
||||||
|
uint8_t lut[] = {0x82,0x20,0x00,0xA0,0x80,0x40,0x63};
|
||||||
|
i2c_master_transmit(cmd_dev, lut, sizeof(lut), pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
static void epd_lut_DU_WB(void) {
|
||||||
|
uint8_t lut[] = {0x82,0x80,0x00,0xC0,0x80,0x80,0x62};
|
||||||
|
i2c_master_transmit(cmd_dev, lut, sizeof(lut), pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void epd_temperature(void) {
|
||||||
|
uint8_t a[] = {0x7E,0x81,0xB4};
|
||||||
|
i2c_master_transmit(cmd_dev, a, sizeof(a), pdMS_TO_TICKS(100));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
uint8_t b[] = {0xE7,0x0E};
|
||||||
|
i2c_master_transmit(cmd_dev, b, sizeof(b), pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void epd_write(const uint8_t *frame, bool clear) {
|
||||||
|
uint8_t w[] = {0xAC,0x2B,0x40,0xA9,0xA8};
|
||||||
|
i2c_master_transmit(cmd_dev, w, 5, pdMS_TO_TICKS(100));
|
||||||
|
i2c_master_transmit(dat_dev, frame, 15, pdMS_TO_TICKS(100));
|
||||||
|
uint8_t e = clear ? 0x03 : 0x00;
|
||||||
|
i2c_master_transmit(dat_dev, &e, 1, pdMS_TO_TICKS(100));
|
||||||
|
uint8_t d[] = {0xAB,0xAA,0xAF};
|
||||||
|
i2c_master_transmit(cmd_dev, d, 3, pdMS_TO_TICKS(100));
|
||||||
|
epd_wait_busy();
|
||||||
|
uint8_t s[] = {0xAE,0x28,0xAD};
|
||||||
|
i2c_master_transmit(cmd_dev, s, 3, pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void epd_init(void) {
|
||||||
|
// Reset
|
||||||
|
gpio_set_level(PIN_RST, 1); vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
gpio_set_level(PIN_RST, 0); vTaskDelay(pdMS_TO_TICKS(20));
|
||||||
|
gpio_set_level(PIN_RST, 1); vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
// Power on
|
||||||
|
uint8_t p[] = {0x2B,0xA7,0xE0};
|
||||||
|
i2c_master_transmit(cmd_dev, p, 3, pdMS_TO_TICKS(100));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
epd_temperature();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Render a value (0-199.9) to the top line: [lead1 ][tens][ones].[tenths] */
|
||||||
|
static void render_temp(uint8_t *f, float t) {
|
||||||
|
if (t < 0) t = 0;
|
||||||
|
if (t > 199.9) t = 199.9;
|
||||||
|
|
||||||
|
int hi = (int)t / 100;
|
||||||
|
int te = ((int)t / 10) % 10;
|
||||||
|
int on = (int)t % 10;
|
||||||
|
int tn = (int)(t * 10) % 10;
|
||||||
|
|
||||||
|
// tens (top)
|
||||||
|
if (hi || te) { f[1] = DIG[te][0]; f[2] = DIG[te][1]; }
|
||||||
|
// ones (top) + DECIMAL (f[4] bit5)
|
||||||
|
f[3] = DIG[on][0];
|
||||||
|
f[4] = DIG[on][1] | BIT_DP_TOP;
|
||||||
|
// tenths (top)
|
||||||
|
f[11] = DIG_L[tn][0];
|
||||||
|
f[12] = DIG_L[tn][1];
|
||||||
|
// leading "1"
|
||||||
|
if (hi) {
|
||||||
|
f[0] |= 0x1f; /* left column of the leading "1" glyph */
|
||||||
|
}
|
||||||
|
// °C
|
||||||
|
f[13] = 0x05;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Render a value (0-99.9) to the bottom line: [tens][ones].[tenths]% */
|
||||||
|
static void render_humidity(uint8_t *f, float h) {
|
||||||
|
if (h < 0) h = 0;
|
||||||
|
if (h > 99.9) h = 99.9;
|
||||||
|
|
||||||
|
int te = ((int)h / 10) % 10;
|
||||||
|
int on = (int)h % 10;
|
||||||
|
int tn = (int)(h * 10) % 10;
|
||||||
|
|
||||||
|
// tens (bottom)
|
||||||
|
if (te) { f[5] = DIG[te][0]; f[6] = DIG[te][1]; }
|
||||||
|
// ones (bottom) + DECIMAL (f[8] bit5)
|
||||||
|
f[7] = DIG[on][0];
|
||||||
|
f[8] = DIG[on][1] | BIT_DP_BOT;
|
||||||
|
// tenths (bottom) + PERCENT (f[10] bit5)
|
||||||
|
f[9] = DIG_L[tn][0];
|
||||||
|
f[10] = DIG_L[tn][1] | BIT_PERCENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void) {
|
||||||
|
// Init I²C
|
||||||
|
i2c_master_bus_config_t bc = {.i2c_port=-1,.sda_io_num=PIN_SDA,.scl_io_num=PIN_SCL,
|
||||||
|
.clk_source=I2C_CLK_SRC_DEFAULT,.glitch_ignore_cnt=7,.flags.enable_internal_pullup=1};
|
||||||
|
ESP_ERROR_CHECK(i2c_new_master_bus(&bc, &bus));
|
||||||
|
i2c_device_config_t cc = {.dev_addr_length=I2C_ADDR_BIT_LEN_7,.device_address=0x3C,.scl_speed_hz=100000};
|
||||||
|
i2c_device_config_t dc = {.dev_addr_length=I2C_ADDR_BIT_LEN_7,.device_address=0x3D,.scl_speed_hz=100000};
|
||||||
|
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus, &cc, &cmd_dev));
|
||||||
|
ESP_ERROR_CHECK(i2c_master_bus_add_device(bus, &dc, &dat_dev));
|
||||||
|
|
||||||
|
// Init GPIO
|
||||||
|
gpio_config_t rc = {.pin_bit_mask=(1ULL<<PIN_RST),.mode=GPIO_MODE_OUTPUT,
|
||||||
|
.pull_up_en=GPIO_PULLUP_DISABLE,.pull_down_en=GPIO_PULLDOWN_DISABLE,.intr_type=GPIO_INTR_DISABLE};
|
||||||
|
gpio_config(&rc); gpio_set_level(PIN_RST, 1);
|
||||||
|
gpio_config_t bc2 = {.pin_bit_mask=(1ULL<<PIN_BUSY),.mode=GPIO_MODE_INPUT,
|
||||||
|
.pull_up_en=GPIO_PULLUP_DISABLE,.pull_down_en=GPIO_PULLDOWN_DISABLE,.intr_type=GPIO_INTR_DISABLE};
|
||||||
|
gpio_config(&bc2);
|
||||||
|
|
||||||
|
// Init temp sensor
|
||||||
|
temperature_sensor_config_t tc = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 60);
|
||||||
|
ESP_ERROR_CHECK(temperature_sensor_install(&tc, &tsens));
|
||||||
|
ESP_ERROR_CHECK(temperature_sensor_enable(tsens));
|
||||||
|
|
||||||
|
// Init display
|
||||||
|
epd_init();
|
||||||
|
|
||||||
|
// Clear
|
||||||
|
uint8_t off[15]; memset(off, 0, 15);
|
||||||
|
epd_lut_GC();
|
||||||
|
epd_write(off, 0); vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
|
||||||
|
float t;
|
||||||
|
while (1) {
|
||||||
|
ESP_ERROR_CHECK(temperature_sensor_get_celsius(tsens, &t));
|
||||||
|
ESP_LOGI("temphummon", "T: %.1f°C", (double)t);
|
||||||
|
|
||||||
|
uint8_t f[15]; memset(f, 0, 15);
|
||||||
|
render_temp(f, t);
|
||||||
|
render_humidity(f, 88.8f); /* fixed sample until a humidity sensor is wired */
|
||||||
|
|
||||||
|
epd_lut_DU_WB();
|
||||||
|
epd_write(f, 0);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
86
flake.lock
generated
Normal file
86
flake.lock
generated
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1782847189,
|
||||||
|
"narHash": "sha256-twXPFqFsrrY5r28Zh7Homgcp2gUMBgQ6WDS98Q/3xFI=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "b6018f87da91d19d0ab4cf979885689b469cdd41",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-25.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-esp-dev": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": [
|
||||||
|
"flake-utils"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1777553768,
|
||||||
|
"narHash": "sha256-2J1ZbfbLUGwpXfLpDTCEpiR4wZD1lVFWiup67DSJRuw=",
|
||||||
|
"owner": "dvdvgt",
|
||||||
|
"repo": "nixpkgs-esp-dev",
|
||||||
|
"rev": "f9b1e211262a4cc9c1a265b227def56ef01c2d56",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "dvdvgt",
|
||||||
|
"ref": "update-v6.0.1",
|
||||||
|
"repo": "nixpkgs-esp-dev",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"nixpkgs-esp-dev": "nixpkgs-esp-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue