Initial readme up
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* VictronBLE - ESP32 library for Victron Energy BLE devices
|
||||
*
|
||||
* Based on Victron's official BLE Advertising protocol documentation
|
||||
* Inspired by hoberman's examples and keshavdv's Python library
|
||||
*
|
||||
* Copyright (c) 2024
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
#ifndef VICTRON_BLE_H
|
||||
#define VICTRON_BLE_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEAdvertisedDevice.h>
|
||||
#include <BLEScan.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "mbedtls/aes.h"
|
||||
|
||||
// Victron manufacturer ID
|
||||
#define VICTRON_MANUFACTURER_ID 0x02E1
|
||||
|
||||
// Device type IDs from Victron protocol
|
||||
enum VictronDeviceType {
|
||||
DEVICE_TYPE_UNKNOWN = 0x00,
|
||||
DEVICE_TYPE_SOLAR_CHARGER = 0x01,
|
||||
DEVICE_TYPE_BATTERY_MONITOR = 0x02,
|
||||
DEVICE_TYPE_INVERTER = 0x03,
|
||||
DEVICE_TYPE_DCDC_CONVERTER = 0x04,
|
||||
DEVICE_TYPE_SMART_LITHIUM = 0x05,
|
||||
DEVICE_TYPE_INVERTER_RS = 0x06,
|
||||
DEVICE_TYPE_SMART_BATTERY_PROTECT = 0x07,
|
||||
DEVICE_TYPE_LYNX_SMART_BMS = 0x08,
|
||||
DEVICE_TYPE_MULTI_RS = 0x09,
|
||||
DEVICE_TYPE_VE_BUS = 0x0A,
|
||||
DEVICE_TYPE_DC_ENERGY_METER = 0x0B
|
||||
};
|
||||
|
||||
// Device state for Solar Charger
|
||||
enum SolarChargerState {
|
||||
CHARGER_OFF = 0,
|
||||
CHARGER_LOW_POWER = 1,
|
||||
CHARGER_FAULT = 2,
|
||||
CHARGER_BULK = 3,
|
||||
CHARGER_ABSORPTION = 4,
|
||||
CHARGER_FLOAT = 5,
|
||||
CHARGER_STORAGE = 6,
|
||||
CHARGER_EQUALIZE = 7,
|
||||
CHARGER_INVERTING = 9,
|
||||
CHARGER_POWER_SUPPLY = 11,
|
||||
CHARGER_EXTERNAL_CONTROL = 252
|
||||
};
|
||||
|
||||
// Base structure for all device data
|
||||
struct VictronDeviceData {
|
||||
String deviceName;
|
||||
String macAddress;
|
||||
VictronDeviceType deviceType;
|
||||
int8_t rssi;
|
||||
uint32_t lastUpdate;
|
||||
bool dataValid;
|
||||
|
||||
VictronDeviceData() : deviceType(DEVICE_TYPE_UNKNOWN), rssi(-100),
|
||||
lastUpdate(0), dataValid(false) {}
|
||||
};
|
||||
|
||||
// Solar Charger specific data
|
||||
struct SolarChargerData : public VictronDeviceData {
|
||||
SolarChargerState chargeState;
|
||||
float batteryVoltage; // V
|
||||
float batteryCurrent; // A
|
||||
float panelVoltage; // V (PV voltage)
|
||||
float panelPower; // W
|
||||
uint16_t yieldToday; // Wh
|
||||
float loadCurrent; // A
|
||||
|
||||
SolarChargerData() : chargeState(CHARGER_OFF), batteryVoltage(0),
|
||||
batteryCurrent(0), panelVoltage(0), panelPower(0),
|
||||
yieldToday(0), loadCurrent(0) {
|
||||
deviceType = DEVICE_TYPE_SOLAR_CHARGER;
|
||||
}
|
||||
};
|
||||
|
||||
// Battery Monitor/SmartShunt specific data
|
||||
struct BatteryMonitorData : public VictronDeviceData {
|
||||
float voltage; // V
|
||||
float current; // A (positive = charging, negative = discharging)
|
||||
float temperature; // °C
|
||||
float auxVoltage; // V (starter battery or midpoint)
|
||||
uint16_t remainingMinutes; // Minutes
|
||||
float consumedAh; // Ah
|
||||
float soc; // State of Charge %
|
||||
bool alarmLowVoltage;
|
||||
bool alarmHighVoltage;
|
||||
bool alarmLowSOC;
|
||||
bool alarmLowTemperature;
|
||||
bool alarmHighTemperature;
|
||||
|
||||
BatteryMonitorData() : voltage(0), current(0), temperature(0),
|
||||
auxVoltage(0), remainingMinutes(0), consumedAh(0),
|
||||
soc(0), alarmLowVoltage(false), alarmHighVoltage(false),
|
||||
alarmLowSOC(false), alarmLowTemperature(false),
|
||||
alarmHighTemperature(false) {
|
||||
deviceType = DEVICE_TYPE_BATTERY_MONITOR;
|
||||
}
|
||||
};
|
||||
|
||||
// Inverter specific data
|
||||
struct InverterData : public VictronDeviceData {
|
||||
float batteryVoltage; // V
|
||||
float batteryCurrent; // A
|
||||
float acPower; // W
|
||||
uint8_t state; // Inverter state
|
||||
bool alarmHighVoltage;
|
||||
bool alarmLowVoltage;
|
||||
bool alarmHighTemperature;
|
||||
bool alarmOverload;
|
||||
|
||||
InverterData() : batteryVoltage(0), batteryCurrent(0), acPower(0),
|
||||
state(0), alarmHighVoltage(false), alarmLowVoltage(false),
|
||||
alarmHighTemperature(false), alarmOverload(false) {
|
||||
deviceType = DEVICE_TYPE_INVERTER;
|
||||
}
|
||||
};
|
||||
|
||||
// DC-DC Converter specific data
|
||||
struct DCDCConverterData : public VictronDeviceData {
|
||||
float inputVoltage; // V
|
||||
float outputVoltage; // V
|
||||
float outputCurrent; // A
|
||||
uint8_t chargeState;
|
||||
uint8_t errorCode;
|
||||
|
||||
DCDCConverterData() : inputVoltage(0), outputVoltage(0), outputCurrent(0),
|
||||
chargeState(0), errorCode(0) {
|
||||
deviceType = DEVICE_TYPE_DCDC_CONVERTER;
|
||||
}
|
||||
};
|
||||
|
||||
// Forward declaration
|
||||
class VictronBLE;
|
||||
|
||||
// Callback interface for device data updates
|
||||
class VictronDeviceCallback {
|
||||
public:
|
||||
virtual ~VictronDeviceCallback() {}
|
||||
virtual void onSolarChargerData(const SolarChargerData& data) {}
|
||||
virtual void onBatteryMonitorData(const BatteryMonitorData& data) {}
|
||||
virtual void onInverterData(const InverterData& data) {}
|
||||
virtual void onDCDCConverterData(const DCDCConverterData& data) {}
|
||||
};
|
||||
|
||||
// Device configuration structure
|
||||
struct VictronDeviceConfig {
|
||||
String name;
|
||||
String macAddress;
|
||||
String encryptionKey; // 32 character hex string
|
||||
VictronDeviceType expectedType;
|
||||
|
||||
VictronDeviceConfig() : expectedType(DEVICE_TYPE_UNKNOWN) {}
|
||||
VictronDeviceConfig(String n, String mac, String key, VictronDeviceType type = DEVICE_TYPE_UNKNOWN)
|
||||
: name(n), macAddress(mac), encryptionKey(key), expectedType(type) {}
|
||||
};
|
||||
|
||||
// Main VictronBLE class
|
||||
class VictronBLE {
|
||||
public:
|
||||
VictronBLE();
|
||||
~VictronBLE();
|
||||
|
||||
// Initialize BLE and start scanning
|
||||
bool begin(uint32_t scanDuration = 5);
|
||||
|
||||
// Add a device to monitor
|
||||
bool addDevice(const VictronDeviceConfig& config);
|
||||
bool addDevice(String name, String macAddress, String encryptionKey,
|
||||
VictronDeviceType expectedType = DEVICE_TYPE_UNKNOWN);
|
||||
|
||||
// Remove a device
|
||||
void removeDevice(String macAddress);
|
||||
|
||||
// Get device count
|
||||
size_t getDeviceCount() const { return devices.size(); }
|
||||
|
||||
// Set callback for data updates
|
||||
void setCallback(VictronDeviceCallback* cb) { callback = cb; }
|
||||
|
||||
// Process scanning (call in loop())
|
||||
void loop();
|
||||
|
||||
// Get latest data for a device
|
||||
bool getSolarChargerData(String macAddress, SolarChargerData& data);
|
||||
bool getBatteryMonitorData(String macAddress, BatteryMonitorData& data);
|
||||
bool getInverterData(String macAddress, InverterData& data);
|
||||
bool getDCDCConverterData(String macAddress, DCDCConverterData& data);
|
||||
|
||||
// Get all devices of a specific type
|
||||
std::vector<String> getDevicesByType(VictronDeviceType type);
|
||||
|
||||
// Enable/disable debug output
|
||||
void setDebug(bool enable) { debugEnabled = enable; }
|
||||
|
||||
// Get last error message
|
||||
String getLastError() const { return lastError; }
|
||||
|
||||
private:
|
||||
friend class VictronBLEAdvertisedDeviceCallbacks;
|
||||
|
||||
struct DeviceInfo {
|
||||
VictronDeviceConfig config;
|
||||
VictronDeviceData* data;
|
||||
uint8_t encryptionKeyBytes[16];
|
||||
|
||||
DeviceInfo() : data(nullptr) {
|
||||
memset(encryptionKeyBytes, 0, 16);
|
||||
}
|
||||
~DeviceInfo() {
|
||||
if (data) delete data;
|
||||
}
|
||||
};
|
||||
|
||||
std::map<String, DeviceInfo*> devices;
|
||||
BLEScan* pBLEScan;
|
||||
VictronDeviceCallback* callback;
|
||||
bool debugEnabled;
|
||||
String lastError;
|
||||
uint32_t scanDuration;
|
||||
bool initialized;
|
||||
|
||||
// Internal methods
|
||||
bool hexStringToBytes(const String& hex, uint8_t* bytes, size_t len);
|
||||
bool decryptAdvertisement(const uint8_t* encrypted, size_t encLen,
|
||||
const uint8_t* key, const uint8_t* iv,
|
||||
uint8_t* decrypted);
|
||||
bool parseAdvertisement(const uint8_t* manufacturerData, size_t len,
|
||||
const String& macAddress);
|
||||
void processDevice(BLEAdvertisedDevice advertisedDevice);
|
||||
|
||||
VictronDeviceData* createDeviceData(VictronDeviceType type);
|
||||
bool parseSolarCharger(const uint8_t* data, size_t len, SolarChargerData& result);
|
||||
bool parseBatteryMonitor(const uint8_t* data, size_t len, BatteryMonitorData& result);
|
||||
bool parseInverter(const uint8_t* data, size_t len, InverterData& result);
|
||||
bool parseDCDCConverter(const uint8_t* data, size_t len, DCDCConverterData& result);
|
||||
|
||||
void debugPrint(const String& message);
|
||||
void debugPrintHex(const char* label, const uint8_t* data, size_t len);
|
||||
|
||||
String macAddressToString(BLEAddress address);
|
||||
String normalizeMAC(String mac);
|
||||
};
|
||||
|
||||
// BLE scan callback class
|
||||
class VictronBLEAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
|
||||
public:
|
||||
VictronBLEAdvertisedDeviceCallbacks(VictronBLE* parent) : victronBLE(parent) {}
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) override;
|
||||
|
||||
private:
|
||||
VictronBLE* victronBLE;
|
||||
};
|
||||
|
||||
#endif // VICTRON_BLE_H
|
||||
Reference in New Issue
Block a user