From 85dce48e45c1dc0c4078b319661d290663e3e278 Mon Sep 17 00:00:00 2001 From: Philippe Giacinti Date: Sat, 6 Feb 2021 17:01:05 +0100 Subject: [PATCH 1/2] hex protocol implementation --- VeDirectFrameHandler.cpp | 100 +++++++++++++++++++++++---- VeDirectFrameHandler.h | 27 +++++++- example/ReadVEDirectFramehandler.ino | 23 +++++- 3 files changed, 131 insertions(+), 19 deletions(-) diff --git a/VeDirectFrameHandler.cpp b/VeDirectFrameHandler.cpp index e15d776..00f73cb 100644 --- a/VeDirectFrameHandler.cpp +++ b/VeDirectFrameHandler.cpp @@ -36,24 +36,34 @@ #include "VeDirectFrameHandler.h" #define MODULE "VE.Frame" // Victron seems to use this to find out where logging messages were generated +#define MAX_HEX_CALLBACK 10 // initial number - buffer is dynamically increased if necessary // The name of the record that contains the checksum. static constexpr char checksumTagName[] = "CHECKSUM"; VeDirectFrameHandler::VeDirectFrameHandler() : + veName(), + veValue(), + frameIndex(0), + veEnd(0), + veHEnd(0), //mStop(false), // don't know what Victron uses this for, not using + logEF(0), mState(IDLE), mChecksum(0), mTextPointer(0), - tempName(), - tempValue(), - frameIndex(0), - veName(), - veValue(), - veEnd(0) + tempName(), + tempValue(), + veHexCBList(0), + veCBEnd(0), + maxCB(MAX_HEX_CALLBACK) { } +VeDirectFrameHandler::~VeDirectFrameHandler() { + if (veHexCBList) delete veHexCBList; +} + /* * rxData * This function is called by the application which passes a byte of serial data @@ -63,7 +73,9 @@ void VeDirectFrameHandler::rxData(uint8_t inbyte) { //if (mStop) return; if ( (inbyte == ':') && (mState != CHECKSUM) ) { + vePushedState = mState; //hex frame can interrupt TEXT mState = RECORD_HEX; + veHEnd = 0; } if (mState != RECORD_HEX) { mChecksum += inbyte; @@ -140,10 +152,8 @@ void VeDirectFrameHandler::rxData(uint8_t inbyte) break; } case RECORD_HEX: - if (hexRxEvent(inbyte)) { - mChecksum = 0; - mState = IDLE; - } + mState = hexRxEvent(inbyte); + if (mState != RECORD_HEX) mChecksum = 0; break; } } @@ -192,18 +202,78 @@ void VeDirectFrameHandler::frameEndEvent(bool valid) { * logE * This function included for continuity and possible future use. */ -void VeDirectFrameHandler::logE(char * module, char * error) { +void VeDirectFrameHandler::logE(const char * module, const char * error) { //Serial.print("MODULE: "); //Serial.println(module); //Serial.print("ERROR: "); //Serial.println(error); - return; + if (logEF) + (*logEF)(module,error); +} + +/* + * addHexCallback + * This function record a new callback for hex frames + */ +void VeDirectFrameHandler::addHexCallback(hexCallback cb, void* data) { + if (veHexCBList==0) { // first time, allocate callbacks buffer + veHexCBList=new VeHexCB[maxCB]; + veCBEnd=0; + } + else if (veCBEnd==maxCB) { // we need to resize the callbacks buffer, we double the max size + int newMax=maxCB*2; + VeHexCB* tmpb=new VeHexCB[newMax]; + memcpy(tmpb, veHexCBList, maxCB*sizeof(VeHexCB)); + maxCB=newMax; + delete veHexCBList; + veHexCBList=tmpb; + } + veHexCBList[veCBEnd].cb=cb; + veHexCBList[veCBEnd].data=data; + veCBEnd++; +} + +/* + * hexIsValid + * This function compute checksum and validate hex frame + */ +#define ascii2hex(v) (v-48-(v>='A'?7:0)) +#define hex2byte(b) (ascii2hex(*(b)))*16+((ascii2hex(*(b+1)))) +static bool hexIsValid(const char* buffer, int size) { + uint8_t checksum=0x55-ascii2hex(buffer[1]); + for (int i=2; i=hexBuffLen) { // oops -buffer overflow - something went wrong, we abort + logE(MODULE,"hexRx buffer overflow - aborting read"); + veHEnd=0; + ret=IDLE; + } + } + + return ret; } diff --git a/VeDirectFrameHandler.h b/VeDirectFrameHandler.h index 7cef3c7..10e9b6b 100644 --- a/VeDirectFrameHandler.h +++ b/VeDirectFrameHandler.h @@ -14,22 +14,38 @@ const byte frameLen = 18; // VE.Direct Protocol: max frame const byte nameLen = 9; // VE.Direct Protocol: max name size is 9 including /0 const byte valueLen = 33; // VE.Direct Protocol: max value size is 33 including /0 const byte buffLen = 40; // Maximum number of lines possible from the device. Current protocol shows this to be the BMV700 at 33 lines. +const byte hexBuffLen = 100; // Maximum size of hex frame - max payload 34 byte (=68 char) + safe buffer + +typedef void (*hexCallback)(const char*, int, void*); + +struct VeHexCB { + hexCallback cb; + void* data; +}; + +typedef void (*logFunction)(const char *, const char *); class VeDirectFrameHandler { public: VeDirectFrameHandler(); + ~VeDirectFrameHandler(); + void setErrorHandler(logFunction f) { logEF=f;} // error handler void rxData(uint8_t inbyte); // byte of serial data to be passed by the application + void addHexCallback(hexCallback, void*); // add function called back when hex frame is ready (sync or async) char veName[buffLen][nameLen] = { }; // public buffer for received names char veValue[buffLen][valueLen] = { }; // public buffer for received values + char veHexBuffer[hexBuffLen] = { }; // public buffer for received hex frames int frameIndex; // which line of the frame are we on int veEnd; // current size (end) of the public buffer + int veHEnd; // size of hex buffer private: //bool mStop; // not sure what Victron uses this for, not using + logFunction logEF; enum States { // state machine IDLE, @@ -42,7 +58,7 @@ class VeDirectFrameHandler { int mState; // current state - uint8_t mChecksum; // checksum value + uint8_t mChecksum; // checksum value char * mTextPointer; // pointer to the private buffer we're writing to, name or value @@ -53,8 +69,13 @@ class VeDirectFrameHandler { void textRxEvent(char *, char *); void frameEndEvent(bool); - void logE(char *, char *); - bool hexRxEvent(uint8_t); + void logE(const char *, const char *); + int hexRxEvent(uint8_t); + + VeHexCB* veHexCBList; + int veCBEnd; + int maxCB; + int vePushedState; }; #endif // FRAMEHANDLER_H_ diff --git a/example/ReadVEDirectFramehandler.ino b/example/ReadVEDirectFramehandler.ino index cd0170a..5d95e8b 100644 --- a/example/ReadVEDirectFramehandler.ino +++ b/example/ReadVEDirectFramehandler.ino @@ -27,13 +27,34 @@ VeDirectFrameHandler myve; // SoftwareSerial #define rxPin D7 // RX using Software Serial so we can use the hardware UART to check the ouput #define txPin D8 // TX Not used -SoftwareSerial veSerial(rxPin, txPin); +SoftwareSerial veSerial(rxPin, txPin); + +// hex frame callback function +void HexCallback(const char* buffer, int size, void* data) { + char tmp[100]; + memcpy(tmp, buffer, size*sizeof(char)); + tmp[size]=0; + Serial.print("received hex frame: "); + Serial.println(tmp); +} + +// log helper +void LogHelper(const char* module, const char* error) { + Serial.print(module); + Serial.print(":"); + Serial.println(error); +} + void setup() { Serial.begin(115200); // output serial port veSerial.begin(19200); // input serial port (VE device) veSerial.flush(); Serial.println("DEBUG-setup"); + // log helper + myve.setErrorHandler(&LogHelper); + // hex protocol callback + myve.addHexCallback(&HexCallback, (void*)42); } void loop() { From f087c0b4ae25672a1740a714cf3fcec5c97cd899 Mon Sep 17 00:00:00 2001 From: Philippe Giacinti Date: Sat, 6 Feb 2021 17:31:34 +0100 Subject: [PATCH 2/2] continue checksum calculation after hex message --- VeDirectFrameHandler.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/VeDirectFrameHandler.cpp b/VeDirectFrameHandler.cpp index 00f73cb..7b7af10 100644 --- a/VeDirectFrameHandler.cpp +++ b/VeDirectFrameHandler.cpp @@ -153,7 +153,6 @@ void VeDirectFrameHandler::rxData(uint8_t inbyte) } case RECORD_HEX: mState = hexRxEvent(inbyte); - if (mState != RECORD_HEX) mChecksum = 0; break; } }