Board test plan: adjustable brightness, Bluetooth, persistent RTC
Branch feat/brightness-bt-rtc, based on master at df2dd84e. Three
kernel/DT/Nix changes land together in this branch (one commit each):
the-panel-brightness-is-adjustable, the-handheld-talks-bluetooth,
the-clock-survives-a-reboot. All three are kernel and device-tree
changes — they need a /boot install and a reboot, not just
nixos-rebuild switch / activation. I have not touched the board; every
command below is either a host build or something the coordinator runs on
the board or over the serial console (/dev/ttyACM0). Board/serial-port
reservation rules apply throughout (AGENTS.md): one operator at a time.
0. Build (host)
nix build .#kernel --max-jobs 1 --cores 6 --no-link --print-out-paths
nix build .#deviceTree --max-jobs 1 --cores 6 --no-link --print-out-paths
nix build .#sdImage-coherent --max-jobs 1 --cores 6 --no-link --print-out-paths
nix build .#nixosConfigurations.k230-coherent-shell.config.system.build.toplevel --max-jobs 1 --cores 6 --no-link --print-out-paths
Record the four store paths in the handoff. sdImage-coherent is the
image to flash; toplevel is the same closure nixos-rebuild would
converge to, useful for diffing what changed against the currently
installed system.
1. Install and reboot
Per docs/uboot-ums.md/docs/evidence/usb-host-validation.md (already
hardware-proven): flash sdImage-coherent via ums 0 mmc 1 +
tools/flash-latest.sh --ums, or the card-reader fallback
tools/flash-latest.sh /dev/disk/by-id/<reader-target>. Reboot. Confirm
the new kernel is actually running before judging anything below:
./tools/console.py /dev/ttyACM0 --wait=3 "uname -a"
Cross-check the reported build against this branch’s commit.
2. Backlight (the-panel-brightness-is-adjustable)
./tools/console.py /dev/ttyACM0 --wait=3 "ls /sys/class/backlight"
Expected: exactly one device directory (e.g. canaan-dsi-backlight).
Failure signatures:
- Empty output /
ls: cannot access: the backlight device did not register — checkdmesg | grep -i backlightfor a probe error, and confirmcanaan,dsi-command-backlightactually landed in the running DTB (fdtdumpthe installed/boot/*.dtbif in doubt). - More than one device: an unexpected second backlight bound; do not
assume which one the Settings stepper is driving —
tools/device_settings.pyrefuses to guess when there is more than one (Settings.backlight()raises"no unique panel backlight").
Write brightness at three points and camera-observe the panel each time (camera on the bench, not touch — this is a visual check, not something to judge by feel):
./tools/console.py /dev/ttyACM0 --wait=1 "cat /sys/class/backlight/*/max_brightness"
./tools/console.py /dev/ttyACM0 --wait=1 "echo 26 > /sys/class/backlight/*/brightness" # ~10% of 255
./tools/console.py /dev/ttyACM0 --wait=1 "echo 128 > /sys/class/backlight/*/brightness" # ~50%
./tools/console.py /dev/ttyACM0 --wait=1 "echo 255 > /sys/class/backlight/*/brightness" # 100%
Expected: the panel visibly dims and brightens across the three writes, camera-observable.
Failure signatures:
- Writes succeed (
echoreturns 0) but the panel does not visibly change: the DCS command is reaching the controller’s write path but not its brightness block — checkdmesgfor"failed to enable brightness control"/"failed to set display brightness"(the-110/ETIMEDOUTsignature already seen elsewhere in this driver’s bring-up history means the DSI link itself is not answering, not a brightness-specific bug). echo: write error: Invalid argument/Permission deniedwriting as root over console: a permission or range problem independent of the udev rule (console root should always be able to write); investigate before blaming the shell-user path.
Cycle a modeset or DPMS-style off/on and confirm the brightness set above
(100%) is still in effect afterward, not the fixed 0xFE default:
./tools/console.py /dev/ttyACM0 --wait=2 "modetest -M canaan -s <connector-id>:1232x568 || true"
./tools/console.py /dev/ttyACM0 --wait=1 "cat /sys/class/backlight/*/actual_brightness"
(modetest from libdrm, already in environment.systemPackages per
nix/hardware.nix; substitute the actual connector ID modetest -M canaan
reports.)
Expected: actual_brightness unchanged from before the modeset, and
the panel’s visible brightness matches.
Failure signature: brightness reverts to the fixed default after the
cycle — the ctx->panel.backlight wiring (design.md decision 4) did not
take effect, or drm_panel_enable()/disable() are not actually being
called on this modeset path; check dmesg for the panel driver’s own
dev_info lines around the cycle.
Also confirm the Settings stepper itself drives the device, once on real glass:
./tools/console.py /dev/ttyACM0 --wait=2 "su - shell -c 'k230-settings brightness 50'"
./tools/console.py /dev/ttyACM0 --wait=1 "cat /sys/class/backlight/*/actual_brightness"
Expected: {"state":"applied",...} from k230-settings, and
actual_brightness at roughly 50% of max_brightness.
Failure signature: {"state":"failed","error":"brightness-denied",...}
— the udev rule did not grant the shell group write access; check
ls -l /sys/class/backlight/*/brightness for its group and mode.
3. Bluetooth (the-handheld-talks-bluetooth)
Plug the USB Bluetooth dongle into the board’s USB host port (see
docs/uboot-usb-host-verdict.txt for confirmed host power/enumeration on
this exact port).
./tools/console.py /dev/ttyACM0 --wait=2 "lsusb"
./tools/console.py /dev/ttyACM0 --wait=3 "bluetoothctl show"
./tools/console.py /dev/ttyACM0 --wait=12 "bluetoothctl scan on"
./tools/console.py /dev/ttyACM0 --wait=3 "btmgmt info"
Expected:
lsusblists the dongle (0a12:0001for the bundled CSR8510 clone, or the attached device’s actual vendor/product ID).bluetoothctl showreports a controller (Controller <MAC> ... Powered: yes).bluetoothctl scan onprints[NEW] Device ...lines for nearby discoverable devices within the 10 s window.btmgmt inforeports the controllercurrent settings: powered ... running.
Failure signatures:
lsusbdoes not list the dongle at all: a USB host power problem, not a Bluetooth-stack problem — cross-check againstdocs/evidence/uboot-usb-host-verdict.txt’s known-good host state, and suspect the physical port/hub before the kernel config.lsusblists the dongle butbluetoothctl showreports no controller, orbtmgmt infonever reaches a powered/running state: this is the LILYGO-quirk failure mode named in design.md — the bundled dongle may need the sameHCI_QUIRK_RESET_ON_CLOSE-adjacent quirks LILYGO’s BSP patches (0056,0057-bluetooth-btusb-*.patch) work around; checkdmesg | grep -i bluetoothforbtusbprobe errors before porting those patches as a follow-up change.bluetoothctl showreports a controller butscan onfinds nothing in a space with other Bluetooth devices present: checkrfkill listfor a software or hardware block.
4. RTC (the-clock-survives-a-reboot)
./tools/console.py /dev/ttyACM0 --wait=2 "ls /dev/rtc0"
./tools/console.py /dev/ttyACM0 --wait=2 "hwclock -r"
./tools/console.py /dev/ttyACM0 --wait=2 "timedatectl"
Expected: /dev/rtc0 exists; hwclock -r reports a plausible current
time (not an error, not the kernel’s compiled-in epoch); timedatectl
shows RTC time populated and close to Universal time/Local time.
Failure signatures:
hwclock -rreportshwclock: Cannot access the Hardware Clock via any known method//dev/rtc0missing:RTC_DRV_K230did not build in, or the DT node did not bind — checkdmesg | grep -i rtcand confirmCONFIG_RTC_DRV_K230=yin the running kernel’s/proc/config.gz(if enabled) or the build’s.config.timedatectl’sRTC timeis present but implausible (far future/past):k230-rtc-sync.servicewrote before a real NTP sync completed — checksystemctl status k230-rtc-syncand its ordering againsttime-sync.target.
Reboot (warm reboot only — do not remove power for this step) and check again:
./tools/console.py /dev/ttyACM0 --wait=2 "reboot"
# wait for the board to come back, then:
./tools/console.py /dev/ttyACM0 --wait=3 "hwclock -r"
Expected: the time reported after reboot is consistent with (a few seconds later than) the time reported before reboot — not reset to the kernel’s compiled-in epoch.
Failure signature: the post-reboot hwclock -r jumps back to a
epoch-adjacent implausible time — the RTC exists but is not actually
retaining the value across even a warm reboot (a stronger negative result
than the UNVERIFIED full-power-off case below); check
systemctl status k230-rtc-sync from the previous boot’s journal
(journalctl -b -1 -u k230-rtc-sync) to see whether it ran and succeeded
before the reboot.
Left UNVERIFIED by this test plan: whether the clock survives a full
power-off (main power removed, not merely a warm reboot). That needs a
separate power-off-and-wait test with the result recorded against task
4.2 of the-clock-survives-a-reboot; do not infer it from the warm-reboot
result above.