Feature: add support for VE.Direct hex messages
This commit is contained in:
parent
8c6e925ca4
commit
aadd7303ac
@ -30,7 +30,7 @@
|
|||||||
* 2020.05.05 - 0.2 - initial release
|
* 2020.05.05 - 0.2 - initial release
|
||||||
* 2020.06.21 - 0.2 - add MIT license, no code changes
|
* 2020.06.21 - 0.2 - add MIT license, no code changes
|
||||||
* 2020.08.20 - 0.3 - corrected #include reference
|
* 2020.08.20 - 0.3 - corrected #include reference
|
||||||
*
|
* 2024.03.08 - 0.4 - adds the ability to send hex commands and disassemble hex messages
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
@ -274,8 +274,6 @@ void VeDirectFrameHandler<T>::processTextData(std::string const& name, std::stri
|
|||||||
_logId, name.c_str(), value.c_str());
|
_logId, name.c_str(), value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* hexRxEvent
|
* hexRxEvent
|
||||||
* This function records hex answers or async messages
|
* This function records hex answers or async messages
|
||||||
@ -287,12 +285,19 @@ int VeDirectFrameHandler<T>::hexRxEvent(uint8_t inbyte)
|
|||||||
|
|
||||||
switch (inbyte) {
|
switch (inbyte) {
|
||||||
case '\n':
|
case '\n':
|
||||||
|
// now we can analyse the hex message
|
||||||
|
_hexBuffer[_hexSize] = '\0';
|
||||||
|
VeDirectHexData data;
|
||||||
|
if (disassembleHexData(data))
|
||||||
|
hexDataHandler(data);
|
||||||
|
|
||||||
// restore previous state
|
// restore previous state
|
||||||
ret=_prevState;
|
ret=_prevState;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_hexSize++;
|
_hexBuffer[_hexSize++]=inbyte;
|
||||||
|
|
||||||
if (_hexSize>=VE_MAX_HEX_LEN) { // oops -buffer overflow - something went wrong, we abort
|
if (_hexSize>=VE_MAX_HEX_LEN) { // oops -buffer overflow - something went wrong, we abort
|
||||||
_msgOut->printf("%s hexRx buffer overflow - aborting read\r\n", _logId);
|
_msgOut->printf("%s hexRx buffer overflow - aborting read\r\n", _logId);
|
||||||
_hexSize=0;
|
_hexSize=0;
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
* 2020.05.05 - 0.2 - initial release
|
* 2020.05.05 - 0.2 - initial release
|
||||||
* 2021.02.23 - 0.3 - change frameLen to 22 per VE.Direct Protocol version 3.30
|
* 2021.02.23 - 0.3 - change frameLen to 22 per VE.Direct Protocol version 3.30
|
||||||
* 2022.08.20 - 0.4 - changes for OpenDTU
|
* 2022.08.20 - 0.4 - changes for OpenDTU
|
||||||
|
* 2024.03.08 - 0.4 - adds the ability to send hex commands and disassemble hex messages
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -18,17 +19,52 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include "VeDirectData.h"
|
#include "VeDirectData.h"
|
||||||
|
|
||||||
|
// hex send commands
|
||||||
|
enum VeDirectHexCommand {
|
||||||
|
ENTER_BOOT = 0x00,
|
||||||
|
PING = 0x01,
|
||||||
|
APP_VERSION = 0x02,
|
||||||
|
PRODUCT_ID = 0x04,
|
||||||
|
RESTART = 0x06,
|
||||||
|
GET = 0x07,
|
||||||
|
SET = 0x08,
|
||||||
|
ASYNC = 0x0A,
|
||||||
|
UNKNOWN = 0x0F
|
||||||
|
};
|
||||||
|
|
||||||
|
// hex receive responses
|
||||||
|
enum VeDirectHexResponse {
|
||||||
|
R_DONE = 0x01,
|
||||||
|
R_UNKNOWN = 0x03,
|
||||||
|
R_ERROR = 0x04,
|
||||||
|
R_PING = 0x05,
|
||||||
|
R_GET = 0x07,
|
||||||
|
R_SET = 0x08,
|
||||||
|
R_ASYNC = 0x0A,
|
||||||
|
};
|
||||||
|
|
||||||
|
// hex response data, contains the disassembeled hex message, forwarded to virtual funktion hexDataHandler()
|
||||||
|
struct VeDirectHexData {
|
||||||
|
VeDirectHexResponse rsp; // hex response type
|
||||||
|
uint16_t id; // register address
|
||||||
|
uint8_t flag; // flag
|
||||||
|
uint32_t value; // value from register
|
||||||
|
char text[VE_MAX_HEX_LEN]; // text/string response
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class VeDirectFrameHandler {
|
class VeDirectFrameHandler {
|
||||||
public:
|
public:
|
||||||
void loop(); // main loop to read ve.direct data
|
virtual void loop(); // main loop to read ve.direct data
|
||||||
uint32_t getLastUpdate() const; // timestamp of last successful frame read
|
uint32_t getLastUpdate() const; // timestamp of last successful frame read
|
||||||
bool isDataValid() const; // return true if data valid and not outdated
|
bool isDataValid() const; // return true if data valid and not outdated
|
||||||
T const& getData() const { return _tmpFrame; }
|
T const& getData() const { return _tmpFrame; }
|
||||||
|
bool sendHexCommand(VeDirectHexCommand cmd, uint16_t id = 0, uint32_t value = 0, uint8_t valunibble = 0); // send hex commands via ve.direct
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
VeDirectFrameHandler();
|
VeDirectFrameHandler();
|
||||||
void init(char const* who, int8_t rx, int8_t tx, Print* msgOut, bool verboseLogging, uint16_t hwSerialPort);
|
void init(char const* who, int8_t rx, int8_t tx, Print* msgOut, bool verboseLogging, uint16_t hwSerialPort);
|
||||||
|
virtual void hexDataHandler(VeDirectHexData const &data) { } // handles the disassembeled hex response
|
||||||
|
|
||||||
bool _verboseLogging;
|
bool _verboseLogging;
|
||||||
Print* _msgOut;
|
Print* _msgOut;
|
||||||
@ -44,6 +80,7 @@ private:
|
|||||||
virtual bool processTextDataDerived(std::string const& name, std::string const& value) = 0;
|
virtual bool processTextDataDerived(std::string const& name, std::string const& value) = 0;
|
||||||
virtual void frameValidEvent() { }
|
virtual void frameValidEvent() { }
|
||||||
int hexRxEvent(uint8_t);
|
int hexRxEvent(uint8_t);
|
||||||
|
bool disassembleHexData(VeDirectHexData &data); //return true if disassembling was possible
|
||||||
|
|
||||||
std::unique_ptr<HardwareSerial> _vedirectSerial;
|
std::unique_ptr<HardwareSerial> _vedirectSerial;
|
||||||
int _state; // current state
|
int _state; // current state
|
||||||
@ -51,6 +88,7 @@ private:
|
|||||||
uint8_t _checksum; // checksum value
|
uint8_t _checksum; // checksum value
|
||||||
char * _textPointer; // pointer to the private buffer we're writing to, name or 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 _name[VE_MAX_VALUE_LEN]; // buffer for the field name
|
||||||
char _value[VE_MAX_VALUE_LEN]; // buffer for the field value
|
char _value[VE_MAX_VALUE_LEN]; // buffer for the field value
|
||||||
std::array<uint8_t, 512> _debugBuffer;
|
std::array<uint8_t, 512> _debugBuffer;
|
||||||
|
|||||||
227
lib/VeDirectFrameHandler/VeDirectFrameHexHandler.cpp
Normal file
227
lib/VeDirectFrameHandler/VeDirectFrameHexHandler.cpp
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
/* VeDirectFrame
|
||||||
|
HexHandler.cpp
|
||||||
|
*
|
||||||
|
* Library to read/write from Victron devices using VE.Direct Hex protocol.
|
||||||
|
* Add on to Victron framehandler reference implementation.
|
||||||
|
*
|
||||||
|
* How to use:
|
||||||
|
* 1. Use sendHexCommand() to send hex messages. Use the Victron documentation to find the parameter.
|
||||||
|
* 2. The from class "VeDirectFrameHandler" derived class X must overwrite the function
|
||||||
|
* void VeDirectFrameHandler::hexDataHandler(VeDirectHexData const &data)
|
||||||
|
* to handle the received hex messages. All hex messages will be forwarted to function hexDataHandler()
|
||||||
|
* 3. Analyse the content of data (struct VeDirectHexData) to check if a message fits.
|
||||||
|
*
|
||||||
|
* 2024.03.08 - 0.4 - adds the ability to send hex commands and to parse hex messages
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "VeDirectFrameHandler.h"
|
||||||
|
|
||||||
|
|
||||||
|
// support for debugging, 0=without extended logging, 1=with extended logging
|
||||||
|
constexpr int MODUL_DEBUG = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* calcHexFrameCheckSum()
|
||||||
|
* help function to calculate the hex checksum
|
||||||
|
*/
|
||||||
|
#define ascii2hex(v) (v-48-(v>='A'?7:0))
|
||||||
|
#define hex2byte(b) (ascii2hex(*(b)))*16+((ascii2hex(*(b+1))))
|
||||||
|
static uint8_t calcHexFrameCheckSum(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AsciiHexLE2Int()
|
||||||
|
* help function to convert AsciiHex Little Endian to uint32_t
|
||||||
|
* ascii: pointer to Ascii Hex Little Endian data
|
||||||
|
* anz: 1,2,4 or 8 nibble
|
||||||
|
*/
|
||||||
|
static uint32_t AsciiHexLE2Int(const char *ascii, const uint8_t anz) {
|
||||||
|
char help[9] = {};
|
||||||
|
|
||||||
|
// sort from little endian format to normal format
|
||||||
|
switch (anz) {
|
||||||
|
case 1:
|
||||||
|
help[0] = ascii[0];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 4:
|
||||||
|
case 8:
|
||||||
|
for (uint8_t i = 0; i < anz; i += 2) {
|
||||||
|
help[i] = ascii[anz-i-2];
|
||||||
|
help[i+1] = ascii[anz-i-1];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (static_cast<uint32_t>(strtoul(help, nullptr, 16)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* disassembleHexData()
|
||||||
|
* analysis the hex message and extract: response, id, flag and value/text
|
||||||
|
* buffer: pointer to message (ascii hex little endian format)
|
||||||
|
* data: disassembeled message
|
||||||
|
* return: true = successful disassembeld, false = hex sum fault or message
|
||||||
|
* do not aligin with VE.Diekt syntax
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
bool VeDirectFrameHandler<T>::disassembleHexData(VeDirectHexData &data) {
|
||||||
|
bool state = false;
|
||||||
|
char * buffer = _hexBuffer;
|
||||||
|
auto len = strlen(buffer);
|
||||||
|
|
||||||
|
// reset hex data first
|
||||||
|
data = {};
|
||||||
|
|
||||||
|
if ((len > 3) && (calcHexFrameCheckSum(buffer, len) == 0x00)) {
|
||||||
|
data.rsp = static_cast<VeDirectHexResponse>(AsciiHexLE2Int(buffer+1, 1));
|
||||||
|
|
||||||
|
switch (data.rsp) {
|
||||||
|
case R_DONE:
|
||||||
|
case R_ERROR:
|
||||||
|
case R_PING:
|
||||||
|
case R_UNKNOWN:
|
||||||
|
strncpy(data.text, buffer+2, len-4);
|
||||||
|
state = true;
|
||||||
|
break;
|
||||||
|
case R_GET:
|
||||||
|
case R_SET:
|
||||||
|
case R_ASYNC:
|
||||||
|
data.id = AsciiHexLE2Int(buffer+2, 4);
|
||||||
|
|
||||||
|
// future option: to analyse the flag here?
|
||||||
|
data.flag = AsciiHexLE2Int(buffer+6, 2);
|
||||||
|
|
||||||
|
if (len == 12) { // 8bit value
|
||||||
|
data.value = AsciiHexLE2Int(buffer+8, 2);
|
||||||
|
state = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 14) { // 16bit value
|
||||||
|
data.value = AsciiHexLE2Int(buffer+8, 4);
|
||||||
|
state = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 18) { // 32bit value
|
||||||
|
data.value = AsciiHexLE2Int(buffer+8, 8);
|
||||||
|
state = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break; // something went wrong
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr(MODUL_DEBUG == 1) {
|
||||||
|
_msgOut->printf("[VE.Direct] debug: disassembleHexData(), rsp: %i, id: 0x%04X, value: 0x%X, Flag: 0x%02X\r\n",
|
||||||
|
data.rsp, data.id, data.value, data.flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_verboseLogging && !state)
|
||||||
|
_msgOut->printf("[VE.Direct] failed to disassemble the hex message: %s\r\n", buffer);
|
||||||
|
|
||||||
|
return (state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* uint2toHexLEString()
|
||||||
|
* help function to convert up to 32 bits into little endian hex String
|
||||||
|
* ascii: pointer to Ascii Hex Little Endian data
|
||||||
|
* anz: 1,2,4 or 8 nibble
|
||||||
|
*/
|
||||||
|
static String Int2HexLEString(uint32_t value, uint8_t anz) {
|
||||||
|
char hexchar[] = "0123456789ABCDEF";
|
||||||
|
char help[9] = {};
|
||||||
|
|
||||||
|
switch (anz) {
|
||||||
|
case 1:
|
||||||
|
help[0] = hexchar[(value & 0x0000000F)];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 4:
|
||||||
|
case 8:
|
||||||
|
for (uint8_t i = 0; i < anz; i += 2) {
|
||||||
|
help[i] = hexchar[(value>>((1+1*i)*4)) & 0x0000000F];
|
||||||
|
help[i+1] = hexchar[(value>>((1*i)*4)) & 0x0000000F];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
return String(help);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sendHexCommand()
|
||||||
|
* send the hex commend after assembling the command string
|
||||||
|
* cmd: command
|
||||||
|
* id: id/register, default 0
|
||||||
|
* value: value to write into a id/register, default 0
|
||||||
|
* valsize: size of the value/id, 8, 16 or 32 bit, default 0
|
||||||
|
* return: true = message assembeld and send, false = it was not possible to put the message together
|
||||||
|
* SAMPLE: ping command: sendHexCommand(PING),
|
||||||
|
* read total DC input power sendHexCommand(GET, 0xEDEC)
|
||||||
|
* set Charge current limit 10A sendHexCommand(SET, 0x2015, 64, 16)
|
||||||
|
*
|
||||||
|
* WARNING: some values are stored in non-volatile memory. Continuous writing, for example from a control loop, will
|
||||||
|
* lead to early failure.
|
||||||
|
* On MPPT for example 0xEDE0 - 0xEDFF. Check the Vivtron doc "BlueSolar-HEX-protocol.pdf"
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
bool VeDirectFrameHandler<T>::sendHexCommand(VeDirectHexCommand cmd, uint16_t id, uint32_t value, uint8_t valsize) {
|
||||||
|
bool ret = false;
|
||||||
|
uint8_t flag = 0x00; // always 0x00
|
||||||
|
|
||||||
|
String txData = ":" + Int2HexLEString(cmd, 1); // add the command nibble
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case PING:
|
||||||
|
case APP_VERSION:
|
||||||
|
case PRODUCT_ID:
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
case GET:
|
||||||
|
case ASYNC:
|
||||||
|
txData += Int2HexLEString(id, 4); // add the id/register (4 nibble)
|
||||||
|
txData += Int2HexLEString(flag, 2); // add the flag (2 nibble)
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
case SET:
|
||||||
|
txData += Int2HexLEString(id, 4); // add the id/register (4 nibble)
|
||||||
|
txData += Int2HexLEString(flag, 2); // add the flag (2 nibble)
|
||||||
|
if ((valsize == 8) || (valsize == 16) || (valsize == 32)) {
|
||||||
|
txData += Int2HexLEString(value, valsize/4); // add value (2-8 nibble)
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
// add the checksum (2 nibble)
|
||||||
|
txData += Int2HexLEString(calcHexFrameCheckSum(txData.c_str(), txData.length()), 2);
|
||||||
|
String send = txData + "\n"; // hex command end byte
|
||||||
|
_vedirectSerial->write(send.c_str(), send.length());
|
||||||
|
|
||||||
|
if constexpr(MODUL_DEBUG == 1) {
|
||||||
|
auto blen = _vedirectSerial->availableForWrite();
|
||||||
|
_msgOut->printf("[VE.Direct] debug: sendHexCommand(): %s, Free FIFO-Buffer: %u\r\n", txData.c_str(), blen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_verboseLogging && !ret)
|
||||||
|
_msgOut->println("[VE.Direct] send hex command fault:" + txData);
|
||||||
|
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
@ -1,6 +1,20 @@
|
|||||||
|
/* VeDirectMpptController.cpp
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 2020.08.20 - 0.0 - ???
|
||||||
|
* 2024.03.18 - 0.1 - add of: - temperature from "Smart Battery Sense" connected over VE.Smart network
|
||||||
|
* - temperature from internal MPPT sensor
|
||||||
|
* - "total DC input power" from MPPT's connected over VE.Smart network
|
||||||
|
*/
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "VeDirectMpptController.h"
|
#include "VeDirectMpptController.h"
|
||||||
|
|
||||||
|
|
||||||
|
// support for debugging, 0=without extended logging, 1=with extended logging
|
||||||
|
constexpr int MODUL_DEBUG = 0;
|
||||||
|
|
||||||
|
|
||||||
void VeDirectMpptController::init(int8_t rx, int8_t tx, Print* msgOut, bool verboseLogging, uint16_t hwSerialPort)
|
void VeDirectMpptController::init(int8_t rx, int8_t tx, Print* msgOut, bool verboseLogging, uint16_t hwSerialPort)
|
||||||
{
|
{
|
||||||
VeDirectFrameHandler::init("MPPT", rx, tx, msgOut, verboseLogging, hwSerialPort);
|
VeDirectFrameHandler::init("MPPT", rx, tx, msgOut, verboseLogging, hwSerialPort);
|
||||||
@ -80,3 +94,95 @@ void VeDirectMpptController::frameValidEvent() {
|
|||||||
_tmpFrame.E = _efficiency.getAverage();
|
_tmpFrame.E = _efficiency.getAverage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
// loop()
|
||||||
|
// send hex commands to MPPT every 5 seconds
|
||||||
|
*/
|
||||||
|
void VeDirectMpptController::loop()
|
||||||
|
{
|
||||||
|
VeDirectFrameHandler::loop();
|
||||||
|
|
||||||
|
// Copy from the "VE.Direct Protocol" documentation
|
||||||
|
// For firmware version v1.52 and below, when no VE.Direct queries are sent to the device, the
|
||||||
|
// charger periodically sends human readable (TEXT) data to the serial port. For firmware
|
||||||
|
// versions v1.53 and above, the charger always periodically sends TEXT data to the serial port.
|
||||||
|
// --> We just use hex commandes for firmware >= 1.53 to keep text messages alive
|
||||||
|
if (atoi(_tmpFrame.FW) >= 153 ) {
|
||||||
|
if ((millis() - _lastPingTime) > 5000) {
|
||||||
|
|
||||||
|
sendHexCommand(GET, 0x2027); // MPPT total DC input power
|
||||||
|
sendHexCommand(GET, 0xEDDB); // MPPT internal temperature
|
||||||
|
sendHexCommand(GET, 0xEDEC); // "Smart Battery Sense" temperature
|
||||||
|
sendHexCommand(GET, 0x200F); // Network info
|
||||||
|
_lastPingTime = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hexDataHandler()
|
||||||
|
* analyse the content of VE.Direct hex messages
|
||||||
|
* Handels the received hex data from the MPPT
|
||||||
|
*/
|
||||||
|
void VeDirectMpptController::hexDataHandler(VeDirectHexData const &data) {
|
||||||
|
bool state = false;
|
||||||
|
|
||||||
|
switch (data.rsp) {
|
||||||
|
case R_GET:
|
||||||
|
case R_ASYNC:
|
||||||
|
|
||||||
|
// check if MPPT internal temperature is available
|
||||||
|
if(data.id == 0xEDDB) {
|
||||||
|
_ExData.T = static_cast<int32_t>(data.value) * 10; // conversion from unit [0.01°C] to unit [m°C]
|
||||||
|
_ExData.Tts = millis();
|
||||||
|
state = true;
|
||||||
|
|
||||||
|
if constexpr(MODUL_DEBUG == 1)
|
||||||
|
_msgOut->printf("[VE.Direct] debug: hexDataHandler(), MTTP Temperature: %.2f°C\r\n", _ExData.T/1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if temperature from "Smart Battery Sense" is available
|
||||||
|
if(data.id == 0xEDEC) {
|
||||||
|
_ExData.TSBS = static_cast<int32_t>(data.value) * 10 - 272150; // conversion from unit [0.01K] to unit [m°C]
|
||||||
|
_ExData.TSBSts = millis();
|
||||||
|
state = true;
|
||||||
|
|
||||||
|
if constexpr(MODUL_DEBUG == 1)
|
||||||
|
_msgOut->printf("[VE.Direct] debug: hexDataHandler(), Battery Temperature: %.2f°C\r\n", _ExData.TSBS/1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if "Total DC power" is available
|
||||||
|
if(data.id == 0x2027) {
|
||||||
|
_ExData.TDCP = data.value * 10; // conversion from unit [0.01W] to unit [mW]
|
||||||
|
_ExData.TDCPts = millis();
|
||||||
|
state = true;
|
||||||
|
|
||||||
|
if constexpr(MODUL_DEBUG == 1)
|
||||||
|
_msgOut->printf("[VE.Direct] debug: hexDataHandler(), Total Power: %.2fW\r\n", _ExData.TDCP/1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if connected MPPT is charge instance master
|
||||||
|
// Hint: not used right now but maybe necessary for future extensions
|
||||||
|
if(data.id == 0x200F) {
|
||||||
|
_veMaster = ((data.value & 0x0F) == 0x02) ? true : false;
|
||||||
|
state = true;
|
||||||
|
|
||||||
|
if constexpr(MODUL_DEBUG == 1)
|
||||||
|
_msgOut->printf("[VE.Direct] debug: hexDataHandler(), Networkmode: 0x%X\r\n", data.value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr(MODUL_DEBUG == 1)
|
||||||
|
_msgOut->printf("[VE.Direct] debug: hexDataHandler(): rsp: %i, id: 0x%04X, value: %i[0x%08X], text: %s\r\n",
|
||||||
|
data.rsp, data.id, data.value, data.value, data.text);
|
||||||
|
|
||||||
|
if (_verboseLogging && state)
|
||||||
|
_msgOut->printf("[VE.Direct] MPPT hex message: rsp: %i, id: 0x%04X, value: %i[0x%08X], text: %s\r\n",
|
||||||
|
data.rsp, data.id, data.value, data.value, data.text);
|
||||||
|
}
|
||||||
|
|||||||
@ -44,8 +44,24 @@ public:
|
|||||||
|
|
||||||
using data_t = veMpptStruct;
|
using data_t = veMpptStruct;
|
||||||
|
|
||||||
|
virtual void loop() final; // main loop to read ve.direct data
|
||||||
|
|
||||||
|
struct veMPPTExStruct {
|
||||||
|
int32_t T; // temperature [m°C] from internal MPPT sensor
|
||||||
|
unsigned long Tts; // time of last recieved value
|
||||||
|
int32_t TSBS; // temperature [m°C] from the "Smart Battery Sense"
|
||||||
|
unsigned long TSBSts; // time of last recieved value
|
||||||
|
uint32_t TDCP; // total DC input power [mW]
|
||||||
|
unsigned long TDCPts; // time of last recieved value
|
||||||
|
};
|
||||||
|
veMPPTExStruct _ExData{};
|
||||||
|
veMPPTExStruct const *getExData() const { return &_ExData; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void hexDataHandler(VeDirectHexData const &data) final;
|
||||||
bool processTextDataDerived(std::string const& name, std::string const& value) final;
|
bool processTextDataDerived(std::string const& name, std::string const& value) final;
|
||||||
void frameValidEvent() final;
|
void frameValidEvent() final;
|
||||||
MovingAverage<float, 5> _efficiency;
|
MovingAverage<float, 5> _efficiency;
|
||||||
|
unsigned long _lastPingTime = 0L; // time of last device PING/GET hex command
|
||||||
|
bool _veMaster = true; // MPPT is instance master
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user