mirror of https://github.com/lumapu/ahoy.git
8 changed files with 843 additions and 1104 deletions
@ -1,380 +0,0 @@ |
|||||
//
|
|
||||
// todo: map hmDefines.h into this hmDecode.h class, no yet used and working
|
|
||||
//
|
|
||||
|
|
||||
#include <Arduino.h> |
|
||||
#include <stdint.h> |
|
||||
#include <stdio.h> |
|
||||
|
|
||||
#include "config.h" |
|
||||
#include "dbg.h" |
|
||||
|
|
||||
class HmDecode { |
|
||||
public: |
|
||||
HmDecode() { |
|
||||
// some member vars here
|
|
||||
} |
|
||||
|
|
||||
~HmDecode() {} |
|
||||
|
|
||||
union serial_u { |
|
||||
uint64_t u64; |
|
||||
uint8_t b[8]; |
|
||||
}; |
|
||||
|
|
||||
// units
|
|
||||
enum { UNIT_V = 0, |
|
||||
UNIT_A, |
|
||||
UNIT_W, |
|
||||
UNIT_WH, |
|
||||
UNIT_KWH, |
|
||||
UNIT_HZ, |
|
||||
UNIT_C, |
|
||||
UNIT_PCT, |
|
||||
UNIT_VAR, |
|
||||
UNIT_NONE }; |
|
||||
// const char* const units[] = {" V", " A", " W", " Wh", " kWh", " Hz", " °C", " %", " var", ""};
|
|
||||
const char PGM_units[][6] PROGMEM{" V ", " A ", " W ", " Wh ", " kWh ", " Hz ", " °C ", " % ", " var ", " "}; // one hidden byte needed at the end for '\0'
|
|
||||
|
|
||||
// field types
|
|
||||
enum { FLD_UDC = 0, |
|
||||
FLD_IDC, |
|
||||
FLD_PDC, |
|
||||
FLD_YD, |
|
||||
FLD_YW, |
|
||||
FLD_YT, |
|
||||
FLD_UAC, |
|
||||
FLD_IAC, |
|
||||
FLD_PAC, |
|
||||
FLD_F, |
|
||||
FLD_T, |
|
||||
FLD_PF, |
|
||||
FLD_EFF, |
|
||||
FLD_IRR, |
|
||||
FLD_Q, |
|
||||
FLD_EVT, |
|
||||
FLD_FW_VERSION, |
|
||||
FLD_FW_BUILD_YEAR, |
|
||||
FLD_FW_BUILD_MONTH_DAY, |
|
||||
FLD_FW_BUILD_HOUR_MINUTE, |
|
||||
FLD_HW_ID, |
|
||||
FLD_ACT_ACTIVE_PWR_LIMIT, |
|
||||
/*FLD_ACT_REACTIVE_PWR_LIMIT, FLD_ACT_PF,*/ FLD_LAST_ALARM_CODE }; |
|
||||
|
|
||||
// PGM Flash memory usage instead of RAM for ARDUINO NANO, idea given by Nick Gammon's great webpage http://gammon.com.au/progmem
|
|
||||
const char PGM_fields[/*NUM ELEMENTS*/][25] PROGMEM{ |
|
||||
{"U_DC"}, |
|
||||
{"I_DC"}, |
|
||||
{"P_DC"}, |
|
||||
{"YieldDay"}, |
|
||||
{"YieldWeek"}, |
|
||||
{"YieldTotal"}, |
|
||||
{"U_AC"}, |
|
||||
{"I_AC"}, |
|
||||
{"P_AC"}, |
|
||||
{"F_AC"}, |
|
||||
{"Temp"}, |
|
||||
{"PF_AC"}, |
|
||||
{"Efficiency"}, |
|
||||
{"Irradiation"}, |
|
||||
{"Q_AC"}, |
|
||||
{"ALARM_MES_ID"}, |
|
||||
{"FWVersion"}, |
|
||||
{"FWBuildYear"}, |
|
||||
{"FWBuildMonthDay"}, |
|
||||
{"FWBuildHourMinute"}, |
|
||||
{"HWPartId"}, |
|
||||
{"active PowerLimit"}, |
|
||||
/* {"reactive PowerLimit"},
|
|
||||
{"Powerfactor"}*/ |
|
||||
{"LastAlarmCode"}, |
|
||||
}; |
|
||||
|
|
||||
// const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
|
|
||||
// "U_AC", "I_AC", "P_AC", "F_AC", "Temp", "PF_AC", "Efficiency", "Irradiation","Q_AC",
|
|
||||
// "ALARM_MES_ID","FWVersion","FWBuildYear","FWBuildMonthDay","FWBuildHourMinute","HWPartId",
|
|
||||
// "active PowerLimit", /*"reactive PowerLimit","Powerfactor",*/ "LastAlarmCode"};
|
|
||||
|
|
||||
const char* const notAvail = "n/a"; |
|
||||
|
|
||||
// mqtt discovery device classes
|
|
||||
enum { DEVICE_CLS_NONE = 0, |
|
||||
DEVICE_CLS_CURRENT, |
|
||||
DEVICE_CLS_ENERGY, |
|
||||
DEVICE_CLS_PWR, |
|
||||
DEVICE_CLS_VOLTAGE, |
|
||||
DEVICE_CLS_FREQ, |
|
||||
DEVICE_CLS_TEMP }; |
|
||||
|
|
||||
const char* const deviceClasses[] = {0, "current", "energy", "power", "voltage", "frequency", "temperature"}; |
|
||||
|
|
||||
enum { STATE_CLS_NONE = 0, |
|
||||
STATE_CLS_MEASUREMENT, |
|
||||
STATE_CLS_TOTAL_INCREASING }; |
|
||||
|
|
||||
const char* const stateClasses[] = {0, "measurement", "total_increasing"}; |
|
||||
|
|
||||
typedef struct { |
|
||||
uint8_t fieldId; // field id
|
|
||||
uint8_t deviceClsId; // device class
|
|
||||
uint8_t stateClsId; // state class
|
|
||||
} byteAssign_fieldDeviceClass; |
|
||||
|
|
||||
const byteAssign_fieldDeviceClass deviceFieldAssignment[] = { |
|
||||
{FLD_UDC, DEVICE_CLS_VOLTAGE, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_IDC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_PDC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_YD, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING}, |
|
||||
{FLD_YW, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING}, |
|
||||
{FLD_YT, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING}, |
|
||||
{FLD_UAC, DEVICE_CLS_VOLTAGE, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_IAC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_PAC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_F, DEVICE_CLS_FREQ, STATE_CLS_NONE}, |
|
||||
{FLD_T, DEVICE_CLS_TEMP, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_PF, DEVICE_CLS_NONE, STATE_CLS_NONE}, |
|
||||
{FLD_EFF, DEVICE_CLS_NONE, STATE_CLS_NONE}, |
|
||||
{FLD_IRR, DEVICE_CLS_NONE, STATE_CLS_NONE}}; |
|
||||
#define DEVICE_CLS_ASSIGN_LIST_LEN (sizeof(deviceFieldAssignment) / sizeof(byteAssign_fieldDeviceClass)) |
|
||||
|
|
||||
// indices to calculation functions, defined in hmInverter.h
|
|
||||
enum { CALC_YT_CH0 = 0, |
|
||||
CALC_YD_CH0, |
|
||||
CALC_UDC_CH, |
|
||||
CALC_PDC_CH0, |
|
||||
CALC_EFF_CH0, |
|
||||
CALC_IRR_CH }; |
|
||||
enum { CMD_CALC = 0xffff }; |
|
||||
|
|
||||
// CH0 is default channel (freq, ac, temp)
|
|
||||
enum { CH0 = 0, |
|
||||
CH1, |
|
||||
CH2, |
|
||||
CH3, |
|
||||
CH4 }; |
|
||||
|
|
||||
enum { INV_TYPE_1CH = 0, |
|
||||
INV_TYPE_2CH, |
|
||||
INV_TYPE_4CH }; |
|
||||
|
|
||||
typedef struct { |
|
||||
uint8_t fieldId; // field id
|
|
||||
uint8_t unitId; // uint id
|
|
||||
uint8_t ch; // channel 0 - 4
|
|
||||
uint8_t start; // pos of first byte in buffer
|
|
||||
uint8_t num; // number of bytes in buffer
|
|
||||
uint16_t div; // divisor / calc command
|
|
||||
} byteAssign_t; |
|
||||
|
|
||||
/**
|
|
||||
* indices are built for the buffer starting with cmd-id in first byte |
|
||||
* (complete payload in buffer) |
|
||||
* */ |
|
||||
|
|
||||
//-------------------------------------
|
|
||||
// HM-Series
|
|
||||
//-------------------------------------
|
|
||||
const byteAssign_t InfoAssignment[] = { |
|
||||
{FLD_FW_VERSION, UNIT_NONE, CH0, 0, 2, 1}, |
|
||||
{FLD_FW_BUILD_YEAR, UNIT_NONE, CH0, 2, 2, 1}, |
|
||||
{FLD_FW_BUILD_MONTH_DAY, UNIT_NONE, CH0, 4, 2, 1}, |
|
||||
{FLD_FW_BUILD_HOUR_MINUTE, UNIT_NONE, CH0, 6, 2, 1}, |
|
||||
{FLD_HW_ID, UNIT_NONE, CH0, 8, 2, 1}}; |
|
||||
#define HMINFO_LIST_LEN (sizeof(InfoAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HMINFO_PAYLOAD_LEN 14 |
|
||||
|
|
||||
const byteAssign_t SystemConfigParaAssignment[] = { |
|
||||
{FLD_ACT_ACTIVE_PWR_LIMIT, UNIT_PCT, CH0, 2, 2, 10} /*,
|
|
||||
{ FLD_ACT_REACTIVE_PWR_LIMIT, UNIT_PCT, CH0, 4, 2, 10 }, |
|
||||
{ FLD_ACT_PF, UNIT_NONE, CH0, 6, 2, 1000 }*/ |
|
||||
}; |
|
||||
#define HMSYSTEM_LIST_LEN (sizeof(SystemConfigParaAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HMSYSTEM_PAYLOAD_LEN 14 |
|
||||
|
|
||||
const byteAssign_t AlarmDataAssignment[] = { |
|
||||
{FLD_LAST_ALARM_CODE, UNIT_NONE, CH0, 0, 2, 1}}; |
|
||||
#define HMALARMDATA_LIST_LEN (sizeof(AlarmDataAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HMALARMDATA_PAYLOAD_LEN 0 // 0: means check is off
|
|
||||
|
|
||||
//-------------------------------------
|
|
||||
// HM300, HM350, HM400
|
|
||||
//-------------------------------------
|
|
||||
const byteAssign_t hm1chAssignment[] = { |
|
||||
{FLD_UDC, UNIT_V, CH1, 2, 2, 10}, |
|
||||
{FLD_IDC, UNIT_A, CH1, 4, 2, 100}, |
|
||||
{FLD_PDC, UNIT_W, CH1, 6, 2, 10}, |
|
||||
{FLD_YD, UNIT_WH, CH1, 12, 2, 1}, |
|
||||
{FLD_YT, UNIT_KWH, CH1, 8, 4, 1000}, |
|
||||
{FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC}, |
|
||||
|
|
||||
{FLD_UAC, UNIT_V, CH0, 14, 2, 10}, |
|
||||
{FLD_IAC, UNIT_A, CH0, 22, 2, 100}, |
|
||||
{FLD_PAC, UNIT_W, CH0, 18, 2, 10}, |
|
||||
{FLD_Q, UNIT_VAR, CH0, 20, 2, 10}, |
|
||||
{FLD_F, UNIT_HZ, CH0, 16, 2, 100}, |
|
||||
{FLD_PF, UNIT_NONE, CH0, 24, 2, 1000}, |
|
||||
{FLD_T, UNIT_C, CH0, 26, 2, 10}, |
|
||||
{FLD_EVT, UNIT_NONE, CH0, 28, 2, 1}, |
|
||||
{FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC}, |
|
||||
{FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC}, |
|
||||
{FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC}, |
|
||||
{FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC}}; |
|
||||
#define HM1CH_LIST_LEN (sizeof(hm1chAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HM1CH_PAYLOAD_LEN 30 |
|
||||
|
|
||||
//-------------------------------------
|
|
||||
// HM600, HM700, HM800
|
|
||||
//-------------------------------------
|
|
||||
const byteAssign_t hm2chAssignment[] = { |
|
||||
{FLD_UDC, UNIT_V, CH1, 2, 2, 10}, |
|
||||
{FLD_IDC, UNIT_A, CH1, 4, 2, 100}, |
|
||||
{FLD_PDC, UNIT_W, CH1, 6, 2, 10}, |
|
||||
{FLD_YD, UNIT_WH, CH1, 22, 2, 1}, |
|
||||
{FLD_YT, UNIT_KWH, CH1, 14, 4, 1000}, |
|
||||
{FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC}, |
|
||||
|
|
||||
{FLD_UDC, UNIT_V, CH2, 8, 2, 10}, |
|
||||
{FLD_IDC, UNIT_A, CH2, 10, 2, 100}, |
|
||||
{FLD_PDC, UNIT_W, CH2, 12, 2, 10}, |
|
||||
{FLD_YD, UNIT_WH, CH2, 24, 2, 1}, |
|
||||
{FLD_YT, UNIT_KWH, CH2, 18, 4, 1000}, |
|
||||
{FLD_IRR, UNIT_PCT, CH2, CALC_IRR_CH, CH2, CMD_CALC}, |
|
||||
|
|
||||
{FLD_UAC, UNIT_V, CH0, 26, 2, 10}, |
|
||||
{FLD_IAC, UNIT_A, CH0, 34, 2, 100}, |
|
||||
{FLD_PAC, UNIT_W, CH0, 30, 2, 10}, |
|
||||
{FLD_Q, UNIT_VAR, CH0, 32, 2, 10}, |
|
||||
{FLD_F, UNIT_HZ, CH0, 28, 2, 100}, |
|
||||
{FLD_PF, UNIT_NONE, CH0, 36, 2, 1000}, |
|
||||
{FLD_T, UNIT_C, CH0, 38, 2, 10}, |
|
||||
{FLD_EVT, UNIT_NONE, CH0, 40, 2, 1}, |
|
||||
{FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC}, |
|
||||
{FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC}, |
|
||||
{FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC}, |
|
||||
{FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC} |
|
||||
|
|
||||
}; |
|
||||
#define HM2CH_LIST_LEN (sizeof(hm2chAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HM2CH_PAYLOAD_LEN 42 |
|
||||
|
|
||||
//-------------------------------------
|
|
||||
// HM1200, HM1500
|
|
||||
//-------------------------------------
|
|
||||
const byteAssign_t hm4chAssignment[] = { |
|
||||
{FLD_UDC, UNIT_V, CH1, 2, 2, 10}, |
|
||||
{FLD_IDC, UNIT_A, CH1, 4, 2, 100}, |
|
||||
{FLD_PDC, UNIT_W, CH1, 8, 2, 10}, |
|
||||
{FLD_YD, UNIT_WH, CH1, 20, 2, 1}, |
|
||||
{FLD_YT, UNIT_KWH, CH1, 12, 4, 1000}, |
|
||||
{FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC}, |
|
||||
|
|
||||
{FLD_UDC, UNIT_V, CH2, CALC_UDC_CH, CH1, CMD_CALC}, |
|
||||
{FLD_IDC, UNIT_A, CH2, 6, 2, 100}, |
|
||||
{FLD_PDC, UNIT_W, CH2, 10, 2, 10}, |
|
||||
{FLD_YD, UNIT_WH, CH2, 22, 2, 1}, |
|
||||
{FLD_YT, UNIT_KWH, CH2, 16, 4, 1000}, |
|
||||
{FLD_IRR, UNIT_PCT, CH2, CALC_IRR_CH, CH2, CMD_CALC}, |
|
||||
|
|
||||
{FLD_UDC, UNIT_V, CH3, 24, 2, 10}, |
|
||||
{FLD_IDC, UNIT_A, CH3, 26, 2, 100}, |
|
||||
{FLD_PDC, UNIT_W, CH3, 30, 2, 10}, |
|
||||
{FLD_YD, UNIT_WH, CH3, 42, 2, 1}, |
|
||||
{FLD_YT, UNIT_KWH, CH3, 34, 4, 1000}, |
|
||||
{FLD_IRR, UNIT_PCT, CH3, CALC_IRR_CH, CH3, CMD_CALC}, |
|
||||
|
|
||||
{FLD_UDC, UNIT_V, CH4, CALC_UDC_CH, CH3, CMD_CALC}, |
|
||||
{FLD_IDC, UNIT_A, CH4, 28, 2, 100}, |
|
||||
{FLD_PDC, UNIT_W, CH4, 32, 2, 10}, |
|
||||
{FLD_YD, UNIT_WH, CH4, 44, 2, 1}, |
|
||||
{FLD_YT, UNIT_KWH, CH4, 38, 4, 1000}, |
|
||||
{FLD_IRR, UNIT_PCT, CH4, CALC_IRR_CH, CH4, CMD_CALC}, |
|
||||
|
|
||||
{FLD_UAC, UNIT_V, CH0, 46, 2, 10}, |
|
||||
{FLD_IAC, UNIT_A, CH0, 54, 2, 100}, |
|
||||
{FLD_PAC, UNIT_W, CH0, 50, 2, 10}, |
|
||||
{FLD_Q, UNIT_VAR, CH0, 52, 2, 10}, |
|
||||
{FLD_F, UNIT_HZ, CH0, 48, 2, 100}, |
|
||||
{FLD_PF, UNIT_NONE, CH0, 56, 2, 1000}, |
|
||||
{FLD_T, UNIT_C, CH0, 58, 2, 10}, |
|
||||
{FLD_EVT, UNIT_NONE, CH0, 60, 2, 1}, |
|
||||
{FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC}, |
|
||||
{FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC}, |
|
||||
{FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC}, |
|
||||
{FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC}}; |
|
||||
#define HM4CH_LIST_LEN (sizeof(hm4chAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HM4CH_PAYLOAD_LEN 62 |
|
||||
|
|
||||
/**
|
|
||||
* simple decoding of 2ch HM-inverter only |
|
||||
*/ |
|
||||
static void decode_userPayload(uint8_t _cmd, uint8_t* _user_payload, uint8_t _ulen, uint32_t _ts, char* _strout, uint8_t _strlen, uint16_t _invtype, uint32_t _plateID) { |
|
||||
volatile uint32_t _val = 0L; |
|
||||
|
|
||||
#if defined(ESP8266) |
|
||||
// for esp8266 environment
|
|
||||
byteAssign_t _bp; // volatile byteAssign_t not compiling ESP8266
|
|
||||
#else |
|
||||
volatile byteAssign_t _bp; // must be volatile for Arduino-Nano, otherwise _bp.ch stays zero
|
|
||||
#endif |
|
||||
|
|
||||
volatile uint8_t _x; |
|
||||
volatile uint8_t _end; |
|
||||
volatile float _fval; |
|
||||
volatile uint8_t _tmp8 = 0xff; |
|
||||
|
|
||||
//_str80[80] = '\0';
|
|
||||
snprintf_P(_strout, _strlen, PSTR("\ndata age: %d sec"), (millis() - _ts) / 1000); |
|
||||
Serial.print(_strout); |
|
||||
|
|
||||
if (_cmd == 0x95 and _ulen == 42) { |
|
||||
//!!! simple HM600/700/800 2ch decoding for cmd=0x95 only !!!!
|
|
||||
|
|
||||
for (_x = 0; _x < HM2CH_LIST_LEN; _x++) { |
|
||||
// read values from given positions in payload
|
|
||||
_bp = hm2chAssignment[_x]; |
|
||||
_val = 0L; |
|
||||
_end = _bp.start + _bp.num; |
|
||||
if (_tmp8 != _bp.ch) { |
|
||||
snprintf_P(_strout, _strlen, PSTR("\nHM800/%04X%08lX/ch%02d "), _invtype, _plateID, _bp.ch); // must be a small "l" in %08lX for Arduino-Nano
|
|
||||
Serial.print(_strout); |
|
||||
// snprintf_P(_strout, _strlen, PSTR("ch%02d/"), _bp.ch);
|
|
||||
// Serial.print(_strout);
|
|
||||
} |
|
||||
_tmp8 = _bp.ch; |
|
||||
|
|
||||
if (CMD_CALC != _bp.div && _bp.div != 0) { |
|
||||
strncpy_P(_strout, &(PGM_fields[_bp.fieldId][0]), _strlen); |
|
||||
Serial.print(_strout); |
|
||||
Serial.print(F(": ")); |
|
||||
|
|
||||
do { |
|
||||
_val <<= 8; |
|
||||
_val |= _user_payload[_bp.start]; |
|
||||
} while (++_bp.start != _end); |
|
||||
_fval = (int)_val / (float)_bp.div; |
|
||||
|
|
||||
if (_bp.unitId == UNIT_NONE) { |
|
||||
Serial.print(_val); |
|
||||
Serial.print(F(" ")); |
|
||||
continue; |
|
||||
} |
|
||||
Serial.print(_fval, 2); // arduino nano does not sprintf(float values), but print() does
|
|
||||
// Serial.print(units[_bp.unitId]);
|
|
||||
strncpy_P(_strout, &PGM_units[_bp.unitId][0], _strlen); |
|
||||
Serial.print(_strout); |
|
||||
|
|
||||
} else { |
|
||||
// do calculations
|
|
||||
// Serial.print(F("not yet"));
|
|
||||
} |
|
||||
} // end for()
|
|
||||
Serial.println(); |
|
||||
|
|
||||
} else { |
|
||||
Serial.print(F("NO DECODER ")); |
|
||||
Serial.print(_cmd, HEX); |
|
||||
} |
|
||||
} // end decodePayload()
|
|
||||
|
|
||||
private: |
|
||||
}; |
|
@ -0,0 +1,394 @@ |
|||||
|
//-----------------------------------------------------------------------------
|
||||
|
// 2022 Ahoy, https://github.com/lumpapu/ahoy
|
||||
|
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
||||
|
//-----------------------------------------------------------------------------
|
||||
|
|
||||
|
// 2022 mb using PROGMEM for lage char* arrays, used and tested for HM800 decoding (2channel), no calculation yet
|
||||
|
// - strip down of hmDefines.h and rename to hmDecode.h
|
||||
|
|
||||
|
// todo: make a decoding class out of this
|
||||
|
|
||||
|
#ifndef __HM_DEFINES_H__ |
||||
|
#define __HM_DEFINES_H__ |
||||
|
|
||||
|
#include "dbg.h" |
||||
|
|
||||
|
union serial_u { |
||||
|
uint64_t u64; |
||||
|
uint8_t b[8]; |
||||
|
}; |
||||
|
|
||||
|
// units
|
||||
|
enum { UNIT_V = 0, |
||||
|
UNIT_A, |
||||
|
UNIT_W, |
||||
|
UNIT_WH, |
||||
|
UNIT_KWH, |
||||
|
UNIT_HZ, |
||||
|
UNIT_C, |
||||
|
UNIT_PCT, |
||||
|
UNIT_VAR, |
||||
|
UNIT_NONE }; |
||||
|
// const char* const units[] = {" V", " A", " W", " Wh", " kWh", " Hz", " °C", " %", " var", ""};
|
||||
|
const char PGM_units[][6] PROGMEM = {" V ", " A ", " W ", " Wh ", " kWh ", " Hz ", " °C ", " % ", " var ", " "}; // one hidden byte needed at the end for '\0'
|
||||
|
|
||||
|
// field types
|
||||
|
enum { FLD_UDC = 0, |
||||
|
FLD_IDC, |
||||
|
FLD_PDC, |
||||
|
FLD_YD, |
||||
|
FLD_YW, |
||||
|
FLD_YT, |
||||
|
FLD_UAC, |
||||
|
FLD_IAC, |
||||
|
FLD_PAC, |
||||
|
FLD_F, |
||||
|
FLD_T, |
||||
|
FLD_PF, |
||||
|
FLD_EFF, |
||||
|
FLD_IRR, |
||||
|
FLD_Q, |
||||
|
FLD_EVT, |
||||
|
FLD_FW_VERSION, |
||||
|
FLD_FW_BUILD_YEAR, |
||||
|
FLD_FW_BUILD_MONTH_DAY, |
||||
|
FLD_FW_BUILD_HOUR_MINUTE, |
||||
|
FLD_HW_ID, |
||||
|
FLD_ACT_ACTIVE_PWR_LIMIT, |
||||
|
/*FLD_ACT_REACTIVE_PWR_LIMIT, FLD_ACT_PF,*/ FLD_LAST_ALARM_CODE }; |
||||
|
|
||||
|
// PGM Flash memory usage instead of RAM for ARDUINO NANO, idea given by Nick Gammon's great webpage http://gammon.com.au/progmem
|
||||
|
const char PGM_fields[/*NUM ELEMENTS*/][25] PROGMEM = { |
||||
|
{"U_DC"}, |
||||
|
{"I_DC"}, |
||||
|
{"P_DC"}, |
||||
|
{"YieldDay"}, |
||||
|
{"YieldWeek"}, |
||||
|
{"YieldTotal"}, |
||||
|
{"U_AC"}, |
||||
|
{"I_AC"}, |
||||
|
{"P_AC"}, |
||||
|
{"F_AC"}, |
||||
|
{"Temp"}, |
||||
|
{"PF_AC"}, |
||||
|
{"Efficiency"}, |
||||
|
{"Irradiation"}, |
||||
|
{"Q_AC"}, |
||||
|
{"ALARM_MES_ID"}, |
||||
|
{"FWVersion"}, |
||||
|
{"FWBuildYear"}, |
||||
|
{"FWBuildMonthDay"}, |
||||
|
{"FWBuildHourMinute"}, |
||||
|
{"HWPartId"}, |
||||
|
{"active PowerLimit"}, |
||||
|
/* {"reactive PowerLimit"},
|
||||
|
{"Powerfactor"}*/ |
||||
|
{"LastAlarmCode"}, |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
const char PGM_invname[][8] PROGMEM = { "HM400", "HM600", "HM800", "HM1200", "HM1500"}; |
||||
|
|
||||
|
// const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
|
||||
|
// "U_AC", "I_AC", "P_AC", "F_AC", "Temp", "PF_AC", "Efficiency", "Irradiation","Q_AC",
|
||||
|
// "ALARM_MES_ID","FWVersion","FWBuildYear","FWBuildMonthDay","FWBuildHourMinute","HWPartId",
|
||||
|
// "active PowerLimit", /*"reactive PowerLimit","Powerfactor",*/ "LastAlarmCode"};
|
||||
|
|
||||
|
const char* const notAvail = "n/a"; |
||||
|
|
||||
|
// mqtt discovery device classes
|
||||
|
enum { DEVICE_CLS_NONE = 0, |
||||
|
DEVICE_CLS_CURRENT, |
||||
|
DEVICE_CLS_ENERGY, |
||||
|
DEVICE_CLS_PWR, |
||||
|
DEVICE_CLS_VOLTAGE, |
||||
|
DEVICE_CLS_FREQ, |
||||
|
DEVICE_CLS_TEMP }; |
||||
|
const char* const deviceClasses[] = {0, "current", "energy", "power", "voltage", "frequency", "temperature"}; |
||||
|
enum { STATE_CLS_NONE = 0, |
||||
|
STATE_CLS_MEASUREMENT, |
||||
|
STATE_CLS_TOTAL_INCREASING }; |
||||
|
const char* const stateClasses[] = {0, "measurement", "total_increasing"}; |
||||
|
typedef struct { |
||||
|
uint8_t fieldId; // field id
|
||||
|
uint8_t deviceClsId; // device class
|
||||
|
uint8_t stateClsId; // state class
|
||||
|
} byteAssign_fieldDeviceClass; |
||||
|
const byteAssign_fieldDeviceClass deviceFieldAssignment[] = { |
||||
|
{FLD_UDC, DEVICE_CLS_VOLTAGE, STATE_CLS_MEASUREMENT}, |
||||
|
{FLD_IDC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT}, |
||||
|
{FLD_PDC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT}, |
||||
|
{FLD_YD, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING}, |
||||
|
{FLD_YW, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING}, |
||||
|
{FLD_YT, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING}, |
||||
|
{FLD_UAC, DEVICE_CLS_VOLTAGE, STATE_CLS_MEASUREMENT}, |
||||
|
{FLD_IAC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT}, |
||||
|
{FLD_PAC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT}, |
||||
|
{FLD_F, DEVICE_CLS_FREQ, STATE_CLS_NONE}, |
||||
|
{FLD_T, DEVICE_CLS_TEMP, STATE_CLS_MEASUREMENT}, |
||||
|
{FLD_PF, DEVICE_CLS_NONE, STATE_CLS_NONE}, |
||||
|
{FLD_EFF, DEVICE_CLS_NONE, STATE_CLS_NONE}, |
||||
|
{FLD_IRR, DEVICE_CLS_NONE, STATE_CLS_NONE}}; |
||||
|
#define DEVICE_CLS_ASSIGN_LIST_LEN (sizeof(deviceFieldAssignment) / sizeof(byteAssign_fieldDeviceClass)) |
||||
|
|
||||
|
// indices to calculation functions, defined in hmInverter.h
|
||||
|
enum { CALC_YT_CH0 = 0, |
||||
|
CALC_YD_CH0, |
||||
|
CALC_UDC_CH, |
||||
|
CALC_PDC_CH0, |
||||
|
CALC_EFF_CH0, |
||||
|
CALC_IRR_CH }; |
||||
|
enum { CMD_CALC = 0xffff }; |
||||
|
|
||||
|
// CH0 is default channel (freq, ac, temp)
|
||||
|
enum { CH0 = 0, |
||||
|
CH1, |
||||
|
CH2, |
||||
|
CH3, |
||||
|
CH4 }; |
||||
|
|
||||
|
enum { INV_TYPE_1CH = 0, |
||||
|
INV_TYPE_2CH, |
||||
|
INV_TYPE_4CH }; |
||||
|
|
||||
|
typedef struct { |
||||
|
uint8_t fieldId; // field id
|
||||
|
uint8_t unitId; // uint id
|
||||
|
uint8_t ch; // channel 0 - 4
|
||||
|
uint8_t start; // pos of first byte in buffer
|
||||
|
uint8_t num; // number of bytes in buffer
|
||||
|
uint16_t div; // divisor / calc command
|
||||
|
} byteAssign_t; |
||||
|
|
||||
|
/**
|
||||
|
* indices are built for the buffer starting with cmd-id in first byte |
||||
|
* (complete payload in buffer) |
||||
|
* */ |
||||
|
|
||||
|
//-------------------------------------
|
||||
|
// HM-Series
|
||||
|
//-------------------------------------
|
||||
|
const byteAssign_t InfoAssignment[] = { |
||||
|
{FLD_FW_VERSION, UNIT_NONE, CH0, 0, 2, 1}, |
||||
|
{FLD_FW_BUILD_YEAR, UNIT_NONE, CH0, 2, 2, 1}, |
||||
|
{FLD_FW_BUILD_MONTH_DAY, UNIT_NONE, CH0, 4, 2, 1}, |
||||
|
{FLD_FW_BUILD_HOUR_MINUTE, UNIT_NONE, CH0, 6, 2, 1}, |
||||
|
{FLD_HW_ID, UNIT_NONE, CH0, 8, 2, 1}}; |
||||
|
#define HMINFO_LIST_LEN (sizeof(InfoAssignment) / sizeof(byteAssign_t)) |
||||
|
#define HMINFO_PAYLOAD_LEN 14 |
||||
|
|
||||
|
const byteAssign_t SystemConfigParaAssignment[] = { |
||||
|
{FLD_ACT_ACTIVE_PWR_LIMIT, UNIT_PCT, CH0, 2, 2, 10} /*,
|
||||
|
{ FLD_ACT_REACTIVE_PWR_LIMIT, UNIT_PCT, CH0, 4, 2, 10 }, |
||||
|
{ FLD_ACT_PF, UNIT_NONE, CH0, 6, 2, 1000 }*/ |
||||
|
}; |
||||
|
#define HMSYSTEM_LIST_LEN (sizeof(SystemConfigParaAssignment) / sizeof(byteAssign_t)) |
||||
|
#define HMSYSTEM_PAYLOAD_LEN 14 |
||||
|
|
||||
|
const byteAssign_t AlarmDataAssignment[] = { |
||||
|
{FLD_LAST_ALARM_CODE, UNIT_NONE, CH0, 0, 2, 1}}; |
||||
|
#define HMALARMDATA_LIST_LEN (sizeof(AlarmDataAssignment) / sizeof(byteAssign_t)) |
||||
|
#define HMALARMDATA_PAYLOAD_LEN 0 // 0: means check is off
|
||||
|
|
||||
|
//-------------------------------------
|
||||
|
// HM300, HM350, HM400
|
||||
|
//-------------------------------------
|
||||
|
const byteAssign_t hm1chAssignment[] = { |
||||
|
{FLD_UDC, UNIT_V, CH1, 2, 2, 10}, |
||||
|
{FLD_IDC, UNIT_A, CH1, 4, 2, 100}, |
||||
|
{FLD_PDC, UNIT_W, CH1, 6, 2, 10}, |
||||
|
{FLD_YD, UNIT_WH, CH1, 12, 2, 1}, |
||||
|
{FLD_YT, UNIT_KWH, CH1, 8, 4, 1000}, |
||||
|
{FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC}, |
||||
|
|
||||
|
{FLD_UAC, UNIT_V, CH0, 14, 2, 10}, |
||||
|
{FLD_IAC, UNIT_A, CH0, 22, 2, 100}, |
||||
|
{FLD_PAC, UNIT_W, CH0, 18, 2, 10}, |
||||
|
{FLD_Q, UNIT_VAR, CH0, 20, 2, 10}, |
||||
|
{FLD_F, UNIT_HZ, CH0, 16, 2, 100}, |
||||
|
{FLD_PF, UNIT_NONE, CH0, 24, 2, 1000}, |
||||
|
{FLD_T, UNIT_C, CH0, 26, 2, 10}, |
||||
|
{FLD_EVT, UNIT_NONE, CH0, 28, 2, 1}, |
||||
|
{FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC}, |
||||
|
{FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC}, |
||||
|
{FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC}, |
||||
|
{FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC}}; |
||||
|
#define HM1CH_LIST_LEN (sizeof(hm1chAssignment) / sizeof(byteAssign_t)) |
||||
|
#define HM1CH_PAYLOAD_LEN 30 |
||||
|
|
||||
|
//-------------------------------------
|
||||
|
// HM600, HM700, HM800
|
||||
|
//-------------------------------------
|
||||
|
const byteAssign_t hm2chAssignment[] = { |
||||
|
{FLD_UDC, UNIT_V, CH1, 2, 2, 10}, |
||||
|
{FLD_IDC, UNIT_A, CH1, 4, 2, 100}, |
||||
|
{FLD_PDC, UNIT_W, CH1, 6, 2, 10}, |
||||
|
{FLD_YD, UNIT_WH, CH1, 22, 2, 1}, |
||||
|
{FLD_YT, UNIT_KWH, CH1, 14, 4, 1000}, |
||||
|
{FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC}, |
||||
|
|
||||
|
{FLD_UDC, UNIT_V, CH2, 8, 2, 10}, |
||||
|
{FLD_IDC, UNIT_A, CH2, 10, 2, 100}, |
||||
|
{FLD_PDC, UNIT_W, CH2, 12, 2, 10}, |
||||
|
{FLD_YD, UNIT_WH, CH2, 24, 2, 1}, |
||||
|
{FLD_YT, UNIT_KWH, CH2, 18, 4, 1000}, |
||||
|
{FLD_IRR, UNIT_PCT, CH2, CALC_IRR_CH, CH2, CMD_CALC}, |
||||
|
|
||||
|
{FLD_UAC, UNIT_V, CH0, 26, 2, 10}, |
||||
|
{FLD_IAC, UNIT_A, CH0, 34, 2, 100}, |
||||
|
{FLD_PAC, UNIT_W, CH0, 30, 2, 10}, |
||||
|
{FLD_Q, UNIT_VAR, CH0, 32, 2, 10}, |
||||
|
{FLD_F, UNIT_HZ, CH0, 28, 2, 100}, |
||||
|
{FLD_PF, UNIT_NONE, CH0, 36, 2, 1000}, |
||||
|
{FLD_T, UNIT_C, CH0, 38, 2, 10}, |
||||
|
{FLD_EVT, UNIT_NONE, CH0, 40, 2, 1}, |
||||
|
{FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC}, |
||||
|
{FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC}, |
||||
|
{FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC}, |
||||
|
{FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC} |
||||
|
|
||||
|
}; |
||||
|
#define HM2CH_LIST_LEN (sizeof(hm2chAssignment) / sizeof(byteAssign_t)) |
||||
|
#define HM2CH_PAYLOAD_LEN 42 |
||||
|
|
||||
|
//-------------------------------------
|
||||
|
// HM1200, HM1500
|
||||
|
//-------------------------------------
|
||||
|
const byteAssign_t hm4chAssignment[] = { |
||||
|
{FLD_UDC, UNIT_V, CH1, 2, 2, 10}, |
||||
|
{FLD_IDC, UNIT_A, CH1, 4, 2, 100}, |
||||
|
{FLD_PDC, UNIT_W, CH1, 8, 2, 10}, |
||||
|
{FLD_YD, UNIT_WH, CH1, 20, 2, 1}, |
||||
|
{FLD_YT, UNIT_KWH, CH1, 12, 4, 1000}, |
||||
|
{FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC}, |
||||
|
|
||||
|
{FLD_UDC, UNIT_V, CH2, CALC_UDC_CH, CH1, CMD_CALC}, |
||||
|
{FLD_IDC, UNIT_A, CH2, 6, 2, 100}, |
||||
|
{FLD_PDC, UNIT_W, CH2, 10, 2, 10}, |
||||
|
{FLD_YD, UNIT_WH, CH2, 22, 2, 1}, |
||||
|
{FLD_YT, UNIT_KWH, CH2, 16, 4, 1000}, |
||||
|
{FLD_IRR, UNIT_PCT, CH2, CALC_IRR_CH, CH2, CMD_CALC}, |
||||
|
|
||||
|
{FLD_UDC, UNIT_V, CH3, 24, 2, 10}, |
||||
|
{FLD_IDC, UNIT_A, CH3, 26, 2, 100}, |
||||
|
{FLD_PDC, UNIT_W, CH3, 30, 2, 10}, |
||||
|
{FLD_YD, UNIT_WH, CH3, 42, 2, 1}, |
||||
|
{FLD_YT, UNIT_KWH, CH3, 34, 4, 1000}, |
||||
|
{FLD_IRR, UNIT_PCT, CH3, CALC_IRR_CH, CH3, CMD_CALC}, |
||||
|
|
||||
|
{FLD_UDC, UNIT_V, CH4, CALC_UDC_CH, CH3, CMD_CALC}, |
||||
|
{FLD_IDC, UNIT_A, CH4, 28, 2, 100}, |
||||
|
{FLD_PDC, UNIT_W, CH4, 32, 2, 10}, |
||||
|
{FLD_YD, UNIT_WH, CH4, 44, 2, 1}, |
||||
|
{FLD_YT, UNIT_KWH, CH4, 38, 4, 1000}, |
||||
|
{FLD_IRR, UNIT_PCT, CH4, CALC_IRR_CH, CH4, CMD_CALC}, |
||||
|
|
||||
|
{FLD_UAC, UNIT_V, CH0, 46, 2, 10}, |
||||
|
{FLD_IAC, UNIT_A, CH0, 54, 2, 100}, |
||||
|
{FLD_PAC, UNIT_W, CH0, 50, 2, 10}, |
||||
|
{FLD_Q, UNIT_VAR, CH0, 52, 2, 10}, |
||||
|
{FLD_F, UNIT_HZ, CH0, 48, 2, 100}, |
||||
|
{FLD_PF, UNIT_NONE, CH0, 56, 2, 1000}, |
||||
|
{FLD_T, UNIT_C, CH0, 58, 2, 10}, |
||||
|
{FLD_EVT, UNIT_NONE, CH0, 60, 2, 1}, |
||||
|
{FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC}, |
||||
|
{FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC}, |
||||
|
{FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC}, |
||||
|
{FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC}}; |
||||
|
#define HM4CH_LIST_LEN (sizeof(hm4chAssignment) / sizeof(byteAssign_t)) |
||||
|
#define HM4CH_PAYLOAD_LEN 62 |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* simple decoding of 1,2,4ch HM-inverter only |
||||
|
*/ |
||||
|
static void decode_InfoREQResp(uint8_t _cmd, uint8_t *_user_payload, uint8_t _ulen, uint32_t _ts, char *_strout, uint8_t _strlen, uint16_t _invtype, uint32_t _plateID) { |
||||
|
volatile int32_t _val = 0L; |
||||
|
|
||||
|
#if defined(ESP8266) |
||||
|
// for esp8266 environment
|
||||
|
byteAssign_t _bp; //volatile byteAssign_t not compiling ESP8266
|
||||
|
#else |
||||
|
volatile byteAssign_t _bp; //must be volatile for Arduino-Nano, otherwise _bp.ch stays zero
|
||||
|
#endif |
||||
|
|
||||
|
volatile uint8_t _x; |
||||
|
volatile uint8_t _end; |
||||
|
volatile float _fval; |
||||
|
volatile uint8_t _tmp8 = 0xff; |
||||
|
|
||||
|
volatile uint8_t LIST_LEN = 0; |
||||
|
volatile uint8_t ixn = 0; |
||||
|
|
||||
|
//_str80[80] = '\0';
|
||||
|
snprintf_P(_strout, _strlen, PSTR("\ndata age: %d sec"), (millis() - _ts) / 1000); |
||||
|
Serial.print(_strout); |
||||
|
|
||||
|
if (_cmd == 0x95) { |
||||
|
|
||||
|
//check given payload len
|
||||
|
if(_ulen == HM2CH_PAYLOAD_LEN) { |
||||
|
ixn = 2; |
||||
|
LIST_LEN = HM2CH_LIST_LEN; |
||||
|
} else if (_ulen == HM4CH_PAYLOAD_LEN) { |
||||
|
ixn = 4; |
||||
|
LIST_LEN = HM4CH_LIST_LEN; |
||||
|
} else if (_ulen == HM1CH_PAYLOAD_LEN) { |
||||
|
ixn = 0; |
||||
|
LIST_LEN = HM1CH_LIST_LEN; |
||||
|
} else { |
||||
|
Serial.print(F("ERR Wrong PL_LEN")); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
for (_x = 0; _x < LIST_LEN; _x++) { |
||||
|
// read values from given positions in payload
|
||||
|
_bp = hm2chAssignment[_x]; |
||||
|
_val = 0L; |
||||
|
_end = _bp.start + _bp.num; |
||||
|
if (_tmp8 != _bp.ch) { |
||||
|
//snprintf_P(_strout, _strlen, PSTR("\nHM800/%04X%08lX/ch%02d "), _invtype, _plateID, _bp.ch); //must be a small "l" in %08lX for Arduino-Nano
|
||||
|
snprintf_P(_strout, _strlen, PSTR("\n%S/%04X%08lX/ch%02d "), PGM_invname[ixn],_invtype, _plateID, _bp.ch); //must be a small "l" in %08lX for Arduino-Nano, https://forum.arduino.cc/t/f-macro-and-sprintf/894536/10
|
||||
|
Serial.print(_strout); |
||||
|
} |
||||
|
_tmp8 = _bp.ch; |
||||
|
|
||||
|
if (CMD_CALC != _bp.div && _bp.div != 0) { |
||||
|
strncpy_P(_strout, &(PGM_fields[_bp.fieldId][0]), _strlen); |
||||
|
Serial.print(_strout); |
||||
|
Serial.print(F(": ")); |
||||
|
|
||||
|
do { |
||||
|
_val <<= 8; |
||||
|
_val |= _user_payload[_bp.start]; |
||||
|
} while (++_bp.start != _end); |
||||
|
if (_bp.num > 2) { |
||||
|
_fval = (int32_t)_val / (float)_bp.div; //must be int32 to handle 4bit value of yield total
|
||||
|
} else { |
||||
|
_fval = (int16_t)_val / (float)_bp.div; //for negative temparture value it must be int16
|
||||
|
} |
||||
|
|
||||
|
if (_bp.unitId == UNIT_NONE) { |
||||
|
Serial.print(_val); |
||||
|
Serial.print(F(" ")); |
||||
|
continue; |
||||
|
} |
||||
|
Serial.print(_fval, 2); // arduino nano does not sprintf(float values), but print() does
|
||||
|
// Serial.print(units[_bp.unitId]);
|
||||
|
strncpy_P(_strout, &PGM_units[_bp.unitId][0], _strlen); |
||||
|
Serial.print(_strout); |
||||
|
|
||||
|
} else { |
||||
|
// do calculations
|
||||
|
// Serial.print(F("not yet"));
|
||||
|
} |
||||
|
} // end for()
|
||||
|
Serial.println(); |
||||
|
|
||||
|
} else { |
||||
|
Serial.print(F("NO DECODER ")); |
||||
|
Serial.print(_cmd, HEX); |
||||
|
} |
||||
|
}//end decodePayload()
|
||||
|
|
||||
|
#endif /*__HM_DEFINES_H__*/ |
@ -1,271 +0,0 @@ |
|||||
//-----------------------------------------------------------------------------
|
|
||||
// 2022 Ahoy, https://github.com/lumpapu/ahoy
|
|
||||
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
|
|
||||
//-----------------------------------------------------------------------------
|
|
||||
|
|
||||
//2022 mb using PROGMEM for lage char* arrays, used and tested for HM800 decoding (2channel), no calculation yet
|
|
||||
|
|
||||
//todo: make a decoding class out of this
|
|
||||
|
|
||||
|
|
||||
#ifndef __HM_DEFINES_H__ |
|
||||
#define __HM_DEFINES_H__ |
|
||||
|
|
||||
#include "dbg.h" |
|
||||
|
|
||||
|
|
||||
union serial_u { |
|
||||
uint64_t u64; |
|
||||
uint8_t b[8]; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// units
|
|
||||
enum {UNIT_V = 0, UNIT_A, UNIT_W, UNIT_WH, UNIT_KWH, UNIT_HZ, UNIT_C, UNIT_PCT, UNIT_VAR, UNIT_NONE}; |
|
||||
//const char* const units[] = {" V", " A", " W", " Wh", " kWh", " Hz", " °C", " %", " var", ""};
|
|
||||
const char PGM_units[][6] PROGMEM {" V ", " A ", " W ", " Wh ", " kWh ", " Hz ", " °C ", " % ", " var ", " "}; //one hidden byte needed at the end for '\0'
|
|
||||
|
|
||||
// field types
|
|
||||
enum {FLD_UDC = 0, FLD_IDC, FLD_PDC, FLD_YD, FLD_YW, FLD_YT, |
|
||||
FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_T, FLD_PF, FLD_EFF, |
|
||||
FLD_IRR, FLD_Q, FLD_EVT, FLD_FW_VERSION, FLD_FW_BUILD_YEAR, |
|
||||
FLD_FW_BUILD_MONTH_DAY, FLD_FW_BUILD_HOUR_MINUTE, FLD_HW_ID, |
|
||||
FLD_ACT_ACTIVE_PWR_LIMIT, /*FLD_ACT_REACTIVE_PWR_LIMIT, FLD_ACT_PF,*/ FLD_LAST_ALARM_CODE}; |
|
||||
|
|
||||
//PGM Flash memory usage instead of RAM for ARDUINO NANO, idea given by Nick Gammon's great webpage http://gammon.com.au/progmem
|
|
||||
const char PGM_fields[/*NUM ELEMENTS*/][25] PROGMEM { |
|
||||
{"U_DC"}, |
|
||||
{"I_DC"}, |
|
||||
{"P_DC"}, |
|
||||
{"YieldDay"}, |
|
||||
{"YieldWeek"}, |
|
||||
{"YieldTotal"}, |
|
||||
{"U_AC"}, |
|
||||
{"I_AC"}, |
|
||||
{"P_AC"}, |
|
||||
{"F_AC"}, |
|
||||
{"Temp"}, |
|
||||
{"PF_AC"}, |
|
||||
{"Efficiency"}, |
|
||||
{"Irradiation"}, |
|
||||
{"Q_AC"}, |
|
||||
{"ALARM_MES_ID"}, |
|
||||
{"FWVersion"}, |
|
||||
{"FWBuildYear"}, |
|
||||
{"FWBuildMonthDay"}, |
|
||||
{"FWBuildHourMinute"}, |
|
||||
{"HWPartId"}, |
|
||||
{"active PowerLimit"}, |
|
||||
/* {"reactive PowerLimit"},
|
|
||||
{"Powerfactor"}*/ |
|
||||
{"LastAlarmCode"}, |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
// const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
|
|
||||
// "U_AC", "I_AC", "P_AC", "F_AC", "Temp", "PF_AC", "Efficiency", "Irradiation","Q_AC",
|
|
||||
// "ALARM_MES_ID","FWVersion","FWBuildYear","FWBuildMonthDay","FWBuildHourMinute","HWPartId",
|
|
||||
// "active PowerLimit", /*"reactive PowerLimit","Powerfactor",*/ "LastAlarmCode"};
|
|
||||
|
|
||||
const char* const notAvail = "n/a"; |
|
||||
|
|
||||
// mqtt discovery device classes
|
|
||||
enum {DEVICE_CLS_NONE = 0, DEVICE_CLS_CURRENT, DEVICE_CLS_ENERGY, DEVICE_CLS_PWR, DEVICE_CLS_VOLTAGE, DEVICE_CLS_FREQ, DEVICE_CLS_TEMP}; |
|
||||
const char* const deviceClasses[] = {0, "current", "energy", "power", "voltage", "frequency", "temperature"}; |
|
||||
enum {STATE_CLS_NONE = 0, STATE_CLS_MEASUREMENT, STATE_CLS_TOTAL_INCREASING}; |
|
||||
const char* const stateClasses[] = {0, "measurement", "total_increasing"}; |
|
||||
typedef struct { |
|
||||
uint8_t fieldId; // field id
|
|
||||
uint8_t deviceClsId; // device class
|
|
||||
uint8_t stateClsId; // state class
|
|
||||
} byteAssign_fieldDeviceClass; |
|
||||
const byteAssign_fieldDeviceClass deviceFieldAssignment[] = { |
|
||||
{FLD_UDC, DEVICE_CLS_VOLTAGE, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_IDC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_PDC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_YD, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING}, |
|
||||
{FLD_YW, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING}, |
|
||||
{FLD_YT, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING}, |
|
||||
{FLD_UAC, DEVICE_CLS_VOLTAGE, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_IAC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_PAC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_F, DEVICE_CLS_FREQ, STATE_CLS_NONE}, |
|
||||
{FLD_T, DEVICE_CLS_TEMP, STATE_CLS_MEASUREMENT}, |
|
||||
{FLD_PF, DEVICE_CLS_NONE, STATE_CLS_NONE}, |
|
||||
{FLD_EFF, DEVICE_CLS_NONE, STATE_CLS_NONE}, |
|
||||
{FLD_IRR, DEVICE_CLS_NONE, STATE_CLS_NONE} |
|
||||
}; |
|
||||
#define DEVICE_CLS_ASSIGN_LIST_LEN (sizeof(deviceFieldAssignment) / sizeof(byteAssign_fieldDeviceClass)) |
|
||||
|
|
||||
// indices to calculation functions, defined in hmInverter.h
|
|
||||
enum {CALC_YT_CH0 = 0, CALC_YD_CH0, CALC_UDC_CH, CALC_PDC_CH0, CALC_EFF_CH0, CALC_IRR_CH}; |
|
||||
enum {CMD_CALC = 0xffff}; |
|
||||
|
|
||||
|
|
||||
// CH0 is default channel (freq, ac, temp)
|
|
||||
enum {CH0 = 0, CH1, CH2, CH3, CH4}; |
|
||||
|
|
||||
enum {INV_TYPE_1CH = 0, INV_TYPE_2CH, INV_TYPE_4CH}; |
|
||||
|
|
||||
|
|
||||
typedef struct { |
|
||||
uint8_t fieldId; // field id
|
|
||||
uint8_t unitId; // uint id
|
|
||||
uint8_t ch; // channel 0 - 4
|
|
||||
uint8_t start; // pos of first byte in buffer
|
|
||||
uint8_t num; // number of bytes in buffer
|
|
||||
uint16_t div; // divisor / calc command
|
|
||||
} byteAssign_t; |
|
||||
|
|
||||
|
|
||||
/**
|
|
||||
* indices are built for the buffer starting with cmd-id in first byte |
|
||||
* (complete payload in buffer) |
|
||||
* */ |
|
||||
|
|
||||
//-------------------------------------
|
|
||||
// HM-Series
|
|
||||
//-------------------------------------
|
|
||||
const byteAssign_t InfoAssignment[] = { |
|
||||
{ FLD_FW_VERSION, UNIT_NONE, CH0, 0, 2, 1 }, |
|
||||
{ FLD_FW_BUILD_YEAR, UNIT_NONE, CH0, 2, 2, 1 }, |
|
||||
{ FLD_FW_BUILD_MONTH_DAY, UNIT_NONE, CH0, 4, 2, 1 }, |
|
||||
{ FLD_FW_BUILD_HOUR_MINUTE, UNIT_NONE, CH0, 6, 2, 1 }, |
|
||||
{ FLD_HW_ID, UNIT_NONE, CH0, 8, 2, 1 } |
|
||||
}; |
|
||||
#define HMINFO_LIST_LEN (sizeof(InfoAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HMINFO_PAYLOAD_LEN 14 |
|
||||
|
|
||||
const byteAssign_t SystemConfigParaAssignment[] = { |
|
||||
{ FLD_ACT_ACTIVE_PWR_LIMIT, UNIT_PCT, CH0, 2, 2, 10 }/*,
|
|
||||
{ FLD_ACT_REACTIVE_PWR_LIMIT, UNIT_PCT, CH0, 4, 2, 10 }, |
|
||||
{ FLD_ACT_PF, UNIT_NONE, CH0, 6, 2, 1000 }*/ |
|
||||
}; |
|
||||
#define HMSYSTEM_LIST_LEN (sizeof(SystemConfigParaAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HMSYSTEM_PAYLOAD_LEN 14 |
|
||||
|
|
||||
const byteAssign_t AlarmDataAssignment[] = { |
|
||||
{ FLD_LAST_ALARM_CODE, UNIT_NONE, CH0, 0, 2, 1 } |
|
||||
}; |
|
||||
#define HMALARMDATA_LIST_LEN (sizeof(AlarmDataAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HMALARMDATA_PAYLOAD_LEN 0 // 0: means check is off
|
|
||||
|
|
||||
|
|
||||
|
|
||||
//-------------------------------------
|
|
||||
// HM300, HM350, HM400
|
|
||||
//-------------------------------------
|
|
||||
const byteAssign_t hm1chAssignment[] = { |
|
||||
{ FLD_UDC, UNIT_V, CH1, 2, 2, 10 }, |
|
||||
{ FLD_IDC, UNIT_A, CH1, 4, 2, 100 }, |
|
||||
{ FLD_PDC, UNIT_W, CH1, 6, 2, 10 }, |
|
||||
{ FLD_YD, UNIT_WH, CH1, 12, 2, 1 }, |
|
||||
{ FLD_YT, UNIT_KWH, CH1, 8, 4, 1000 }, |
|
||||
{ FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC }, |
|
||||
|
|
||||
{ FLD_UAC, UNIT_V, CH0, 14, 2, 10 }, |
|
||||
{ FLD_IAC, UNIT_A, CH0, 22, 2, 100 }, |
|
||||
{ FLD_PAC, UNIT_W, CH0, 18, 2, 10 }, |
|
||||
{ FLD_Q, UNIT_VAR, CH0, 20, 2, 10 }, |
|
||||
{ FLD_F, UNIT_HZ, CH0, 16, 2, 100 }, |
|
||||
{ FLD_PF, UNIT_NONE, CH0, 24, 2, 1000 }, |
|
||||
{ FLD_T, UNIT_C, CH0, 26, 2, 10 }, |
|
||||
{ FLD_EVT, UNIT_NONE, CH0, 28, 2, 1 }, |
|
||||
{ FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC }, |
|
||||
{ FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC }, |
|
||||
{ FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC }, |
|
||||
{ FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC } |
|
||||
}; |
|
||||
#define HM1CH_LIST_LEN (sizeof(hm1chAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HM1CH_PAYLOAD_LEN 30 |
|
||||
|
|
||||
|
|
||||
//-------------------------------------
|
|
||||
// HM600, HM700, HM800
|
|
||||
//-------------------------------------
|
|
||||
const byteAssign_t hm2chAssignment[] = { |
|
||||
{ FLD_UDC, UNIT_V, CH1, 2, 2, 10 }, |
|
||||
{ FLD_IDC, UNIT_A, CH1, 4, 2, 100 }, |
|
||||
{ FLD_PDC, UNIT_W, CH1, 6, 2, 10 }, |
|
||||
{ FLD_YD, UNIT_WH, CH1, 22, 2, 1 }, |
|
||||
{ FLD_YT, UNIT_KWH, CH1, 14, 4, 1000 }, |
|
||||
{ FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC }, |
|
||||
|
|
||||
{ FLD_UDC, UNIT_V, CH2, 8, 2, 10 }, |
|
||||
{ FLD_IDC, UNIT_A, CH2, 10, 2, 100 }, |
|
||||
{ FLD_PDC, UNIT_W, CH2, 12, 2, 10 }, |
|
||||
{ FLD_YD, UNIT_WH, CH2, 24, 2, 1 }, |
|
||||
{ FLD_YT, UNIT_KWH, CH2, 18, 4, 1000 }, |
|
||||
{ FLD_IRR, UNIT_PCT, CH2, CALC_IRR_CH, CH2, CMD_CALC }, |
|
||||
|
|
||||
{ FLD_UAC, UNIT_V, CH0, 26, 2, 10 }, |
|
||||
{ FLD_IAC, UNIT_A, CH0, 34, 2, 100 }, |
|
||||
{ FLD_PAC, UNIT_W, CH0, 30, 2, 10 }, |
|
||||
{ FLD_Q, UNIT_VAR, CH0, 32, 2, 10 }, |
|
||||
{ FLD_F, UNIT_HZ, CH0, 28, 2, 100 }, |
|
||||
{ FLD_PF, UNIT_NONE, CH0, 36, 2, 1000 }, |
|
||||
{ FLD_T, UNIT_C, CH0, 38, 2, 10 }, |
|
||||
{ FLD_EVT, UNIT_NONE, CH0, 40, 2, 1 }, |
|
||||
{ FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC }, |
|
||||
{ FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC }, |
|
||||
{ FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC }, |
|
||||
{ FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC } |
|
||||
|
|
||||
}; |
|
||||
#define HM2CH_LIST_LEN (sizeof(hm2chAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HM2CH_PAYLOAD_LEN 42 |
|
||||
|
|
||||
|
|
||||
//-------------------------------------
|
|
||||
// HM1200, HM1500
|
|
||||
//-------------------------------------
|
|
||||
const byteAssign_t hm4chAssignment[] = { |
|
||||
{ FLD_UDC, UNIT_V, CH1, 2, 2, 10 }, |
|
||||
{ FLD_IDC, UNIT_A, CH1, 4, 2, 100 }, |
|
||||
{ FLD_PDC, UNIT_W, CH1, 8, 2, 10 }, |
|
||||
{ FLD_YD, UNIT_WH, CH1, 20, 2, 1 }, |
|
||||
{ FLD_YT, UNIT_KWH, CH1, 12, 4, 1000 }, |
|
||||
{ FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC }, |
|
||||
|
|
||||
{ FLD_UDC, UNIT_V, CH2, CALC_UDC_CH, CH1, CMD_CALC }, |
|
||||
{ FLD_IDC, UNIT_A, CH2, 6, 2, 100 }, |
|
||||
{ FLD_PDC, UNIT_W, CH2, 10, 2, 10 }, |
|
||||
{ FLD_YD, UNIT_WH, CH2, 22, 2, 1 }, |
|
||||
{ FLD_YT, UNIT_KWH, CH2, 16, 4, 1000 }, |
|
||||
{ FLD_IRR, UNIT_PCT, CH2, CALC_IRR_CH, CH2, CMD_CALC }, |
|
||||
|
|
||||
{ FLD_UDC, UNIT_V, CH3, 24, 2, 10 }, |
|
||||
{ FLD_IDC, UNIT_A, CH3, 26, 2, 100 }, |
|
||||
{ FLD_PDC, UNIT_W, CH3, 30, 2, 10 }, |
|
||||
{ FLD_YD, UNIT_WH, CH3, 42, 2, 1 }, |
|
||||
{ FLD_YT, UNIT_KWH, CH3, 34, 4, 1000 }, |
|
||||
{ FLD_IRR, UNIT_PCT, CH3, CALC_IRR_CH, CH3, CMD_CALC }, |
|
||||
|
|
||||
{ FLD_UDC, UNIT_V, CH4, CALC_UDC_CH, CH3, CMD_CALC }, |
|
||||
{ FLD_IDC, UNIT_A, CH4, 28, 2, 100 }, |
|
||||
{ FLD_PDC, UNIT_W, CH4, 32, 2, 10 }, |
|
||||
{ FLD_YD, UNIT_WH, CH4, 44, 2, 1 }, |
|
||||
{ FLD_YT, UNIT_KWH, CH4, 38, 4, 1000 }, |
|
||||
{ FLD_IRR, UNIT_PCT, CH4, CALC_IRR_CH, CH4, CMD_CALC }, |
|
||||
|
|
||||
{ FLD_UAC, UNIT_V, CH0, 46, 2, 10 }, |
|
||||
{ FLD_IAC, UNIT_A, CH0, 54, 2, 100 }, |
|
||||
{ FLD_PAC, UNIT_W, CH0, 50, 2, 10 }, |
|
||||
{ FLD_Q, UNIT_VAR, CH0, 52, 2, 10 }, |
|
||||
{ FLD_F, UNIT_HZ, CH0, 48, 2, 100 }, |
|
||||
{ FLD_PF, UNIT_NONE, CH0, 56, 2, 1000 }, |
|
||||
{ FLD_T, UNIT_C, CH0, 58, 2, 10 }, |
|
||||
{ FLD_EVT, UNIT_NONE, CH0, 60, 2, 1 }, |
|
||||
{ FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC }, |
|
||||
{ FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC }, |
|
||||
{ FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC }, |
|
||||
{ FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC } |
|
||||
}; |
|
||||
#define HM4CH_LIST_LEN (sizeof(hm4chAssignment) / sizeof(byteAssign_t)) |
|
||||
#define HM4CH_PAYLOAD_LEN 62 |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
#endif /*__HM_DEFINES_H__*/ |
|
Loading…
Reference in new issue