PCB & pinout
How everything is wired to the ESP32-C6, plus flash partitions and eFuses, for anyone who wants to run their own firmware.
This page is for tinkerers. The ZerryBit is an ESP32-C6 board with a standard UART flashing header, and nothing stops you from building and flashing your own firmware. Below is the full wiring, the buses each peripheral uses, and the flash and eFuse layout the stock firmware expects.
Flashing your own firmware is at your own risk. It won't brick the hardware (the UART header always works), but the stock cloud pairing relies on a device key described below, so keep a copy of the stock firmware if you want to switch back.
GPIO pin map
All GPIO numbers are ESP32-C6 numbering, grouped by subsystem.
E-paper display (SPI)
| Function | Pin | Notes |
|---|---|---|
| MOSI | IO22 | SPI data out |
| SCLK | IO23 | SPI clock |
| CS | IO18 | Chip select |
| DC | IO7 | Data/command select |
| RST | IO8 | Reset |
| BUSY | IO19 | Status input, HIGH = idle |
There is no MISO line; the display is write-only.
Rotary encoder & button
| Function | Pin | Notes |
|---|---|---|
| Push button | IO1 | Active LOW, idle HIGH via external 10 kΩ pull-up |
| Channel A | IO2 | PCNT input, normally HIGH |
| Channel B | IO3 | PCNT input, normally HIGH |
Climate sensor (BME280, I2C)
| Function | Pin | Notes |
|---|---|---|
| SCL | IO20 | I2C clock |
| SDA | IO21 | I2C data |
| Sensor power | IO11 | MOSFET gate, drive HIGH to power the sensor |
Power & charging
| Function | Pin | Notes |
|---|---|---|
| Battery voltage | IO0 | ADC1 channel 0, behind a 2 × 2 MΩ divider (pin sees half the cell voltage) |
| Charger status (/CHG) | IO6 | From the BQ24074: LOW = charging, HIGH = not charging; external 10 kΩ pull-up |
Buses and protocols
E-paper (SPI). The 800 × 480 panel has a UC8179 controller on SPI2_HOST at 20 MHz, mode 0. Full refreshes use the controller's built-in OTP waveform; partial refreshes load a custom LUT. BUSY is polled HIGH-idle. The panel is put into deep sleep (0x07 0xA5) between wakes.
BME280 (I2C). I2C_NUM_0 at 100 kHz on IO20/IO21. Power comes through a MOSFET on IO11; the stock firmware holds it on permanently.
Rotary encoder (PCNT). A Bourns PEC11J, 9 pulses / 18 detents per revolution, counted by the ESP32-C6 pulse-counter peripheral in x2 mode (both edges of channel A, direction from channel B). That yields exactly ±1 count per detent. In the stock firmware, physical clockwise rotation produces a negative delta. Note that the quadrature lines settle at HIGH/HIGH and LOW/LOW on alternating detents, which matters if you wake from light sleep on encoder movement: capture the current A/B levels before sleeping and compare after waking to tell a real rotation from button-press coupling.
Button. Plain GPIO on IO1, active LOW, debounce in software (the stock firmware uses 50 ms, with a 300 ms double-click window).
Charger status. The BQ24074's /CHG output on IO6 is open-drain, active LOW. It only distinguishes actively charging from not charging; a full-and-docked device reads the same as an unplugged one. When docked and full, the charger's auto-recharge cycle makes /CHG toggle slowly, so expect that in your logic.
Battery sensing. IO0 reads the cell through the divider with 12 dB attenuation. Multiply the pin voltage by 2 for the cell voltage. Read it with the radio off for a near open-circuit value.
The hardware reset is on the board. Holding the button longer than about 12 seconds resets the ESP32-C6 in hardware, independent of firmware. Your own firmware doesn't need to (and shouldn't) implement anything for this, and any hold gesture you design should stay comfortably under 10 seconds.
Flashing header
On the side of the PCB sits a 2.54 mm 6-pin female header for a USB-UART adapter. Remove the enclosure's top cover and the header is reachable from the side; the PCB itself stays in place.
The pins, from left to right:
┌──────┬──────┬──────┬──────┬──────┬──────┐
│ 3V3 │ TX │ RX │ EN │ BOOT │ GND │
└──────┴──────┴──────┴──────┴──────┴──────┘To enter flash mode, hold BOOT low while pulsing EN low, then flash with esptool as with any ESP32-C6 (the flash is 8 MB). Easier still: an ESP32 auto download module (the common auto-program adapters with DTR/RTS wired to EN and BOOT) handles the sequence for you, so flashing becomes a single click.
Flash partitions
The stock partition table on the 8 MB flash:
| Partition | Offset | Size | Purpose |
|---|---|---|---|
nvs | 0x9000 | 32 KB | Settings storage (encrypted) |
otadata | 0x11000 | 8 KB | Which OTA slot is active |
factory | 0x20000 | 1.75 MB | Golden recovery firmware, never updated over the air |
ota_0 | 0x1E0000 | 2.5 MB | Firmware slot A |
ota_1 | 0x460000 | 2.5 MB | Firmware slot B |
storage | 0x6E0000 | ~1.1 MB | Cache for full-screen widget images |
esp_secure_cert | 0x7FE000 | 8 KB | The device's RSA private-key blob (see below) |
Over-the-air updates ping-pong between ota_0 and ota_1, with automatic rollback to the previous slot if a new image fails to boot.
eFuses and the device key
The ESP32-C6's one-time-programmable eFuses are used for three things:
| eFuse block | Contents |
|---|---|
BLOCK_KEY0 | HMAC key for NVS encryption (settings, Wi-Fi credentials). Read-protected. |
BLOCK_KEY2 | HMAC key for the DS peripheral, read-protected. It seals the RSA-2048 device key stored in esp_secure_cert: the chip can sign with the key, but the key can never be read out. |
BLOCK_USR_DATA | Hardware version and colour code, written at the factory. |
Two things follow for custom firmware:
- Secure Boot is not enabled, deliberately. The chip boots any correctly built image, which is what makes custom firmware possible in the first place.
- The stock NVS partition is encrypted against the KEY0 HMAC key. Custom firmware can use ESP-IDF's HMAC-based NVS encryption with the same eFuse key, or simply erase the partition and use plain NVS.
Restoring stock firmware
The full stock image (bootloader, partition table, and app, flashed at offset 0x0) is available on request while we set up a public download; ask on our Discord. Because the device key never leaves the chip, restoring stock firmware restores cloud pairing too.