Skip to content

Commit

Permalink
New class for Victron Chargers and first support for sending VE.Direc…
Browse files Browse the repository at this point in the history
…t hex commands
  • Loading branch information
philippsandhaus committed Oct 20, 2023
1 parent b833d5a commit f0e686e
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 10 deletions.
14 changes: 14 additions & 0 deletions lib/VeDirectFrameHandler/VeDirectChargerController.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include <Arduino.h>
#include "VeDirectChargerController.h"

/*
* setBatteryCurrentLimit
* This function sets battery current limit. Don't call it in a loop because
* Victron is writing battery current limit in non volatile memory.
*/
void VeDirectChargerController::setBatteryCurrentLimit(uint16_t batteryCurrentLimit)
{
//uint16_t veBatCurrentLimit = batteryCurrentLimit * 10;
sendHexCommand(SET,BATTERY_MAXIMUM_CURRENT,DEFAULT_FLAG0, batteryCurrentLimit * 10);
_msgOut->printf("[Victron Charger] Set Victron batteryMaximumCurrent to %dA.\r\n",batteryCurrentLimit);
}
9 changes: 9 additions & 0 deletions lib/VeDirectFrameHandler/VeDirectChargerController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include <Arduino.h>
#include "VeDirectFrameHandler.h"

class VeDirectChargerController : public VeDirectFrameHandler {
public:
void setBatteryCurrentLimit(uint16_t batteryCurrentLimit);
};
84 changes: 79 additions & 5 deletions lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,56 @@ bool VeDirectFrameHandler::textRxEvent(std::string const& who, char* name, char*
}


static uint8_t calcHexChecksum(String message) {
char buffer[2];
uint8_t sum=0;

for (int i=0; i < message.length(); i=i+2) {
buffer[0] = message[i] == ':' ? '0' : message[i];
buffer[1] = message[i+1];
sum += strtoul(buffer,NULL,16);
}

// calculate victron check value
// sum of command + flag + value + check = 0x55
return (0x55 - sum);
}

/*
* isHexFrameValid
* This function computes the checksum and validates a hex frame
*/
#define ascii2hex(v) (v-48-(v>='A'?7:0))
#define hex2byte(b) (ascii2hex(*(b)))*16+((ascii2hex(*(b+1))))
static bool isHexFrameValid(const char* buffer, int size) {
uint8_t checksum=0x55-ascii2hex(buffer[1]);
for (int i=2; i<size; i+=2) checksum -= hex2byte(buffer+i);
return (checksum==0);
}

