Add a board-aware battery SOC system replacing the two independent
linear approximations that existed in the UI helpers.
New helpers/battery_curve.{h,c}: a 21-point (5% step) OCV lookup table
with integer linear interpolation. The default generic LiPo curve is a
weak symbol — any board can override it by dropping a battery_curve.c
into its board directory. CMakeLists.txt selects the board-specific file
when present, falling back to the generic.
Board.h gains getBattPercent() (default 0); ZephyrBoard implements it
via battery_curve_lookup().
Board-specific curves added for boards with measured cell data:
t1000_e, rak_wismesh_tag, thinknode_m6, sensecap_solar, wio_tracker_l1.
ADC multiplier corrections applied across all nRF52840 boards:
- Boards using a correctly-derived 3600×ratio formula get +0.5% to
compensate for nRF SAADC gain error (7200→7236, 6300→6332, etc.)
- rak_wismesh_tag, rak3401_1watt, gat562_30s had multipliers copied
from Arduino's 3.0V AREF formula; corrected to 3600×1.73×1.005=6259
- xiao_nrf52840 (10911) left unchanged — empirically calibrated value
above the theoretical, assumed already correct for that hardware
get adc.multiplier now reports current mV reading and the board's
curve 100% target, making field calibration self-guiding.
- README: add Room Server to "Device Roles" + a build command in "Building"
- ARCHITECTURE.md: role list two -> four (add Room Server + the
previously-undocumented Observer)
- Repeater_CLI_commands.md: note the room server shares this CLI
- boards/example_board/README.md: add the room_server.conf build snippet
Cleanups 1–3 (cosmetic):
Removed the dead uart1_default/uart1_sleep pinctrl groups + stale "GPS on UART1" comments → gat562_30s-pinctrl.dtsi
Removed the QSPI pinctrl groups (board has no QSPI; pins collide with the joystick) and replaced them with a comment explaining why
Untangled the GPS UART0-vs-async rationale in the DTS and the &uart1 comment
GPS driver switch (functional, you approved the full switch) → gat562_30s.dts, board.conf:
gnss-nmea-generic → luatos,air530z with on-off-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH> (matches the sibling rak_wismesh_tag, same AT6558R chip)
Deleted the always-on gps_power_en gpio-hog (the driver owns P1.02 now → GPS can be powered down)
Removed the CONFIG_UART_ASYNC_API=y override
Updated all related comments
Add the MeshCore Room Server role to ZephCore — a store-and-forward
shared message room. Clients log in with an admin or guest password
and post messages; the server pushes each new post to all other
logged-in clients (round-robin, per-client sync cursor, ACK + retry,
3-strike eviction).
Ported from upstream MeshCore's simple_room_server, structured as a
near-clone of RepeaterMesh so it reuses the proven ACL, region
filtering, CLI, adverts and telemetry; the post buffer + push engine
are the only net-new pieces:
- RoomServerMesh: PostInfo ring (MAX_UNSYNCED_POSTS=32), addPost,
pushPostToClient, getUnsyncedCount, processAck, onAckRecv, the
loop() push driver; room login (onAnonDataRecv parses the
sync_since cursor); posts/admin-CLI/keep-alive (onPeerDataRecv);
ADV_TYPE_ROOM advert; ServerStats wire layout; disable_fwd=1.
- main_room_server.cpp: event-loop entry (USB serial CLI, no BLE).
- Kconfig: ZEPHCORE_ROLE_ROOM_SERVER + ZEPHCORE_MAX_UNSYNCED_POSTS.
- CMakeLists role gating; boards/common/room_server.conf.
Post frame, SHA-256 ACK and login-reply layouts match upstream for
MeshCore app compatibility. Builds for gat562_30s
(FLASH 30.95% / RAM 32.46%). Not yet hardware-tested.
The GAT562 30S Mesh Kit is an nRF52840 + SX1262 board built on the
RAK4631 WisBlock core module with a 30 dBm (1 W) external PA, so it
reuses the proven rak4631 LoRa path. Deltas vs rak4631: 1.8 V TCXO
(per the MeshCore variant), battery multiplier 6232, buzzer on P1.01,
an AT6558R UART GPS, active-high LEDs, and a 5-way joystick that
selects the joystick companion menu UI.
A single board serves both the full kit and the screenless solar
repeater pod; absent peripherals (OLED, GPS, joystick, buzzer) are
optional in devicetree and skipped at runtime.
Board-specific notes:
- GPS: AT6558R NMEA on UART0 @ 9600 (P0.15 RX / P0.16 TX), powered by a
WB_IO2/P1.02 gpio-hog and parsed by gnss-nmea-generic. Uses
CONFIG_UART_ASYNC_API (DMA receive); nRF UARTE interrupt-driven RX did
not feed the modem backend.
- The 6 button/joystick inputs are routed to GPIO SENSE (sense-edge-mask
on &gpio0) so they don't consume the GPIOTE channel the SX1262 DIO1
"done" IRQ needs.
Registration: board definition, CMakeLists.txt platform-conf match,
build.sh (nRF_boards), supported_boards.md, the top-level README board
table, and the porting-guide board table.
Hardware-tested on the kit, companion and repeater: LoRa TX/RX with BLE
active (no CAD errors), BLE pairing (PIN 123456) and two-way messaging,
joystick menu UI, SSD1306 OLED, buzzer, battery %, UF2 + DFU-zip
flashing, and a 14-satellite GPS fix.
553b71c made production the default but broke configure+link:
- ZEPHCORE_COMPANION_USB sat inside the role choice (recursive dep)
- CONFIG_RESET_ON_FATAL_ERROR was an undefined, unimplemented symbol
- USB companion sources still gated on CONFIG_LOG, not ZEPHCORE_USB_STACK
Move the USB toggle out of the choice, add a real
ZEPHCORE_RESET_ON_FATAL_ERROR Kconfig + k_sys_fatal_error_handler that
cold-reboots, and match the CMake USB guard to ZEPHCORE_USB_STACK.
Production (LOG=n, ASSERT=n, RTT=n, reboot-on-fatal) is now the prj.conf
default; debug.conf is the opt-in bundle. Removed prod.conf and the
logging.conf auto-include; relocated RTT/ASSERT out of the always-on
platform confs so they no longer override the prod defaults.
Add CONFIG_ZEPHCORE_COMPANION_USB so the USB CDC companion transport
compiles independently of logging (default-y on USB-capable companions,
opt-in on ESP32-S3 via esp32s3_usb.conf). Gate all USB sites behind one
ZEPHCORE_USB_STACK macro.
Rework BLE/USB interface arbitration to first-come-first-served: neither
transport evicts a live session. Make active_iface mutation thread-safe
(mutex + atomic claim) across the BLE callback thread and USB workqueue.
Share the ESP32-S3 USB OTG / console DTS via common dtsi includes; enable
uart0 (GPIO43/44) on station_g2 and xiao so the console reroute works.
Single ZephyrUSBCDC module owns the usbd context, 1200-baud DFU
detection, and DTR transitions for both roles. The boot banner
now blocks on a k_event signalled by the usbd_msg_callback when
DTR transitions high — host attached → wakes immediately; no host
→ bounded timeout (2 s repeater, 1 s companion). Replaces the
fixed k_sleep delays in both mains.
Deletes the companion's 10 s DTR-polling work — line state changes
arrive as events now, same callback handles disconnect (resets V3
parser, flips active_iface) and DFU touch (reboots to bootloader).
Side effect: prod companion no longer enumerates a phantom CDC ACM
port (CONFIG_LOG=n skips the whole stack instead of auto-initing
an unused device).
1. CONFIG_BT_DEVICE_NAME_GATT_WRITABLE=y removed. The default GAP
Device Name write permission is plain BT_GATT_PERM_WRITE — no
bonding required (Zephyr gap_svc.c:158). Any connected peer
(bonded or not) could rename the device. Worse, a GAP write
updates bt_get_name() but NOT prefs.node_name, so the advertised
name wouldn't track the renamed value. Rename now flows
exclusively through CMD_SET_ADVERT_NAME, which is NUS-protected
(AUTHEN required) and properly propagates via
zephcore_ble_update_name() to prefs + GATT + adv data.
2. CONFIG_BT_DIS_FW_REV_STR synced from "1.13.0" to "v1.15.1-zephyr"
to match CompanionMesh.cpp CMD_DEVICE_QUERY's version string.
Comment added requiring the two to stay in sync.
stolen from Zephyr main:
1. Issue StopTimerOnPreamble=1 before SetRxDutyCycle so the chip's
timer is not reset on every preamble detect (per §13.1 of the
datasheet). Without this, duty cycle effectively never sleeps in
noisy RF and current draw spikes.
2. On IRQ_RX_TX_TIMEOUT during duty-cycle RX, re-arm via
sx126x_restart_rx() instead of falling through to set_sleep().
The old path silently killed duty cycle after the first preamble
false-positive.
3. On recv_duty_cycle(NULL) cancel, wake the radio before issuing
SetStandby — BUSY stays asserted during the sleep phase and the
standby command was being dropped.
Also adds a dc_timeout_restarts atomic counter incremented on the Fix 2
path, exposed end-to-end: sx126x_ext.h accessors → LoRaRadioBase vtable
→ SX126xRadio override → CommonCLICallbacks → RepeaterMesh. Query via
`get dc.restarts` on the repeater CLI; cleared by `clear stats`. High
values indicate a noisy environment or a too-loose preamble threshold.
(+increase ESP BT stack because future zephyr pin advance will trip that mine)
- only two leds, red and blue
- fix pin definition and polarity for leds
- remove unmapped pins
- swap buttons, so they match datasheet and marking on buttons