mirror of https://github.com/lumapu/ahoy.git
1 changed files with 380 additions and 0 deletions
@ -0,0 +1,380 @@ |
|||||
|
//
|
||||
|
// 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: |
||||
|
}; |
Loading…
Reference in new issue