LuxLibre › Firmware › Ports › ESP32-C3 port
LuxLibre on the Espressif ESP32-C3, written for ESP-IDF 5.x and the FlashKa keychain boards: v0.5 (../../boards/flashka-keychain-v05/board.h) with one button, v0.6 (../../boards/flashka-keychain-v06/board.h) with three buttons on a resistor ladder, of which LuxLibre uses the main one (below), and v0.7 (../../boards/flashka-keychain-v07/board.h): the v0.6 pins and ladder with a stronger LED driver (3552 mA at full duty; LuxLibre’s level 150 is 2850 mA, so the LED never gets more than its 3.0 A rating) and one 14500 Li-ion cell (Vapcell F15, 1500 mAh).
Status: experimental. The port is written against the ESP-IDF 5.x API and has been checked
against declarations of that API; the GitHub workflow builds it for every FlashKa board in the
espressif/idf container once the repository is published, as a job that may fail until it has
passed once. It has not been compiled with ESP-IDF itself or run on a FlashKa yet. The
resistor-ladder path for v0.6 is untested on hardware as well; check it with the
ladder bring-up below. Until the port has passed the bring-up
tests (docs/testing.md), the original FlashKa firmware (written in
Rust, with the Bluetooth app and deep sleep, on v0.6 also the presenter mode) stays the reference
firmware for that light: hardware/flashka-keychain-v05/,
hardware/flashka-keychain-v06/ (its firmware
also builds the v0.7 image, firmware 0.7.0).
Install ESP-IDF 5.x (Espressif’s guide), then:
cd firmware/ports/esp32c3
idf.py set-target esp32c3
idf.py -DLX_BOARD=flashka-keychain-v05 build # or flashka-keychain-v06, flashka-keychain-v07, edco-v01
idf.py -p <serial port> flash monitor
LX_BOARD selects the folder under firmware/boards/. When you switch boards, run
idf.py fullclean first, or give each board its own build directory with -B. The console
(help, status, set, …) runs over the chip’s built-in USB serial/JTAG interface, so
idf.py monitor or any serial terminal reaches it.
| Function | ESP32-C3 resource |
|---|---|
| Main LED | LEDC PWM, 13 bit, 9.77 kHz, filtered into the current set point of the FlashKa’s buck driver; LED_EN switches the driver off at level 0 |
| Indicator | three LEDC channels for the common-anode RGB LED (v0.6: the two RGB LEDs of the light ring, in parallel on the same pins) |
| Button | v0.5: GPIO3, digital, low = pressed, internal pull-up on. v0.6: a resistor ladder read with ADC1 on GPIO3, wake-up on GPIO9 (below) |
| Laser | a GPIO behind the hardware slide-switch interlock (S2) |
| Battery voltage | ADC1 channel 0 (cell / 2) with the curve-fitting calibration |
| Temperature | ADC1 channel 1, 100 kΩ NTC (B 4250) with 100 kΩ to 3.3 V, table from lx_ntc.h |
| Charger | TP4057 status pins; the charge-set pin selects normal current (low), 10 mA trickle (floating) or off (high). The board has no VBUS sense pin, so while charging is off the pin is floated for 100 ms every 5 s to see whether USB is plugged in, only while the cell temperature allows charging (CHG-3) |
| Sleep | light sleep with wake-up on the button (GPIO; on v0.6 GPIO9) and a timer; while charging is allowed, also on the charger status pins, so that plugging in USB wakes the light and charging starts (to verify on hardware: no leakage from the pull-ups into the unpowered charger) |
| Storage | one 256-byte NVS blob, written only when it changes; the RAM copy is updated only after a successful write |
| Watchdog | task watchdog, 3 s, with panic reset; brown-out detector on (sdkconfig.defaults) |
| Tick | FreeRTOS vTaskDelayUntil, 10 ms (CONFIG_FREERTOS_HZ=1000) |
A resistor ladder puts several buttons on one pin. On the v0.6 board the net BTN has a 10 kΩ pull-up (R9) to 3.3 V; the main button connects BTN straight to ground, next through 1 kΩ and previous through 2.2 kΩ, so every press gives its own voltage. BTN goes to two pins: GPIO3 measures it with the ADC, GPIO9 reads it as a digital input.
| Pressed | BTN | LuxLibre |
|---|---|---|
| nothing | 3.30 V | released |
| main (alone or with any other) | 0 V | pressed |
| previous + next together | 0.212 V | released (ignored) |
| next | 0.300 V | released (ignored) |
| previous | 0.595 V | released (ignored) |
LuxLibre’s user interface has one button (FW-3). The slide buttons belong to the original FlashKa firmware (level steps, and the presenter mode: a Bluetooth keyboard that turns slides), which LuxLibre does not have. How the port handles the ladder:
button_down() is true when one calibrated reading is
below LX_ESP_BTN_MAIN_BELOW_MV (106 mV, midway between main at 0 V and the lowest other level,
0.212 V). The core debounces it like any button, and the main button always wins because it
pulls BTN to 0 V whatever else is pressed.LADDER_POLL_MS, LADDER_STUCK_MS, LADDER_STUCK_POLL_MS in
main/hal_esp32c3.cpp).How another board declares a ladder: docs/firmware/porting.md.
None of this has run on a board yet. On the first v0.6 board, before relying on the port:
status is planned.CONFIG_BOOTLOADER_LOG_LEVEL_NONE silences the
bootloader; the ROM’s own boot messages are controlled by eFuse and strapping settings, not by
the firmware). On v0.6 the red resistors are 330 Ω instead of 1 kΩ and LuxLibre has no colour
balance per board: check the mixed colours (yellow, white) on the bench.CHG_MAXLVL.