no name 2 years ago
parent
commit
9adde50036
  1. 2
      src/app.cpp
  2. 33
      src/hm/hmInverter.h
  3. 3
      src/hm/hmPayload.h
  4. 9
      src/hm/hmRadio.h
  5. 4
      src/hm/hmSystem.h
  6. 38
      src/plugins/SML_OBIS_Parser.cpp
  7. 9
      src/web/RestApi.h
  8. 38
      src/web/html/visualization.html

2
src/app.cpp

@ -381,7 +381,7 @@ void app::tickSend(void) {
DPRINTLN(DBG_WARN, F("NRF24 not connected!")); DPRINTLN(DBG_WARN, F("NRF24 not connected!"));
return; return;
} }
if (mIVCommunicationOn) { if (mIVCommunicationOn && mTimestamp) {
if (!mSys.Radio.mBufCtrl.empty()) { if (!mSys.Radio.mBufCtrl.empty()) {
if (mConfig->serial.debug) { if (mConfig->serial.debug) {
DPRINT(DBG_DEBUG, F("recbuf not empty! #")); DPRINT(DBG_DEBUG, F("recbuf not empty! #"));

33
src/hm/hmInverter.h

@ -127,11 +127,13 @@ class Inverter {
record_t<REC_TYP> recordInfo; // structure for info values record_t<REC_TYP> recordInfo; // structure for info values
record_t<REC_TYP> recordConfig; // structure for system config values record_t<REC_TYP> recordConfig; // structure for system config values
record_t<REC_TYP> recordAlarm; // structure for alarm values record_t<REC_TYP> recordAlarm; // structure for alarm values
//String lastAlarmMsg;
bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null) bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null)
bool isConnected; // shows if inverter was successfully identified (fw version and hardware info) bool isConnected; // shows if inverter was successfully identified (fw version and hardware info)
uint32_t pac_sum; // average calc for chart: sum of ac power values for cur interval uint32_t pac_sum; // average calc for chart: sum of ac power values for cur interval
uint16_t pac_cnt; // average calc for chart: number of ac power values for cur interval uint16_t pac_cnt; // average calc for chart: number of ac power values for cur interval
uint16_t alarmCode; // last Alarm
uint32_t alarmStart;
uint32_t alarmEnd;
Inverter() { Inverter() {
ivGen = IV_HM; ivGen = IV_HM;
@ -141,9 +143,9 @@ class Inverter {
mDevControlRequest = false; mDevControlRequest = false;
devControlCmd = InitDataState; devControlCmd = InitDataState;
initialized = false; initialized = false;
//lastAlarmMsg = "nothing";
alarmMesIndex = 0; alarmMesIndex = 0;
isConnected = false; isConnected = false;
alarmCode = 0;
} }
~Inverter() { ~Inverter() {
@ -178,9 +180,11 @@ class Inverter {
uint8_t getQueuedCmd() { uint8_t getQueuedCmd() {
if (_commandQueue.empty()) { if (_commandQueue.empty()) {
if (ivGen != IV_MI) { if (ivGen != IV_MI) {
if (getFwVersion() == 0) if (getFwVersion()) {
enqueCommand<InfoCommand>(InverterDevInform_All); // firmware version
enqueCommand<InfoCommand>(RealTimeRunData_Debug); // live data enqueCommand<InfoCommand>(RealTimeRunData_Debug); // live data
} else {
enqueCommand<InfoCommand>(InverterDevInform_All); // firmware version
}
} else if (ivGen == IV_MI){ } else if (ivGen == IV_MI){
if (getFwVersion() == 0) if (getFwVersion() == 0)
enqueCommand<InfoCommand>(InverterDevInform_All); // firmware version; might not work, esp. for 1/2 ch hardware enqueCommand<InfoCommand>(InverterDevInform_All); // firmware version; might not work, esp. for 1/2 ch hardware
@ -321,9 +325,6 @@ class Inverter {
} }
else if (rec->assign == AlarmDataAssignment) { else if (rec->assign == AlarmDataAssignment) {
DPRINTLN(DBG_DEBUG, "add alarm"); DPRINTLN(DBG_DEBUG, "add alarm");
//if (getPosByChFld(0, FLD_LAST_ALARM_CODE, rec) == pos){
// lastAlarmMsg = getAlarmStr(rec->record[pos]);
//}
} }
else else
DPRINTLN(DBG_WARN, F("add with unknown assginment")); DPRINTLN(DBG_WARN, F("add with unknown assginment"));
@ -489,18 +490,22 @@ class Inverter {
return 0; return 0;
uint16_t wCode = ((uint16_t)pyld[startOff]) << 8 | pyld[startOff+1]; uint16_t wCode = ((uint16_t)pyld[startOff]) << 8 | pyld[startOff+1];
uint32_t startTimeOffset = 0, endTimeOffset = 0;
alarmStart = 0;
alarmEnd = 0;
if (((wCode >> 13) & 0x01) == 1) // check if is AM or PM if (((wCode >> 13) & 0x01) == 1) // check if is AM or PM
startTimeOffset = 12 * 60 * 60; alarmStart = 12 * 60 * 60;
if (((wCode >> 12) & 0x01) == 1) // check if is AM or PM if (((wCode >> 12) & 0x01) == 1) // check if is AM or PM
endTimeOffset = 12 * 60 * 60; alarmEnd = 12 * 60 * 60;
*start = (((uint16_t)pyld[startOff + 4] << 8) | ((uint16_t)pyld[startOff + 5])) + startTimeOffset; alarmCode = pyld[startOff+1];
*endTime = (((uint16_t)pyld[startOff + 6] << 8) | ((uint16_t)pyld[startOff + 7])) + endTimeOffset; alarmStart += (((uint16_t)pyld[startOff + 4] << 8) | ((uint16_t)pyld[startOff + 5]));
alarmEnd += (((uint16_t)pyld[startOff + 6] << 8) | ((uint16_t)pyld[startOff + 7]));
*start = alarmStart;
*endTime = alarmEnd;
DPRINTLN(DBG_INFO, "Alarm #" + String(pyld[startOff+1]) + " '" + String(getAlarmStr(pyld[startOff+1])) + "' start: " + ah::getTimeStr(*start) + ", end: " + ah::getTimeStr(*endTime)); DPRINTLN(DBG_INFO, "Alarm " + String(pyld[startOff+3]) + ", #" + String(alarmCode) + " '" + String(getAlarmStr(alarmCode)) + "' start: " + ah::getTimeStr(alarmStart) + ", end: " + ah::getTimeStr(alarmEnd));
return pyld[startOff+1]; return alarmCode;
} }
String getAlarmStr(uint16_t alarmCode) { String getAlarmStr(uint16_t alarmCode) {

3
src/hm/hmPayload.h

@ -100,7 +100,8 @@ class HmPayload {
iv->setValue(pos, rec, 0.0f); iv->setValue(pos, rec, 0.0f);
} }
} }
iv->alarmCode = 0; // design: every day a new start
rec->ts = 0; // design: every day a new start
notify(RealTimeRunData_Debug); notify(RealTimeRunData_Debug);
} }

9
src/hm/hmRadio.h

@ -21,7 +21,6 @@
#define ALL_FRAMES 0x80 #define ALL_FRAMES 0x80
#define SINGLE_FRAME 0x81 #define SINGLE_FRAME 0x81
#define SEND_CHANNEL_QUALITY_INTEGRATOR_SIZE 4
#define SEND_CHANNEL_MAX_QUALITY 4 #define SEND_CHANNEL_MAX_QUALITY 4
#define SEND_CHANNEL_MIN_QUALITY -6 #define SEND_CHANNEL_MIN_QUALITY -6
#define SEND_CHANNEL_QUALITY_GOOD 2 #define SEND_CHANNEL_QUALITY_GOOD 2
@ -247,7 +246,12 @@ class HmRadio {
mTxBuf[10] = cmd; // cid mTxBuf[10] = cmd; // cid
mTxBuf[11] = 0x00; mTxBuf[11] = 0x00;
CP_U32_LittleEndian(&mTxBuf[12], ts); CP_U32_LittleEndian(&mTxBuf[12], ts);
if (cmd == RealTimeRunData_Debug || cmd == AlarmData ) {
if (cmd == AlarmData) {
// ask for last signalled alarm id only (index is one less)
if (alarmMesId) {
alarmMesId--;
}
mTxBuf[18] = (alarmMesId >> 8) & 0xff; mTxBuf[18] = (alarmMesId >> 8) & 0xff;
mTxBuf[19] = (alarmMesId ) & 0xff; mTxBuf[19] = (alarmMesId ) & 0xff;
} }
@ -301,7 +305,6 @@ class HmRadio {
void addSendChannelQuality (int8_t quality) void addSendChannelQuality (int8_t quality)
{ {
// continous averaging
// assume: mTxChIdx is still the last send channel index used // assume: mTxChIdx is still the last send channel index used
quality = mChQuality[mTxChIdx] + quality; quality = mChQuality[mTxChIdx] + quality;
if (quality < SEND_CHANNEL_MIN_QUALITY) { if (quality < SEND_CHANNEL_MIN_QUALITY) {

4
src/hm/hmSystem.h

@ -181,7 +181,7 @@ class HmSystem {
day(time_today), month(time_today), year(time_today)); day(time_today), month(time_today), year(time_today));
file = LittleFS.open (file_name, "r"); file = LittleFS.open (file_name, "r");
if (!file) { if (!file) {
DPRINT (DBG_WARN, "open_hist, failed to open "); DPRINT (DBG_VERBOSE, "open_hist, failed to open "); // typical: during night time after midnight
DBGPRINTLN (file_name); DBGPRINTLN (file_name);
} }
} else { } else {
@ -236,7 +236,7 @@ class HmSystem {
*pac = get_pac_average (false); *pac = get_pac_average (false);
return true; return true;
} }
DPRINTLN (DBG_INFO, "get_cur_value: none"); DPRINTLN (DBG_VERBOSE, "get_cur_value: none");
return false; return false;
} }

38
src/plugins/SML_OBIS_Parser.cpp

@ -629,15 +629,6 @@ bool sml_get_list_entries (uint16_t layer)
// Also: an info_len > 2 could be due to corrupt data. So better break. // Also: an info_len > 2 could be due to corrupt data. So better break.
uint16 len_info = (*cur_serial_buf & SML_EXT_LENGTH) ? 2 : 1; uint16 len_info = (*cur_serial_buf & SML_EXT_LENGTH) ? 2 : 1;
#ifdef undef
DPRINT (DBG_INFO, "get_list_entries");
DBGPRINT (", layer " + String (layer));
DBGPRINT (", entries " + String (sml_list_layer_entries[layer]));
DBGPRINT (", type 0x" + String (type, HEX));
DBGPRINT (", len_info " + String (len_info));
DBGPRINTLN (", sml_len " + String (sml_serial_len));
#endif
if (sml_serial_len < len_info) { if (sml_serial_len < len_info) {
if (cur_serial_buf > sml_serial_buf) { if (cur_serial_buf > sml_serial_buf) {
memmove (sml_serial_buf, cur_serial_buf, sml_serial_len); memmove (sml_serial_buf, cur_serial_buf, sml_serial_len);
@ -745,9 +736,7 @@ bool sml_get_list_entries (uint16_t layer)
layer++; layer++;
cur_sml_list_layer = layer; cur_sml_list_layer = layer;
sml_list_layer_entries[layer] = entry_len; sml_list_layer_entries[layer] = entry_len;
#ifdef undef DPRINTLN(DBG_VERBOSE, "Open layer " + String(layer) + ", entries " + String(entry_len));
DPRINTLN(DBG_INFO, "Open layer " + String(layer) + ", entries " + String(entry_len));
#endif
if (!sml_serial_len) { if (!sml_serial_len) {
error = true; error = true;
} }
@ -779,9 +768,7 @@ bool sml_get_list_entries (uint16_t layer)
} }
} }
while (!sml_list_layer_entries[layer]) { while (!sml_list_layer_entries[layer]) {
#ifdef undef DPRINTLN(DBG_VERBOSE, "COMPLETE, layer " + String (layer));
DPRINTLN(DBG_INFO, "COMPLETE, layer " + String (layer));
#endif
if (layer) { if (layer) {
layer--; layer--;
cur_sml_list_layer = layer; cur_sml_list_layer = layer;
@ -834,9 +821,7 @@ uint16_t sml_parse_stream (uint16 len)
cur_serial_buf = sml_serial_buf; cur_serial_buf = sml_serial_buf;
} }
sml_state = SML_ST_FIND_VERSION; sml_state = SML_ST_FIND_VERSION;
#ifdef undef DPRINTLN(DBG_VERBOSE, "START_TAG, rest " + String(sml_serial_len));
DPRINTLN(DBG_INFO, "START_TAG, rest " + String(sml_serial_len));
#endif
} else { } else {
cur_serial_buf = last_serial_buf + sml_serial_len; cur_serial_buf = last_serial_buf + sml_serial_len;
last_serial_buf = cur_serial_buf; last_serial_buf = cur_serial_buf;
@ -861,13 +846,8 @@ uint16_t sml_parse_stream (uint16 len)
sml_telegram_crc = sml_calc_crc (sml_telegram_crc, sizeof (version_seq), cur_serial_buf); sml_telegram_crc = sml_calc_crc (sml_telegram_crc, sizeof (version_seq), cur_serial_buf);
sml_msg_failure = 0; sml_msg_failure = 0;
sml_state = SML_ST_FIND_MSG; sml_state = SML_ST_FIND_MSG;
#ifdef undef DPRINTLN(DBG_VERBOSE, "VERSION, rest " + String (sml_serial_len - sizeof (version_seq)));
DPRINTLN(DBG_INFO, "VERSION, rest " + String (sml_serial_len - sizeof (version_seq)));
#endif
} else { } else {
#ifdef undef
DPRINTLN(DBG_INFO, "no VERSION, rest " + String (sml_serial_len - sizeof (version_seq)));
#endif
sml_state = SML_ST_FIND_START_TAG; sml_state = SML_ST_FIND_START_TAG;
} }
sml_serial_len -= sizeof (version_seq); sml_serial_len -= sizeof (version_seq);
@ -892,10 +872,8 @@ uint16_t sml_parse_stream (uint16 len)
parse_continue = true; parse_continue = true;
} else if ((*cur_serial_buf & 0x70) == 0x70) { } else if ((*cur_serial_buf & 0x70) == 0x70) {
/* todo: extended list on 1st level (does this happen in real life?) */ /* todo: extended list on 1st level (does this happen in real life?) */
DPRINTLN (DBG_VERBOSE, "TOPLIST 0x" + String(*cur_serial_buf, HEX) + ", rest " + String (sml_serial_len - 1));
sml_telegram_crc = sml_calc_crc (sml_telegram_crc, 1, cur_serial_buf); sml_telegram_crc = sml_calc_crc (sml_telegram_crc, 1, cur_serial_buf);
#ifdef undef
DPRINTLN (DBG_INFO, "TOPLIST 0x" + String(*cur_serial_buf, HEX) + ", rest " + String (sml_serial_len - 1));
#endif
sml_state = SML_ST_FIND_LIST_ENTRIES; sml_state = SML_ST_FIND_LIST_ENTRIES;
cur_sml_list_layer = 0; cur_sml_list_layer = 0;
sml_message = SML_MSG_NONE; sml_message = SML_MSG_NONE;
@ -951,9 +929,7 @@ uint16_t sml_parse_stream (uint16 len)
sml_state = SML_ST_FIND_LIST_ENTRIES; sml_state = SML_ST_FIND_LIST_ENTRIES;
sml_list_layer_entries[cur_sml_list_layer]--; sml_list_layer_entries[cur_sml_list_layer]--;
while (!sml_list_layer_entries[cur_sml_list_layer]) { while (!sml_list_layer_entries[cur_sml_list_layer]) {
#ifdef undef DPRINTLN(DBG_VERBOSE, "COMPLETE, layer " + String (cur_sml_list_layer));
DPRINTLN(DBG_INFO, "COMPLETE, layer " + String (cur_sml_list_layer));
#endif
if (cur_sml_list_layer) { if (cur_sml_list_layer) {
cur_sml_list_layer--; cur_sml_list_layer--;
} else { } else {
@ -1006,7 +982,7 @@ uint16_t sml_parse_stream (uint16 len)
", Yield in " + String (obis_yield_in_all_value) + ", Yield in " + String (obis_yield_in_all_value) +
", Yield out " + String (obis_yield_out_all_value)); ", Yield out " + String (obis_yield_out_all_value));
#else #else
DPRINTLN(DBG_INFO, "Power " + String (obis_power_all_value)); DPRINTLN(DBG_VERBOSE, "Power " + String (obis_power_all_value));
#endif #endif
sml_handle_obis_pac (obis_power_all_value); sml_handle_obis_pac (obis_power_all_value);
} else { } else {

9
src/web/RestApi.h

@ -514,7 +514,16 @@ class RestApi {
obj[F("grid_power")] = sml_get_obis_pac (); obj[F("grid_power")] = sml_get_obis_pac ();
} }
#endif #endif
if (iv->alarmCode) {
time_t midnight = mApp->getTimestamp();
midnight -= midnight % 86400;
obj[F("alarm_str")] = iv->getAlarmStr(iv->alarmCode);
obj[F("alarm_start")] = String(midnight + iv->alarmStart);
if (iv->alarmEnd && (iv->alarmEnd > iv->alarmStart)) {
obj[F("alarm_end")] = String(midnight + iv->alarmEnd);
}
}
JsonArray ch = obj.createNestedArray("ch"); JsonArray ch = obj.createNestedArray("ch");
// AC // AC

38
src/web/html/visualization.html

@ -186,23 +186,31 @@
]); ]);
} }
function tsInfo(ts) { function tsInfo(ts, alarm_str, alarm_start, alarm_end) {
var ageInfo = "Last received data requested at: "; var ageInfo = "Last received data at ";
var alarmInfo = "";
if(ts > 0) { if(ts > 0) {
var date = new Date(ts * 1000); var date = new Date(ts * 1000);
ageInfo += date.toLocaleString('de-DE'); ageInfo += date.toLocaleTimeString('de-DE');
} }
else else {
ageInfo += "nothing received"; ageInfo += "---";
}
return ml("div", {class: "mb-5"}, [ if (alarm_str !== undefined) {
ml("div", {class: "row p-1 ts-h mx-2"}, var start = new Date(alarm_start * 1000);
ml("div", {class: "col"}, "") alarmInfo += alarm_str + " at ";
), alarmInfo += start.toLocaleTimeString('de-DE');
ml("div", {class: "row p-2 ts-bg mx-2"}, if (alarm_end !== undefined) {
ml("div", {class: "col mx-2"}, ageInfo) var end = new Date(alarm_end * 1000);
) alarmInfo += " until ";
]); alarmInfo += end.toLocaleTimeString('de-DE');
}
}
return ml("div", {class: "mb-5"},
ml("div", {class: "row p-2 ts-bg mx-2"}, [
ml("span", {class: "col mx-2"}, ageInfo),
ml("span", {class: "col mx-2"}, alarmInfo)
]));
} }
function parseIv(obj) { function parseIv(obj) {
@ -220,7 +228,7 @@
ml("div", {}, [ ml("div", {}, [
ivHead(obj), ivHead(obj),
ml("div", {class: "row mb-2"}, chn), ml("div", {class: "row mb-2"}, chn),
tsInfo(obj.ts_last_success) tsInfo(obj.ts_last_success, obj.alarm_str, obj.alarm_start, obj.alarm_end)
]) ])
); );

Loading…
Cancel
Save