Compare commits
3 Commits
4944757903
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e7024d9983 | |||
| 261cc0d1fe | |||
| 39a89c816c |
@@ -198,3 +198,86 @@ a843eb9 Keep v0.3.1
|
|||||||
- examples/Receiver/src/main.cpp
|
- examples/Receiver/src/main.cpp
|
||||||
- examples/Repeater/src/main.cpp
|
- examples/Repeater/src/main.cpp
|
||||||
|
|
||||||
|
|
||||||
|
### Session: 2026-02-28 14:32
|
||||||
|
**Commits:**
|
||||||
|
```
|
||||||
|
4944757 Fix to be non blocking without tasks
|
||||||
|
31765c7 Update notes
|
||||||
|
84d153c Single callback version - vastly simplified.
|
||||||
|
```
|
||||||
|
**Modified files:**
|
||||||
|
- examples/Logger/src/main.cpp
|
||||||
|
- examples/MultiDevice/src/main.cpp
|
||||||
|
- examples/Repeater/src/main.cpp
|
||||||
|
- library.json
|
||||||
|
- library.properties
|
||||||
|
- src/VictronBLE.cpp
|
||||||
|
- src/VictronBLE.h
|
||||||
|
|
||||||
|
|
||||||
|
### Session: 2026-02-28 14:33
|
||||||
|
**Commits:**
|
||||||
|
```
|
||||||
|
4944757 Fix to be non blocking without tasks
|
||||||
|
31765c7 Update notes
|
||||||
|
84d153c Single callback version - vastly simplified.
|
||||||
|
```
|
||||||
|
**Modified files:**
|
||||||
|
- .claude/CLAUDE.md
|
||||||
|
- VERSIONS
|
||||||
|
- examples/Logger/src/main.cpp
|
||||||
|
- examples/MultiDevice/src/main.cpp
|
||||||
|
- examples/Repeater/src/main.cpp
|
||||||
|
- library.json
|
||||||
|
- library.properties
|
||||||
|
- src/VictronBLE.cpp
|
||||||
|
- src/VictronBLE.h
|
||||||
|
|
||||||
|
|
||||||
|
### Session: 2026-02-28 14:36
|
||||||
|
**Commits:**
|
||||||
|
```
|
||||||
|
4944757 Fix to be non blocking without tasks
|
||||||
|
31765c7 Update notes
|
||||||
|
```
|
||||||
|
**Modified files:**
|
||||||
|
- .claude/CLAUDE.md
|
||||||
|
- VERSIONS
|
||||||
|
- examples/Logger/src/main.cpp
|
||||||
|
- examples/MultiDevice/src/main.cpp
|
||||||
|
- examples/Repeater/src/main.cpp
|
||||||
|
- library.json
|
||||||
|
- library.properties
|
||||||
|
- src/VictronBLE.cpp
|
||||||
|
- src/VictronBLE.h
|
||||||
|
|
||||||
|
|
||||||
|
### Session: 2026-02-28 14:40
|
||||||
|
**Commits:**
|
||||||
|
```
|
||||||
|
39a89c8 Versions v0.4 ready for release
|
||||||
|
4944757 Fix to be non blocking without tasks
|
||||||
|
31765c7 Update notes
|
||||||
|
```
|
||||||
|
**Modified files:**
|
||||||
|
- .claude/CLAUDE.md
|
||||||
|
- README.md
|
||||||
|
- VERSIONS
|
||||||
|
- library.json
|
||||||
|
- library.properties
|
||||||
|
|
||||||
|
|
||||||
|
### Session: 2026-02-28 14:48
|
||||||
|
**Commits:**
|
||||||
|
```
|
||||||
|
261cc0d Improve readme ready for v0.4 release
|
||||||
|
39a89c8 Versions v0.4 ready for release
|
||||||
|
4944757 Fix to be non blocking without tasks
|
||||||
|
31765c7 Update notes
|
||||||
|
```
|
||||||
|
**Modified files:**
|
||||||
|
- .claude/CLAUDE.md
|
||||||
|
- README.md
|
||||||
|
- REVIEW.md
|
||||||
|
|
||||||
|
|||||||
279
README.md
279
README.md
@@ -2,16 +2,15 @@
|
|||||||
|
|
||||||
ESP32 library for reading Victron Energy device data via Bluetooth Low Energy (BLE) advertisements.
|
ESP32 library for reading Victron Energy device data via Bluetooth Low Energy (BLE) advertisements.
|
||||||
|
|
||||||
**⚠️ INITIAL RELEASE - LIMITED TESTING DONE**
|
**⚠️ API CHANGE in v0.4 — not backwards compatible with v0.3.x**
|
||||||
|
|
||||||
This is an initial release (v0.3.1) and has been tested with MPPT on an ESP32-S3 and ESP32-C3.
|
v0.4 is a major rework of the library internals: new 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.
|
||||||
Use with caution and please report any issues you encounter. Testing and feedback are greatly appreciated!
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Why another library? Most of the Victron BLE examples are built into other frameworks (e.g. ESPHome) and I want a library that can be used in all ESP32 systems, including ESPHome or other frameworks. With long term plan to try and move others to this library and improve code with many eyes.
|
Why another library? Most of the Victron BLE examples are built into other frameworks (e.g. ESPHome) and I want a library that can be used in all ESP32 systems, including ESPHome or other frameworks. With long term plan to try and move others to this library and improve code with many eyes.
|
||||||
|
|
||||||
Currently supportin ESP32 S and C series (tested on older ESP32, and ESP32-S3 and ESP32-C3). Other chipsets can be added with abstraction of Bluetooth code.
|
Currently supporting ESP32 S and C series (tested on older ESP32, ESP32-S3 and ESP32-C3). Other chipsets can be added with abstraction of Bluetooth code.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -81,38 +80,34 @@ Use the VictronConnect app to get your device's encryption key:
|
|||||||
|
|
||||||
VictronBLE victron;
|
VictronBLE victron;
|
||||||
|
|
||||||
// Callback for data updates
|
// Callback — receives a VictronDevice*, switch on deviceType
|
||||||
class MyCallback : public VictronDeviceCallback {
|
void onVictronData(const VictronDevice* dev) {
|
||||||
public:
|
if (dev->deviceType == DEVICE_TYPE_SOLAR_CHARGER) {
|
||||||
void onSolarChargerData(const SolarChargerData& data) override {
|
Serial.printf("Solar %s: %.2fV %.2fA %dW\n",
|
||||||
Serial.printf("Solar: %.2fV, %.2fA, %dW\n",
|
dev->name,
|
||||||
data.batteryVoltage,
|
dev->solar.batteryVoltage,
|
||||||
data.batteryCurrent,
|
dev->solar.batteryCurrent,
|
||||||
data.panelPower);
|
(int)dev->solar.panelPower);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
MyCallback callback;
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
||||||
// Initialize library
|
|
||||||
victron.begin(5); // 5 second scan duration
|
victron.begin(5); // 5 second scan duration
|
||||||
victron.setCallback(&callback);
|
victron.setCallback(onVictronData);
|
||||||
|
|
||||||
// Add your device (replace with your MAC and key)
|
// Add your device (replace with your MAC and key)
|
||||||
victron.addDevice(
|
victron.addDevice(
|
||||||
"My MPPT", // Name
|
"My MPPT", // Name
|
||||||
"AA:BB:CC:DD:EE:FF", // MAC address
|
"AA:BB:CC:DD:EE:FF", // MAC address
|
||||||
"0123456789abcdef0123456789abcdef", // Encryption key
|
"0123456789abcdef0123456789abcdef", // Encryption key
|
||||||
DEVICE_TYPE_SOLAR_CHARGER // Device type
|
DEVICE_TYPE_SOLAR_CHARGER // Device type (optional, auto-detected)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
victron.loop();
|
victron.loop(); // Non-blocking, returns immediately
|
||||||
delay(100);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -125,130 +120,113 @@ void loop() {
|
|||||||
```cpp
|
```cpp
|
||||||
bool begin(uint32_t scanDuration = 5);
|
bool begin(uint32_t scanDuration = 5);
|
||||||
```
|
```
|
||||||
Initialize BLE and start scanning. Returns `true` on success.
|
Initialize BLE scanning. Returns `true` on success.
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `scanDuration`: BLE scan duration in seconds (default: 5)
|
- `scanDuration`: BLE scan window in seconds (default: 5)
|
||||||
|
|
||||||
#### Device Management
|
#### Device Management
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
bool addDevice(String name, String macAddress, String encryptionKey,
|
bool addDevice(const char* name, const char* mac, const char* hexKey,
|
||||||
VictronDeviceType expectedType = DEVICE_TYPE_UNKNOWN);
|
VictronDeviceType type = DEVICE_TYPE_UNKNOWN);
|
||||||
```
|
```
|
||||||
Add a device to monitor.
|
Add a device to monitor (max 8 devices).
|
||||||
|
|
||||||
**Parameters:**
|
**Parameters:**
|
||||||
- `name`: Friendly name for the device
|
- `name`: Friendly name for the device
|
||||||
- `macAddress`: Device MAC address (format: "AA:BB:CC:DD:EE:FF")
|
- `mac`: Device MAC address (format: `"AA:BB:CC:DD:EE:FF"` or `"aabbccddeeff"`)
|
||||||
- `encryptionKey`: 32-character hex encryption key from VictronConnect
|
- `hexKey`: 32-character hex encryption key from VictronConnect
|
||||||
- `expectedType`: Device type (optional, for validation)
|
- `type`: Device type (optional, auto-detected from BLE advertisement)
|
||||||
|
|
||||||
**Returns:** `true` on success
|
**Returns:** `true` on success
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void removeDevice(String macAddress);
|
size_t getDeviceCount() const;
|
||||||
```
|
|
||||||
Remove a device from monitoring.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
size_t getDeviceCount();
|
|
||||||
```
|
```
|
||||||
Get the number of configured devices.
|
Get the number of configured devices.
|
||||||
|
|
||||||
#### Data Access
|
#### Callback
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
bool getSolarChargerData(String macAddress, SolarChargerData& data);
|
void setCallback(VictronCallback cb);
|
||||||
bool getBatteryMonitorData(String macAddress, BatteryMonitorData& data);
|
|
||||||
bool getInverterData(String macAddress, InverterData& data);
|
|
||||||
bool getDCDCConverterData(String macAddress, DCDCConverterData& data);
|
|
||||||
```
|
```
|
||||||
Get latest data for a specific device. Returns `true` if data is valid.
|
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.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::vector<String> getDevicesByType(VictronDeviceType type);
|
typedef void (*VictronCallback)(const VictronDevice* device);
|
||||||
```
|
```
|
||||||
Get MAC addresses of all devices of a specific type.
|
|
||||||
|
|
||||||
#### Callbacks
|
#### Configuration
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void setCallback(VictronDeviceCallback* callback);
|
void setMinInterval(uint32_t ms);
|
||||||
```
|
```
|
||||||
Set callback object to receive data updates automatically.
|
Set minimum callback interval per device (default: 1000ms). Callbacks are also suppressed when the device nonce hasn't changed (data unchanged).
|
||||||
|
|
||||||
#### Utilities
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void setDebug(bool enable);
|
void setDebug(bool enable);
|
||||||
```
|
```
|
||||||
Enable/disable debug output to Serial.
|
Enable/disable debug output to Serial.
|
||||||
|
|
||||||
```cpp
|
#### Main Loop
|
||||||
String getLastError();
|
|
||||||
```
|
|
||||||
Get last error message.
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void loop();
|
void loop();
|
||||||
```
|
```
|
||||||
Process BLE scanning and data updates. Call this in your main loop.
|
Call in your main loop. Non-blocking — returns immediately if a scan is already running. Scan restarts automatically when it completes.
|
||||||
|
|
||||||
### Data Structures
|
### Data Structures
|
||||||
|
|
||||||
#### SolarChargerData
|
#### VictronDevice (main struct)
|
||||||
|
|
||||||
|
All device types share this struct. Access type-specific data via the union member matching `deviceType`.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct SolarChargerData {
|
struct VictronDevice {
|
||||||
String deviceName;
|
char name[32];
|
||||||
String macAddress;
|
char mac[13]; // 12 hex chars + null
|
||||||
|
VictronDeviceType deviceType;
|
||||||
int8_t rssi; // Signal strength (dBm)
|
int8_t rssi; // Signal strength (dBm)
|
||||||
uint32_t lastUpdate; // millis() of last update
|
uint32_t lastUpdate; // millis() of last update
|
||||||
bool dataValid; // Data validity flag
|
bool dataValid;
|
||||||
|
union {
|
||||||
SolarChargerState chargeState; // Charging state
|
VictronSolarData solar;
|
||||||
|
VictronBatteryData battery;
|
||||||
|
VictronInverterData inverter;
|
||||||
|
VictronDCDCData dcdc;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### VictronSolarData
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct VictronSolarData {
|
||||||
|
uint8_t chargeState; // SolarChargerState enum
|
||||||
|
uint8_t errorCode;
|
||||||
float batteryVoltage; // V
|
float batteryVoltage; // V
|
||||||
float batteryCurrent; // A
|
float batteryCurrent; // A
|
||||||
float panelVoltage; // V (calculated)
|
|
||||||
float panelPower; // W
|
float panelPower; // W
|
||||||
uint16_t yieldToday; // Wh
|
uint16_t yieldToday; // Wh
|
||||||
float loadCurrent; // A (if load output present)
|
float loadCurrent; // A (if load output present)
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
**Charge States:**
|
**Charge States** (`chargeState` values):
|
||||||
- `CHARGER_OFF` - Off
|
`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`
|
||||||
- `CHARGER_LOW_POWER` - Low power
|
|
||||||
- `CHARGER_FAULT` - Fault
|
|
||||||
- `CHARGER_BULK` - Bulk charging
|
|
||||||
- `CHARGER_ABSORPTION` - Absorption
|
|
||||||
- `CHARGER_FLOAT` - Float
|
|
||||||
- `CHARGER_STORAGE` - Storage mode
|
|
||||||
- `CHARGER_EQUALIZE` - Equalize
|
|
||||||
- `CHARGER_INVERTING` - Inverting (HUB-4)
|
|
||||||
- `CHARGER_POWER_SUPPLY` - Power supply mode
|
|
||||||
- `CHARGER_EXTERNAL_CONTROL` - External control
|
|
||||||
|
|
||||||
#### BatteryMonitorData
|
#### VictronBatteryData
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct BatteryMonitorData {
|
struct VictronBatteryData {
|
||||||
String deviceName;
|
|
||||||
String macAddress;
|
|
||||||
int8_t rssi;
|
|
||||||
uint32_t lastUpdate;
|
|
||||||
bool dataValid;
|
|
||||||
|
|
||||||
float voltage; // V
|
float voltage; // V
|
||||||
float current; // A (+ charging, - discharging)
|
float current; // A (+ charging, - discharging)
|
||||||
float temperature; // °C (if configured)
|
float temperature; // C (0 if aux is voltage)
|
||||||
float auxVoltage; // V (starter battery/midpoint)
|
float auxVoltage; // V (0 if aux is temperature)
|
||||||
uint16_t remainingMinutes; // Time remaining
|
uint16_t remainingMinutes;
|
||||||
float consumedAh; // Ah consumed
|
float consumedAh; // Ah
|
||||||
float soc; // State of charge %
|
float soc; // State of charge %
|
||||||
|
|
||||||
// Alarms
|
|
||||||
bool alarmLowVoltage;
|
bool alarmLowVoltage;
|
||||||
bool alarmHighVoltage;
|
bool alarmHighVoltage;
|
||||||
bool alarmLowSOC;
|
bool alarmLowSOC;
|
||||||
@@ -257,39 +235,25 @@ struct BatteryMonitorData {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
#### InverterData
|
#### VictronInverterData
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct InverterData {
|
struct VictronInverterData {
|
||||||
String deviceName;
|
|
||||||
String macAddress;
|
|
||||||
int8_t rssi;
|
|
||||||
uint32_t lastUpdate;
|
|
||||||
bool dataValid;
|
|
||||||
|
|
||||||
float batteryVoltage; // V
|
float batteryVoltage; // V
|
||||||
float batteryCurrent; // A
|
float batteryCurrent; // A
|
||||||
float acPower; // W (+ inverting, - charging)
|
float acPower; // W (+ inverting, - charging)
|
||||||
uint8_t state; // Inverter state
|
uint8_t state;
|
||||||
|
|
||||||
// Alarms
|
|
||||||
bool alarmHighVoltage;
|
|
||||||
bool alarmLowVoltage;
|
bool alarmLowVoltage;
|
||||||
|
bool alarmHighVoltage;
|
||||||
bool alarmHighTemperature;
|
bool alarmHighTemperature;
|
||||||
bool alarmOverload;
|
bool alarmOverload;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
#### DCDCConverterData
|
#### VictronDCDCData
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct DCDCConverterData {
|
struct VictronDCDCData {
|
||||||
String deviceName;
|
|
||||||
String macAddress;
|
|
||||||
int8_t rssi;
|
|
||||||
uint32_t lastUpdate;
|
|
||||||
bool dataValid;
|
|
||||||
|
|
||||||
float inputVoltage; // V
|
float inputVoltage; // V
|
||||||
float outputVoltage; // V
|
float outputVoltage; // V
|
||||||
float outputCurrent; // A
|
float outputCurrent; // A
|
||||||
@@ -305,80 +269,54 @@ struct DCDCConverterData {
|
|||||||
```cpp
|
```cpp
|
||||||
void setup() {
|
void setup() {
|
||||||
victron.begin(5);
|
victron.begin(5);
|
||||||
victron.setCallback(&callback);
|
victron.setCallback(onVictronData);
|
||||||
|
|
||||||
// Add multiple devices
|
// Add multiple devices (type is auto-detected from BLE advertisements)
|
||||||
victron.addDevice("MPPT 1", "AA:BB:CC:DD:EE:01", "key1...", DEVICE_TYPE_SOLAR_CHARGER);
|
victron.addDevice("MPPT 1", "AA:BB:CC:DD:EE:01", "key1...");
|
||||||
victron.addDevice("MPPT 2", "AA:BB:CC:DD:EE:02", "key2...", DEVICE_TYPE_SOLAR_CHARGER);
|
victron.addDevice("MPPT 2", "AA:BB:CC:DD:EE:02", "key2...");
|
||||||
victron.addDevice("SmartShunt", "AA:BB:CC:DD:EE:03", "key3...", DEVICE_TYPE_BATTERY_MONITOR);
|
victron.addDevice("SmartShunt", "AA:BB:CC:DD:EE:03", "key3...");
|
||||||
victron.addDevice("Inverter", "AA:BB:CC:DD:EE:04", "key4...", DEVICE_TYPE_INVERTER);
|
victron.addDevice("Inverter", "AA:BB:CC:DD:EE:04", "key4...");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Manual Data Polling
|
### Handling Multiple Device Types
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void loop() {
|
void onVictronData(const VictronDevice* dev) {
|
||||||
victron.loop();
|
switch (dev->deviceType) {
|
||||||
|
case DEVICE_TYPE_SOLAR_CHARGER:
|
||||||
// Query specific device
|
Serial.printf("%s: %.2fV %dW\n", dev->name,
|
||||||
SolarChargerData mpptData;
|
dev->solar.batteryVoltage, (int)dev->solar.panelPower);
|
||||||
if (victron.getSolarChargerData("AA:BB:CC:DD:EE:FF", mpptData)) {
|
break;
|
||||||
if (mpptData.dataValid) {
|
case DEVICE_TYPE_BATTERY_MONITOR:
|
||||||
// Use data
|
Serial.printf("%s: %.2fV %.1f%%\n", dev->name,
|
||||||
float power = mpptData.panelPower;
|
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;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(1000);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Find All Devices of a Type
|
### Callback Throttling
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void loop() {
|
void setup() {
|
||||||
victron.loop();
|
victron.begin(5);
|
||||||
|
victron.setCallback(onVictronData);
|
||||||
// Get all solar chargers
|
victron.setMinInterval(2000); // Callback at most every 2 seconds per device
|
||||||
std::vector<String> mppts = victron.getDevicesByType(DEVICE_TYPE_SOLAR_CHARGER);
|
|
||||||
|
// ...
|
||||||
for (const String& mac : mppts) {
|
|
||||||
SolarChargerData data;
|
|
||||||
if (victron.getSolarChargerData(mac, data)) {
|
|
||||||
Serial.println(data.deviceName + ": " + String(data.panelPower) + "W");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delay(5000);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Callback Interface
|
|
||||||
|
|
||||||
Implement `VictronDeviceCallback` to receive automatic updates:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
class MyCallback : public VictronDeviceCallback {
|
|
||||||
public:
|
|
||||||
void onSolarChargerData(const SolarChargerData& data) override {
|
|
||||||
// Handle solar charger update
|
|
||||||
}
|
|
||||||
|
|
||||||
void onBatteryMonitorData(const BatteryMonitorData& data) override {
|
|
||||||
// Handle battery monitor update
|
|
||||||
}
|
|
||||||
|
|
||||||
void onInverterData(const InverterData& data) override {
|
|
||||||
// Handle inverter update
|
|
||||||
}
|
|
||||||
|
|
||||||
void onDCDCConverterData(const DCDCConverterData& data) override {
|
|
||||||
// Handle DC-DC converter update
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### No Data Received
|
### No Data Received
|
||||||
@@ -418,7 +356,10 @@ Based on official [Victron BLE documentation](https://www.victronenergy.com/live
|
|||||||
See the `examples/` directory for:
|
See the `examples/` directory for:
|
||||||
|
|
||||||
- **MultiDevice**: Monitor multiple devices with callbacks
|
- **MultiDevice**: Monitor multiple devices with callbacks
|
||||||
- More examples coming soon!
|
- **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
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
26
REVIEW.md
26
REVIEW.md
@@ -1,6 +1,6 @@
|
|||||||
# VictronBLE Code Review
|
# VictronBLE Code Review
|
||||||
|
|
||||||
## Part 1: Bug Fixes, Efficiency & Simplification
|
## Part 1: Bug Fixes, Efficiency & Simplification ✅ COMPLETE (v0.4.1)
|
||||||
|
|
||||||
### Bugs
|
### Bugs
|
||||||
|
|
||||||
@@ -117,10 +117,34 @@ void victron_loop();
|
|||||||
~4 functions instead of ~15 methods.
|
~4 functions instead of ~15 methods.
|
||||||
|
|
||||||
|
|
||||||
|
All items implemented in v0.4.1. See [VERSIONS](VERSIONS) for full changelog.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Part 2: Multi-Platform BLE Support
|
## Part 2: Multi-Platform BLE Support
|
||||||
|
|
||||||
|
### Recommended Test Hardware
|
||||||
|
|
||||||
|
Two cheap BLE development boards for testing the platform abstraction:
|
||||||
|
|
||||||
|
**1. Seeed XIAO nRF52840 (~$10 USD)**
|
||||||
|
- Nordic nRF52840 SoC, Bluetooth 5.0, onboard antenna
|
||||||
|
- Arduino-compatible via Adafruit nRF52 board support package
|
||||||
|
- Ultra-small (21x17.5mm), USB-C, battery charging built in
|
||||||
|
- 1MB flash, 256KB RAM, 2MB QSPI flash
|
||||||
|
- Has mbedtls available via the nRF SDK
|
||||||
|
- https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html
|
||||||
|
|
||||||
|
**2. Raspberry Pi Pico W (~$6 USD)**
|
||||||
|
- RP2040 + Infineon CYW43439 (WiFi + Bluetooth 5.2 with BLE)
|
||||||
|
- Arduino-compatible via arduino-pico core (earlephilhower)
|
||||||
|
- BLE Central role supported (needed for passive scanning)
|
||||||
|
- Very widely available and cheap
|
||||||
|
- Different architecture (ARM Cortex-M0+) from ESP32 (Xtensa/RISC-V), good for testing portability
|
||||||
|
- https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||||
|
|
||||||
|
Both boards are under $15, Arduino-compatible, and have BLE Central support needed for passive scanning of Victron advertisements. They use different BLE stacks (nRF SoftDevice vs CYW43 BTstack) which will validate the transport abstraction layer.
|
||||||
|
|
||||||
### Current BLE Dependencies
|
### Current BLE Dependencies
|
||||||
|
|
||||||
All ESP32-specific BLE code is confined to:
|
All ESP32-specific BLE code is confined to:
|
||||||
|
|||||||
53
VERSIONS
53
VERSIONS
@@ -1,5 +1,58 @@
|
|||||||
# Version History
|
# Version History
|
||||||
|
|
||||||
|
## 0.4.1 (2026-02-28)
|
||||||
|
|
||||||
|
Major rework of library internals. Breaking API change — not backwards compatible with 0.3.x.
|
||||||
|
|
||||||
|
### Callback API rewrite
|
||||||
|
- Replaced virtual callback class (`VictronDeviceCallback` with 4 override methods) with a
|
||||||
|
single function pointer (`VictronCallback`). Users now provide a plain function instead of
|
||||||
|
subclassing. The callback receives a `VictronDevice*` and switches on `deviceType` to access
|
||||||
|
the appropriate data via a tagged union.
|
||||||
|
|
||||||
|
### Non-blocking BLE scanning
|
||||||
|
- `loop()` is now non-blocking — returns immediately if a scan is already running.
|
||||||
|
Previously it blocked for the entire scan duration (default 5 seconds).
|
||||||
|
- Scan restarts automatically when it completes.
|
||||||
|
|
||||||
|
### Callback throttling
|
||||||
|
- Nonce-based deduplication: skips decrypt/parse/callback when the device's data hasn't
|
||||||
|
changed (detected via the nonce field in the BLE advertisement header).
|
||||||
|
- Configurable minimum interval (`setMinInterval()`, default 1000ms) limits callback
|
||||||
|
frequency even when data is changing rapidly.
|
||||||
|
- Encryption key byte check before AES decryption for early rejection of mismatched keys.
|
||||||
|
|
||||||
|
### Memory and code reduction
|
||||||
|
- Replaced `std::map<String, DeviceInfo*>` with a fixed array (max 8 devices, linear search).
|
||||||
|
Eliminates heap allocation for device storage.
|
||||||
|
- Replaced Arduino `String` with fixed `char[]` arrays throughout (MAC: 12 chars, name: 32 chars).
|
||||||
|
Eliminates heap fragmentation from dynamic string operations.
|
||||||
|
- Replaced inheritance hierarchy (`VictronDeviceData` base + 4 derived classes) with a flat
|
||||||
|
`VictronDevice` struct using a tagged union. No more `new`/`delete` for device data.
|
||||||
|
- Removed `std::map` and `std::vector` includes entirely.
|
||||||
|
- Source reduced from ~970 lines to ~510 lines (48% reduction).
|
||||||
|
- Flash savings: ~11-14 KB across examples.
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
- Fixed undefined behavior: derived objects were deleted through a base pointer without a
|
||||||
|
virtual destructor. Now uses flat structs, no polymorphic delete.
|
||||||
|
- Removed incorrect `panelVoltage` calculation (was dividing PV power by battery current,
|
||||||
|
which is wrong for MPPT chargers). The BLE protocol does not transmit PV voltage.
|
||||||
|
- Removed spurious `nullPad` byte from manufacturer data struct.
|
||||||
|
- Device type is now auto-detected from the BLE advertisement record type. The type
|
||||||
|
parameter in `addDevice()` is optional.
|
||||||
|
|
||||||
|
### Removed features (commented out in header for reference)
|
||||||
|
- `VictronDeviceConfig` struct — use `addDevice(name, mac, key, type)` directly
|
||||||
|
- Per-type getter methods (`getSolarChargerData()`, etc.) — use callback instead
|
||||||
|
- `removeDevice()`, `getDevicesByType()`, `getLastError()`
|
||||||
|
|
||||||
|
### Examples updated
|
||||||
|
- All examples updated for new callback API
|
||||||
|
- Removed `panelVoltage` from ESPNow packet structs (Repeater, FakeRepeater, Receiver)
|
||||||
|
- Removed unnecessary `delay(100)` from loop functions
|
||||||
|
- Added ESPNow Repeater and Receiver examples
|
||||||
|
|
||||||
## 0.3.1 (2026-02-11)
|
## 0.3.1 (2026-02-11)
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|||||||
12
library.json
12
library.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "victronble",
|
"name": "victronble",
|
||||||
"version": "0.3.1",
|
"version": "0.4.1",
|
||||||
"description": "ESP32 library for reading Victron Energy device data via Bluetooth Low Energy (BLE) advertisements. Supports SmartSolar MPPT, SmartShunt, BMV, MultiPlus, Orion and other Victron devices.",
|
"description": "ESP32 library for reading Victron Energy device data via Bluetooth Low Energy (BLE) advertisements. Supports SmartSolar MPPT, SmartShunt, BMV, MultiPlus, Orion and other Victron devices.",
|
||||||
"keywords": "victron, ble, bluetooth, solar, mppt, battery, smartshunt, smartsolar, bmv, inverter, multiplus, esp32, iot, energy, monitoring",
|
"keywords": "victron, ble, bluetooth, solar, mppt, battery, smartshunt, smartsolar, bmv, inverter, multiplus, esp32, iot, energy, monitoring",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -36,6 +36,16 @@
|
|||||||
"name": "Repeater",
|
"name": "Repeater",
|
||||||
"base": "examples/Repeater",
|
"base": "examples/Repeater",
|
||||||
"files": ["src/main.cpp"]
|
"files": ["src/main.cpp"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Receiver",
|
||||||
|
"base": "examples/Receiver",
|
||||||
|
"files": ["src/main.cpp"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FakeRepeater",
|
||||||
|
"base": "examples/FakeRepeater",
|
||||||
|
"files": ["src/main.cpp"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"export": {
|
"export": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name=VictronBLE
|
name=VictronBLE
|
||||||
version=0.3.1
|
version=0.4.1
|
||||||
author=Scott Penrose
|
author=Scott Penrose
|
||||||
maintainer=Scott Penrose <scottp@dd.com.au>
|
maintainer=Scott Penrose <scottp@dd.com.au>
|
||||||
sentence=ESP32 library for reading Victron Energy device data via BLE for any ESP32
|
sentence=ESP32 library for reading Victron Energy device data via BLE for any ESP32
|
||||||
|
|||||||
Reference in New Issue
Block a user