The RAK4631 overlay declared no zephcore,rtc-i2c candidate, so a fitted RAK12002 (an RV3028 at 0x52) was never probed or written, and every power cut started the node at 1970. Declaring it is not enough: the RV3028 ships with backup switchover disabled (EEPROM 37h BSM = 00), so a power cut still loses the time. The overlay now declares the RV3028 with the zero-mask rtc-i2c.dtsi uses for it, and a new optional rv3028-eeprom-config descriptor property holds (register, mask, value) triplets for its EEPROM configuration registers 35h-37h. The RAK4631 sets direct switchover with the trickle charger on (TCE, BSM = 01, TCR 3 kOhm), as the RV-3028-C7 Application Manual (Rev. 1.4) 7.3 prescribes for a capacitor backup, FEDE set as 3.15.6 says it always should be, and CLKOUT off. The configuration has to live in EEPROM: the chip reloads its configuration RAM from EEPROM at power-on and every midnight (4.6.2). The store runs only when a read identified the chip (RTC_FOUND, not an adoption on a failed second read) and a BSF-checked time read just before it showed no switchover. It follows 3.15.6 (BSM 00 or 10 for any EEPROM access): EERD held, switchover off in RAM, each configured byte compared against the EEPROM with a single-byte read and written only if it differs, and a closing Refresh that restores the stored switchover mode, read back on every boot. 36h, the factory trim, is never written. Switchover is turned back on only once EEbusy reads 0; otherwise it stays off until a retry or the daily refresh. A failed store sets the config in RAM (also only once EEbusy reads 0). A failed or skipped store is retried every 10 minutes, at most 3 times per boot, each retry identifying the chip again first. A switchover disables the chip's I2C mid-transfer (4.2), so at a node with rv3028-eeprom-config the time is read at boot and written on each sync with BSF cleared before and checked after. A read that saw a switchover gives no time. A write sets year 00h first and the real year last, so a power loss cutting it between its first and last byte leaves year 2000 and the next boot takes no time from it, and a write not confirmed is repeated every 5 s, up to 12 times, with the time run on. All of the RV3028 code is compiled only when some node carries rv3028-eeprom-config, so other boards, thinknode_m1 among them, build the same RTC object as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ZephCore
MeshCore, but it runs on Zephyr.
Flash it · Boards · CLI · Design · Release notes
ZephCore is firmware for MeshCore LoRa mesh nodes. It talks to the same mesh, pairs with the same MeshCore apps and answers the same CLI commands as the original firmware. What is different is underneath: instead of Arduino, it is built on Zephyr RTOS.
If you already run MeshCore, a ZephCore node is just another node on your mesh. Your neighbours will not notice, and neither will your phone.
It is a community port, not the official firmware. The protocol and the apps belong to the MeshCore project; ZephCore follows them and does not change what goes over the air.
Note
About 99% of this code was written by AI. A human decides what gets built and tests it on real radios. More on that at the bottom.
Get it on your device
- Web flasher (easiest): open zephcore.meshcore.dev in Chrome or Edge, pick your board and role, plug in, flash.
- Mesh America Device Configurator: apps.meshamerica.com carries the same builds.
- By hand: download from Releases. nRF52 boards
take the
.uf2by drag and drop; ESP32 boards take the-merged.binat offset 0 with esptool.
Warning
Coming from stock MeshCore (or going back)? On nRF52 the bootloader stays the same, so there is nothing extra to flash. But settings do not carry over between the two firmwares in either direction: expect to set the node up again.
Then pair with the MeshCore app as usual (default BLE PIN 123456). Not sure your board is covered? Check
the supported boards list.
What a node can be
| Role | What it does | How you talk to it |
|---|---|---|
| Companion | Your personal node: contacts, channels, offline message queue | MeshCore app over BLE or USB (WiFi on some ESP32 boards) |
| Repeater | Relays packets for everyone else | Text CLI over USB, or remote admin over the mesh |
| Room server | A shared message board that keeps posts for clients who were away | Same CLI as the repeater; users log in from the app |
| Observer | Listens only and publishes what it hears to MQTT over WiFi (ESP32) | Same CLI |
One role per firmware image; you choose it when you flash.
What you get on top of stock MeshCore
All of this is local behaviour. Nothing here changes the wire protocol, so mixed meshes work fine.
- Self-tuning retransmit timing. Repeaters measure how crowded their neighbourhood is and space their
retransmits to match, instead of using fixed
txdelayvalues. (ADR 0007) - Adaptive listen-before-talk. Channel-activity detection calibrates itself to the local noise. (ADAPTIVE_CAD.md)
- Optional RX duty cycle. The radio chip itself naps between preamble checks to cut receive current, on radios that support it. (ADR 0006)
- Mesh time sync. Nodes without GPS can agree on the time. Off by default. (MESHTIMESYNC.md)
- An observer role. A listen-only node that publishes everything it hears to MQTT over WiFi (ESP32).
- MQTT uplink on a repeater. An ESP32 repeater can report what it hears to MQTT over WiFi while it keeps repeating.
- LEDs you control. Turn them all off with
set leds off, or choose what each one shows: blink on transmit, on receive or both, and a heartbeat that can signal unread messages. The receive blink tells you at a glance whether a repeater is hearing anything. - 2.4 GHz on LR2021 boards. Set a 2.4 GHz frequency and the radio switches to its high-band path on its own.
- Runs on Linux too. The same code runs as a normal process on a Raspberry Pi or Femtofox with a real SX1262 attached. (LINUX_NATIVE.md)
Hardware
| Platform | Boards | With published firmware |
|---|---|---|
| nRF52840 | 22 | 21 |
| ESP32 family | 16 | 14 |
| nRF54L | 4 | 3 |
| EFR32MG24 | 1 | 1 |
| STM32WL | 1 | 1 |
| Native Linux (presets) | 3 | 3 |
RAK4631, SenseCAP T1000-E, Wio Tracker L1, Heltec T114 and V3/V4, XIAO ESP32-S3, nRF54L15 and MG24, Seeed LoRa-E5, Femtofox, Raspberry Pi with a RAK6421 HAT, and more. The full list with exact build strings is in docs/supported_boards.md. Want to add one? See the board porting guide.
Build it yourself
The quickest start is the dev container: open the repo in VS Code and choose Reopen in Container. Otherwise install the Zephyr SDK and west, then:
# once
west init -l zephcore
west update
# a companion, then a repeater
west build -b wio_tracker_l1 zephcore --pristine
west build -b rak4631 zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf"
west flash
Roles and extras are config fragments in zephcore/boards/common/, added through EXTRA_CONF_FILE
(separate several with ;):
| Fragment | Gives you |
|---|---|
| (none) | Companion |
repeater.conf |
Repeater |
room_server.conf |
Room server |
observer.conf |
Observer (ESP32) |
debug.conf |
Logging and asserts |
packet_logging.conf |
One clean RAW/RX/TX line per packet, nothing else |
Every other variant, flashing per platform (ESP32 blobs and --sysbuild, nRF54L, MG24) and the Kconfig
options are in docs/BUILDING.md.
For Zephyr people
ZephCore is a plain west workspace application, and it tries to be a well-behaved one:
- Zephyr is pinned to a
maincommit, bumped deliberately. (ADR 0005) - A small patch set in
zephcore/patches/zephyris applied at configure time and fails the build loudly if it stops applying. One patch owns each upstream file. If you maintain one of those files, that directory is the list of things we would love to see fixed upstream. - Radios: the in-tree SX126x and SX127x drivers, plus out-of-tree LR11xx and LR20xx drivers, behind one adapter.
- Boards are devicetree and Kconfig only. A board directory declares hardware, never role policy;
config is layered from
prj.confdown toboard.conf. - ESP32: simple boot for companions, MCUboot with sysbuild where WiFi OTA is involved (ADR 0001); native USB on S3 companions (ADR 0002).
- Tests: the mesh core builds and runs on the host under ASan and UBSan in CI.
Documentation
| DESIGN.md | The system view: roles, layers, rules |
| ARCHITECTURE.md | Component reference |
| adr/ | Decisions and why they were made |
| BUILDING.md | Every build variant, platform notes, Kconfig options |
| Repeater_CLI_commands.md | Every CLI command |
| supported_boards.md | Boards and build strings |
| LINUX_NATIVE.md | Running on a Pi or Femtofox |
| releasenotes/ | What changed in each release |
Who wrote this?
Mostly robots. Around 99% of the code in this repository was written by AI (Claude and Cursor). One human points, argues, says "no, not like that", and flashes the result onto a bench full of real radios to see whether it actually works.
The ideas that matter are not ours either: the mesh protocol, the apps and most new features come from the MeshCore project and the people doing the hard thinking there.
What keeps it honest: changes are tested on real hardware before release (mostly nRF boards on our own bench; the ESP32 boards are largely tested by the community, so reports are very welcome), the mesh core has host tests in CI, and the decisions are written down in ADRs so the next robot does not undo them.
Comic by TWONKS. Borrowed with love; go read the rest.
License
MIT, same as upstream MeshCore; see LICENSE, which also lists the vendored pieces (Monocypher, Zephyr patches). Logo by recrof, WTFPL.
