mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-11 16:06:38 +02:00
Merge branch 'main' into dev
# Conflicts: # tools/esp8266/app.cpp # tools/esp8266/hmInverters.h # tools/esp8266/hmSystem.h * added missing files
This commit is contained in:
commit
5844795447
11 changed files with 913 additions and 565 deletions
|
@ -5,7 +5,8 @@ Various tools, examples, and documentation for communicating with Hoymiles micro
|
||||||
|
|
||||||
In particular:
|
In particular:
|
||||||
|
|
||||||
* `doc\hoymiles-format-description.txt` is a detailed description of the communications format and the history of this project
|
* `doc/hoymiles-format-description.txt` is a detailed description of the communications format and the history of this project
|
||||||
* The `tools` folder contains various software tools for RaspberryPi and Arduino
|
* `doc/getting-started-ESP8266.md` shows the hardware setup for an ESP8266-based system
|
||||||
|
* The `tools` folder contains various software tools for RaspberryPi, Arduino and ESP8266/ESP32
|
||||||
|
|
||||||
Contributors are always welcome!
|
Contributors are always welcome!
|
||||||
|
|
BIN
doc/AhoyMiles.fzz
Normal file
BIN
doc/AhoyMiles.fzz
Normal file
Binary file not shown.
BIN
doc/AhoyMiles_bb.png
Normal file
BIN
doc/AhoyMiles_bb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
doc/AhoyMiles_schem.png
Normal file
BIN
doc/AhoyMiles_schem.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
doc/HM-400 data.xlsx
Executable file
BIN
doc/HM-400 data.xlsx
Executable file
Binary file not shown.
45
doc/getting-started-ESP8266.md
Normal file
45
doc/getting-started-ESP8266.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# Getting Started with an ESP8266
|
||||||
|
|
||||||
|
Wire Connections
|
||||||
|
|
||||||
|
```ditaa
|
||||||
|
+-----------+ +-----------+
|
||||||
|
| ESP8266 |--colour--| nRF24L01+ |
|
||||||
|
| | | |
|
||||||
|
| GND |---black--|[GND] |
|
||||||
|
| +3.3V |----red---| VCC |
|
||||||
|
| D4 |---grey---| CE |
|
||||||
|
| D8 |--purple--| CSN |
|
||||||
|
| D5 |---blue---| SCK |
|
||||||
|
| D7 |---green--| MOSI |
|
||||||
|
| D6 |---brown--| MISO |
|
||||||
|
| D3 |--yellow--| IRQ |
|
||||||
|
+-----------+ +-----------+
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Fritzing diagrams & schematics
|
||||||
|
* [AhoyMiles_bb.png](./AhoyMiles_bb.png)
|
||||||
|
* [AhoyMiles_schem.png](./AhoyMiles_schem.png)
|
||||||
|
* [AhoyMiles.fzz](./AhoyMiles.fzz)
|
||||||
|
|
||||||
|
Libraries to be installed in Arduino IDE:
|
||||||
|
* RF24
|
||||||
|
* TimeLib
|
||||||
|
|
||||||
|
Verify & Compile
|
||||||
|
* Connect to WiFi Network `ESP AHOY`
|
||||||
|
* Use password `esp_8266`
|
||||||
|
* Connect to Network settings
|
||||||
|
|
||||||
|
Setup
|
||||||
|
* WiFi
|
||||||
|
* Enter SSID `mynetwork`
|
||||||
|
* Enter Password `mypassword`
|
||||||
|
* Device Host Name
|
||||||
|
* Enter Device Name `esp-ahoy`
|
||||||
|
* General
|
||||||
|
* Hoymiles Address (e.g. 114173123456) `11:41:73:12:34:56`
|
||||||
|
* [x] Reboot device after successful save
|
||||||
|
Save
|
File diff suppressed because it is too large
Load diff
|
@ -482,7 +482,7 @@ void app::saveValues(bool webSend = true) {
|
||||||
// type
|
// type
|
||||||
mWeb->arg("inv" + String(i) + "Type").toCharArray(buf, 20);
|
mWeb->arg("inv" + String(i) + "Type").toCharArray(buf, 20);
|
||||||
uint8_t type = atoi(buf);
|
uint8_t type = atoi(buf);
|
||||||
mEep->write(ADDR_INV_TYPE + (i * MAX_NAME_LENGTH), type);
|
mEep->write(ADDR_INV_TYPE + i, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
interval = mWeb->arg("invInterval").toInt();
|
interval = mWeb->arg("invInterval").toInt();
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
#define VERSION_MAJOR 0
|
#define VERSION_MAJOR 0
|
||||||
#define VERSION_MINOR 2
|
#define VERSION_MINOR 2
|
||||||
#define VERSION_PATCH 10
|
#define VERSION_PATCH 11
|
||||||
|
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
@ -39,7 +39,7 @@ typedef struct {
|
||||||
// EEPROM
|
// EEPROM
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
#define SSID_LEN 32
|
#define SSID_LEN 32
|
||||||
#define PWD_LEN 32
|
#define PWD_LEN 63
|
||||||
#define DEVNAME_LEN 16
|
#define DEVNAME_LEN 16
|
||||||
#define CRC_LEN 2 // uint16_t
|
#define CRC_LEN 2 // uint16_t
|
||||||
|
|
||||||
|
|
122
tools/esp8266/hmDefines.h
Normal file
122
tools/esp8266/hmDefines.h
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#ifndef __HM_DEFINES_H__
|
||||||
|
#define __HM_DEFINES_H__
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// units
|
||||||
|
enum {UNIT_V = 0, UNIT_A, UNIT_W, UNIT_WH, UNIT_KWH, UNIT_HZ, UNIT_C, UNIT_PCT};
|
||||||
|
const char* const units[] = {"V", "A", "W", "Wh", "kWh", "Hz", "°C", "%"};
|
||||||
|
|
||||||
|
// 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_PCT};
|
||||||
|
const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
|
||||||
|
"U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct"};
|
||||||
|
|
||||||
|
|
||||||
|
union serial_u {
|
||||||
|
uint64_t u64;
|
||||||
|
uint8_t b[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// CH0 is default channel (freq, ac, temp)
|
||||||
|
enum {CH0 = 0, CH1, CH2, CH3, CH4};
|
||||||
|
// received command ids, special command CMDFF for calculations
|
||||||
|
enum {CMD01 = 0x01, CMD02, CMD03, CMD82 = 0x82, CMD83, CMD84, CMDFF=0xff};
|
||||||
|
|
||||||
|
enum {INV_TYPE_HM600 = 0, INV_TYPE_HM1200, INV_TYPE_HM400};
|
||||||
|
const char* const invTypes[] = {"HM600", "HM1200 / HM1500", "HM400"};
|
||||||
|
#define NUM_INVERTER_TYPES 3
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t fieldId; // field id
|
||||||
|
uint8_t unitId; // uint id
|
||||||
|
uint8_t ch; // channel 0 - 3
|
||||||
|
uint8_t cmdId; // received command id
|
||||||
|
uint8_t start; // pos of first byte in buffer
|
||||||
|
uint8_t num; // number of bytes in buffer
|
||||||
|
uint16_t div; // divisor
|
||||||
|
} byteAssign_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* indices are built for the buffer starting with cmd-id in first byte
|
||||||
|
* (complete payload in buffer)
|
||||||
|
* */
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
// HM400 HM350?, HM300?
|
||||||
|
//-------------------------------------
|
||||||
|
const byteAssign_t hm400assignment[] = {
|
||||||
|
{ FLD_UDC, UNIT_V, CH1, CMD01, 3, 2, 10 },
|
||||||
|
{ FLD_IDC, UNIT_A, CH1, CMD01, 5, 2, 100 },
|
||||||
|
{ FLD_PDC, UNIT_W, CH1, CMD01, 7, 2, 10 },
|
||||||
|
{ FLD_YT, UNIT_KWH, CH1, CMD01, 9, 4, 1000 },
|
||||||
|
{ FLD_YD, UNIT_WH, CH1, CMD01, 13, 2, 1000 },
|
||||||
|
{ FLD_UAC, UNIT_V, CH0, CMD01, 15, 2, 10 },
|
||||||
|
{ FLD_F, UNIT_HZ, CH0, CMD82, 1, 2, 100 },
|
||||||
|
{ FLD_PAC, UNIT_W, CH0, CMD82, 3, 2, 10 },
|
||||||
|
{ FLD_IAC, UNIT_A, CH0, CMD82, 7, 2, 100 },
|
||||||
|
{ FLD_T, UNIT_C, CH0, CMD82, 11, 2, 10 }
|
||||||
|
};
|
||||||
|
#define HM400_LIST_LEN (sizeof(hm400assignment) / sizeof(byteAssign_t))
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
// HM600, HM700
|
||||||
|
//-------------------------------------
|
||||||
|
const byteAssign_t hm600assignment[] = {
|
||||||
|
{ FLD_UDC, UNIT_V, CH1, CMD01, 3, 2, 10 },
|
||||||
|
{ FLD_IDC, UNIT_A, CH1, CMD01, 5, 2, 100 },
|
||||||
|
{ FLD_PDC, UNIT_W, CH1, CMD01, 7, 2, 10 },
|
||||||
|
{ FLD_UDC, UNIT_V, CH2, CMD01, 9, 2, 10 },
|
||||||
|
{ FLD_IDC, UNIT_A, CH2, CMD01, 11, 2, 100 },
|
||||||
|
{ FLD_PDC, UNIT_W, CH2, CMD01, 13, 2, 10 },
|
||||||
|
{ FLD_YW, UNIT_WH, CH0, CMD02, 1, 2, 1 },
|
||||||
|
{ FLD_YT, UNIT_KWH, CH0, CMD02, 3, 4, 1000 },
|
||||||
|
{ FLD_YD, UNIT_WH, CH1, CMD02, 7, 2, 1 },
|
||||||
|
{ FLD_YD, UNIT_WH, CH2, CMD02, 9, 2, 1 },
|
||||||
|
{ FLD_UAC, UNIT_V, CH0, CMD02, 11, 2, 10 },
|
||||||
|
{ FLD_F, UNIT_HZ, CH0, CMD02, 13, 2, 100 },
|
||||||
|
{ FLD_IAC, UNIT_A, CH0, CMD02, 15, 2, 10 },
|
||||||
|
{ FLD_T, UNIT_C, CH0, CMD83, 7, 2, 10 }
|
||||||
|
};
|
||||||
|
#define HM600_LIST_LEN (sizeof(hm600assignment) / sizeof(byteAssign_t))
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
// HM1200, HM1500
|
||||||
|
//-------------------------------------
|
||||||
|
const byteAssign_t hm1200assignment[] = {
|
||||||
|
{ FLD_UDC, UNIT_V, CH1, CMD01, 3, 2, 10 },
|
||||||
|
{ FLD_IDC, UNIT_A, CH1, CMD01, 5, 2, 100 },
|
||||||
|
{ FLD_PDC, UNIT_W, CH1, CMD01, 9, 2, 10 },
|
||||||
|
{ FLD_YD, UNIT_WH, CH1, CMD02, 5, 2, 1 },
|
||||||
|
{ FLD_YT, UNIT_KWH, CH1, CMD01, 13, 4, 1000 },
|
||||||
|
{ FLD_UDC, UNIT_V, CH2, CMD02, 9, 2, 10 },
|
||||||
|
{ FLD_IDC, UNIT_A, CH2, CMD01, 7, 2, 100 },
|
||||||
|
{ FLD_PDC, UNIT_W, CH2, CMD01, 11, 2, 10 },
|
||||||
|
{ FLD_YD, UNIT_WH, CH2, CMD02, 7, 2, 1 },
|
||||||
|
{ FLD_YT, UNIT_KWH, CH2, CMD02, 1, 4, 1000 },
|
||||||
|
{ FLD_IDC, UNIT_A, CH3, CMD02, 11, 2, 100 },
|
||||||
|
{ FLD_PDC, UNIT_W, CH3, CMD02, 15, 2, 10 },
|
||||||
|
{ FLD_YD, UNIT_WH, CH3, CMD03, 11, 2, 1 },
|
||||||
|
{ FLD_YT, UNIT_KWH, CH3, CMD03, 3, 4, 1000 },
|
||||||
|
{ FLD_IDC, UNIT_A, CH4, CMD02, 13, 2, 100 },
|
||||||
|
{ FLD_PDC, UNIT_W, CH4, CMD03, 1, 2, 10 },
|
||||||
|
{ FLD_YD, UNIT_WH, CH4, CMD03, 13, 2, 1 },
|
||||||
|
{ FLD_YT, UNIT_KWH, CH4, CMD03, 7, 4, 1000 },
|
||||||
|
{ FLD_UAC, UNIT_V, CH0, CMD03, 15, 2, 10 },
|
||||||
|
{ FLD_IAC, UNIT_A, CH0, CMD84, 7, 2, 100 },
|
||||||
|
{ FLD_PAC, UNIT_W, CH0, CMD84, 3, 2, 10 },
|
||||||
|
{ FLD_F, UNIT_HZ, CH0, CMD84, 1, 2, 100 },
|
||||||
|
{ FLD_PCT, UNIT_PCT, CH0, CMD84, 9, 2, 10 },
|
||||||
|
{ FLD_T, UNIT_C, CH0, CMD84, 11, 2, 10 }
|
||||||
|
};
|
||||||
|
#define HM1200_LIST_LEN (sizeof(hm1200assignment) / sizeof(byteAssign_t))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*__HM_DEFINES_H__*/
|
143
tools/esp8266/hmInverter.h
Normal file
143
tools/esp8266/hmInverter.h
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
#ifndef __HM_INVERTER_H__
|
||||||
|
#define __HM_INVERTER_H__
|
||||||
|
|
||||||
|
#include "hmDefines.h"
|
||||||
|
|
||||||
|
template <class RECORDTYPE=float>
|
||||||
|
class Inverter {
|
||||||
|
public:
|
||||||
|
uint8_t id; // unique id
|
||||||
|
char name[MAX_NAME_LENGTH]; // human readable name, eg. "HM-600.1"
|
||||||
|
uint8_t type; // integer which refers to inverter type
|
||||||
|
byteAssign_t* assign; // type of inverter
|
||||||
|
uint8_t listLen; // length of assignments
|
||||||
|
serial_u serial; // serial number as on barcode
|
||||||
|
serial_u radioId; // id converted to modbus
|
||||||
|
uint8_t channels; // number of PV channels (1-4)
|
||||||
|
RECORDTYPE *record; // pointer for values
|
||||||
|
|
||||||
|
Inverter() {
|
||||||
|
getAssignment();
|
||||||
|
toRadioId();
|
||||||
|
record = new RECORDTYPE[listLen];
|
||||||
|
memset(record, 0, sizeof(RECORDTYPE) * listLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Inverter() {
|
||||||
|
// TODO: cleanup
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getPosByChFld(uint8_t channel, uint8_t fieldId) {
|
||||||
|
uint8_t pos = 0;
|
||||||
|
for(; pos < listLen; pos++) {
|
||||||
|
if((assign[pos].ch == channel) && (assign[pos].fieldId == fieldId))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (pos >= listLen) ? 0xff : pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getFieldName(uint8_t pos) {
|
||||||
|
return fields[assign[pos].fieldId];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getUnit(uint8_t pos) {
|
||||||
|
return units[assign[pos].unitId];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getChannel(uint8_t pos) {
|
||||||
|
return assign[pos].ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getCmdId(uint8_t pos) {
|
||||||
|
return assign[pos].cmdId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addValue(uint8_t pos, uint8_t buf[]) {
|
||||||
|
uint8_t ptr = assign[pos].start;
|
||||||
|
uint8_t end = ptr + assign[pos].num;
|
||||||
|
uint16_t div = assign[pos].div;
|
||||||
|
|
||||||
|
uint32_t val = 0;
|
||||||
|
do {
|
||||||
|
val <<= 8;
|
||||||
|
val |= buf[ptr];
|
||||||
|
} while(++ptr != end);
|
||||||
|
|
||||||
|
record[pos] = (RECORDTYPE)(val) / (RECORDTYPE)(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
RECORDTYPE getValue(uint8_t pos) {
|
||||||
|
return record[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void toRadioId(void) {
|
||||||
|
radioId.u64 = 0ULL;
|
||||||
|
radioId.b[4] = serial.b[0];
|
||||||
|
radioId.b[3] = serial.b[1];
|
||||||
|
radioId.b[2] = serial.b[2];
|
||||||
|
radioId.b[1] = serial.b[3];
|
||||||
|
radioId.b[0] = 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getAssignment(void) {
|
||||||
|
if(INV_TYPE_HM600 == type) {
|
||||||
|
listLen = (uint8_t)(HM600_LIST_LEN);
|
||||||
|
assign = (byteAssign_t*)hm600assignment;
|
||||||
|
channels = 2;
|
||||||
|
}
|
||||||
|
else if(INV_TYPE_HM1200 == type) {
|
||||||
|
listLen = (uint8_t)(HM1200_LIST_LEN);
|
||||||
|
assign = (byteAssign_t*)hm1200assignment;
|
||||||
|
channels = 4;
|
||||||
|
}
|
||||||
|
else if(INV_TYPE_HM400 == type) {
|
||||||
|
listLen = (uint8_t)(HM400_LIST_LEN);
|
||||||
|
assign = (byteAssign_t*)hm400assignment;
|
||||||
|
channels = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
listLen = 0;
|
||||||
|
channels = 0;
|
||||||
|
assign = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To calculate values which are not transmitted by the unit there is a generic
|
||||||
|
* list of functions which can be linked to the assignment.
|
||||||
|
* The special command 0xff (CMDFF) must be used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef float (*func_t)(Inverter<> *);
|
||||||
|
typedef struct {
|
||||||
|
uint8_t funcId; // unique id
|
||||||
|
func_t func; // function pointer
|
||||||
|
} calcFunc_t;
|
||||||
|
|
||||||
|
static float calcYieldTotalCh0(Inverter<> *iv) {
|
||||||
|
if(NULL != iv) {
|
||||||
|
float yield[iv->channels];
|
||||||
|
for(uint8_t i = 1; i <= iv->channels; i++) {
|
||||||
|
uint8_t pos = iv->getPosByChFld(i, FLD_YT);
|
||||||
|
//yield[i-1] = iv->getValue(iv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float calcYieldDayCh0(Inverter<> *iv) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {CALC_YT_CH0 = 0, CALC_YD_CH0};
|
||||||
|
|
||||||
|
const calcFunc_t calcFunctions[] = {
|
||||||
|
{ CALC_YT_CH0, &calcYieldTotalCh0 },
|
||||||
|
{ CALC_YD_CH0, &calcYieldDayCh0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*__HM_INVERTER_H__*/
|
Loading…
Add table
Add a link
Reference in a new issue