179 lines
6.4 KiB
Markdown
179 lines
6.4 KiB
Markdown
# nrfwarch
|
|
|
|
Smartwatch firmware for the [Seeed Xiao nRF54L15 Sense](https://www.seeedstudio.com/XIAO-nRF54L15-Sense-p-6494.html) with a GDEY0154D61LT 1.54" e-ink display.
|
|
|
|
Built on [Zephyr RTOS](https://docs.zephyrproject.org/) v4.0. Targets the `xiao_nrf54l15/nrf54l15/cpuapp` board. Includes a display simulator that renders the watchface on your desktop using [feh](https://feh.finalrewind.org/).
|
|
|
|
## Hardware
|
|
|
|
- **MCU**: Nordic nRF54L15 (ARM Cortex-M33, 128KB RAM, 1MB Flash)
|
|
- **Board**: Seeed Xiao nRF54L15 Sense (includes LSM6DSO IMU, PDM mic)
|
|
- **Display**: Good Display GDEY0154D61LT 1.54" e-ink, 200x200px, SSD1680 controller, SPI
|
|
|
|
### E-ink wiring
|
|
|
|
| Signal | Xiao Pin | Function |
|
|
|--------|----------|----------|
|
|
| MOSI | D10 | SPI data out |
|
|
| CLK | D8 | SPI clock |
|
|
| CS | D1 | Chip select |
|
|
| DC | D3 | Data/command |
|
|
| RST | D0 | Hardware reset |
|
|
| BUSY | D5 | Busy status |
|
|
|
|
## Quick start
|
|
|
|
### 1. Enter dev shell
|
|
|
|
```bash
|
|
nix develop
|
|
```
|
|
|
|
### 2. Set up Zephyr SDK (first time only)
|
|
|
|
Downloads Zephyr v4.0 workspace and SDK 0.17.0, patches binaries for NixOS:
|
|
|
|
```bash
|
|
nix run .#setup-sdk
|
|
```
|
|
|
|
### 3. Run the display simulator
|
|
|
|
Builds and runs the watchface simulator. Time advances 1 minute per second. A feh window opens showing the e-ink output in real time. The simulator uses the same rendering code that runs on the real hardware.
|
|
|
|
```bash
|
|
nix run .#sim
|
|
```
|
|
|
|
This pipes the native_sim binary output through `sim/view.py`, which converts the framebuffer to PBM and auto-launches feh with `--auto-reload`. The output file is `/tmp/nrfwarch.pbm`.
|
|
|
|
### 4. Run tests
|
|
|
|
17 ztest cases covering framebuffer operations, font data, and rendering:
|
|
|
|
```bash
|
|
nix run .#test
|
|
```
|
|
|
|
### 5. Build for hardware
|
|
|
|
Open the project in [nRF Connect for VS Code](https://marketplace.visualstudio.com/items?itemName=nordic-semiconductor.nrf-connect) and add the application with board target `xiao_nrf54l15/nrf54l15/cpuapp`. Or build from the command line:
|
|
|
|
```bash
|
|
west build -b xiao_nrf54l15/nrf54l15/cpuapp .
|
|
```
|
|
|
|
## Display simulation
|
|
|
|
The simulator lets you develop the watchface visually without any hardware. It runs the actual Zephyr kernel and the real `watchface.c` / `fonts.c` code -- the only difference is the EPD driver writes to a file instead of SPI.
|
|
|
|
How it works:
|
|
|
|
1. `sim/src/epd_sim.c` implements the same API as `src/epd_drv.c`, but instead of sending SPI commands it dumps the raw framebuffer over stdout as hex, framed with `!F!` / `!E!` markers
|
|
2. `sim/view.py` reads the hex frames from stdin, inverts the polarity (framebuffer uses 1=white, PBM uses 1=black), and writes a PBM image to `/tmp/nrfwarch.pbm`
|
|
3. feh watches the file with `--auto-reload` and updates the display window
|
|
|
|
The simulated time starts at 12:00 and advances 1 minute every 0.8 seconds. Battery decreases by 2% per simulated hour. A full 24-hour cycle runs in about 19 minutes.
|
|
|
|
### Manual usage
|
|
|
|
If you want to run the sim without feh or pipe the output yourself:
|
|
|
|
```bash
|
|
# Run the binary directly (outputs hex frames to stdout)
|
|
./build-sim/zephyr/zephyr.exe
|
|
|
|
# Convert a single frame to PBM manually
|
|
./build-sim/zephyr/zephyr.exe | python3 sim/view.py
|
|
|
|
# Just view the current frame
|
|
feh /tmp/nrfwarch.pbm
|
|
|
|
# Convert to PNG for sharing
|
|
pnmtopng /tmp/nrfwarch.pbm > watchface.png
|
|
```
|
|
|
|
### Iterating on the watchface
|
|
|
|
1. Edit `src/watchface.c` or modify digit shapes in `tools/gen_fonts.py`
|
|
2. If fonts changed: `python3 tools/gen_fonts.py`
|
|
3. Rebuild sim: `cmake -B build-sim -GNinja -DBOARD=qemu_x86 -S sim && ninja -C build-sim`
|
|
4. Run: `./build-sim/zephyr/zephyr.exe | python3 sim/view.py`
|
|
5. feh auto-reloads when the PBM changes
|
|
|
|
## Project layout
|
|
|
|
```
|
|
src/
|
|
main.c Entry point, minute-by-minute watch face loop
|
|
epd_drv.c/h SSD1680 e-ink driver (SPI, full/partial refresh, deep sleep)
|
|
watchface.c/h Framebuffer renderer (200x200 monochrome)
|
|
fonts.c/h Bitmap fonts (24x40 digits, 8x40 colon)
|
|
|
|
sim/
|
|
src/main.c Simulated main loop (accelerated time, no hardware)
|
|
src/epd_sim.c EPD mock - dumps framebuffer as hex over stdout
|
|
view.py Reads hex frames, writes PBM, launches feh
|
|
CMakeLists.txt Sim build config
|
|
prj.conf Sim Kconfig
|
|
|
|
boards/
|
|
xiao_nrf54l15_nrf54l15_cpuapp.overlay Devicetree overlay (SPI, GPIO, I2C, BT)
|
|
xiao_nrf54l15_nrf54l15_cpuapp.conf Board-specific Kconfig
|
|
|
|
dts/bindings/
|
|
nrfwarch,epd-ctrl.yaml Custom binding for EPD GPIO control node
|
|
|
|
tests/
|
|
src/main.c 17 ztest cases (framebuffer, pixels, fonts, rendering)
|
|
prj.conf Test build config
|
|
|
|
prj.conf Main application config
|
|
CMakeLists.txt Build definition
|
|
flake.nix Nix flake (dev shell, setup-sdk, test, sim)
|
|
|
|
tools/
|
|
gen_fonts.py Font generator (run to regenerate src/fonts.*)
|
|
```
|
|
|
|
## Flake commands
|
|
|
|
| Command | Description |
|
|
|---------|-------------|
|
|
| `nix develop` | Enter dev shell with all tools |
|
|
| `nix run .#setup-sdk` | Download and set up Zephyr SDK + workspace |
|
|
| `nix run .#test` | Build and run ztest suite on QEMU |
|
|
| `nix run .#sim` | Run display simulator with feh viewer |
|
|
|
|
## What works
|
|
|
|
- SSD1680 e-ink driver with full refresh and partial refresh
|
|
- Framebuffer-based watch face with time rendering (HH:MM) and battery indicator
|
|
- Deep sleep / wake for the display
|
|
- Display simulator with real-time feh output
|
|
- 17 passing tests covering framebuffer operations, font data integrity, and rendering
|
|
|
|
## In progress / planned
|
|
|
|
- BLE Current Time Service (CTS) client for phone time sync
|
|
- GRTC-based RTC for persistent timekeeping across deep sleep
|
|
- Battery monitoring via ADC (channel 7, VBAT regulator)
|
|
- LSM6DSO IMU wrist-tilt wake detection
|
|
- System-off power management with regulator control
|
|
- Partial refresh for minute updates (avoid full refresh flicker)
|
|
- BLE Battery Service (BAS) and notifications
|
|
|
|
## References
|
|
|
|
- [Seeed Xiao nRF54L15 Wiki](https://wiki.seeedstudio.com/xiao_nrf54l15_sense_getting_started/)
|
|
- [Zephyr RTOS Documentation](https://docs.zephyrproject.org/)
|
|
- [Zephyr QEMU x86 Board](https://docs.zephyrproject.org/latest/boards/qemu/x86/doc/index.html)
|
|
- [Zephyr ztest Framework](https://docs.zephyrproject.org/latest/develop/test/ztest.html)
|
|
- [SSD1680 datasheet](https://www.good-display.com/companyfile/409.html) (GDEY0154D61LT)
|
|
- [Sample repo: Toastee0/Seeed-Xiao-nRF54L15](https://github.com/Toastee0/Seeed-Xiao-nRF54L15)
|
|
- [E-ink driver reference: jcontrerasf/E-ink-display-Zephyr](https://github.com/jcontrerasf/E-ink-display-Zephyr)
|
|
|
|
## License
|
|
|
|
Apache 2.0
|