Merge remote-tracking branch 'tbnobody/OpenDTU/master'
This commit is contained in:
commit
a9336968c7
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
9
include/Utils.h
Normal file
9
include/Utils.h
Normal file
@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class Utils {
|
||||
public:
|
||||
static uint32_t getChipId();
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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 }
|
||||
};
|
||||
};
|
||||
@ -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);
|
||||
}
|
||||
@ -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 }
|
||||
};
|
||||
};
|
||||
@ -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);
|
||||
}
|
||||
@ -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 }
|
||||
};
|
||||
};
|
||||
@ -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();
|
||||
|
||||
@ -1,21 +1,24 @@
|
||||
#include "DevInfoParser.h"
|
||||
#include <cstring>
|
||||
|
||||
#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<uint16_t>(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]
|
||||
|
||||
@ -79,7 +79,17 @@ float StatisticsParser::getChannelFieldValue(uint8_t channel, uint8_t fieldId)
|
||||
val |= _payloadStatistic[ptr];
|
||||
} while (++ptr != end);
|
||||
|
||||
return static_cast<float>(val) / static_cast<float>(div);
|
||||
float result;
|
||||
if (b[pos].isSigned && b[pos].num == 2) {
|
||||
result = static_cast<float>(static_cast<int16_t>(val));
|
||||
} else if (b[pos].isSigned && b[pos].num == 4) {
|
||||
result = static_cast<float>(static_cast<int32_t>(val));
|
||||
} else {
|
||||
result = static_cast<float>(val);
|
||||
}
|
||||
|
||||
result /= static_cast<float>(div);
|
||||
return result;
|
||||
} else {
|
||||
// Value has to be calculated
|
||||
return calcFunctions[b[pos].start].func(this, b[pos].num);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
@ -4,6 +4,7 @@
|
||||
*/
|
||||
#include "NetworkSettings.h"
|
||||
#include "Configuration.h"
|
||||
#include "Utils.h"
|
||||
#include "defaults.h"
|
||||
#include <WiFi.h>
|
||||
#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;
|
||||
11
src/Utils.cpp
Normal file
11
src/Utils.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "Utils.h"
|
||||
#include <Esp.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@ -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();
|
||||
|
||||
@ -228,21 +228,21 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
||||
return;
|
||||
}
|
||||
|
||||
if (root[F("hour")].as<uint>() < 0 || root[F("hour")].as<uint>() > 23) {
|
||||
if (root[F("hour")].as<uint>() > 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<uint>() < 0 || root[F("minute")].as<uint>() > 59) {
|
||||
if (root[F("minute")].as<uint>() > 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<uint>() < 0 || root[F("second")].as<uint>() > 59) {
|
||||
if (root[F("second")].as<uint>() > 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<uint>() - 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");
|
||||
|
||||
@ -13,6 +13,10 @@
|
||||
<a href="https://github.com/tbnobody/OpenDTU/issues" target="_blank">here</a>.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Detected max. Power</td>
|
||||
<td>{{ devInfoList.max_power }} W</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bootloader Version</td>
|
||||
<td>{{ formatVersion(devInfoList.fw_bootloader_version) }}</td>
|
||||
|
||||
@ -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,
|
||||
}
|
||||
5
webapp/src/types/LimitConfig.ts
Normal file
5
webapp/src/types/LimitConfig.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface LimitConfig {
|
||||
serial: number,
|
||||
limit_value: number,
|
||||
limit_type: number
|
||||
}
|
||||
5
webapp/src/types/LimitStatus.ts
Normal file
5
webapp/src/types/LimitStatus.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface LimitStatus {
|
||||
limit_relative: number,
|
||||
max_power: number,
|
||||
limit_set_status: string,
|
||||
}
|
||||
@ -181,12 +181,12 @@
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" id="inputCurrentLimit"
|
||||
aria-describedby="currentLimitType" v-model="currentLimit" disabled />
|
||||
aria-describedby="currentLimitType" v-model="currentLimitRelative" disabled />
|
||||
<span class="input-group-text" id="currentLimitType">%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4" v-if="maxPower > 0">
|
||||
<div class="col-sm-4" v-if="currentLimitList.max_power > 0">
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" id="inputCurrentLimitAbsolute"
|
||||
aria-describedby="currentLimitTypeAbsolute" v-model="currentLimitAbsolute"
|
||||
@ -201,12 +201,12 @@
|
||||
Status:</label>
|
||||
<div class="col-sm-9">
|
||||
<span class="badge" :class="{
|
||||
'bg-danger': successCommandLimit == 'Failure',
|
||||
'bg-warning': successCommandLimit == 'Pending',
|
||||
'bg-success': successCommandLimit == 'Ok',
|
||||
'bg-secondary': successCommandLimit == 'Unknown',
|
||||
'bg-danger': currentLimitList.limit_set_status == 'Failure',
|
||||
'bg-warning': currentLimitList.limit_set_status == 'Pending',
|
||||
'bg-success': currentLimitList.limit_set_status == 'Ok',
|
||||
'bg-secondary': currentLimitList.limit_set_status == 'Unknown',
|
||||
}">
|
||||
{{ successCommandLimit }}
|
||||
{{ currentLimitList.limit_set_status }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -217,7 +217,7 @@
|
||||
<div class="input-group">
|
||||
<input type="number" name="inputTargetLimit" class="form-control"
|
||||
id="inputTargetLimit" :min="targetLimitMin" :max="targetLimitMax"
|
||||
v-model="targetLimit">
|
||||
v-model="targetLimitList.limit_value">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">{{ targetLimitTypeText
|
||||
}}</button>
|
||||
@ -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;
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user