VictronBLE
A portable Arduino library for reading Victron Energy device data via Bluetooth Low Energy (BLE) advertisements — runs on both ESP32 and nRF52840.
⚠️ API CHANGE in v0.4 — not backwards compatible with v0.3.x
v0.4 is a major rework of the library internals: new callback API, reduced memory usage, non-blocking scanning. See 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.
Features
- ✅ Multi-Platform: One API for ESP32 and nRF52840; backend chosen at compile time
- ✅ 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)
- ✅ Clean API: Simple, intuitive interface with callback support
- ✅ No Pairing Required: Reads BLE advertisement data directly
- ✅ Low Power: Uses passive BLE scanning
- ✅ Full Data Access: Battery voltage, current, SOC, power, alarms, and more
- ✅ Production Ready: Error handling, data validation, debug logging
Supported Devices
- Solar Chargers: SmartSolar MPPT, BlueSolar MPPT (with BLE dongle)
- Battery Monitors: SmartShunt, BMV-712 Smart, BMV-700 series
- Inverters: MultiPlus, Quattro, Phoenix (with VE.Bus BLE dongle)
- DC-DC Converters: Orion Smart, Orion XS
- AC Chargers: Blue Smart IP22 / IP65 / IP67 chargers
- Others: Smart Battery Protect, Lynx Smart BMS, Smart Lithium batteries
Hardware Requirements
- An ESP32 (original / S / C series) or an nRF52840 board (Adafruit/Seeed Bluefruit core — e.g. Seeed XIAO nRF52840)
- 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.
Installation
PlatformIO
- Add to
platformio.ini(recommended — installs from the PlatformIO registry):
lib_deps =
scottp/victronble
Or install directly from git. Note the .git suffix is required — the bare
repository URL is not accepted by PlatformIO:
lib_deps =
https://gitea.sh3d.com.au/Sh3d/VictronBLE.git
- Or clone into your project's
libfolder:
cd lib
git clone https://gitea.sh3d.com.au/Sh3d/VictronBLE.git
nRF52840 board note
The nRF52 backend uses the Bluefruit library from the Adafruit/Seeed nRF52
core, so pick a board that uses that core. For the Seeed XIAO nRF52840, the
board files live in a community platform fork — use the *_adafruit variant
(the plain xiaoble variant uses the mbed core, which has no Bluefruit):
[env:xiao_nrf52840]
platform = https://github.com/maxgerhardt/platform-nordicnrf52
board = xiaoble_adafruit ; XIAO nRF52840 Sense: xiaoblesense_adafruit
framework = arduino
lib_deps = scottp/victronble
The Adafruit Feather nRF52840 (board = adafruit_feather_nrf52840) works out of
the box with the stock PlatformIO nordicnrf52 platform. The MultiDevice
example's platformio.ini includes ready-made ESP32 and nRF52 environments.
Arduino IDE
- Download or clone this repository
- Move the
VictronBLEfolder to your Arduino libraries directory - Restart Arduino IDE
Quick Start
1. Get Your Encryption Keys
Use the VictronConnect app to get your device's encryption key:
- Open VictronConnect
- Connect to your device
- Go to Settings → Product Info
- Enable "Instant readout via Bluetooth"
- Click "Show" next to "Instant readout details"
- Copy the encryption key (32 hexadecimal characters)
- Note your device's MAC address
2. Basic Example
#include <Arduino.h>
#include "VictronBLE.h"
VictronBLE victron;
// Callback — receives a VictronDevice*, switch on deviceType
void onVictronData(const VictronDevice* dev) {
if (dev->deviceType == DEVICE_TYPE_SOLAR_CHARGER) {
Serial.printf("Solar %s: %.2fV %.2fA %dW\n",
dev->name,
dev->solar.batteryVoltage,
dev->solar.batteryCurrent,
(int)dev->solar.panelPower);
}
}
void setup() {
Serial.begin(115200);
victron.begin(5); // 5 second scan duration
victron.setCallback(onVictronData);
// Add your device (replace with your MAC and key)
victron.addDevice(
"My MPPT", // Name
"AA:BB:CC:DD:EE:FF", // MAC address
"0123456789abcdef0123456789abcdef", // Encryption key
DEVICE_TYPE_SOLAR_CHARGER // Device type (optional, auto-detected)
);
}
void loop() {
victron.loop(); // Non-blocking, returns immediately
}
API Reference
VictronBLE Class
Initialization
bool begin(uint32_t scanDuration = 5);
Initialize BLE scanning. Returns true on success.
Parameters:
scanDuration: BLE scan window in seconds (default: 5)
Device Management
bool addDevice(const char* name, const char* mac, const char* hexKey,
VictronDeviceType type = DEVICE_TYPE_UNKNOWN);
Add a device to monitor (max 8 devices).
Parameters:
name: Friendly name for the devicemac: Device MAC address (format:"AA:BB:CC:DD:EE:FF"or"aabbccddeeff")hexKey: 32-character hex encryption key from VictronConnecttype: Device type (optional, auto-detected from BLE advertisement)
Returns: true on success
size_t getDeviceCount() const;
Get the number of configured devices.
Callback
void setCallback(VictronCallback cb);
Set a function pointer callback. Called when new data arrives from a device. The callback receives a const VictronDevice* — switch on deviceType to access the appropriate data union member.
typedef void (*VictronCallback)(const VictronDevice* device);
Configuration
void setMinInterval(uint32_t ms);
Set minimum callback interval per device (default: 1000ms). Callbacks are also suppressed when the device nonce hasn't changed (data unchanged).
void setDebug(bool enable);
Enable/disable debug output to Serial.
Main Loop
void loop();
Call in your main loop. Non-blocking — returns immediately if a scan is already running. Scan restarts automatically when it completes.
Data Structures
VictronDevice (main struct)
All device types share this struct. Access type-specific data via the union member matching deviceType.
struct VictronDevice {
char name[32];
char mac[13]; // 12 hex chars + null
VictronDeviceType deviceType;
int8_t rssi; // Signal strength (dBm)
uint32_t lastUpdate; // millis() of last update
bool dataValid;
union {
VictronSolarData solar;
VictronBatteryData battery;
VictronInverterData inverter;
VictronDCDCData dcdc;
};
};
VictronSolarData
struct VictronSolarData {
uint8_t chargeState; // SolarChargerState enum
uint8_t errorCode;
float batteryVoltage; // V
float batteryCurrent; // A
float panelPower; // W
uint16_t yieldToday; // Wh
float loadCurrent; // A (if load output present)
};
Charge States (chargeState values):
CHARGER_OFF, CHARGER_LOW_POWER, CHARGER_FAULT, CHARGER_BULK, CHARGER_ABSORPTION, CHARGER_FLOAT, CHARGER_STORAGE, CHARGER_EQUALIZE, CHARGER_INVERTING, CHARGER_POWER_SUPPLY, CHARGER_EXTERNAL_CONTROL
VictronBatteryData
struct VictronBatteryData {
float voltage; // V
float current; // A (+ charging, - discharging)
float temperature; // C (0 if aux is voltage)
float auxVoltage; // V (0 if aux is temperature)
uint16_t remainingMinutes;
float consumedAh; // Ah
float soc; // State of charge %
bool alarmLowVoltage;
bool alarmHighVoltage;
bool alarmLowSOC;
bool alarmLowTemperature;
bool alarmHighTemperature;
};
VictronInverterData
struct VictronInverterData {
float batteryVoltage; // V
float batteryCurrent; // A
float acPower; // W (+ inverting, - charging)
uint8_t state;
bool alarmLowVoltage;
bool alarmHighVoltage;
bool alarmHighTemperature;
bool alarmOverload;
};
VictronDCDCData
struct VictronDCDCData {
float inputVoltage; // V
float outputVoltage; // V
float outputCurrent; // A
uint8_t chargeState;
uint8_t errorCode;
};
VictronACChargerData
struct VictronACChargerData {
uint8_t chargeState; // SolarChargerState enum (shared charger states)
uint8_t errorCode;
float voltage1; // V (output 1)
float current1; // A (output 1)
float voltage2; // V (output 2, 0 if absent)
float current2; // A (output 2, 0 if absent)
float voltage3; // V (output 3, 0 if absent)
float current3; // A (output 3, 0 if absent)
float temperature; // C (0 if not available)
float acCurrent; // A (0 if not available)
};
Advanced Usage
Multiple Devices
void setup() {
victron.begin(5);
victron.setCallback(onVictronData);
// Add multiple devices (type is auto-detected from BLE advertisements)
victron.addDevice("MPPT 1", "AA:BB:CC:DD:EE:01", "key1...");
victron.addDevice("MPPT 2", "AA:BB:CC:DD:EE:02", "key2...");
victron.addDevice("SmartShunt", "AA:BB:CC:DD:EE:03", "key3...");
victron.addDevice("Inverter", "AA:BB:CC:DD:EE:04", "key4...");
}
Handling Multiple Device Types
void onVictronData(const VictronDevice* dev) {
switch (dev->deviceType) {
case DEVICE_TYPE_SOLAR_CHARGER:
Serial.printf("%s: %.2fV %dW\n", dev->name,
dev->solar.batteryVoltage, (int)dev->solar.panelPower);
break;
case DEVICE_TYPE_BATTERY_MONITOR:
Serial.printf("%s: %.2fV %.1f%%\n", dev->name,
dev->battery.voltage, dev->battery.soc);
break;
case DEVICE_TYPE_INVERTER:
Serial.printf("%s: %dW\n", dev->name, (int)dev->inverter.acPower);
break;
case DEVICE_TYPE_DCDC_CONVERTER:
Serial.printf("%s: %.2fV -> %.2fV\n", dev->name,
dev->dcdc.inputVoltage, dev->dcdc.outputVoltage);
break;
case DEVICE_TYPE_AC_CHARGER:
Serial.printf("%s: %.2fV %.2fA %.0fC\n", dev->name,
dev->acCharger.voltage1, dev->acCharger.current1,
dev->acCharger.temperature);
break;
default:
break;
}
}
Callback Throttling
void setup() {
victron.begin(5);
victron.setCallback(onVictronData);
victron.setMinInterval(2000); // Callback at most every 2 seconds per device
// ...
}
Troubleshooting
No Data Received
- Check encryption key: Must be exactly 32 hex characters from VictronConnect
- Verify MAC address: Use correct format (AA:BB:CC:DD:EE:FF)
- Enable Instant Readout: Must be enabled in VictronConnect settings
- Check range: BLE range is typically 10-30 meters
- Disconnect VictronConnect: App must be disconnected from device
- Enable debug:
victron.setDebug(true);to see detailed logs
Decryption Failures
- Encryption key must match exactly
- Victron may have multiple keys per device; use the current one
- Keys are case-insensitive hex
Poor Performance
- Reduce
scanDurationfor faster updates (minimum 1 second) - Increase
scanDurationfor better reliability (5-10 seconds recommended) - Ensure good signal strength (RSSI > -80 dBm)
Protocol Details
This library implements the Victron BLE Advertising protocol:
- Encryption: AES-128-CTR
- Update Rate: ~1 Hz from Victron devices
- No Pairing: Reads broadcast advertisements
- No Connection: Extremely low power consumption
Based on official Victron BLE documentation.
Architecture & Portability
The library keeps everything platform-independent except the BLE radio:
src/
├── VictronBLE.{h,cpp} Common API, device management, payload decoding
├── crypto/vble_aes.{h,c} Bundled AES-128-CTR (no external dependency)
├── esp32/ ESP32 backend — Bluedroid BLEScan
└── nrf52/ nRF52 backend — Bluefruit passive scan
- 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. - 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.
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().
Examples
See the examples/ directory for:
- MultiDevice: Monitor multiple devices with callbacks. One sketch, multiple
PlatformIO environments — builds for ESP32 (
esp32dev, …) and nRF52840 (xiao_nrf52840,adafruit_feather_nrf52840). - Logger: Change-detection logging for Solar Charger data
- Repeater: Collect BLE data and re-transmit via ESPNow broadcast
- Receiver: Receive ESPNow packets from a Repeater and display data
- FakeRepeater: Generate test ESPNow packets without real Victron hardware
Contributing
The primary repository is hosted on Gitea, with a mirror on GitHub at https://github.com/SH3D/VictronBLE. Since the Gitea instance does not currently allow public sign-ups, please raise issues and pull requests on the GitHub mirror.
Contributions welcome! Please:
- Fork the GitHub mirror
- Create a feature branch
- Test thoroughly on real hardware
- Submit a pull request
Credits
This library was inspired by and builds upon excellent prior work:
- hoberman's Victron BLE Advertising examples: ESP32 examples demonstrating Victron BLE protocol implementation
- keshavdv's victron-ble Python library: Comprehensive Python implementation of Victron BLE protocol
- Protocol documentation and specifications by Victron Energy
License
MIT License - see LICENSE file for details
Copyright (c) 2025 Scott Penrose scottp@dd.com.au
- https://www.sh3d.com.au/ - Sh3d
- https://www.dd.com.au/ - Digital Dimensions
Disclaimer
This library is not officially supported by Victron Energy. Use at your own risk.
Version History
See VERSIONS file for detailed changelog and release history.
Support
- 📫 Report issues on the GitHub mirror (the Gitea instance does not currently allow public sign-ups). Bug reports, device decode problems and new device requests are all welcome — debug log output is very helpful.
- 📖 Check the examples directory
- 🔧 Enable debug mode for diagnostics
- 📚 See Victron documentation