First try to implement Alarm Log fetching
This commit is contained in:
parent
4f2d705314
commit
b9bb753906
@ -27,6 +27,9 @@ void HoymilesClass::loop()
|
|||||||
Serial.println(iv->serial(), HEX);
|
Serial.println(iv->serial(), HEX);
|
||||||
|
|
||||||
iv->sendStatsRequest(_radio.get());
|
iv->sendStatsRequest(_radio.get());
|
||||||
|
|
||||||
|
// Fetch event log
|
||||||
|
iv->sendAlarmLogRequest(_radio.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++inverterPos >= getNumInverters()) {
|
if (++inverterPos >= getNumInverters()) {
|
||||||
|
|||||||
@ -120,6 +120,7 @@ void HoymilesRadio::loop()
|
|||||||
inverter_transaction_t* t = _txBuffer.getBack();
|
inverter_transaction_t* t = _txBuffer.getBack();
|
||||||
auto inv = Hoymiles.getInverterBySerial(t->target.u64);
|
auto inv = Hoymiles.getInverterBySerial(t->target.u64);
|
||||||
inv->setLastRequest(t->requestType);
|
inv->setLastRequest(t->requestType);
|
||||||
|
inv->clearRxFragmentBuffer();
|
||||||
sendEsbPacket(t->target, t->mainCmd, t->subCmd, t->payload, t->len, t->timeout);
|
sendEsbPacket(t->target, t->mainCmd, t->subCmd, t->payload, t->len, t->timeout);
|
||||||
_txBuffer.popBack();
|
_txBuffer.popBack();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,49 @@ bool HM_Abstract::sendStatsRequest(HoymilesRadio* radio)
|
|||||||
|
|
||||||
payload.requestType = RequestType::Stats;
|
payload.requestType = RequestType::Stats;
|
||||||
|
|
||||||
clearRxFragmentBuffer();
|
radio->enqueTransaction(&payload);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HM_Abstract::sendAlarmLogRequest(HoymilesRadio* radio)
|
||||||
|
{
|
||||||
|
struct tm timeinfo;
|
||||||
|
if (!getLocalTime(&timeinfo)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasChannelFieldValue(CH0, FLD_EVT_LOG)) {
|
||||||
|
if ((uint8_t)getChannelFieldValue(CH0, FLD_EVT_LOG) == _lastAlarmLogCnt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastAlarmLogCnt = (uint8_t)getChannelFieldValue(CH0, FLD_EVT_LOG);
|
||||||
|
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
|
||||||
|
inverter_transaction_t payload;
|
||||||
|
|
||||||
|
memset(payload.payload, 0, MAX_RF_PAYLOAD_SIZE);
|
||||||
|
|
||||||
|
payload.target.u64 = serial();
|
||||||
|
payload.mainCmd = 0x15;
|
||||||
|
payload.subCmd = 0x80;
|
||||||
|
payload.timeout = 200;
|
||||||
|
payload.len = 16;
|
||||||
|
|
||||||
|
payload.payload[0] = 0x11;
|
||||||
|
payload.payload[1] = 0x00;
|
||||||
|
|
||||||
|
HoymilesRadio::u32CpyLittleEndian(&payload.payload[2], now); // sets the 4 following elements {2, 3, 4, 5}
|
||||||
|
|
||||||
|
uint16_t crc = crc16(&payload.payload[0], 14);
|
||||||
|
payload.payload[14] = (crc >> 8) & 0xff;
|
||||||
|
payload.payload[15] = (crc)&0xff;
|
||||||
|
|
||||||
|
payload.requestType = RequestType::AlarmLog;
|
||||||
|
|
||||||
radio->enqueTransaction(&payload);
|
radio->enqueTransaction(&payload);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -6,4 +6,8 @@ class HM_Abstract : public InverterAbstract {
|
|||||||
public:
|
public:
|
||||||
HM_Abstract(uint64_t serial);
|
HM_Abstract(uint64_t serial);
|
||||||
bool sendStatsRequest(HoymilesRadio* radio);
|
bool sendStatsRequest(HoymilesRadio* radio);
|
||||||
|
bool sendAlarmLogRequest(HoymilesRadio* radio);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t _lastAlarmLogCnt = 0;
|
||||||
};
|
};
|
||||||
@ -5,6 +5,7 @@
|
|||||||
InverterAbstract::InverterAbstract(uint64_t serial)
|
InverterAbstract::InverterAbstract(uint64_t serial)
|
||||||
{
|
{
|
||||||
_serial.u64 = serial;
|
_serial.u64 = serial;
|
||||||
|
_alarmLogParser.reset(new AlarmLogParser());
|
||||||
memset(_payloadStats, 0, MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE);
|
memset(_payloadStats, 0, MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,6 +29,11 @@ const char* InverterAbstract::name()
|
|||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AlarmLogParser* InverterAbstract::EventLog()
|
||||||
|
{
|
||||||
|
return _alarmLogParser.get();
|
||||||
|
}
|
||||||
|
|
||||||
void InverterAbstract::clearRxFragmentBuffer()
|
void InverterAbstract::clearRxFragmentBuffer()
|
||||||
{
|
{
|
||||||
memset(_rxFragmentBuffer, 0, MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE);
|
memset(_rxFragmentBuffer, 0, MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE);
|
||||||
@ -113,6 +119,17 @@ uint8_t InverterAbstract::verifyAllFragments()
|
|||||||
offs += (_rxFragmentBuffer[i].len);
|
offs += (_rxFragmentBuffer[i].len);
|
||||||
}
|
}
|
||||||
_lastStatsUpdate = millis();
|
_lastStatsUpdate = millis();
|
||||||
|
|
||||||
|
} else if (getLastRequest() == RequestType::AlarmLog) {
|
||||||
|
// Move all fragments into target buffer
|
||||||
|
uint8_t offs = 0;
|
||||||
|
_alarmLogParser.get()->clearBuffer();
|
||||||
|
for (uint8_t i = 0; i < _rxFragmentMaxPacketId; i++) {
|
||||||
|
_alarmLogParser.get()->appendFragment(offs, _rxFragmentBuffer[i].fragment, _rxFragmentBuffer[i].len);
|
||||||
|
offs += (_rxFragmentBuffer[i].len);
|
||||||
|
}
|
||||||
|
_lastAlarmLogUpdate = millis();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Unkown response received");
|
Serial.println("Unkown response received");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "../parser/AlarmLogParser.h"
|
||||||
#include "HoymilesRadio.h"
|
#include "HoymilesRadio.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
@ -130,10 +131,13 @@ public:
|
|||||||
const char* getChannelFieldName(uint8_t channel, uint8_t fieldId);
|
const char* getChannelFieldName(uint8_t channel, uint8_t fieldId);
|
||||||
|
|
||||||
virtual bool sendStatsRequest(HoymilesRadio* radio) = 0;
|
virtual bool sendStatsRequest(HoymilesRadio* radio) = 0;
|
||||||
|
virtual bool sendAlarmLogRequest(HoymilesRadio* radio) = 0;
|
||||||
uint32_t getLastStatsUpdate();
|
uint32_t getLastStatsUpdate();
|
||||||
|
|
||||||
void setLastRequest(RequestType request);
|
void setLastRequest(RequestType request);
|
||||||
|
|
||||||
|
AlarmLogParser* EventLog();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
RequestType getLastRequest();
|
RequestType getLastRequest();
|
||||||
|
|
||||||
@ -147,7 +151,10 @@ private:
|
|||||||
|
|
||||||
uint8_t _payloadStats[MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE];
|
uint8_t _payloadStats[MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE];
|
||||||
uint32_t _lastStatsUpdate = 0;
|
uint32_t _lastStatsUpdate = 0;
|
||||||
|
uint32_t _lastAlarmLogUpdate = 0;
|
||||||
uint16_t _chanMaxPower[CH4];
|
uint16_t _chanMaxPower[CH4];
|
||||||
|
|
||||||
RequestType _lastRequest = RequestType::None;
|
RequestType _lastRequest = RequestType::None;
|
||||||
|
|
||||||
|
std::unique_ptr<AlarmLogParser> _alarmLogParser;
|
||||||
};
|
};
|
||||||
236
lib/Hoymiles/src/parser/AlarmLogParser.cpp
Normal file
236
lib/Hoymiles/src/parser/AlarmLogParser.cpp
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
#include "AlarmLogParser.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
void AlarmLogParser::clearBuffer()
|
||||||
|
{
|
||||||
|
memset(_payloadAlarmLog, 0, ALARM_LOG_ENTRY_COUNT * ALARM_LOG_ENTRY_SIZE);
|
||||||
|
_alarmLogLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmLogParser::appendFragment(uint8_t offset, uint8_t* payload, uint8_t len)
|
||||||
|
{
|
||||||
|
memcpy(&_payloadAlarmLog[offset], payload, len);
|
||||||
|
_alarmLogLength += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t AlarmLogParser::getEntryCount()
|
||||||
|
{
|
||||||
|
return (_alarmLogLength - 2) / ALARM_LOG_ENTRY_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmLogParser::getLogEntry(uint8_t entryId, AlarmLogEntry* entry)
|
||||||
|
{
|
||||||
|
uint8_t entryStartOffset = 2 + entryId * ALARM_LOG_ENTRY_SIZE;
|
||||||
|
|
||||||
|
entry->MessageId = _payloadAlarmLog[entryStartOffset + 1];
|
||||||
|
|
||||||
|
switch (entry->MessageId) {
|
||||||
|
case 1:
|
||||||
|
entry->Message = String(F("Inverter start"));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
entry->Message = String(F("DTU command failed"));
|
||||||
|
break;
|
||||||
|
case 121:
|
||||||
|
entry->Message = String(F("Over temperature protection"));
|
||||||
|
break;
|
||||||
|
case 125:
|
||||||
|
entry->Message = String(F("Grid configuration parameter error"));
|
||||||
|
break;
|
||||||
|
case 126:
|
||||||
|
entry->Message = String(F("Software error code 126"));
|
||||||
|
break;
|
||||||
|
case 127:
|
||||||
|
entry->Message = String(F("Firmware error"));
|
||||||
|
break;
|
||||||
|
case 128:
|
||||||
|
entry->Message = String(F("Software error code 128"));
|
||||||
|
break;
|
||||||
|
case 129:
|
||||||
|
entry->Message = String(F("Software error code 129"));
|
||||||
|
break;
|
||||||
|
case 130:
|
||||||
|
entry->Message = String(F("Offline"));
|
||||||
|
break;
|
||||||
|
case 141:
|
||||||
|
entry->Message = String(F("Grid overvoltage"));
|
||||||
|
break;
|
||||||
|
case 142:
|
||||||
|
entry->Message = String(F("Average grid overvoltage"));
|
||||||
|
break;
|
||||||
|
case 143:
|
||||||
|
entry->Message = String(F("Grid undervoltage"));
|
||||||
|
break;
|
||||||
|
case 144:
|
||||||
|
entry->Message = String(F("Grid overfrequency"));
|
||||||
|
break;
|
||||||
|
case 145:
|
||||||
|
entry->Message = String(F("Grid underfrequency"));
|
||||||
|
break;
|
||||||
|
case 146:
|
||||||
|
entry->Message = String(F("Rapid grid frequency change"));
|
||||||
|
break;
|
||||||
|
case 147:
|
||||||
|
entry->Message = String(F("Power grid outage"));
|
||||||
|
break;
|
||||||
|
case 148:
|
||||||
|
entry->Message = String(F("Grid disconnection"));
|
||||||
|
break;
|
||||||
|
case 149:
|
||||||
|
entry->Message = String(F("Island detected"));
|
||||||
|
break;
|
||||||
|
case 205:
|
||||||
|
entry->Message = String(F("Input port 1 & 2 overvoltage"));
|
||||||
|
break;
|
||||||
|
case 206:
|
||||||
|
entry->Message = String(F("Input port 3 & 4 overvoltage"));
|
||||||
|
break;
|
||||||
|
case 207:
|
||||||
|
entry->Message = String(F("Input port 1 & 2 undervoltage"));
|
||||||
|
break;
|
||||||
|
case 208:
|
||||||
|
entry->Message = String(F("Input port 3 & 4 undervoltage"));
|
||||||
|
break;
|
||||||
|
case 209:
|
||||||
|
entry->Message = String(F("Port 1 no input"));
|
||||||
|
break;
|
||||||
|
case 210:
|
||||||
|
entry->Message = String(F("Port 2 no input"));
|
||||||
|
break;
|
||||||
|
case 211:
|
||||||
|
entry->Message = String(F("Port 3 no input"));
|
||||||
|
break;
|
||||||
|
case 212:
|
||||||
|
entry->Message = String(F("Port 4 no input"));
|
||||||
|
break;
|
||||||
|
case 213:
|
||||||
|
entry->Message = String(F("PV-1 & PV-2 abnormal wiring"));
|
||||||
|
break;
|
||||||
|
case 214:
|
||||||
|
entry->Message = String(F("PV-3 & PV-4 abnormal wiring"));
|
||||||
|
break;
|
||||||
|
case 215:
|
||||||
|
entry->Message = String(F("PV-1 Input overvoltage"));
|
||||||
|
break;
|
||||||
|
case 216:
|
||||||
|
entry->Message = String(F("PV-1 Input undervoltage"));
|
||||||
|
break;
|
||||||
|
case 217:
|
||||||
|
entry->Message = String(F("PV-2 Input overvoltage"));
|
||||||
|
break;
|
||||||
|
case 218:
|
||||||
|
entry->Message = String(F("PV-2 Input undervoltage"));
|
||||||
|
break;
|
||||||
|
case 219:
|
||||||
|
entry->Message = String(F("PV-3 Input overvoltage"));
|
||||||
|
break;
|
||||||
|
case 220:
|
||||||
|
entry->Message = String(F("PV-3 Input undervoltage"));
|
||||||
|
break;
|
||||||
|
case 221:
|
||||||
|
entry->Message = String(F("PV-4 Input overvoltage"));
|
||||||
|
break;
|
||||||
|
case 222:
|
||||||
|
entry->Message = String(F("PV-4 Input undervoltage"));
|
||||||
|
break;
|
||||||
|
case 301:
|
||||||
|
entry->Message = String(F("Hardware error code 301"));
|
||||||
|
break;
|
||||||
|
case 302:
|
||||||
|
entry->Message = String(F("Hardware error code 302"));
|
||||||
|
break;
|
||||||
|
case 303:
|
||||||
|
entry->Message = String(F("Hardware error code 303"));
|
||||||
|
break;
|
||||||
|
case 304:
|
||||||
|
entry->Message = String(F("Hardware error code 304"));
|
||||||
|
break;
|
||||||
|
case 305:
|
||||||
|
entry->Message = String(F("Hardware error code 305"));
|
||||||
|
break;
|
||||||
|
case 306:
|
||||||
|
entry->Message = String(F("Hardware error code 306"));
|
||||||
|
break;
|
||||||
|
case 307:
|
||||||
|
entry->Message = String(F("Hardware error code 307"));
|
||||||
|
break;
|
||||||
|
case 308:
|
||||||
|
entry->Message = String(F("Hardware error code 308"));
|
||||||
|
break;
|
||||||
|
case 309:
|
||||||
|
entry->Message = String(F("Hardware error code 309"));
|
||||||
|
break;
|
||||||
|
case 310:
|
||||||
|
entry->Message = String(F("Hardware error code 310"));
|
||||||
|
break;
|
||||||
|
case 311:
|
||||||
|
entry->Message = String(F("Hardware error code 311"));
|
||||||
|
break;
|
||||||
|
case 312:
|
||||||
|
entry->Message = String(F("Hardware error code 312"));
|
||||||
|
break;
|
||||||
|
case 313:
|
||||||
|
entry->Message = String(F("Hardware error code 313"));
|
||||||
|
break;
|
||||||
|
case 314:
|
||||||
|
entry->Message = String(F("Hardware error code 314"));
|
||||||
|
break;
|
||||||
|
case 5041:
|
||||||
|
entry->Message = String(F("Error code-04 Port 1"));
|
||||||
|
break;
|
||||||
|
case 5042:
|
||||||
|
entry->Message = String(F("Error code-04 Port 2"));
|
||||||
|
break;
|
||||||
|
case 5043:
|
||||||
|
entry->Message = String(F("Error code-04 Port 3"));
|
||||||
|
break;
|
||||||
|
case 5044:
|
||||||
|
entry->Message = String(F("Error code-04 Port 4"));
|
||||||
|
break;
|
||||||
|
case 5051:
|
||||||
|
entry->Message = String(F("PV Input 1 Overvoltage/Undervoltage"));
|
||||||
|
break;
|
||||||
|
case 5052:
|
||||||
|
entry->Message = String(F("PV Input 2 Overvoltage/Undervoltage"));
|
||||||
|
break;
|
||||||
|
case 5053:
|
||||||
|
entry->Message = String(F("PV Input 3 Overvoltage/Undervoltage"));
|
||||||
|
break;
|
||||||
|
case 5054:
|
||||||
|
entry->Message = String(F("PV Input 4 Overvoltage/Undervoltage"));
|
||||||
|
break;
|
||||||
|
case 5060:
|
||||||
|
entry->Message = String(F("Abnormal bias"));
|
||||||
|
break;
|
||||||
|
case 5070:
|
||||||
|
entry->Message = String(F("Over temperature protection"));
|
||||||
|
break;
|
||||||
|
case 5080:
|
||||||
|
entry->Message = String(F("Grid Overvoltage/Undervoltage"));
|
||||||
|
break;
|
||||||
|
case 5090:
|
||||||
|
entry->Message = String(F("Grid Overfrequency/Underfrequency"));
|
||||||
|
break;
|
||||||
|
case 5100:
|
||||||
|
entry->Message = String(F("Island detected"));
|
||||||
|
break;
|
||||||
|
case 5120:
|
||||||
|
entry->Message = String(F("EEPROM reading and writing error"));
|
||||||
|
break;
|
||||||
|
case 5150:
|
||||||
|
entry->Message = String(F("10 min value grid overvoltage"));
|
||||||
|
break;
|
||||||
|
case 5200:
|
||||||
|
entry->Message = String(F("Firmware error"));
|
||||||
|
break;
|
||||||
|
case 8310:
|
||||||
|
entry->Message = String(F("Shut down"));
|
||||||
|
break;
|
||||||
|
case 9000:
|
||||||
|
entry->Message = String(F("Microinverter is suspected of being stolen"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
entry->Message = String(F("Unknown"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
lib/Hoymiles/src/parser/AlarmLogParser.h
Normal file
26
lib/Hoymiles/src/parser/AlarmLogParser.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#define ALARM_LOG_ENTRY_COUNT 15
|
||||||
|
#define ALARM_LOG_ENTRY_SIZE 12
|
||||||
|
|
||||||
|
struct AlarmLogEntry {
|
||||||
|
uint16_t MessageId;
|
||||||
|
String Message;
|
||||||
|
time_t StartTime;
|
||||||
|
time_t EndTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AlarmLogParser {
|
||||||
|
public:
|
||||||
|
void clearBuffer();
|
||||||
|
void appendFragment(uint8_t offset, uint8_t* payload, uint8_t len);
|
||||||
|
|
||||||
|
uint8_t getEntryCount();
|
||||||
|
void getLogEntry(uint8_t entryId, AlarmLogEntry* entry);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t _payloadAlarmLog[ALARM_LOG_ENTRY_SIZE * ALARM_LOG_ENTRY_COUNT];
|
||||||
|
uint8_t _alarmLogLength;
|
||||||
|
};
|
||||||
@ -17,7 +17,8 @@ typedef struct {
|
|||||||
|
|
||||||
enum class RequestType {
|
enum class RequestType {
|
||||||
None,
|
None,
|
||||||
Stats
|
Stats,
|
||||||
|
AlarmLog
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user