// used to convert uint8_t hex into hex string
static char HEX_MAP[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

static String uint4toHexString(uint8_t value) {
return (String(HEX_MAP[(value & 0x0F)]));
}

static String uint8toHexString(uint8_t value) {
return (
String(HEX_MAP[(value >> 4)])
+ String(HEX_MAP[(value & 0x0F)])
);
}

static String uint16toHexString(uint16_t value) {
// victron uses little endian
return (
String(HEX_MAP[((value & 0xF0)>> 4)])
+ String(HEX_MAP[(value & 0x0F)])
+ String(HEX_MAP[((value)>> 12)])
+ String(HEX_MAP[((value & 0xF00)>> 8)])
);
}

/*
* hexRxEvent
Expand All @@ -264,12 +314,23 @@ int VeDirectFrameHandler::hexRxEvent(uint8_t inbyte) {

switch (inbyte) {
case '\n':
if (isHexFrameValid(_hexBuffer,_hexSize)) {
// TODO: handle hex frame here
if (_verboseLogging) _msgOut->printf("[VE.Direct] hex frame received: %.*s\r\n", _hexSize, _hexBuffer);
if (_hexBuffer[1]=='A') {
// asynchronous message
// TODO: Handle asynchronous message here
} else {
// answer to previously sent command
// TODO: Handle answer here
}
}
// restore previous state
ret=_prevState;
break;

default:
_hexSize++;
_hexBuffer[_hexSize++]=inbyte;
if (_hexSize>=VE_MAX_HEX_LEN) { // oops -buffer overflow - something went wrong, we abort
_msgOut->println("[VE.Direct] hexRx buffer overflow - aborting read");
_hexSize=0;
Expand All @@ -290,11 +351,24 @@ bool VeDirectFrameHandler::isDataValid(veStruct const& frame) const {
return true;
}

uint32_t VeDirectFrameHandler::getLastUpdate() const
{
uint32_t VeDirectFrameHandler::getLastUpdate() const {
return _lastUpdate;
}

/*
* sendSetHexCommand
* This function uses set command to set uint16 value.
*/
void VeDirectFrameHandler::sendHexCommand(VeDirectHexCommand cmd, VeDirectHexId id, VeDirectFlag flag, uint16_t value) {
String txData = ":" + uint4toHexString(cmd);
txData += uint16toHexString(id);
txData += uint8toHexString(flag);
txData += uint16toHexString(value);
txData += uint8toHexString(calcHexChecksum(txData));
_vedirectSerial->print(txData+"\n");
if (_verboseLogging) _msgOut->println("[VE.Direct] send hex command: " + txData);
}

template<typename T>
String const& VeDirectFrameHandler::getAsString(std::map<T, String> const& values, T val)
{
Expand All @@ -311,12 +385,12 @@ template String const& VeDirectFrameHandler::getAsString(std::map<uint8_t, Strin
template String const& VeDirectFrameHandler::getAsString(std::map<uint16_t, String> const& values, uint16_t val);
template String const& VeDirectFrameHandler::getAsString(std::map<uint32_t, String> const& values, uint32_t val);


/*
* getPidAsString
* This function returns the product id (PID) as readable text.
*/
String VeDirectFrameHandler::veStruct::getPidAsString() const
{
String VeDirectFrameHandler::veStruct::getPidAsString() const {
static const std::map<uint16_t, String> values = {
{ 0x0300, F("BlueSolar MPPT 70|15") },
{ 0xA040, F("BlueSolar MPPT 75|50") },
Expand Down
33 changes: 30 additions & 3 deletions lib/VeDirectFrameHandler/VeDirectFrameHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,32 @@
#define VE_MAX_VALUE_LEN 33 // VE.Direct Protocol: max value size is 33 including /0
#define VE_MAX_HEX_LEN 100 // Maximum size of hex frame - max payload 34 byte (=68 char) + safe buffer


// hex commands
enum VeDirectHexCommand {
ENTER_BOOT = 0x00,
PING = 0x01,
APP_VERSION = 0x02,
PRODUCT_ID = 0x04,
RESTART = 0x06,
GET = 0x07,
SET = 0x08,
ASYNC = 0x0A
};

// hex ids
enum VeDirectHexId {
BATTERY_MAXIMUM_CURRENT = 0xEDF0,
CHARGER_MAXIMUM_CURRENT = 0xEDDF,
CHARGER_CURRENT = 0xEDD7,
CHARGER_VOLTAGE = 0xEDD5,
CHARGER_ERROR_CODE = 0xEDDA,
};

enum VeDirectFlag {
DEFAULT_FLAG0 = 0x00,
};

class VeDirectFrameHandler {
public:
VeDirectFrameHandler();
Expand All @@ -30,7 +56,6 @@ class VeDirectFrameHandler {
bool _verboseLogging;
Print* _msgOut;
uint32_t _lastUpdate;

typedef struct {
uint16_t PID = 0; // product id
char SER[VE_MAX_VALUE_LEN]; // serial number
Expand All @@ -42,6 +67,7 @@ class VeDirectFrameHandler {
String getPidAsString() const; // product id as string
} veStruct;

void sendHexCommand(VeDirectHexCommand cmd, VeDirectHexId id, VeDirectFlag flag, uint16_t value);
bool textRxEvent(std::string const& who, char* name, char* value, veStruct& frame);
bool isDataValid(veStruct const& frame) const; // return true if data valid and not outdated

Expand All @@ -55,13 +81,14 @@ class VeDirectFrameHandler {
virtual void textRxEvent(char *, char *) = 0;
virtual void frameValidEvent() = 0;
int hexRxEvent(uint8_t);

std::unique_ptr<HardwareSerial> _vedirectSerial;
int _state; // current state
int _prevState; // previous state
uint8_t _checksum; // checksum value
char * _textPointer; // pointer to the private buffer we're writing to, name or value
int _hexSize; // length of hex buffer
int _hexSize; // length of hex buffer
char _hexBuffer[VE_MAX_HEX_LEN] = { }; // buffer for received hex frames
char _name[VE_MAX_VALUE_LEN]; // buffer for the field name
char _value[VE_MAX_VALUE_LEN]; // buffer for the field value
std::array<uint8_t, 512> _debugBuffer;
Expand Down
4 changes: 2 additions & 2 deletions lib/VeDirectFrameHandler/VeDirectMpptController.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once

#include <Arduino.h>
#include "VeDirectFrameHandler.h"
#include "VeDirectChargerController.h"

template<typename T, size_t WINDOW_SIZE>
class MovingAverage {
Expand Down Expand Up @@ -35,7 +35,7 @@ class MovingAverage {
size_t _count;
};

class VeDirectMpptController : public VeDirectFrameHandler {
class VeDirectMpptController : public VeDirectChargerController {
public:
VeDirectMpptController() = default;

Expand Down

0 comments on commit f0e686e

Please sign in to comment.