Bug fixes for reported issues
This commit is contained in:
@@ -29,6 +29,7 @@ Currently supporting ESP32 S and C series (tested on older ESP32, ESP32-S3 and E
|
|||||||
- **Battery Monitors**: SmartShunt, BMV-712 Smart, BMV-700 series
|
- **Battery Monitors**: SmartShunt, BMV-712 Smart, BMV-700 series
|
||||||
- **Inverters**: MultiPlus, Quattro, Phoenix (with VE.Bus BLE dongle)
|
- **Inverters**: MultiPlus, Quattro, Phoenix (with VE.Bus BLE dongle)
|
||||||
- **DC-DC Converters**: Orion Smart, Orion XS
|
- **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
|
- **Others**: Smart Battery Protect, Lynx Smart BMS, Smart Lithium batteries
|
||||||
|
|
||||||
## Hardware Requirements
|
## Hardware Requirements
|
||||||
@@ -40,16 +41,23 @@ Currently supporting ESP32 S and C series (tested on older ESP32, ESP32-S3 and E
|
|||||||
|
|
||||||
### PlatformIO
|
### PlatformIO
|
||||||
|
|
||||||
1. Add to `platformio.ini`:
|
1. Add to `platformio.ini` (recommended — installs from the PlatformIO registry):
|
||||||
```ini
|
```ini
|
||||||
lib_deps =
|
lib_deps =
|
||||||
https://gitea.sh3d.com.au/Sh3d/VictronBLE
|
scottp/victronble
|
||||||
|
```
|
||||||
|
|
||||||
|
Or install directly from git. Note the **`.git` suffix is required** — the bare
|
||||||
|
repository URL is not accepted by PlatformIO:
|
||||||
|
```ini
|
||||||
|
lib_deps =
|
||||||
|
https://gitea.sh3d.com.au/Sh3d/VictronBLE.git
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Or clone into your project's `lib` folder:
|
2. Or clone into your project's `lib` folder:
|
||||||
```bash
|
```bash
|
||||||
cd lib
|
cd lib
|
||||||
git clone https://gitea.sh3d.com.au/Sh3d/VictronBLE
|
git clone https://gitea.sh3d.com.au/Sh3d/VictronBLE.git
|
||||||
```
|
```
|
||||||
|
|
||||||
### Arduino IDE
|
### Arduino IDE
|
||||||
@@ -262,6 +270,23 @@ struct VictronDCDCData {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### VictronACChargerData
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
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
|
## Advanced Usage
|
||||||
|
|
||||||
### Multiple Devices
|
### Multiple Devices
|
||||||
@@ -299,6 +324,11 @@ void onVictronData(const VictronDevice* dev) {
|
|||||||
Serial.printf("%s: %.2fV -> %.2fV\n", dev->name,
|
Serial.printf("%s: %.2fV -> %.2fV\n", dev->name,
|
||||||
dev->dcdc.inputVoltage, dev->dcdc.outputVoltage);
|
dev->dcdc.inputVoltage, dev->dcdc.outputVoltage);
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -397,7 +427,9 @@ See [VERSIONS](VERSIONS) file for detailed changelog and release history.
|
|||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
- 📫 Report issues on GitHub
|
- 📫 Report issues: the Gitea instance does not currently allow public sign-ups, so
|
||||||
|
email <scottp@dd.com.au> with bug reports, device decode problems, or new device
|
||||||
|
requests (debug log output is very helpful).
|
||||||
- 📖 Check the examples directory
|
- 📖 Check the examples directory
|
||||||
- 🔧 Enable debug mode for diagnostics
|
- 🔧 Enable debug mode for diagnostics
|
||||||
- 📚 See [Victron documentation](https://www.victronenergy.com/live/)
|
- 📚 See [Victron documentation](https://www.victronenergy.com/live/)
|
||||||
|
|||||||
@@ -1,5 +1,33 @@
|
|||||||
# Version History
|
# Version History
|
||||||
|
|
||||||
|
## 0.5.0 (2026-06-04)
|
||||||
|
|
||||||
|
Decoding accuracy fixes (thanks to community bug reports from Karsten, Cory, Kevin and Dan)
|
||||||
|
plus new AC Charger support. Field layouts verified against the keshavdv/victron-ble
|
||||||
|
reference implementation and the Victron "Extra Manufacturer Data" specification.
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
- **Battery monitor decoding rewritten.** The `victronBatteryMonitorPayload` struct had
|
||||||
|
wrong field widths (8-bit alarm instead of 16, no 2-bit aux-mode field) which cascaded
|
||||||
|
and misaligned current, consumed Ah and SOC. `parseBatteryMonitor()` now decodes the
|
||||||
|
bit-packed payload directly by bit offset: signed voltage, 16-bit alarm, aux value +
|
||||||
|
2-bit aux mode (replacing the unreliable `< 3000` voltage/temperature heuristic),
|
||||||
|
22-bit signed current, 20-bit consumed Ah (×-0.1 Ah), 10-bit SOC (×0.1 %).
|
||||||
|
- **Solar charger battery current scale fixed.** Was multiplied by 0.01 (10× too small);
|
||||||
|
the field is in 0.1 A units. Load current is now read as the correct 9-bit field.
|
||||||
|
- **Compile error with newer ESP32 BLE library fixed.** `getManufacturerData()` now returns
|
||||||
|
an Arduino `String` on recent cores; `processDevice()` handles both `String` and
|
||||||
|
`std::string` while preserving the binary payload's embedded null bytes.
|
||||||
|
- **Device type IDs corrected.** The enum was off-by-one from 0x07 onward, mislabelling
|
||||||
|
AC chargers (0x08) as Lynx Smart BMS. Now matches the Victron protocol:
|
||||||
|
0x07 GX Device, 0x08 AC Charger, 0x09 Smart Battery Protect, 0x0A Lynx Smart BMS,
|
||||||
|
0x0B Multi RS, 0x0C VE.Bus, 0x0D DC Energy Meter, 0x0F Orion XS.
|
||||||
|
|
||||||
|
### New features
|
||||||
|
- **AC Charger support** (Blue Smart IP22/IP65/IP67, device type 0x08). New
|
||||||
|
`VictronACChargerData` struct (three output voltage/current banks, temperature, AC
|
||||||
|
current) and `DEVICE_TYPE_AC_CHARGER` handling.
|
||||||
|
|
||||||
## 0.4.1 (2026-02-28)
|
## 0.4.1 (2026-02-28)
|
||||||
|
|
||||||
Major rework of library internals. Breaking API change — not backwards compatible with 0.3.x.
|
Major rework of library internals. Breaking API change — not backwards compatible with 0.3.x.
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "victronble",
|
"name": "victronble",
|
||||||
"version": "0.4.1",
|
"version": "0.5.0",
|
||||||
"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": {
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
name=VictronBLE
|
name=VictronBLE
|
||||||
version=0.4.1
|
version=0.5.0
|
||||||
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
|
||||||
|
|||||||
+96
-37
@@ -87,7 +87,11 @@ void VictronBLEAdvertisedDeviceCallbacks::onResult(BLEAdvertisedDevice advertise
|
|||||||
void VictronBLE::processDevice(BLEAdvertisedDevice& advertisedDevice) {
|
void VictronBLE::processDevice(BLEAdvertisedDevice& advertisedDevice) {
|
||||||
if (!advertisedDevice.haveManufacturerData()) return;
|
if (!advertisedDevice.haveManufacturerData()) return;
|
||||||
|
|
||||||
std::string raw = advertisedDevice.getManufacturerData();
|
// getManufacturerData() returns std::string on older ESP32 BLE libraries and
|
||||||
|
// an Arduino String on newer ones. Both expose c_str()/length(); constructing
|
||||||
|
// from (ptr, len) preserves the embedded null bytes present in binary data.
|
||||||
|
auto mfg = advertisedDevice.getManufacturerData();
|
||||||
|
std::string raw(mfg.c_str(), mfg.length());
|
||||||
if (raw.length() < 10) return;
|
if (raw.length() < 10) return;
|
||||||
|
|
||||||
// Quick vendor ID check before any other work
|
// Quick vendor ID check before any other work
|
||||||
@@ -180,6 +184,10 @@ bool VictronBLE::parseAdvertisement(DeviceEntry* entry, const victronManufacture
|
|||||||
entry->device.deviceType = DEVICE_TYPE_DCDC_CONVERTER;
|
entry->device.deviceType = DEVICE_TYPE_DCDC_CONVERTER;
|
||||||
ok = parseDCDCConverter(decrypted, VICTRON_ENCRYPTED_LEN, entry->device.dcdc);
|
ok = parseDCDCConverter(decrypted, VICTRON_ENCRYPTED_LEN, entry->device.dcdc);
|
||||||
break;
|
break;
|
||||||
|
case DEVICE_TYPE_AC_CHARGER:
|
||||||
|
entry->device.deviceType = DEVICE_TYPE_AC_CHARGER;
|
||||||
|
ok = parseACCharger(decrypted, VICTRON_ENCRYPTED_LEN, entry->device.acCharger);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (debugEnabled) Serial.printf("[VictronBLE] Unknown type: 0x%02X\n", mfg.victronRecordType);
|
if (debugEnabled) Serial.printf("[VictronBLE] Unknown type: 0x%02X\n", mfg.victronRecordType);
|
||||||
return false;
|
return false;
|
||||||
@@ -222,11 +230,13 @@ bool VictronBLE::parseSolarCharger(const uint8_t* data, size_t len, VictronSolar
|
|||||||
|
|
||||||
result.chargeState = p->deviceState;
|
result.chargeState = p->deviceState;
|
||||||
result.errorCode = p->errorCode;
|
result.errorCode = p->errorCode;
|
||||||
result.batteryVoltage = p->batteryVoltage * 0.01f;
|
result.batteryVoltage = p->batteryVoltage * 0.01f; // 0.01V units
|
||||||
result.batteryCurrent = p->batteryCurrent * 0.01f;
|
result.batteryCurrent = p->batteryCurrent * 0.1f; // 0.1A units
|
||||||
result.yieldToday = p->yieldToday * 10;
|
result.yieldToday = p->yieldToday * 10;
|
||||||
result.panelPower = p->inputPower;
|
result.panelPower = p->inputPower;
|
||||||
result.loadCurrent = (p->loadCurrent != 0xFFFF) ? p->loadCurrent * 0.01f : 0;
|
// Load current is a 9-bit field (0.1A units); 0x1FF = no load output
|
||||||
|
uint16_t loadRaw = p->loadCurrent & 0x1FF;
|
||||||
|
result.loadCurrent = (loadRaw != 0x1FF) ? loadRaw * 0.1f : 0;
|
||||||
|
|
||||||
if (debugEnabled) {
|
if (debugEnabled) {
|
||||||
Serial.printf("[VictronBLE] Solar: %.2fV %.2fA %dW State:%d\n",
|
Serial.printf("[VictronBLE] Solar: %.2fV %.2fA %dW State:%d\n",
|
||||||
@@ -236,47 +246,96 @@ bool VictronBLE::parseSolarCharger(const uint8_t* data, size_t len, VictronSolar
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VictronBLE::parseBatteryMonitor(const uint8_t* data, size_t len, VictronBatteryData& result) {
|
bool VictronBLE::parseACCharger(const uint8_t* data, size_t len, VictronACChargerData& result) {
|
||||||
if (len < sizeof(victronBatteryMonitorPayload)) return false;
|
// Payload is bit-packed (10 fields, 104 bits ending in byte 12). Decode LSB-first.
|
||||||
const auto* p = reinterpret_cast<const victronBatteryMonitorPayload*>(data);
|
if (len < 13) return false;
|
||||||
|
|
||||||
result.remainingMinutes = p->remainingMins;
|
size_t bit = 0;
|
||||||
result.voltage = p->batteryVoltage * 0.01f;
|
auto readBits = [&](uint8_t width) -> uint32_t {
|
||||||
|
uint32_t value = 0;
|
||||||
|
for (uint8_t i = 0; i < width; i++) {
|
||||||
|
size_t b = bit + i;
|
||||||
|
value |= (uint32_t)((data[b >> 3] >> (b & 7)) & 0x01) << i;
|
||||||
|
}
|
||||||
|
bit += width;
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
// Alarm bits
|
result.chargeState = (uint8_t)readBits(8);
|
||||||
result.alarmLowVoltage = (p->alarms & 0x01) != 0;
|
result.errorCode = (uint8_t)readBits(8);
|
||||||
result.alarmHighVoltage = (p->alarms & 0x02) != 0;
|
|
||||||
result.alarmLowSOC = (p->alarms & 0x04) != 0;
|
|
||||||
result.alarmLowTemperature = (p->alarms & 0x10) != 0;
|
|
||||||
result.alarmHighTemperature = (p->alarms & 0x20) != 0;
|
|
||||||
|
|
||||||
// Aux data: voltage or temperature (heuristic: < 30V = voltage)
|
uint32_t v1 = readBits(13), i1 = readBits(11);
|
||||||
// NOTE: Victron protocol uses a flag bit for this, but it's not exposed
|
uint32_t v2 = readBits(13), i2 = readBits(11);
|
||||||
// in the BLE advertisement. This heuristic may misclassify edge cases.
|
uint32_t v3 = readBits(13), i3 = readBits(11);
|
||||||
if (p->auxData < 3000) {
|
uint32_t temp = readBits(7);
|
||||||
result.auxVoltage = p->auxData * 0.01f;
|
uint32_t acCur = readBits(9);
|
||||||
result.temperature = 0;
|
|
||||||
} else {
|
result.voltage1 = (v1 != 0x1FFF) ? v1 * 0.01f : 0;
|
||||||
result.temperature = (p->auxData * 0.01f) - 273.15f;
|
result.current1 = (i1 != 0x7FF) ? i1 * 0.1f : 0;
|
||||||
result.auxVoltage = 0;
|
result.voltage2 = (v2 != 0x1FFF) ? v2 * 0.01f : 0;
|
||||||
|
result.current2 = (i2 != 0x7FF) ? i2 * 0.1f : 0;
|
||||||
|
result.voltage3 = (v3 != 0x1FFF) ? v3 * 0.01f : 0;
|
||||||
|
result.current3 = (i3 != 0x7FF) ? i3 * 0.1f : 0;
|
||||||
|
result.temperature = (temp != 0x7F) ? (float)temp - 40.0f : 0; // C offset by -40
|
||||||
|
result.acCurrent = (acCur != 0x1FF) ? acCur * 0.1f : 0;
|
||||||
|
|
||||||
|
if (debugEnabled) {
|
||||||
|
Serial.printf("[VictronBLE] AC Charger: %.2fV %.2fA Temp:%.0fC State:%d\n",
|
||||||
|
result.voltage1, result.current1, result.temperature, result.chargeState);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Battery current (22-bit signed, 1 mA units)
|
bool VictronBLE::parseBatteryMonitor(const uint8_t* data, size_t len, VictronBatteryData& result) {
|
||||||
int32_t current = p->currentLow |
|
// The payload is bit-packed and not byte-aligned, so it is decoded by bit
|
||||||
(p->currentMid << 8) |
|
// offset directly rather than via a struct. SOC ends at bit 117 (byte 14).
|
||||||
((p->currentHigh_consumedLow & 0x3F) << 16);
|
if (len < 15) return false;
|
||||||
if (current & 0x200000) current |= 0xFFC00000; // Sign extend
|
|
||||||
|
// TTG (bits 0-15), unsigned minutes
|
||||||
|
result.remainingMinutes = data[0] | ((uint16_t)data[1] << 8);
|
||||||
|
|
||||||
|
// Voltage (bits 16-31), signed, 0.01V units
|
||||||
|
result.voltage = (int16_t)(data[2] | ((uint16_t)data[3] << 8)) * 0.01f;
|
||||||
|
|
||||||
|
// Alarm (bits 32-47), 16-bit bitmask
|
||||||
|
uint16_t alarm = data[4] | ((uint16_t)data[5] << 8);
|
||||||
|
result.alarmLowVoltage = (alarm & 0x0001) != 0;
|
||||||
|
result.alarmHighVoltage = (alarm & 0x0002) != 0;
|
||||||
|
result.alarmLowSOC = (alarm & 0x0004) != 0;
|
||||||
|
result.alarmLowTemperature = (alarm & 0x0010) != 0;
|
||||||
|
result.alarmHighTemperature = (alarm & 0x0020) != 0;
|
||||||
|
|
||||||
|
// Aux value (bits 48-63) interpreted per aux mode (bits 64-65)
|
||||||
|
uint16_t auxRaw = data[6] | ((uint16_t)data[7] << 8);
|
||||||
|
uint8_t auxMode = data[8] & 0x03; // 0=aux voltage, 1=midpoint, 2=temperature, 3=none
|
||||||
|
if (auxMode == 0) {
|
||||||
|
result.auxVoltage = auxRaw * 0.01f;
|
||||||
|
result.temperature = 0;
|
||||||
|
} else if (auxMode == 2) {
|
||||||
|
result.temperature = auxRaw * 0.01f - 273.15f; // 0.01K -> C
|
||||||
|
result.auxVoltage = 0;
|
||||||
|
} else {
|
||||||
|
result.auxVoltage = 0;
|
||||||
|
result.temperature = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Battery current (bits 66-87), 22-bit signed, 0.001A units
|
||||||
|
int32_t current = ((uint32_t)(data[8] >> 2) & 0x3F)
|
||||||
|
| ((uint32_t)data[9] << 6)
|
||||||
|
| ((uint32_t)data[10] << 14);
|
||||||
|
if (current & 0x200000) current |= 0xFFC00000; // Sign extend 22-bit
|
||||||
result.current = current * 0.001f;
|
result.current = current * 0.001f;
|
||||||
|
|
||||||
// Consumed Ah (18-bit signed, 10 mAh units)
|
// Consumed Ah (bits 88-107), 20-bit, stored as a positive count, 0.1Ah units.
|
||||||
int32_t consumedAh = ((p->currentHigh_consumedLow & 0xC0) >> 6) |
|
// Reported as a negative value (amp-hours consumed).
|
||||||
(p->consumedMid << 2) |
|
uint32_t consumed = (uint32_t)data[11]
|
||||||
(p->consumedHigh << 10);
|
| ((uint32_t)data[12] << 8)
|
||||||
if (consumedAh & 0x20000) consumedAh |= 0xFFFC0000; // Sign extend
|
| ((uint32_t)(data[13] & 0x0F) << 16);
|
||||||
result.consumedAh = consumedAh * 0.01f;
|
result.consumedAh = -((float)consumed * 0.1f);
|
||||||
|
|
||||||
// SOC (10-bit, 0.1% units)
|
// SOC (bits 108-117), 10-bit, 0.1% units
|
||||||
result.soc = (p->soc & 0x3FF) * 0.1f;
|
uint16_t soc = ((uint16_t)(data[13] >> 4) | ((uint16_t)data[14] << 4)) & 0x3FF;
|
||||||
|
result.soc = soc * 0.1f;
|
||||||
|
|
||||||
if (debugEnabled) {
|
if (debugEnabled) {
|
||||||
Serial.printf("[VictronBLE] Battery: %.2fV %.2fA SOC:%.1f%%\n",
|
Serial.printf("[VictronBLE] Battery: %.2fV %.2fA SOC:%.1f%%\n",
|
||||||
|
|||||||
+30
-22
@@ -33,11 +33,14 @@ enum VictronDeviceType {
|
|||||||
DEVICE_TYPE_DCDC_CONVERTER = 0x04,
|
DEVICE_TYPE_DCDC_CONVERTER = 0x04,
|
||||||
DEVICE_TYPE_SMART_LITHIUM = 0x05,
|
DEVICE_TYPE_SMART_LITHIUM = 0x05,
|
||||||
DEVICE_TYPE_INVERTER_RS = 0x06,
|
DEVICE_TYPE_INVERTER_RS = 0x06,
|
||||||
DEVICE_TYPE_SMART_BATTERY_PROTECT = 0x07,
|
DEVICE_TYPE_GX_DEVICE = 0x07,
|
||||||
DEVICE_TYPE_LYNX_SMART_BMS = 0x08,
|
DEVICE_TYPE_AC_CHARGER = 0x08,
|
||||||
DEVICE_TYPE_MULTI_RS = 0x09,
|
DEVICE_TYPE_SMART_BATTERY_PROTECT = 0x09,
|
||||||
DEVICE_TYPE_VE_BUS = 0x0A,
|
DEVICE_TYPE_LYNX_SMART_BMS = 0x0A,
|
||||||
DEVICE_TYPE_DC_ENERGY_METER = 0x0B
|
DEVICE_TYPE_MULTI_RS = 0x0B,
|
||||||
|
DEVICE_TYPE_VE_BUS = 0x0C,
|
||||||
|
DEVICE_TYPE_DC_ENERGY_METER = 0x0D,
|
||||||
|
DEVICE_TYPE_ORION_XS = 0x0F
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Device state for Solar Charger ---
|
// --- Device state for Solar Charger ---
|
||||||
@@ -72,27 +75,17 @@ struct victronManufacturerData {
|
|||||||
struct victronSolarChargerPayload {
|
struct victronSolarChargerPayload {
|
||||||
uint8_t deviceState;
|
uint8_t deviceState;
|
||||||
uint8_t errorCode;
|
uint8_t errorCode;
|
||||||
int16_t batteryVoltage; // 10mV units
|
int16_t batteryVoltage; // 0.01V units (signed)
|
||||||
int16_t batteryCurrent; // 10mA units (signed)
|
int16_t batteryCurrent; // 0.1A units (signed)
|
||||||
uint16_t yieldToday; // 10Wh units
|
uint16_t yieldToday; // 0.01kWh (10Wh) units
|
||||||
uint16_t inputPower; // 1W units
|
uint16_t inputPower; // 1W units
|
||||||
uint16_t loadCurrent; // 10mA units (0xFFFF = no load)
|
uint16_t loadCurrent; // 9-bit field, 0.1A units (0x1FF = no load)
|
||||||
uint8_t reserved[2];
|
uint8_t reserved[2];
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct victronBatteryMonitorPayload {
|
// NOTE: The battery monitor payload is bit-packed (16-bit alarm, 2-bit aux mode,
|
||||||
uint16_t remainingMins;
|
// 22-bit current, 20-bit consumed Ah, 10-bit SOC) and does NOT byte-align, so it
|
||||||
uint16_t batteryVoltage; // 10mV units
|
// is decoded by bit offset directly in parseBatteryMonitor() rather than a struct.
|
||||||
uint8_t alarms;
|
|
||||||
uint16_t auxData; // 10mV (voltage) or 0.01K (temperature)
|
|
||||||
uint8_t currentLow;
|
|
||||||
uint8_t currentMid;
|
|
||||||
uint8_t currentHigh_consumedLow; // Current bits 16-21 (low 6), consumed bits 0-1 (high 2)
|
|
||||||
uint8_t consumedMid;
|
|
||||||
uint8_t consumedHigh;
|
|
||||||
uint16_t soc; // 0.1% units (10-bit)
|
|
||||||
uint8_t reserved[2];
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
struct victronInverterPayload {
|
struct victronInverterPayload {
|
||||||
uint8_t deviceState;
|
uint8_t deviceState;
|
||||||
@@ -129,6 +122,19 @@ struct VictronSolarData {
|
|||||||
float loadCurrent; // A
|
float loadCurrent; // A
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
};
|
||||||
|
|
||||||
struct VictronBatteryData {
|
struct VictronBatteryData {
|
||||||
float voltage; // V
|
float voltage; // V
|
||||||
float current; // A
|
float current; // A
|
||||||
@@ -179,6 +185,7 @@ struct VictronDevice {
|
|||||||
VictronBatteryData battery;
|
VictronBatteryData battery;
|
||||||
VictronInverterData inverter;
|
VictronInverterData inverter;
|
||||||
VictronDCDCData dcdc;
|
VictronDCDCData dcdc;
|
||||||
|
VictronACChargerData acCharger;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -236,6 +243,7 @@ private:
|
|||||||
void processDevice(BLEAdvertisedDevice& dev);
|
void processDevice(BLEAdvertisedDevice& dev);
|
||||||
bool parseAdvertisement(DeviceEntry* entry, const victronManufacturerData& mfg);
|
bool parseAdvertisement(DeviceEntry* entry, const victronManufacturerData& mfg);
|
||||||
bool parseSolarCharger(const uint8_t* data, size_t len, VictronSolarData& result);
|
bool parseSolarCharger(const uint8_t* data, size_t len, VictronSolarData& result);
|
||||||
|
bool parseACCharger(const uint8_t* data, size_t len, VictronACChargerData& result);
|
||||||
bool parseBatteryMonitor(const uint8_t* data, size_t len, VictronBatteryData& result);
|
bool parseBatteryMonitor(const uint8_t* data, size_t len, VictronBatteryData& result);
|
||||||
bool parseInverter(const uint8_t* data, size_t len, VictronInverterData& result);
|
bool parseInverter(const uint8_t* data, size_t len, VictronInverterData& result);
|
||||||
bool parseDCDCConverter(const uint8_t* data, size_t len, VictronDCDCData& result);
|
bool parseDCDCConverter(const uint8_t* data, size_t len, VictronDCDCData& result);
|
||||||
|
|||||||
Reference in New Issue
Block a user