diff --git a/README.md b/README.md index cda5fd7e..e2139069 100644 --- a/README.md +++ b/README.md @@ -231,3 +231,4 @@ A documentation of all available MQTT Topics can be found here: [MQTT Documentat - [Ahoy](https://github.com/grindylow/ahoy) - [DTU Simulator](https://github.com/Ziyatoe/DTUsimMI1x00-Hoymiles) - [OpenDTU extended to talk to Victrons MPPT battery chargers (Ve.Direct)](https://github.com/helgeerbe/OpenDTU_VeDirect) +- [BreakoutBoard - sample printed circuit board for OpenDTU and Ahoy](https://github.com/dokuhn/openDTU-BreakoutBoard) diff --git a/docs/MQTT_Topics.md b/docs/MQTT_Topics.md index c9ba54b8..404e5e57 100644 --- a/docs/MQTT_Topics.md +++ b/docs/MQTT_Topics.md @@ -39,7 +39,7 @@ serial will be replaced with the serial number of the inverter. | [serial]/0/temperature | R | Temperature of inverter in degree celsius | Degree Celsius (°C) | | [serial]/0/voltage | R | AC voltage in volt | Volt (V) | | [serial]/0/yieldday | R | Energy converted to AC per day in watt hours | Watt hours (Wh) | -| [serial]/0/yieldtotal | R | Energy converted to AC since reset watt hours | Watt hours (kWh) | +| [serial]/0/yieldtotal | R | Energy converted to AC since reset watt hours | Kilo watt hours (kWh) | ### DC input channel topics @@ -52,7 +52,7 @@ serial will be replaced with the serial number of the inverter. | [serial]/[1-4]/power | R | DC power of specific input in watt | Watt (W) | | [serial]/[1-4]/voltage | R | DC voltage of specific input in volt | Volt (V) | | [serial]/[1-4]/yieldday | R | Energy converted to AC per day on specific input | Watt hours (Wh) | -| [serial]/[1-4]/yieldtotal | R | Energy converted to AC since reset on specific input | Watt hours (kWh) | +| [serial]/[1-4]/yieldtotal | R | Energy converted to AC since reset on specific input | Kilo watt hours (kWh) | ### Inverter limit specific topics diff --git a/include/NetworkSettings.h b/include/NetworkSettings.h index 7f699bd4..1ad3c6a4 100644 --- a/include/NetworkSettings.h +++ b/include/NetworkSettings.h @@ -62,7 +62,6 @@ private: void setStaticIp(); void setupMode(); void NetworkEvent(WiFiEvent_t event); - static uint32_t getChipId(); bool adminEnabled = true; bool forceDisconnection = false; int adminTimeoutCounter = 0; diff --git a/include/Utils.h b/include/Utils.h new file mode 100644 index 00000000..b4387b8b --- /dev/null +++ b/include/Utils.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + +#include + +class Utils { +public: + static uint32_t getChipId(); +}; diff --git a/lib/Hoymiles/src/commands/CommandAbstract.cpp b/lib/Hoymiles/src/commands/CommandAbstract.cpp index 9ef9ad5d..c425204f 100644 --- a/lib/Hoymiles/src/commands/CommandAbstract.cpp +++ b/lib/Hoymiles/src/commands/CommandAbstract.cpp @@ -38,7 +38,7 @@ void CommandAbstract::setTargetAddress(uint64_t address) convertSerialToPacketId(&_payload[1], address); _targetAddress = address; } -const uint64_t CommandAbstract::getTargetAddress() +uint64_t CommandAbstract::getTargetAddress() { return _targetAddress; } @@ -49,7 +49,7 @@ void CommandAbstract::setRouterAddress(uint64_t address) _routerAddress = address; } -const uint64_t CommandAbstract::getRouterAddress() +uint64_t CommandAbstract::getRouterAddress() { return _routerAddress; } diff --git a/lib/Hoymiles/src/commands/CommandAbstract.h b/lib/Hoymiles/src/commands/CommandAbstract.h index 8dd658c7..c551cfe0 100644 --- a/lib/Hoymiles/src/commands/CommandAbstract.h +++ b/lib/Hoymiles/src/commands/CommandAbstract.h @@ -19,10 +19,10 @@ public: uint8_t getDataSize(); void setTargetAddress(uint64_t address); - const uint64_t getTargetAddress(); + uint64_t getTargetAddress(); void setRouterAddress(uint64_t address); - const uint64_t getRouterAddress(); + uint64_t getRouterAddress(); void setTimeout(uint32_t timeout); uint32_t getTimeout(); diff --git a/lib/Hoymiles/src/inverters/HM_1CH.cpp b/lib/Hoymiles/src/inverters/HM_1CH.cpp index 730a63da..5de99138 100644 --- a/lib/Hoymiles/src/inverters/HM_1CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_1CH.cpp @@ -33,7 +33,7 @@ const byteAssign_t* HM_1CH::getByteAssignment() return byteAssignment; } -const uint8_t HM_1CH::getAssignmentCount() +uint8_t HM_1CH::getAssignmentCount() { return sizeof(byteAssignment) / sizeof(byteAssign_t); } \ No newline at end of file diff --git a/lib/Hoymiles/src/inverters/HM_1CH.h b/lib/Hoymiles/src/inverters/HM_1CH.h index 7c0b7193..6f10745d 100644 --- a/lib/Hoymiles/src/inverters/HM_1CH.h +++ b/lib/Hoymiles/src/inverters/HM_1CH.h @@ -8,28 +8,28 @@ public: static bool isValidSerial(uint64_t serial); String typeName(); const byteAssign_t* getByteAssignment(); - const uint8_t getAssignmentCount(); + uint8_t getAssignmentCount(); private: const byteAssign_t byteAssignment[18] = { - { 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_UDC, UNIT_V, CH1, 2, 2, 10, false }, + { FLD_IDC, UNIT_A, CH1, 4, 2, 100, false }, + { FLD_PDC, UNIT_W, CH1, 6, 2, 10, false }, + { FLD_YD, UNIT_WH, CH1, 12, 2, 1, false }, + { FLD_YT, UNIT_KWH, CH1, 8, 4, 1000, false }, + { FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC, false }, - { 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_PRA, UNIT_VA, CH0, 20, 2, 10 }, - { FLD_F, UNIT_HZ, CH0, 16, 2, 100 }, - { FLD_PCT, UNIT_PCT, CH0, 24, 2, 10 }, - { FLD_T, UNIT_C, CH0, 26, 2, 10 }, - { FLD_EVT_LOG, UNIT_CNT, 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 } + { FLD_UAC, UNIT_V, CH0, 14, 2, 10, false }, + { FLD_IAC, UNIT_A, CH0, 22, 2, 100, false }, + { FLD_PAC, UNIT_W, CH0, 18, 2, 10, false }, + { FLD_PRA, UNIT_VA, CH0, 20, 2, 10, false }, + { FLD_F, UNIT_HZ, CH0, 16, 2, 100, false }, + { FLD_PCT, UNIT_PCT, CH0, 24, 2, 10, false }, + { FLD_T, UNIT_C, CH0, 26, 2, 10, true }, + { FLD_EVT_LOG, UNIT_CNT, CH0, 28, 2, 1, false }, + { FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC, false }, + { FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC, false }, + { FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC, false }, + { FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC, false } }; }; \ No newline at end of file diff --git a/lib/Hoymiles/src/inverters/HM_2CH.cpp b/lib/Hoymiles/src/inverters/HM_2CH.cpp index 6730a259..aea62cb3 100644 --- a/lib/Hoymiles/src/inverters/HM_2CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_2CH.cpp @@ -33,7 +33,7 @@ const byteAssign_t* HM_2CH::getByteAssignment() return byteAssignment; } -const uint8_t HM_2CH::getAssignmentCount() +uint8_t HM_2CH::getAssignmentCount() { return sizeof(byteAssignment) / sizeof(byteAssign_t); } \ No newline at end of file diff --git a/lib/Hoymiles/src/inverters/HM_2CH.h b/lib/Hoymiles/src/inverters/HM_2CH.h index cd7289a5..71a5cfe1 100644 --- a/lib/Hoymiles/src/inverters/HM_2CH.h +++ b/lib/Hoymiles/src/inverters/HM_2CH.h @@ -8,35 +8,35 @@ public: static bool isValidSerial(uint64_t serial); String typeName(); const byteAssign_t* getByteAssignment(); - const uint8_t getAssignmentCount(); + uint8_t getAssignmentCount(); private: const byteAssign_t byteAssignment[24] = { - { 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, CH1, 2, 2, 10, false }, + { FLD_IDC, UNIT_A, CH1, 4, 2, 100, false }, + { FLD_PDC, UNIT_W, CH1, 6, 2, 10, false }, + { FLD_YD, UNIT_WH, CH1, 22, 2, 1, false }, + { FLD_YT, UNIT_KWH, CH1, 14, 4, 1000, false }, + { FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC, false }, - { 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_UDC, UNIT_V, CH2, 8, 2, 10, false }, + { FLD_IDC, UNIT_A, CH2, 10, 2, 100, false }, + { FLD_PDC, UNIT_W, CH2, 12, 2, 10, false }, + { FLD_YD, UNIT_WH, CH2, 24, 2, 1, false }, + { FLD_YT, UNIT_KWH, CH2, 18, 4, 1000, false }, + { FLD_IRR, UNIT_PCT, CH2, CALC_IRR_CH, CH2, CMD_CALC, false }, - { 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_PRA, UNIT_VA, CH0, 32, 2, 10 }, - { FLD_F, UNIT_HZ, CH0, 28, 2, 100 }, - { FLD_PCT, UNIT_PCT, CH0, 36, 2, 10 }, - { FLD_T, UNIT_C, CH0, 38, 2, 10 }, - { FLD_EVT_LOG, UNIT_CNT, 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 } + { FLD_UAC, UNIT_V, CH0, 26, 2, 10, false }, + { FLD_IAC, UNIT_A, CH0, 34, 2, 100, false }, + { FLD_PAC, UNIT_W, CH0, 30, 2, 10, false }, + { FLD_PRA, UNIT_VA, CH0, 32, 2, 10, false }, + { FLD_F, UNIT_HZ, CH0, 28, 2, 100, false }, + { FLD_PCT, UNIT_PCT, CH0, 36, 2, 10, false }, + { FLD_T, UNIT_C, CH0, 38, 2, 10, true }, + { FLD_EVT_LOG, UNIT_CNT, CH0, 40, 2, 1, false }, + { FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC, false }, + { FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC, false }, + { FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC, false }, + { FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC, false } }; }; \ No newline at end of file diff --git a/lib/Hoymiles/src/inverters/HM_4CH.cpp b/lib/Hoymiles/src/inverters/HM_4CH.cpp index af8548f5..908f163c 100644 --- a/lib/Hoymiles/src/inverters/HM_4CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_4CH.cpp @@ -33,7 +33,7 @@ const byteAssign_t* HM_4CH::getByteAssignment() return byteAssignment; } -const uint8_t HM_4CH::getAssignmentCount() +uint8_t HM_4CH::getAssignmentCount() { return sizeof(byteAssignment) / sizeof(byteAssign_t); } \ No newline at end of file diff --git a/lib/Hoymiles/src/inverters/HM_4CH.h b/lib/Hoymiles/src/inverters/HM_4CH.h index 617c839e..d824be64 100644 --- a/lib/Hoymiles/src/inverters/HM_4CH.h +++ b/lib/Hoymiles/src/inverters/HM_4CH.h @@ -8,49 +8,49 @@ public: static bool isValidSerial(uint64_t serial); String typeName(); const byteAssign_t* getByteAssignment(); - const uint8_t getAssignmentCount(); + uint8_t getAssignmentCount(); private: const byteAssign_t byteAssignment[36] = { - { 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, CH1, 2, 2, 10, false }, + { FLD_IDC, UNIT_A, CH1, 4, 2, 100, false }, + { FLD_PDC, UNIT_W, CH1, 8, 2, 10, false }, + { FLD_YD, UNIT_WH, CH1, 20, 2, 1, false }, + { FLD_YT, UNIT_KWH, CH1, 12, 4, 1000, false }, + { FLD_IRR, UNIT_PCT, CH1, CALC_IRR_CH, CH1, CMD_CALC, false }, - { 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, CH2, CALC_UDC_CH, CH1, CMD_CALC, false }, + { FLD_IDC, UNIT_A, CH2, 6, 2, 100, false }, + { FLD_PDC, UNIT_W, CH2, 10, 2, 10, false }, + { FLD_YD, UNIT_WH, CH2, 22, 2, 1, false }, + { FLD_YT, UNIT_KWH, CH2, 16, 4, 1000, false }, + { FLD_IRR, UNIT_PCT, CH2, CALC_IRR_CH, CH2, CMD_CALC, false }, - { 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, CH3, 24, 2, 10, false }, + { FLD_IDC, UNIT_A, CH3, 26, 2, 100, false }, + { FLD_PDC, UNIT_W, CH3, 30, 2, 10, false }, + { FLD_YD, UNIT_WH, CH3, 42, 2, 1, false }, + { FLD_YT, UNIT_KWH, CH3, 34, 4, 1000, false }, + { FLD_IRR, UNIT_PCT, CH3, CALC_IRR_CH, CH3, CMD_CALC, false }, - { 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_UDC, UNIT_V, CH4, CALC_UDC_CH, CH3, CMD_CALC, false }, + { FLD_IDC, UNIT_A, CH4, 28, 2, 100, false }, + { FLD_PDC, UNIT_W, CH4, 32, 2, 10, false }, + { FLD_YD, UNIT_WH, CH4, 44, 2, 1, false }, + { FLD_YT, UNIT_KWH, CH4, 38, 4, 1000, false }, + { FLD_IRR, UNIT_PCT, CH4, CALC_IRR_CH, CH4, CMD_CALC, false }, - { 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_PRA, UNIT_VA, CH0, 52, 2, 10 }, - { FLD_F, UNIT_HZ, CH0, 48, 2, 100 }, - { FLD_PCT, UNIT_PCT, CH0, 56, 2, 10 }, - { FLD_T, UNIT_C, CH0, 58, 2, 10 }, - { FLD_EVT_LOG, UNIT_CNT, 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 } + { FLD_UAC, UNIT_V, CH0, 46, 2, 10, false }, + { FLD_IAC, UNIT_A, CH0, 54, 2, 100, false }, + { FLD_PAC, UNIT_W, CH0, 50, 2, 10, false }, + { FLD_PRA, UNIT_VA, CH0, 52, 2, 10, false }, + { FLD_F, UNIT_HZ, CH0, 48, 2, 100, false }, + { FLD_PCT, UNIT_PCT, CH0, 56, 2, 10, false }, + { FLD_T, UNIT_C, CH0, 58, 2, 10, true }, + { FLD_EVT_LOG, UNIT_CNT, CH0, 60, 2, 1, false }, + { FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC, false }, + { FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC, false }, + { FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC, false }, + { FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC, false } }; }; \ No newline at end of file diff --git a/lib/Hoymiles/src/inverters/InverterAbstract.h b/lib/Hoymiles/src/inverters/InverterAbstract.h index 9e97939b..5d962706 100644 --- a/lib/Hoymiles/src/inverters/InverterAbstract.h +++ b/lib/Hoymiles/src/inverters/InverterAbstract.h @@ -37,7 +37,7 @@ public: const char* name(); virtual String typeName() = 0; virtual const byteAssign_t* getByteAssignment() = 0; - virtual const uint8_t getAssignmentCount() = 0; + virtual uint8_t getAssignmentCount() = 0; bool isProducing(); bool isReachable(); diff --git a/lib/Hoymiles/src/parser/DevInfoParser.cpp b/lib/Hoymiles/src/parser/DevInfoParser.cpp index 864e7c0e..aabb455f 100644 --- a/lib/Hoymiles/src/parser/DevInfoParser.cpp +++ b/lib/Hoymiles/src/parser/DevInfoParser.cpp @@ -1,21 +1,24 @@ #include "DevInfoParser.h" #include +#define ALL 0xff + typedef struct { - uint8_t hwPart[3]; + uint8_t hwPart[4]; uint16_t maxPower; const char* modelName; } devInfo_t; const devInfo_t devInfo[] = { - { { 0x10, 0x10, 0x10 }, 300, "HM-300" }, - { { 0x10, 0x10, 0x20 }, 350, "HM-350" }, - { { 0x10, 0x10, 0x40 }, 400, "HM-400" }, - { { 0x10, 0x11, 0x10 }, 600, "HM-600" }, - { { 0x10, 0x11, 0x20 }, 700, "HM-700" }, - { { 0x10, 0x11, 0x40 }, 800, "HM-800" }, - { { 0x10, 0x12, 0x10 }, 1200, "HM-1200" }, - { { 0x10, 0x12, 0x30 }, 1500, "HM-1500" } + { { 0x10, 0x10, 0x10, ALL }, 300, "HM-300" }, + { { 0x10, 0x10, 0x20, ALL }, 350, "HM-350" }, + { { 0x10, 0x10, 0x40, ALL }, 400, "HM-400" }, + { { 0x10, 0x11, 0x10, ALL }, 600, "HM-600" }, + { { 0x10, 0x11, 0x20, ALL }, 700, "HM-700" }, + { { 0x10, 0x11, 0x40, ALL }, 800, "HM-800" }, + { { 0x10, 0x12, 0x10, ALL }, 1200, "HM-1200" }, + { { 0x10, 0x12, 0x30, ALL }, 1500, "HM-1500" }, + { { 0x10, 0x10, 0x10, 0x15 }, static_cast(300 * 0.7), "HM-300" }, // HM-300 factory limitted to 70% }; void DevInfoParser::clearBufferAll() @@ -79,7 +82,7 @@ uint16_t DevInfoParser::getFwBuildVersion() time_t DevInfoParser::getFwBuildDateTime() { - struct tm timeinfo = { 0 }; + struct tm timeinfo = { }; timeinfo.tm_year = ((((uint16_t)_payloadDevInfoAll[2]) << 8) | _payloadDevInfoAll[3]) - 1900; timeinfo.tm_mon = ((((uint16_t)_payloadDevInfoAll[4]) << 8) | _payloadDevInfoAll[5]) / 100 - 1; @@ -135,6 +138,17 @@ String DevInfoParser::getHwModelName() uint8_t DevInfoParser::getDevIdx() { uint8_t pos; + // Check for all 4 bytes first + for (pos = 0; pos < sizeof(devInfo) / sizeof(devInfo_t); pos++) { + if (devInfo[pos].hwPart[0] == _payloadDevInfoSimple[2] + && devInfo[pos].hwPart[1] == _payloadDevInfoSimple[3] + && devInfo[pos].hwPart[2] == _payloadDevInfoSimple[4] + && devInfo[pos].hwPart[3] == _payloadDevInfoSimple[5]) { + return pos; + } + } + + // Then only for 3 bytes for (pos = 0; pos < sizeof(devInfo) / sizeof(devInfo_t); pos++) { if (devInfo[pos].hwPart[0] == _payloadDevInfoSimple[2] && devInfo[pos].hwPart[1] == _payloadDevInfoSimple[3] diff --git a/lib/Hoymiles/src/parser/StatisticsParser.cpp b/lib/Hoymiles/src/parser/StatisticsParser.cpp index 1789eeeb..a6abc83a 100644 --- a/lib/Hoymiles/src/parser/StatisticsParser.cpp +++ b/lib/Hoymiles/src/parser/StatisticsParser.cpp @@ -79,7 +79,17 @@ float StatisticsParser::getChannelFieldValue(uint8_t channel, uint8_t fieldId) val |= _payloadStatistic[ptr]; } while (++ptr != end); - return static_cast(val) / static_cast(div); + float result; + if (b[pos].isSigned && b[pos].num == 2) { + result = static_cast(static_cast(val)); + } else if (b[pos].isSigned && b[pos].num == 4) { + result = static_cast(static_cast(val)); + } else { + result = static_cast(val); + } + + result /= static_cast(div); + return result; } else { // Value has to be calculated return calcFunctions[b[pos].start].func(this, b[pos].num); diff --git a/lib/Hoymiles/src/parser/StatisticsParser.h b/lib/Hoymiles/src/parser/StatisticsParser.h index d21f82e6..b3327fe1 100644 --- a/lib/Hoymiles/src/parser/StatisticsParser.h +++ b/lib/Hoymiles/src/parser/StatisticsParser.h @@ -68,6 +68,7 @@ typedef struct { uint8_t start; // pos of first byte in buffer uint8_t num; // number of bytes in buffer uint16_t div; // divisor / calc command + bool isSigned; // allow negative numbers } byteAssign_t; class StatisticsParser : public Parser { diff --git a/platformio.ini b/platformio.ini index ad7a7943..b91ddf79 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,7 +18,7 @@ platform = espressif32@>=5.2.0 build_flags = -D=${PIOENV} -DCOMPONENT_EMBED_FILES=webapp_dist/index.html.gz:webapp_dist/zones.json.gz:webapp_dist/favicon.ico:webapp_dist/js/app.js.gz - -Wall + -Wall -Wextra -Werror lib_deps = https://github.com/yubox-node-org/ESPAsyncWebServer @@ -35,16 +35,17 @@ monitor_filters = esp32_exception_decoder, time, log2file, colorize monitor_speed = 115200 upload_protocol = esptool - -[env:generic] -board = esp32dev +; Specify port here. Comment out (add ; in front of line) to use auto detection. monitor_port = COM4 upload_port = COM4 +[env:generic] +board = esp32dev + + [env:olimex_esp32_poe] ; https://www.olimex.com/Products/IoT/ESP32/ESP32-POE/open-source-hardware - board = esp32-poe build_flags = ${env.build_flags} -DHOYMILES_PIN_MISO=15 @@ -55,13 +56,9 @@ build_flags = ${env.build_flags} -DHOYMILES_PIN_CS=5 -DOPENDTU_ETHERNET -monitor_port = COM3 -upload_port = COM3 - [env:olimex_esp32_evb] ; https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB/open-source-hardware - board = esp32-evb build_flags = ${env.build_flags} -DHOYMILES_PIN_MISO=15 @@ -72,9 +69,6 @@ build_flags = ${env.build_flags} -DHOYMILES_PIN_CS=17 -DOPENDTU_ETHERNET -monitor_port = /dev/tty.usbserial-1450 -upload_port = /dev/tty.usbserial-1450 - [env:d1 mini esp32] board = wemos_d1_mini32 @@ -90,3 +84,15 @@ build_flags = -DVICTRON_PIN_RX=22 monitor_port = /dev/cu.usbserial-01E68DD0 upload_port = /dev/cu.usbserial-01E68DD0 + +[env:wt32_eth01] +; http://www.wireless-tag.com/portfolio/wt32-eth01/ +board = wt32-eth01 +build_flags = ${env.build_flags} + -DHOYMILES_PIN_MISO=4 + -DHOYMILES_PIN_MOSI=2 + -DHOYMILES_PIN_SCLK=32 + -DHOYMILES_PIN_IRQ=33 + -DHOYMILES_PIN_CE=14 + -DHOYMILES_PIN_CS=15 + -DOPENDTU_ETHERNET \ No newline at end of file diff --git a/src/NetworkSettings.cpp b/src/NetworkSettings.cpp index f4df6da3..732f3ec2 100644 --- a/src/NetworkSettings.cpp +++ b/src/NetworkSettings.cpp @@ -4,6 +4,7 @@ */ #include "NetworkSettings.h" #include "Configuration.h" +#include "Utils.h" #include "defaults.h" #include #ifdef OPENDTU_ETHERNET @@ -142,7 +143,7 @@ void NetworkSettingsClass::enableAdminMode() String NetworkSettingsClass::getApName() { - return String(ACCESS_POINT_NAME + String(getChipId())); + return String(ACCESS_POINT_NAME + String(Utils::getChipId())); } void NetworkSettingsClass::loop() @@ -385,7 +386,7 @@ String NetworkSettingsClass::getHostname() char resultHostname[WIFI_MAX_HOSTNAME_STRLEN + 1]; uint8_t pos = 0; - uint32_t chipId = getChipId(); + uint32_t chipId = Utils::getChipId(); snprintf(preparedHostname, WIFI_MAX_HOSTNAME_STRLEN + 1, config.WiFi_Hostname, chipId); const char* pC = preparedHostname; @@ -431,13 +432,4 @@ network_mode NetworkSettingsClass::NetworkMode() return _networkMode; } -uint32_t NetworkSettingsClass::getChipId() -{ - uint32_t chipId = 0; - for (int i = 0; i < 17; i += 8) { - chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; - } - return chipId; -} - NetworkSettingsClass NetworkSettings; \ No newline at end of file diff --git a/src/Utils.cpp b/src/Utils.cpp new file mode 100644 index 00000000..17769b7a --- /dev/null +++ b/src/Utils.cpp @@ -0,0 +1,11 @@ +#include "Utils.h" +#include + +uint32_t Utils::getChipId() +{ + uint32_t chipId = 0; + for (int i = 0; i < 17; i += 8) { + chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; + } + return chipId; +} \ No newline at end of file diff --git a/src/WebApi_devinfo.cpp b/src/WebApi_devinfo.cpp index 7795f567..318b2f02 100644 --- a/src/WebApi_devinfo.cpp +++ b/src/WebApi_devinfo.cpp @@ -42,6 +42,7 @@ void WebApiDevInfoClass::onDevInfoStatus(AsyncWebServerRequest* request) devInfoObj[F("hw_part_number")] = inv->DevInfo()->getHwPartNumber(); devInfoObj[F("hw_version")] = inv->DevInfo()->getHwVersion(); devInfoObj[F("hw_model_name")] = inv->DevInfo()->getHwModelName(); + devInfoObj[F("max_power")] = inv->DevInfo()->getMaxPower(); char timebuffer[32]; const time_t t = inv->DevInfo()->getFwBuildDateTime(); diff --git a/src/WebApi_ntp.cpp b/src/WebApi_ntp.cpp index 47726d69..5f2bae71 100644 --- a/src/WebApi_ntp.cpp +++ b/src/WebApi_ntp.cpp @@ -228,21 +228,21 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request) return; } - if (root[F("hour")].as() < 0 || root[F("hour")].as() > 23) { + if (root[F("hour")].as() > 23) { retMsg[F("message")] = F("Hour must be a number between 0 and 23!"); response->setLength(); request->send(response); return; } - if (root[F("minute")].as() < 0 || root[F("minute")].as() > 59) { + if (root[F("minute")].as() > 59) { retMsg[F("message")] = F("Minute must be a number between 0 and 59!"); response->setLength(); request->send(response); return; } - if (root[F("second")].as() < 0 || root[F("second")].as() > 59) { + if (root[F("second")].as() > 59) { retMsg[F("message")] = F("Second must be a number between 0 and 59!"); response->setLength(); request->send(response); @@ -258,7 +258,7 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request) local.tm_year = root[F("year")].as() - 1900; // years since 1900 time_t t = mktime(&local); - struct timeval now = { .tv_sec = t }; + struct timeval now = { .tv_sec = t, .tv_usec = 0 }; settimeofday(&now, NULL); retMsg[F("type")] = F("success"); diff --git a/webapp/src/components/DevInfo.vue b/webapp/src/components/DevInfo.vue index b7175a0e..b9216831 100644 --- a/webapp/src/components/DevInfo.vue +++ b/webapp/src/components/DevInfo.vue @@ -13,6 +13,10 @@ here. + + Detected max. Power + {{ devInfoList.max_power }} W + Bootloader Version {{ formatVersion(devInfoList.fw_bootloader_version) }} diff --git a/webapp/src/types/DevInfoStatus.ts b/webapp/src/types/DevInfoStatus.ts index 76f4cff2..e42b5fb7 100644 --- a/webapp/src/types/DevInfoStatus.ts +++ b/webapp/src/types/DevInfoStatus.ts @@ -5,5 +5,6 @@ export interface DevInfoStatus { fw_build_datetime: Date, hw_part_number: number, hw_version: number, - hw_model_name: string + hw_model_name: string, + max_power: number, } \ No newline at end of file diff --git a/webapp/src/types/LimitConfig.ts b/webapp/src/types/LimitConfig.ts new file mode 100644 index 00000000..4d811be3 --- /dev/null +++ b/webapp/src/types/LimitConfig.ts @@ -0,0 +1,5 @@ +export interface LimitConfig { + serial: number, + limit_value: number, + limit_type: number +} \ No newline at end of file diff --git a/webapp/src/types/LimitStatus.ts b/webapp/src/types/LimitStatus.ts new file mode 100644 index 00000000..48d797ee --- /dev/null +++ b/webapp/src/types/LimitStatus.ts @@ -0,0 +1,5 @@ +export interface LimitStatus { + limit_relative: number, + max_power: number, + limit_set_status: string, +} \ No newline at end of file diff --git a/webapp/src/views/HomeView.vue b/webapp/src/views/HomeView.vue index 43eb74b3..c4c7bd78 100644 --- a/webapp/src/views/HomeView.vue +++ b/webapp/src/views/HomeView.vue @@ -181,12 +181,12 @@
+ aria-describedby="currentLimitType" v-model="currentLimitRelative" disabled /> %
-
+
- {{ successCommandLimit }} + {{ currentLimitList.limit_set_status }}
@@ -217,7 +217,7 @@
+ v-model="targetLimitList.limit_value"> @@ -334,6 +334,8 @@ import VedirectView from '@/views/VedirectView.vue'; import type { DevInfoStatus } from '@/types/DevInfoStatus'; import type { EventlogItems } from '@/types/EventlogStatus'; import type { Inverters } from '@/types/LiveDataStatus'; +import type { LimitStatus } from '@/types/LimitStatus'; +import type { LimitConfig } from '@/types/LimitConfig'; export default defineComponent({ components: { @@ -370,14 +372,11 @@ export default defineComponent({ devInfoLoading: true, limitSettingView: {} as bootstrap.Modal, - limitSettingSerial: 0, limitSettingLoading: true, - currentLimit: 0, - currentLimitAbsolute: 0, - successCommandLimit: "", - maxPower: 0, - targetLimit: 0, + currentLimitList: {} as LimitStatus, + targetLimitList: {} as LimitConfig, + targetLimitMin: 10, targetLimitMax: 100, targetLimitTypeText: "Relative (%)", @@ -428,6 +427,17 @@ export default defineComponent({ } } }, + computed: { + currentLimitAbsolute(): number { + if (this.currentLimitList.max_power > 0) { + return Number((this.currentLimitList.limit_relative * this.currentLimitList.max_power / 100).toFixed(1)); + } + return 0; + }, + currentLimitRelative(): number { + return Number((this.currentLimitList.limit_relative).toFixed(1)); + } + }, methods: { getInitialData() { this.dataLoading = true; @@ -518,24 +528,20 @@ export default defineComponent({ this.devInfoView.show(); }, onHideLimitSettings() { - this.limitSettingSerial = 0; - this.targetLimit = 0; - this.targetLimitType = 1; - this.targetLimitTypeText = "Relative (%)"; this.showAlertLimit = false; }, onShowLimitSettings(serial: number) { + this.targetLimitList.serial = 0; + this.targetLimitList.limit_value = 0; + this.targetLimitType = 1; + this.targetLimitTypeText = "Relative (%)"; + this.limitSettingLoading = true; fetch("/api/limit/status") .then((response) => response.json()) .then((data) => { - this.maxPower = data[serial].max_power; - this.currentLimit = Number((data[serial].limit_relative).toFixed(1)); - if (this.maxPower > 0) { - this.currentLimitAbsolute = Number((this.currentLimit * this.maxPower / 100).toFixed(1)); - } - this.successCommandLimit = data[serial].limit_set_status; - this.limitSettingSerial = serial; + this.currentLimitList = data[serial]; + this.targetLimitList.serial = serial; this.limitSettingLoading = false; }); @@ -544,15 +550,11 @@ export default defineComponent({ onSubmitLimit(e: Event) { e.preventDefault(); - const data = { - serial: this.limitSettingSerial, - limit_value: this.targetLimit, - limit_type: (this.targetLimitPersistent ? 256 : 0) + this.targetLimitType, - }; + this.targetLimitList.limit_type = (this.targetLimitPersistent ? 256 : 0) + this.targetLimitType const formData = new FormData(); - formData.append("data", JSON.stringify(data)); + formData.append("data", JSON.stringify(this.targetLimitList)); - console.log(data); + console.log(this.targetLimitList); fetch("/api/limit/config", { method: "POST", @@ -588,7 +590,7 @@ export default defineComponent({ } else { this.targetLimitTypeText = "Absolute (W)"; this.targetLimitMin = 10; - this.targetLimitMax = 1500; + this.targetLimitMax = (this.currentLimitList.max_power > 0 ? this.currentLimitList.max_power : 1500); } this.targetLimitType = type; },