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);
|
||||
|
||||
iv->sendStatsRequest(_radio.get());
|
||||
|
||||
// Fetch event log
|
||||
iv->sendAlarmLogRequest(_radio.get());
|
||||
}
|
||||
|
||||
if (++inverterPos >= getNumInverters()) {
|
||||
|
||||
@ -120,6 +120,7 @@ void HoymilesRadio::loop()
|
||||
inverter_transaction_t* t = _txBuffer.getBack();
|
||||
auto inv = Hoymiles.getInverterBySerial(t->target.u64);
|
||||
inv->setLastRequest(t->requestType);
|
||||
inv->clearRxFragmentBuffer();
|
||||
sendEsbPacket(t->target, t->mainCmd, t->subCmd, t->payload, t->len, t->timeout);
|
||||
_txBuffer.popBack();
|
||||
}
|
||||
|
||||
@ -37,7 +37,49 @@ bool HM_Abstract::sendStatsRequest(HoymilesRadio* radio)
|
||||
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
@ -6,4 +6,8 @@ class HM_Abstract : public InverterAbstract {
|
||||
public:
|
||||
HM_Abstract(uint64_t serial);
|
||||
bool sendStatsRequest(HoymilesRadio* radio);
|
||||
bool sendAlarmLogRequest(HoymilesRadio* radio);
|
||||
|
||||
private:
|
||||
uint8_t _lastAlarmLogCnt = 0;
|
||||
};
|
||||
@ -5,6 +5,7 @@
|
||||
InverterAbstract::InverterAbstract(uint64_t serial)
|
||||
{
|
||||
_serial.u64 = serial;
|
||||
_alarmLogParser.reset(new AlarmLogParser());
|
||||
memset(_payloadStats, 0, MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
@ -28,6 +29,11 @@ const char* InverterAbstract::name()
|
||||
return _name;
|
||||
}
|
||||
|
||||
AlarmLogParser* InverterAbstract::EventLog()
|
||||
{
|
||||
return _alarmLogParser.get();
|
||||
}
|
||||
|
||||
void InverterAbstract::clearRxFragmentBuffer()
|
||||
{
|
||||
memset(_rxFragmentBuffer, 0, MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE);
|
||||
@ -113,6 +119,17 @@ uint8_t InverterAbstract::verifyAllFragments()
|
||||
offs += (_rxFragmentBuffer[i].len);
|
||||
}
|
||||
_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 {
|
||||
Serial.println("Unkown response received");
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../parser/AlarmLogParser.h"
|
||||
#include "HoymilesRadio.h"
|
||||
#include "types.h"
|
||||
#include <Arduino.h>
|
||||
@ -130,10 +131,13 @@ public:
|
||||
const char* getChannelFieldName(uint8_t channel, uint8_t fieldId);
|
||||
|
||||
virtual bool sendStatsRequest(HoymilesRadio* radio) = 0;
|
||||
virtual bool sendAlarmLogRequest(HoymilesRadio* radio) = 0;
|
||||
uint32_t getLastStatsUpdate();
|
||||
|
||||
void setLastRequest(RequestType request);
|
||||
|
||||
AlarmLogParser* EventLog();
|
||||
|
||||
protected:
|
||||
RequestType getLastRequest();
|
||||
|
||||
@ -147,7 +151,10 @@ private:
|
||||
|
||||
uint8_t _payloadStats[MAX_RF_FRAGMENT_COUNT * MAX_RF_PAYLOAD_SIZE];
|
||||
uint32_t _lastStatsUpdate = 0;
|
||||
uint32_t _lastAlarmLogUpdate = 0;
|
||||
uint16_t _chanMaxPower[CH4];
|
||||
|
||||
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 {
|
||||
None,
|
||||
Stats
|
||||
Stats,
|
||||
AlarmLog
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user