This file outlines how to extend and maintain RP2 (RP2040) board support for Kaluma, and the conventions for agents working on this repository.
- Baseline for RP2 work: tag
1.2.1 - Default branch:
master - Working branch convention:
feature/<board>-support - Commit style: squash to a single, tidy commit when opening a PR
- Images:
kaluma-kb2040kaluma-rp2040-touch-lcd-1-28
- Scripts:
tools/docker/build_kb2040.shtools/docker/build_rp2040_touch_lcd_1_28.sh
- Outputs UF2 to:
./build - Flash: Hold BOOTSEL, plug board in, copy
*.uf2toRPI-RP2mass storage
npm installnode build.js --target rp2 --board <board>(e.g.,kb2040orrp2040-touch-lcd-1-28)- Flash via BOOTSEL by copying UF2 from
build/
- A (firmware):
KALUMA_BINARY_MAX = 0xFC000(1008 KB) - B (storage): 16 KB → sectors 0–3
- C (user program): 512 KB → sectors 4–131
- D (filesystem): remainder of flash after A/B/C
- Sector size: 4096 bytes; Flash page size: 256 bytes
- Total flash (default): 4 MB
- A (firmware):
KALUMA_BINARY_MAX = 0xF0000(960 KB) - B (storage): 64 KB → sectors 0–15
- C (user program): 1536 KB → sectors 16–399
- D (filesystem): remainder of flash after A/B/C
- Sector size: 4096 bytes
// in board.js (Pico 2)
const fs = require('fs');
const { VFSLittleFS } = require('vfs_lfs');
const { Flash } = require('flash');
fs.register('lfs', VFSLittleFS);
const total = new Flash().count; // sectors after A
const bd = new Flash(400, total - 400); // B=16, C=384 → start=400
fs.mount('/', bd, 'lfs', true);Mount LittleFS using the full available space after A/B/C.
// in board.js
const fs = require('fs');
const { VFSLittleFS } = require('vfs_lfs');
const { Flash } = require('flash');
fs.register('lfs', VFSLittleFS);
const total = new Flash().count; // sectors after A
const bd = new Flash(132, total - 132); // B=4, C=128 → start=132
fs.mount('/', bd, 'lfs', true);- Create
targets/rp2/boards/<board>/with:board.h: partitions and macros (copy pico as baseline; adjust flash counts)board.c:void board_init() {}(or board-specific init)board.js: setglobal.board.name, optional pins (LED,BUTTON,NEOPIXEL), and dynamic LFS mount
- Add to
targets/rp2/target.cmake:- RP2040:
elseif(BOARD STREQUAL "<board>")→set(PICO_BOARD pico); set defaults (e.g.,PICO_FLASH_SIZE_BYTES, XOSC delay) - RP2350:
elseif(BOARD STREQUAL "<board>")→set(PICO_BOARD pico2 or pico2_w); toolchain switches to Cortex-M33
- RP2040:
- Build and flash (Docker or manual)
- Verify in REPL:
board.name- Pin constants (
board.BUTTON, etc.) - Flash probe:
new (require('flash').Flash)().count - FS works:
require('fs').readdir('/')
- Vendor pinouts (e.g., Adafruit Learning System: KB2040 Pinouts)
- CircuitPython boards repo:
ports/raspberrypi/boards/<board>/pins.c - Pico SDK board headers:
lib/pico-sdk/src/boards/include/boards/*.h - Datasheets/schematics (RP2040 + board PDF)
- Community firmware (QMK, CircuitPython, MicroPython) for cross-checks
- Tools:
gcc-arm-none-eabi,libnewlib-arm-none-eabi,libstdc++-arm-none-eabi-newlib,cmake,python3,nodejs,npm - Provide
pythonsymlink topython3inside the image - Initialize submodules inside the container; run a
node build.js --cleanbefore building to avoid host cache conflicts
- Do: keep changes scoped; keep a single clean commit for PRs
- Do: prefer dynamic D filesystem sizing
- Don’t: change Kaluma core behavior without explicit approval
- Don’t: introduce breaking changes for existing targets
- New
targets/rp2/boards/<board>/board.{h,c,js}andREADME.md targets/rp2/target.cmakemapping- Optional Dockerfile and
tools/docker/build_<board>.sh - Single squashed commit with concise message