Examples and samples ready for Zephyr release

This commit is contained in:
2026-08-26 00:58:57 +10:00
parent 67ff8721c0
commit 22af47854d
20 changed files with 1158 additions and 29 deletions
+125 -19
View File
@@ -1,22 +1,22 @@
# VictronBLE
A portable Arduino library for reading Victron Energy device data via Bluetooth Low Energy (BLE) advertisements — runs on both **ESP32** and **nRF52840**.
A portable library for reading Victron Energy device data via Bluetooth Low Energy (BLE) advertisements. Use it as an **Arduino** library on ESP32 and nRF52840, as a **Zephyr** module on any Bluetooth-capable board, or drop the **pure C99 core** into anything else.
v0.6 adds **multi-platform support** (ESP32 + nRF52840) via a hardware-abstracted BLE backend and dependency-free bundled crypto. (v0.5 brought the decoding accuracy fixes and AC charger support; v0.4 reworked the internals — function-pointer callback API, reduced memory usage, non-blocking scanning.) See [VERSIONS](VERSIONS) for full details. A stable **v1.0** release with a consistent, long-term API is coming soon.
v0.7 splits the library into a **dependency-free pure C99 core** (decode + decrypt, no BLE stack, no allocation, no I/O) plus thin platform layers: the Arduino C++ wrapper, and a **Zephyr module** with its own observer API. (v0.6 added multi-platform support for ESP32 + nRF52840; v0.5 brought the decoding accuracy fixes and AC charger support.) See [VERSIONS](VERSIONS) for full details. A stable **v1.0** release with a consistent, long-term API is coming soon.
---
Why another library? Most of the Victron BLE examples are built into other frameworks (e.g. ESPHome) or are locked to a single chip. The goal here is one library that works across ESP32 and nRF52 (and is easy to extend to more), usable standalone or inside ESPHome and other frameworks, with a long-term plan to move others onto it and improve the code with many eyes.
Supports **ESP32** (original, S and C series — tested on older ESP32, ESP32-S3 and ESP32-C3) and **nRF52840** (Adafruit/Seeed Bluefruit core, e.g. Seeed XIAO nRF52840). All decoding and decryption is shared; only a thin BLE scanning backend is platform-specific (`src/esp32/`, `src/nrf52/`), so other chipsets can be added by implementing one more backend.
Under Arduino it supports **ESP32** (original, S and C series — tested on older ESP32, ESP32-S3 and ESP32-C3) and **nRF52840** (Adafruit/Seeed Bluefruit core, e.g. Seeed XIAO nRF52840). Under **Zephyr** it is board-agnostic — anything with a Bluetooth controller and the observer role (tested on nRF52840DK and RAK4631). All decoding and decryption is shared; only the BLE scanning layer is platform-specific, so other stacks can be added by implementing one more backend.
## Features
-**Multi-Platform**: One API for ESP32 and nRF52840; backend chosen at compile time
-**Multi-Platform**: ESP32 and nRF52840 under Arduino, any Bluetooth board under Zephyr
-**No External Dependencies**: Bundled AES-128-CTR — no mbedTLS or crypto library needed
-**Multiple Device Support**: Monitor multiple Victron devices simultaneously
-**All Device Types**: Solar chargers, battery monitors, inverters, DC-DC converters, AC chargers
-**Framework Friendly**: Works with Arduino (and ESP-IDF on ESP32)
-**Framework Friendly**: Arduino library, Zephyr module, or the bare C core
-**Clean API**: Simple, intuitive interface with callback support
-**No Pairing Required**: Reads BLE advertisement data directly
-**Low Power**: Uses passive BLE scanning
@@ -35,11 +35,13 @@ Supports **ESP32** (original, S and C series — tested on older ESP32, ESP32-S3
## Hardware Requirements
- An ESP32 (original / S / C series) **or** an nRF52840 board (Adafruit/Seeed
Bluefruit core — e.g. Seeed XIAO nRF52840)
Bluefruit core — e.g. Seeed XIAO nRF52840) for the Arduino API
- Or any Zephyr-supported board with a Bluetooth controller (tested on
nRF52840DK and RAK4631)
- Victron devices with BLE "Instant Readout" enabled
The BLE backend is selected automatically at compile time from the board's
architecture — no code changes are needed to switch platforms.
Under Arduino the BLE backend is selected automatically at compile time from
the board's architecture — no code changes are needed to switch platforms.
## Installation
@@ -87,6 +89,78 @@ example's `platformio.ini` includes ready-made ESP32 and nRF52 environments.
2. Move the `VictronBLE` folder to your Arduino libraries directory
3. Restart Arduino IDE
### Zephyr
The repository is a Zephyr module (`zephyr/module.yml`), so Zephyr finds it
automatically once it is in your workspace. Add it to your `west.yml`:
```yaml
manifest:
remotes:
- name: sh3d
url-base: https://gitea.sh3d.com.au/Sh3d
projects:
- name: VictronBLE
remote: sh3d
revision: main
path: modules/lib/victronble
```
Then `west update`, and enable it in your `prj.conf`:
```
CONFIG_BT=y
CONFIG_BT_OBSERVER=y
CONFIG_VICTRONBLE=y
CONFIG_CBPRINTF_FP_SUPPORT=y # only if you print the float fields
```
`CONFIG_VICTRONBLE` depends on `CONFIG_BT_OBSERVER`, and the application must
call `bt_enable()` before `victronble_start()` — the library scans, it does not
own the Bluetooth stack.
To build against a local checkout that is not in the manifest, point Zephyr at
it directly instead:
```sh
west build -b nrf52840dk/nrf52840 -d /tmp/build /path/to/app \
-- -DZEPHYR_EXTRA_MODULES=/path/to/VictronBLE
```
#### Zephyr API
```c
#include "victronble_zephyr.h"
int victronble_cb_register(struct victronble_cb *cb);
int victronble_device_add(const bt_addr_le_t *addr, const uint8_t key[16]);
int victronble_device_remove(const bt_addr_le_t *addr);
void victronble_watch_set(bool on); /* log every advert, no keys needed */
int victronble_start(void);
int victronble_stop(void);
void victronble_get_stats(struct victronble_stats *out);
```
Records are decoded on a dedicated thread, not the Bluetooth RX thread, so
your `record` callback can log freely without stalling the controller.
#### Kconfig options
| Option | Default | Purpose |
|---|---|---|
| `VICTRONBLE_MAX_DEVICES` | 4 | Size of the monitored-device registry |
| `VICTRONBLE_QUEUE_DEPTH` | 8 | Adverts buffered between the RX and decode threads |
| `VICTRONBLE_THREAD_STACK_SIZE` | 2048 | Decode thread stack |
| `VICTRONBLE_THREAD_PRIORITY` | 10 | Decode thread priority (preemptible) |
| `VICTRONBLE_DEDUP` | y | Suppress repeated adverts by nonce |
| `VICTRONBLE_SCAN_INTERVAL` | 2048 | Scan interval, 0.625 ms units (1.28 s) |
| `VICTRONBLE_SCAN_WINDOW` | 18 | Scan window, 0.625 ms units (11.25 ms) |
| `VICTRONBLE_LOG_LEVEL` | — | Standard Zephyr per-module log level |
Working applications are in [`samples/`](samples/) — start with
[`samples/scan`](samples/scan/) to discover your devices, then
[`samples/observer`](samples/observer/) to read them.
## Quick Start
### 1. Get Your Encryption Keys
@@ -379,6 +453,12 @@ void setup() {
5. **Disconnect VictronConnect**: App must be disconnected from device
6. **Enable debug**: `victron.setDebug(true);` to see detailed logs
On **Zephyr**, the stats line from `victronble_get_stats()` narrows this down
fast. If `adverts` climbs but `queued` and `decoded` stay at zero, the device
is being heard but never matched: check the Bluetooth address **type**, which
must be `random` for Victron devices, not `public`. Or run `samples/scan`,
which needs neither addresses nor keys.
### Decryption Failures
- Encryption key must match exactly
@@ -407,31 +487,45 @@ Based on official [Victron BLE documentation](https://www.victronenergy.com/live
The library keeps everything platform-independent except the BLE radio:
```
include/
├── victronble.h Pure C99 core API — decode one advert, no I/O
└── victronble_zephyr.h Zephyr observer API
src/
├── VictronBLE.{h,cpp} Common API, device management, payload decoding
├── victronble_core.c Decrypt + parse; no BLE, no alloc, reentrant
├── crypto/vble_aes.{h,c} Bundled AES-128-CTR (no external dependency)
├── VictronBLE.{h,cpp} Arduino C++ wrapper over the core
├── esp32/ ESP32 backend — Bluedroid BLEScan
── nrf52/ nRF52 backend — Bluefruit passive scan
── nrf52/ nRF52 backend — Bluefruit passive scan
└── victronble_zephyr.c Zephyr backend — passive scan + decode thread
CMakeLists.txt, Kconfig Zephyr module glue (ignored by PlatformIO)
```
- **A portable core.** `victronble_core.c` is C99 with no dependencies: no
Arduino, no BLE stack, no allocation, no I/O, reentrant. Give it a
manufacturer-data blob and a key, get a record back. Everything else —
scanning, device registries, rate limiting, logging — belongs to the
platform layers. `examples/NativeDecode` runs it on a PC.
- **One BLE HAL.** Each backend extracts the manufacturer data, MAC and RSSI
from a scan result and calls the shared `onAdvertisement()`. All decryption and
decoding is common code. The correct backend is selected automatically at
compile time from the board architecture (`ARDUINO_ARCH_ESP32` /
`ARDUINO_ARCH_NRF52`) — there is nothing platform-specific in your sketch.
from a scan result and hands it to the core. Under Arduino the correct
backend is selected automatically at compile time from the board
architecture (`ARDUINO_ARCH_ESP32` / `ARDUINO_ARCH_NRF52`) — there is
nothing platform-specific in your sketch. Under Zephyr the backend is
`victronble_zephyr.c`, selected by `CONFIG_VICTRONBLE`.
- **No external crypto.** AES-128-CTR is bundled (a trimmed, NIST-verified
tiny-AES), so the library no longer depends on mbedTLS or any crypto library
and builds identically on every target.
- **Adding a platform** means implementing one more backend (scan → extract →
`onAdvertisement`); the rest is reused unchanged.
hand to the core); the rest is reused unchanged.
> The data callback runs in the BLE event context (the scan task on ESP32, the
> SoftDevice/Bluefruit handler on nRF52). Keep work in the callback light — copy
> what you need and process it from `loop()`.
> Under Arduino the data callback runs in the BLE event context (the scan task
> on ESP32, the SoftDevice/Bluefruit handler on nRF52). Keep work in the
> callback light — copy what you need and process it from `loop()`.
> Under Zephyr this does not apply: records are delivered from the library's
> own decode thread, so callbacks may log and block.
## Examples
See the `examples/` directory for:
Arduino / PlatformIO, in [`examples/`](examples/):
- **MultiDevice**: Monitor multiple devices with callbacks. One sketch, multiple
PlatformIO environments — builds for ESP32 (`esp32dev`, …) and nRF52840
@@ -441,6 +535,18 @@ See the `examples/` directory for:
- **Receiver**: Receive ESPNow packets from a Repeater and display data
- **FakeRepeater**: Generate test ESPNow packets without real Victron hardware
Zephyr, in [`samples/`](samples/):
- **scan**: List every Victron device advertising nearby. No keys needed —
run this first to find your MAC addresses.
- **observer**: Monitor known devices and log every decoded field. The
reference for the Zephyr API.
No hardware at all, in [`examples/`](examples/):
- **NativeDecode**: Decode an advertisement on your PC with plain `make`.
Good for checking a key or a sniffer capture before you flash anything.
## Contributing
The primary repository is hosted on [Gitea](https://gitea.sh3d.com.au/Sh3d/VictronBLE),