first version
This commit is contained in:
parent
30440472f7
commit
1e7f6b8f0f
@ -19,7 +19,7 @@ public:
|
||||
void init();
|
||||
void loop();
|
||||
private:
|
||||
std::map<String, String> _kv_map;
|
||||
veStruct _kvFrame;
|
||||
uint32_t _lastPublish;
|
||||
};
|
||||
|
||||
|
||||
@ -41,6 +41,16 @@ char MODULE[] = "VE.Frame"; // Victron seems to use this to find out where loggi
|
||||
// The name of the record that contains the checksum.
|
||||
static constexpr char checksumTagName[] = "CHECKSUM";
|
||||
|
||||
// state machine
|
||||
enum States {
|
||||
IDLE,
|
||||
RECORD_BEGIN,
|
||||
RECORD_NAME,
|
||||
RECORD_VALUE,
|
||||
CHECKSUM,
|
||||
RECORD_HEX
|
||||
};
|
||||
|
||||
HardwareSerial VedirectSerial(1);
|
||||
|
||||
VeDirectFrameHandler VeDirect;
|
||||
@ -49,8 +59,10 @@ VeDirectFrameHandler::VeDirectFrameHandler() :
|
||||
//mStop(false), // don't know what Victron uses this for, not using
|
||||
_state(IDLE),
|
||||
_checksum(0),
|
||||
_textPointer(0),
|
||||
_name(""),
|
||||
_value(""),
|
||||
_tmpFrame(),
|
||||
_pollInterval(5),
|
||||
_lastPoll(0)
|
||||
{
|
||||
@ -107,7 +119,8 @@ void VeDirectFrameHandler::rxData(uint8_t inbyte)
|
||||
}
|
||||
break;
|
||||
case RECORD_BEGIN:
|
||||
_name = (char) inbyte;
|
||||
_textPointer = _name;
|
||||
*_textPointer++ = inbyte;
|
||||
_state = RECORD_NAME;
|
||||
break;
|
||||
case RECORD_NAME:
|
||||
@ -115,18 +128,22 @@ void VeDirectFrameHandler::rxData(uint8_t inbyte)
|
||||
switch(inbyte) {
|
||||
case '\t':
|
||||
// the Checksum record indicates a EOR
|
||||
if (_name.equals(checksumTagName)) {
|
||||
if ( _textPointer < (_name + sizeof(_name)) ) {
|
||||
*_textPointer = 0; /* Zero terminate */
|
||||
if (strcmp(_name, checksumTagName) == 0) {
|
||||
_state = CHECKSUM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_textPointer = _value; /* Reset value pointer */
|
||||
_state = RECORD_VALUE;
|
||||
_value = "";
|
||||
break;
|
||||
case '#': /* Ignore # from serial number*/
|
||||
break;
|
||||
default:
|
||||
// add byte to name, but do no overflow
|
||||
_name += (char) inbyte;
|
||||
if ( _textPointer < (_name + sizeof(_name)) )
|
||||
*_textPointer++ = inbyte;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -134,14 +151,18 @@ void VeDirectFrameHandler::rxData(uint8_t inbyte)
|
||||
// The record value is being received. The \r indicates a new record.
|
||||
switch(inbyte) {
|
||||
case '\n':
|
||||
_tmpMap[_name] = _value;
|
||||
if ( _textPointer < (_value + sizeof(_value)) ) {
|
||||
*_textPointer = 0; // make zero ended
|
||||
textRxEvent(_name, _value);
|
||||
}
|
||||
_state = RECORD_BEGIN;
|
||||
break;
|
||||
case '\r': /* Skip */
|
||||
break;
|
||||
default:
|
||||
// add byte to value, but do no overflow
|
||||
_value += (char) inbyte;
|
||||
if ( _textPointer < (_value + sizeof(_value)) )
|
||||
*_textPointer++ = inbyte;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -164,6 +185,72 @@ void VeDirectFrameHandler::rxData(uint8_t inbyte)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* textRxEvent
|
||||
* This function is called every time a new name/value is successfully parsed. It writes the values to the temporary buffer.
|
||||
*/
|
||||
void VeDirectFrameHandler::textRxEvent(char * name, char * value) {
|
||||
if (strcmp(name, "PID") == 0) {
|
||||
_tmpFrame.PID = strtol(value, nullptr, 0);
|
||||
}
|
||||
else if (strcmp(name, "SER") == 0) {
|
||||
strcpy(_tmpFrame.SER, value);
|
||||
}
|
||||
else if (strcmp(name, "FW") == 0) {
|
||||
strcpy(_tmpFrame.FW, value);
|
||||
}
|
||||
else if (strcmp(name, "LOAD") == 0) {
|
||||
if (strcmp(value, "ON") == 0)
|
||||
_tmpFrame.LOAD = true;
|
||||
else
|
||||
_tmpFrame.LOAD = false;
|
||||
}
|
||||
else if (strcmp(name, "CS") == 0) {
|
||||
_tmpFrame.CS = atoi(value);
|
||||
}
|
||||
else if (strcmp(name, "ERR") == 0) {
|
||||
_tmpFrame.ERR = atoi(value);
|
||||
}
|
||||
else if (strcmp(name, "OR") == 0) {
|
||||
_tmpFrame.OR = strtol(value, nullptr, 0);
|
||||
}
|
||||
else if (strcmp(name, "MPPT") == 0) {
|
||||
_tmpFrame.MPPT = atoi(value);
|
||||
}
|
||||
else if (strcmp(name, "HSDS") == 0) {
|
||||
_tmpFrame.HSDS = atoi(value);
|
||||
}
|
||||
else if (strcmp(name, "V") == 0) {
|
||||
_tmpFrame.V = round(atof(value) / 10.0) / 100.0;
|
||||
}
|
||||
else if (strcmp(name, "I") == 0) {
|
||||
_tmpFrame.I = round(atof(value) / 10.0) / 100.0;
|
||||
}
|
||||
else if (strcmp(name, "VPV") == 0) {
|
||||
_tmpFrame.VPV = round(atof(value) / 10.0) / 100.0;
|
||||
}
|
||||
else if (strcmp(name, "PPV") == 0) {
|
||||
_tmpFrame.PPV = atoi(value);
|
||||
}
|
||||
else if (strcmp(name, "H19") == 0) {
|
||||
_tmpFrame.H19 = atof(value) / 100.0;
|
||||
}
|
||||
else if (strcmp(name, "H20") == 0) {
|
||||
_tmpFrame.H20 = atof(value) / 100.0;
|
||||
}
|
||||
else if (strcmp(name, "H21") == 0) {
|
||||
_tmpFrame.H21 = atoi(value);
|
||||
}
|
||||
else if (strcmp(name, "H22") == 0) {
|
||||
_tmpFrame.H22 = atof(value) / 100.0;
|
||||
}
|
||||
else if (strcmp(name, "H23") == 0) {
|
||||
_tmpFrame.H23 = atoi(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* frameEndEvent
|
||||
* This function is called at the end of the received frame. If the checksum is valid, the temp buffer is read line by line.
|
||||
@ -172,10 +259,10 @@ void VeDirectFrameHandler::rxData(uint8_t inbyte)
|
||||
*/
|
||||
void VeDirectFrameHandler::frameEndEvent(bool valid) {
|
||||
if ( valid ) {
|
||||
veMap = _tmpMap;
|
||||
veFrame = _tmpFrame;
|
||||
setLastUpdate();
|
||||
}
|
||||
_tmpMap.clear();
|
||||
_tmpFrame = {};
|
||||
}
|
||||
|
||||
/*
|
||||
@ -199,13 +286,10 @@ bool VeDirectFrameHandler::hexRxEvent(uint8_t inbyte) {
|
||||
}
|
||||
|
||||
bool VeDirectFrameHandler::isDataValid() {
|
||||
if (veMap.empty()) {
|
||||
return false;
|
||||
}
|
||||
if ((millis() - getLastUpdate()) / 1000 > _pollInterval * 5) {
|
||||
return false;
|
||||
}
|
||||
if (veMap.find("SER") == veMap.end()) {
|
||||
if (strlen(veFrame.SER) == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -229,12 +313,11 @@ void VeDirectFrameHandler::setLastUpdate()
|
||||
* getPidAsString
|
||||
* This function returns the product id (PID) as readable text.
|
||||
*/
|
||||
String VeDirectFrameHandler::getPidAsString(const char* pid)
|
||||
String VeDirectFrameHandler::getPidAsString(uint16_t pid)
|
||||
{
|
||||
String strPID ="";
|
||||
|
||||
long lPID = strtol(pid, nullptr, 0);
|
||||
switch(lPID) {
|
||||
switch(pid) {
|
||||
case 0x0300:
|
||||
strPID = "BlueSolar MPPT 70|15";
|
||||
break;
|
||||
@ -452,12 +535,11 @@ String VeDirectFrameHandler::getPidAsString(const char* pid)
|
||||
* getCsAsString
|
||||
* This function returns the state of operations (CS) as readable text.
|
||||
*/
|
||||
String VeDirectFrameHandler::getCsAsString(const char* cs)
|
||||
String VeDirectFrameHandler::getCsAsString(uint8_t cs)
|
||||
{
|
||||
String strCS ="";
|
||||
|
||||
int iCS = atoi(cs);
|
||||
switch(iCS) {
|
||||
switch(cs) {
|
||||
case 0:
|
||||
strCS = "OFF";
|
||||
break;
|
||||
@ -495,12 +577,11 @@ String VeDirectFrameHandler::getCsAsString(const char* cs)
|
||||
* getErrAsString
|
||||
* This function returns error state (ERR) as readable text.
|
||||
*/
|
||||
String VeDirectFrameHandler::getErrAsString(const char* err)
|
||||
String VeDirectFrameHandler::getErrAsString(uint8_t err)
|
||||
{
|
||||
String strERR ="";
|
||||
|
||||
int iERR = atoi(err);
|
||||
switch(iERR) {
|
||||
switch(err) {
|
||||
case 0:
|
||||
strERR = "No error";
|
||||
break;
|
||||
@ -571,12 +652,11 @@ String VeDirectFrameHandler::getErrAsString(const char* err)
|
||||
* getOrAsString
|
||||
* This function returns the off reason (OR) as readable text.
|
||||
*/
|
||||
String VeDirectFrameHandler::getOrAsString(const char* offReason)
|
||||
String VeDirectFrameHandler::getOrAsString(uint32_t offReason)
|
||||
{
|
||||
String strOR ="";
|
||||
|
||||
long lOR = strtol(offReason, nullptr, 0);
|
||||
switch(lOR) {
|
||||
switch(offReason) {
|
||||
case 0x00000000:
|
||||
strOR = "Not off";
|
||||
break;
|
||||
@ -617,14 +697,13 @@ String VeDirectFrameHandler::getOrAsString(const char* offReason)
|
||||
* getMpptAsString
|
||||
* This function returns the state of MPPT (MPPT) as readable text.
|
||||
*/
|
||||
String VeDirectFrameHandler::getMpptAsString(const char* mppt)
|
||||
String VeDirectFrameHandler::getMpptAsString(uint8_t mppt)
|
||||
{
|
||||
String strMPPT ="";
|
||||
|
||||
int iMPPT = atoi(mppt);
|
||||
switch(iMPPT) {
|
||||
switch(mppt) {
|
||||
case 0:
|
||||
strMPPT = "Off";
|
||||
strMPPT = "OFF";
|
||||
break;
|
||||
case 1:
|
||||
strMPPT = "Voltage or current limited";
|
||||
|
||||
@ -22,7 +22,29 @@
|
||||
#define VICTRON_PIN_RX 22 // HardwareSerial RX Pin
|
||||
#endif
|
||||
|
||||
#define VE_MAX_NAME_LEN 9 // VE.Direct Protocol: max name size is 9 including /0
|
||||
#define VE_MAX_VALUE_LEN 33 // VE.Direct Protocol: max value size is 33 including /0
|
||||
|
||||
typedef struct {
|
||||
uint16_t PID; // pruduct id
|
||||
char SER[VE_MAX_VALUE_LEN]; // serial number
|
||||
char FW[VE_MAX_VALUE_LEN]; // firmware release number
|
||||
bool LOAD; // virtual load output state (on if battery voltage reaches upper limit, off if battery reaches lower limit)
|
||||
uint8_t CS; // current state of operation e. g. OFF or Bulk
|
||||
uint8_t ERR; // error code
|
||||
uint32_t OR; // off reason
|
||||
uint8_t MPPT; // state of MPP tracker
|
||||
uint16_t HSDS; // day sequence number 1...365
|
||||
double V; // battery voltage in V
|
||||
double I; // battery current in A
|
||||
double VPV; // panel voltage in V
|
||||
double PPV; // panel power in W
|
||||
double H19; // yield total kWh
|
||||
double H20; // yield today kWh
|
||||
uint16_t H21; // maximum power today W
|
||||
double H22; // yield yesterday kWh
|
||||
uint16_t H23; // maximum power yesterday W
|
||||
} veStruct;
|
||||
|
||||
class VeDirectFrameHandler {
|
||||
|
||||
@ -34,37 +56,29 @@ public:
|
||||
void loop(); // main loop to read ve.direct data
|
||||
unsigned long getLastUpdate(); // timestamp of last successful frame read
|
||||
bool isDataValid(); // return true if data valid and not outdated
|
||||
String getPidAsString(const char* pid); // product id as string
|
||||
String getCsAsString(const char* cs); // current state as string
|
||||
String getErrAsString(const char* err); // errer state as string
|
||||
String getOrAsString(const char* offReason); // off reason as string
|
||||
String getMpptAsString(const char* mppt); // state of mppt as string
|
||||
String getPidAsString(uint16_t pid); // product id as string
|
||||
String getCsAsString(uint8_t cs); // current state as string
|
||||
String getErrAsString(uint8_t err); // errer state as string
|
||||
String getOrAsString(uint32_t offReason); // off reason as string
|
||||
String getMpptAsString(uint8_t mppt); // state of mppt as string
|
||||
|
||||
std::map<String, String> veMap; // public map for received name and value pairs
|
||||
veStruct veFrame; // public map for received name and value pairs
|
||||
|
||||
private:
|
||||
void setLastUpdate(); // set timestampt after successful frame read
|
||||
void rxData(uint8_t inbyte); // byte of serial data
|
||||
void textRxEvent(char *, char *);
|
||||
void frameEndEvent(bool); // copy temp map to public map
|
||||
void logE(const char *, const char *);
|
||||
bool hexRxEvent(uint8_t);
|
||||
|
||||
//bool mStop; // not sure what Victron uses this for, not using
|
||||
|
||||
enum States { // state machine
|
||||
IDLE,
|
||||
RECORD_BEGIN,
|
||||
RECORD_NAME,
|
||||
RECORD_VALUE,
|
||||
CHECKSUM,
|
||||
RECORD_HEX
|
||||
};
|
||||
|
||||
int _state; // current state
|
||||
uint8_t _checksum; // checksum value
|
||||
String _name; // buffer for the field name
|
||||
String _value; // buffer for the field value
|
||||
std::map<String, String> _tmpMap; // private map for received name and value pairs
|
||||
char * _textPointer; // pointer to the private buffer we're writing to, name or value
|
||||
char _name[VE_MAX_VALUE_LEN]; // buffer for the field name
|
||||
char _value[VE_MAX_VALUE_LEN]; // buffer for the field value
|
||||
veStruct _tmpFrame; // private struct for received name and value pairs
|
||||
unsigned long _pollInterval;
|
||||
unsigned long _lastPoll;
|
||||
};
|
||||
|
||||
@ -83,7 +83,7 @@ void MqttHandleVedirectHassClass::publishConfig()
|
||||
|
||||
void MqttHandleVedirectHassClass::publishSensor(const char* caption, const char* subTopic, const char* deviceClass, const char* stateClass, const char* unitOfMeasurement )
|
||||
{
|
||||
String serial = VeDirect.veMap["SER"];
|
||||
String serial = VeDirect.veFrame.SER;
|
||||
|
||||
String sensorId = caption;
|
||||
sensorId.replace(" ", "_");
|
||||
@ -93,7 +93,10 @@ void MqttHandleVedirectHassClass::publishSensor(const char* caption, const char*
|
||||
+ "/" + sensorId
|
||||
+ "/config";
|
||||
|
||||
String statTopic = MqttSettings.getPrefix() + "victron/" + VeDirect.veMap["SER"] + "/" + subTopic;
|
||||
String statTopic = MqttSettings.getPrefix() + "victron/";
|
||||
statTopic.concat(VeDirect.veFrame.SER);
|
||||
statTopic.concat("/");
|
||||
statTopic.concat(subTopic);
|
||||
|
||||
DynamicJsonDocument root(1024);
|
||||
root[F("name")] = caption;
|
||||
@ -124,7 +127,7 @@ void MqttHandleVedirectHassClass::publishSensor(const char* caption, const char*
|
||||
}
|
||||
void MqttHandleVedirectHassClass::publishBinarySensor(const char* caption, const char* subTopic, const char* payload_on, const char* payload_off)
|
||||
{
|
||||
String serial = VeDirect.veMap["SER"];
|
||||
String serial = VeDirect.veFrame.SER;
|
||||
|
||||
String sensorId = caption;
|
||||
sensorId.replace(" ", "_");
|
||||
@ -134,7 +137,10 @@ void MqttHandleVedirectHassClass::publishBinarySensor(const char* caption, const
|
||||
+ "/" + sensorId
|
||||
+ "/config";
|
||||
|
||||
String statTopic = MqttSettings.getPrefix() + "victron/" + VeDirect.veMap["SER"] + "/" + subTopic;
|
||||
String statTopic = MqttSettings.getPrefix() + "victron/";
|
||||
statTopic.concat(VeDirect.veFrame.SER);
|
||||
statTopic.concat("/");
|
||||
statTopic.concat(subTopic);
|
||||
|
||||
DynamicJsonDocument root(1024);
|
||||
root[F("name")] = caption;
|
||||
@ -153,11 +159,12 @@ void MqttHandleVedirectHassClass::publishBinarySensor(const char* caption, const
|
||||
|
||||
void MqttHandleVedirectHassClass::createDeviceInfo(JsonObject& object)
|
||||
{
|
||||
object[F("name")] = "Victron(" + VeDirect.veMap["SER"] + ")";
|
||||
object[F("ids")] = VeDirect.veMap["SER"];
|
||||
String serial = VeDirect.veFrame.SER;
|
||||
object[F("name")] = "Victron(" + serial + ")";
|
||||
object[F("ids")] = serial;
|
||||
object[F("cu")] = String(F("http://")) + NetworkSettings.localIP().toString();
|
||||
object[F("mf")] = F("OpenDTU");
|
||||
object[F("mdl")] = VeDirect.getPidAsString(VeDirect.veMap["PID"].c_str());
|
||||
object[F("mdl")] = VeDirect.getPidAsString(VeDirect.veFrame.PID);
|
||||
object[F("sw")] = AUTO_GIT_HASH;
|
||||
}
|
||||
|
||||
|
||||
@ -29,65 +29,69 @@ void MqttHandleVedirectClass::loop()
|
||||
}
|
||||
|
||||
if (millis() - _lastPublish > (config.Mqtt_PublishInterval * 1000)) {
|
||||
String key;
|
||||
String value;
|
||||
String mapedValue;
|
||||
bool bChanged = false;
|
||||
String serial = VeDirect.veMap["SER"];
|
||||
String topic = "victron/";
|
||||
topic.concat(VeDirect.veFrame.SER);
|
||||
topic.concat("/");
|
||||
|
||||
String topic = "";
|
||||
for (auto it = VeDirect.veMap.begin(); it != VeDirect.veMap.end(); ++it) {
|
||||
key = it->first;
|
||||
value = it->second;
|
||||
|
||||
if (config.Vedirect_UpdatesOnly){
|
||||
// Mark changed values
|
||||
auto a = _kv_map.find(key);
|
||||
bChanged = true;
|
||||
if (a != _kv_map.end()) {
|
||||
if (a->first.equals(value)) {
|
||||
bChanged = false;
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.PID != _kvFrame.PID)
|
||||
MqttSettings.publish(topic + "PID", VeDirect.getPidAsString(VeDirect.veFrame.PID));
|
||||
if (!config.Vedirect_UpdatesOnly || strcmp(VeDirect.veFrame.SER, _kvFrame.SER) != 0)
|
||||
MqttSettings.publish(topic + "SER", VeDirect.veFrame.SER );
|
||||
if (!config.Vedirect_UpdatesOnly || strcmp(VeDirect.veFrame.FW, _kvFrame.FW) != 0)
|
||||
MqttSettings.publish(topic + "FW", VeDirect.veFrame.FW);
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.LOAD != _kvFrame.LOAD)
|
||||
MqttSettings.publish(topic + "LOAD", VeDirect.veFrame.LOAD == true ? "ON": "OFF");
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.CS != _kvFrame.CS)
|
||||
MqttSettings.publish(topic + "CS", VeDirect.getCsAsString(VeDirect.veFrame.CS));
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.ERR != _kvFrame.ERR)
|
||||
MqttSettings.publish(topic + "ERR", VeDirect.getErrAsString(VeDirect.veFrame.ERR));
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.OR != _kvFrame.OR)
|
||||
MqttSettings.publish(topic + "OR", VeDirect.getOrAsString(VeDirect.veFrame.OR));
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.MPPT != _kvFrame.MPPT)
|
||||
MqttSettings.publish(topic + "MPPT", VeDirect.getMpptAsString(VeDirect.veFrame.MPPT));
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.HSDS != _kvFrame.HSDS) {
|
||||
value = VeDirect.veFrame.HSDS;
|
||||
MqttSettings.publish(topic + "HSDS", value);
|
||||
}
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.V != _kvFrame.V) {
|
||||
value = VeDirect.veFrame.V;
|
||||
MqttSettings.publish(topic + "V", value);
|
||||
}
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.I != _kvFrame.I) {
|
||||
value = VeDirect.veFrame.I;
|
||||
MqttSettings.publish(topic + "I", value);
|
||||
}
|
||||
|
||||
// publish only changed key, values pairs
|
||||
if (!config.Vedirect_UpdatesOnly || (bChanged && config.Vedirect_UpdatesOnly)) {
|
||||
topic = "victron/" + serial + "/";
|
||||
topic.concat(key);
|
||||
if (key.equals("PID")) {
|
||||
mapedValue = VeDirect.getPidAsString(value.c_str());
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.VPV != _kvFrame.VPV) {
|
||||
value = VeDirect.veFrame.VPV;
|
||||
MqttSettings.publish(topic + "VPV", value);
|
||||
}
|
||||
else if (key.equals("CS")) {
|
||||
mapedValue = VeDirect.getCsAsString(value.c_str());
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.PPV != _kvFrame.PPV) {
|
||||
value = VeDirect.veFrame.PPV;
|
||||
MqttSettings.publish(topic + "PPV", value);
|
||||
}
|
||||
else if (key.equals("ERR")) {
|
||||
mapedValue = VeDirect.getErrAsString(value.c_str());
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.H19 != _kvFrame.H19) {
|
||||
value = VeDirect.veFrame.H19;
|
||||
MqttSettings.publish(topic + "H19", value);
|
||||
}
|
||||
else if (key.equals("OR")) {
|
||||
mapedValue = VeDirect.getOrAsString(value.c_str());
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.H20 != _kvFrame.H20) {
|
||||
value = VeDirect.veFrame.H20;
|
||||
MqttSettings.publish(topic + "H20", value);
|
||||
}
|
||||
else if (key.equals("MPPT")) {
|
||||
mapedValue = VeDirect.getMpptAsString(value.c_str());
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.H21 != _kvFrame.H21) {
|
||||
value = VeDirect.veFrame.H21;
|
||||
MqttSettings.publish(topic + "H21", value);
|
||||
}
|
||||
else if (key.equals("V") ||
|
||||
key.equals("I") ||
|
||||
key.equals("VPV")) {
|
||||
mapedValue = round(value.toDouble() / 10.0) / 100.0;
|
||||
}
|
||||
else if (key.equals("H19") ||
|
||||
key.equals("H20") ||
|
||||
key.equals("H22")) {
|
||||
mapedValue = value.toDouble() / 100.0;
|
||||
}
|
||||
else {
|
||||
mapedValue = value;
|
||||
}
|
||||
MqttSettings.publish(topic.c_str(), mapedValue.c_str());
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.H22 != _kvFrame.H22) {
|
||||
value = VeDirect.veFrame.H22;
|
||||
MqttSettings.publish(topic + "H22", value);
|
||||
}
|
||||
if (!config.Vedirect_UpdatesOnly || VeDirect.veFrame.H23 != _kvFrame.H23) {
|
||||
value = VeDirect.veFrame.H23;
|
||||
MqttSettings.publish(topic + "H23", value);
|
||||
}
|
||||
if (config.Vedirect_UpdatesOnly){
|
||||
_kv_map = VeDirect.veMap;
|
||||
_kvFrame= VeDirect.veFrame;
|
||||
}
|
||||
_lastPublish = millis();
|
||||
}
|
||||
|
||||
@ -190,12 +190,11 @@ bool PowerLimiterClass::canUseDirectSolarPower()
|
||||
CONFIG_T& config = Configuration.get();
|
||||
|
||||
if (!config.PowerLimiter_SolarPassTroughEnabled
|
||||
|| !config.Vedirect_Enabled
|
||||
|| !VeDirect.veMap.count("PPV")) {
|
||||
|| !config.Vedirect_Enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (VeDirect.veMap["PPV"].toInt() < 10) {
|
||||
if (VeDirect.veFrame.PPV < 10.0) {
|
||||
// Not enough power
|
||||
return false;
|
||||
}
|
||||
@ -209,7 +208,7 @@ uint32_t PowerLimiterClass::getDirectSolarPower()
|
||||
return 0;
|
||||
}
|
||||
|
||||
return VeDirect.veMap["PPV"].toInt();
|
||||
return (uint32_t) round(VeDirect.veFrame.PPV);
|
||||
}
|
||||
|
||||
float PowerLimiterClass::getLoadCorrectedVoltage(std::shared_ptr<InverterAbstract> inverter)
|
||||
|
||||
@ -82,37 +82,37 @@ void WebApiWsVedirectLiveClass::generateJsonResponse(JsonVariant& root)
|
||||
// device info
|
||||
root["data_age"] = (millis() - VeDirect.getLastUpdate() ) / 1000;
|
||||
root["age_critical"] = !VeDirect.isDataValid();
|
||||
root["PID"] = VeDirect.getPidAsString(VeDirect.veMap["PID"].c_str());
|
||||
root["SER"] = VeDirect.veMap["SER"];
|
||||
root["FW"] = VeDirect.veMap["FW"];
|
||||
root["LOAD"] = VeDirect.veMap["LOAD"];
|
||||
root["CS"] = VeDirect.getCsAsString(VeDirect.veMap["CS"].c_str());
|
||||
root["ERR"] = VeDirect.getErrAsString(VeDirect.veMap["ERR"].c_str());
|
||||
root["OR"] = VeDirect.getOrAsString(VeDirect.veMap["OR"].c_str());
|
||||
root["MPPT"] = VeDirect.getMpptAsString(VeDirect.veMap["MPPT"].c_str());
|
||||
root["HSDS"]["v"] = VeDirect.veMap["HSDS"].toInt();
|
||||
root["PID"] = VeDirect.getPidAsString(VeDirect.veFrame.PID);
|
||||
root["SER"] = VeDirect.veFrame.SER;
|
||||
root["FW"] = VeDirect.veFrame.FW;
|
||||
root["LOAD"] = VeDirect.veFrame.LOAD == true ? "ON" : "OFF";
|
||||
root["CS"] = VeDirect.getCsAsString(VeDirect.veFrame.CS);
|
||||
root["ERR"] = VeDirect.getErrAsString(VeDirect.veFrame.ERR);
|
||||
root["OR"] = VeDirect.getOrAsString(VeDirect.veFrame.OR);
|
||||
root["MPPT"] = VeDirect.getMpptAsString(VeDirect.veFrame.MPPT);
|
||||
root["HSDS"]["v"] = VeDirect.veFrame.HSDS;
|
||||
root["HSDS"]["u"] = "Days";
|
||||
|
||||
// battery info
|
||||
root["V"]["v"] = round(VeDirect.veMap["V"].toDouble() / 10.0) / 100.0;
|
||||
root["V"]["v"] = VeDirect.veFrame.V;
|
||||
root["V"]["u"] = "V";
|
||||
root["I"]["v"] = round(VeDirect.veMap["I"].toDouble() / 10.0) / 100.0;
|
||||
root["I"]["v"] = VeDirect.veFrame.I;
|
||||
root["I"]["u"] = "A";
|
||||
|
||||
// panel info
|
||||
root["VPV"]["v"] = round(VeDirect.veMap["VPV"].toDouble() / 10.0) / 100.0;
|
||||
root["VPV"]["v"] = VeDirect.veFrame.VPV;
|
||||
root["VPV"]["u"] = "V";
|
||||
root["PPV"]["v"] = VeDirect.veMap["PPV"].toInt();
|
||||
root["PPV"]["v"] = VeDirect.veFrame.PPV;
|
||||
root["PPV"]["u"] = "W";
|
||||
root["H19"]["v"] = VeDirect.veMap["H19"].toDouble() / 100.0;
|
||||
root["H19"]["v"] = VeDirect.veFrame.H19;
|
||||
root["H19"]["u"] = "kWh";
|
||||
root["H20"]["v"] = VeDirect.veMap["H20"].toDouble() / 100.0;
|
||||
root["H20"]["v"] = VeDirect.veFrame.H20;
|
||||
root["H20"]["u"] = "kWh";
|
||||
root["H21"]["v"] = VeDirect.veMap["H21"].toInt();
|
||||
root["H21"]["v"] = VeDirect.veFrame.H21;
|
||||
root["H21"]["u"] = "W";
|
||||
root["H22"]["v"] = VeDirect.veMap["H22"].toDouble() / 100.0;
|
||||
root["H22"]["v"] = VeDirect.veFrame.H22;
|
||||
root["H22"]["u"] = "kWh";
|
||||
root["H23"]["v"] = VeDirect.veMap["H23"].toInt();
|
||||
root["H23"]["v"] = VeDirect.veFrame.H23;
|
||||
root["H23"]["u"] = "W";
|
||||
|
||||
if (VeDirect.getLastUpdate() > _newestVedirectTimestamp) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user