A secure embedded architecture for plant monitoring systems, implemented as a CHERIoT-RTOS application targeting the Sonata board. Leverages CHERI capabilities for compartmentalized IoT communication (MQTT over TLS).
This project depends on sonata-software for the CHERIoT SDK, network stack, and build toolchain. Clone it as a sibling directory:
root/
├── sonata-software/ # provides SDK, network stack, nix dev environment
└── cheriot-plantnode/ # this project
Enter the sonata-software nix development environment, which provides cheriot-clang, xmake, llvm-strip, uf2conv, and all other required tools:
cd sonata-software
nix developFrom inside the nix shell, run xmake pointing at this project:
# from the sonata-software/ directory (while inside nix develop)
xmake -P ../cheriot-plantnodeIPv4-only networks: disable IPv6 before building to avoid connection issues:
xmake config -P ../cheriot-plantnode --IPv6=n
This compiles the firmware and automatically produces three UF2 images (one per flash slot) under cheriot-plantnode/build/uf2/:
build/uf2/
├── plantnode.slot1.uf2 # base address 0x00000000
├── plantnode.slot2.uf2 # base address 0x10000000
└── plantnode.slot3.uf2 # base address 0x20000000
Plug in the Sonata board over USB. It will appear as a USB mass-storage drive. Copy the UF2 for the desired slot to the drive:
cp ./build/uf2/plantnode.slot2.uf2 /run/media/$USER/SONATA/firmware.uf2The board reboots automatically and boots from the selected slot. Use the slot-select switch on the board to choose which slot to run at startup.
Debug output is printed over UART at 921600 baud. Connect with any serial terminal:
picocom -b 921600 /dev/ttyUSB1 # adjust device as needed
# or
screen /dev/ttyUSB1 921600On a successful start you should see:
PlantNode: === PlantNode firmware starting ===
PlantNode MQTT: Starting network stack...
PlantNode MQTT: Network stack started.
PlantNode MQTT: Synchronising time via SNTP...
...
The charter/ directory contains a Python application that acts as the plotter for PlantNode devices. It connects to the same MQTT broker over TLS, completes the Noise-N key exchange, and decrypts sensor telemetry in real time. Cryptography is handled with libhydrogen in its native form on the sonata, and wrapped into the cydrogen wrapper for the charter.
The charter uses two sets of cryptographic material, both stored under charter/keys/ (excluded from version control):
keys/ca.crt— the MQTT broker's CA certificate, used to authenticate the TLS connection to the broker. Copy it from the broker's certificate directory (e.g.../mosquitto_broker/certs/ca.crt).keys/verifier.pub/keys/verifier.key— the Noise-N static keypair that identifies this verifier. The public key is compiled into the device firmware (kVerifierPublicKeyinsrc/crypto.cc); the secret key stays on the host and is never shared.
See charter/README.md for setup and usage instructions.
cheriot-plantnode/
├── src/ # Firmware source (CHERIoT compartments)
├── charter/ # Host-side verifier (Python)
│ ├── keys/ # CA cert + Noise-N verifier keypair (not committed)
│ └── README.md
├── build/
│ └── uf2/ # generated UF2 images (after build)
├── xmake.lua # build configuration
├── README.md
└── LICENSE