From 8bfbd8d45b7e1460cc9334f05d9b114d52f69c27 Mon Sep 17 00:00:00 2001 From: lumapu Date: Wed, 20 Apr 2022 20:51:18 +0200 Subject: [PATCH] * added HM1200 decoder * added HTML to visualize the read values --- tools/esp8266/app.cpp | 47 ++++++++++++++- tools/esp8266/app.h | 4 ++ tools/esp8266/defines.h | 2 +- tools/esp8266/hm1200Decode.h | 87 ++++++++++++++++++++++++++++ tools/esp8266/hoymiles.h | 2 +- tools/esp8266/html/conv.bat | 1 + tools/esp8266/html/h/hoymiles_html.h | 1 + tools/esp8266/html/h/index_html.h | 2 +- tools/esp8266/html/h/style_css.h | 2 +- tools/esp8266/html/hoymiles.html | 42 ++++++++++++++ tools/esp8266/html/index.html | 1 + tools/esp8266/html/style.css | 57 ++++++++++++++---- 12 files changed, 232 insertions(+), 16 deletions(-) create mode 100644 tools/esp8266/hm1200Decode.h create mode 100644 tools/esp8266/html/h/hoymiles_html.h create mode 100644 tools/esp8266/html/hoymiles.html diff --git a/tools/esp8266/app.cpp b/tools/esp8266/app.cpp index 0b6db0a0..b080df25 100644 --- a/tools/esp8266/app.cpp +++ b/tools/esp8266/app.cpp @@ -1,11 +1,13 @@ #include "app.h" #include "html/h/index_html.h" +#include "html/h/hoymiles_html.h" extern String setup_html; //----------------------------------------------------------------------------- app::app() : Main() { mHoymiles = new hoymiles(); + mDecoder = new hm1200Decode(); mBufCtrl = new CircularBuffer(mBuffer, PACKET_BUFFER_SIZE); @@ -32,6 +34,8 @@ void app::setup(const char *ssid, const char *pwd, uint32_t timeout) { mWeb->on("/setup", std::bind(&app::showSetup, this)); mWeb->on("/save", std::bind(&app::showSave, this)); mWeb->on("/cmdstat", std::bind(&app::showCmdStatistics, this)); + mWeb->on("/hoymiles", std::bind(&app::showHoymiles, this)); + mWeb->on("/livedata", std::bind(&app::showLiveData, this)); if(mSettingsValid) mEep->read(ADDR_HOY_ADDR, mHoymiles->mAddrBytes, HOY_ADDR_LEN); @@ -56,14 +60,16 @@ void app::loop(void) { uint8_t len, rptCnt; NRF24_packet_t *p = mBufCtrl->getBack(); - //mHoymiles->dumpBuf("RAW ", p->packet, PACKET_BUFFER_SIZE); + //mHoymiles->dumpBuf("RAW ", p->packet, MAX_RF_PAYLOAD_SIZE); if(mHoymiles->checkCrc(p->packet, &len, &rptCnt)) { // process buffer only on first occurrence if((0 != len) && (0 == rptCnt)) { Serial.println("CMD " + String(p->packet[11], HEX)); mHoymiles->dumpBuf("Payload ", p->packet, len); - // @TODO: do analysis here + + mDecoder->convert(&p->packet[11], len); + if(p->packet[11] == 0x01) mCmds[0]++; else if(p->packet[11] == 0x02) mCmds[1]++; else if(p->packet[11] == 0x03) mCmds[2]++; @@ -268,6 +274,43 @@ void app::showCmdStatistics(void) { } +//----------------------------------------------------------------------------- +void app::showHoymiles(void) { + String html = hoymiles_html; + html.replace("{DEVICE}", mDeviceName); + html.replace("{VERSION}", mVersion); + mWeb->send(200, "text/html", html); +} + + +//----------------------------------------------------------------------------- +void app::showLiveData(void) { + String modHtml = ""; + + String unit[5] = {"V", "A", "W", "Wh", "kWh"}; + String info[5] = {"VOLTAGE", "CURRENT", "POWER", "YIELD DAY", "YIELD"}; + + for(uint8_t i = 0; i < 4; i++) { + modHtml += "
CHANNEL " + String(i) + ""; + for(uint8_t j = 0; j < 5; j++) { + modHtml += ""; + switch(j) { + default: modHtml += String(mDecoder->mData.ch_dc[i/2].u); break; + case 1: modHtml += String(mDecoder->mData.ch_dc[i].i); break; + case 2: modHtml += String(mDecoder->mData.ch_dc[i].p); break; + case 3: modHtml += String(mDecoder->mData.ch_dc[i].y_d); break; + case 4: modHtml += String(mDecoder->mData.ch_dc[i].y_t); break; + } + modHtml += "" + unit[j] + ""; + modHtml += "" + info[j] + ""; + } + modHtml += "
"; + } + + mWeb->send(200, "text/html", modHtml); +} + + //----------------------------------------------------------------------------- void app::saveValues(bool webSend = true) { Main::saveValues(false); // general configuration diff --git a/tools/esp8266/app.h b/tools/esp8266/app.h index 01883643..6c4d3157 100644 --- a/tools/esp8266/app.h +++ b/tools/esp8266/app.h @@ -9,6 +9,7 @@ #include "CircularBuffer.h" #include "hoymiles.h" +#include "hm1200Decode.h" class app : public Main { @@ -30,6 +31,8 @@ class app : public Main { void showSetup(void); void showSave(void); void showCmdStatistics(void); + void showHoymiles(void); + void showLiveData(void); void saveValues(bool webSend); void dumpBuf(uint8_t buf[], uint8_t len); @@ -39,6 +42,7 @@ class app : public Main { RF24 *mRadio; hoymiles *mHoymiles; + hm1200Decode *mDecoder; CircularBuffer *mBufCtrl; NRF24_packet_t mBuffer[PACKET_BUFFER_SIZE]; diff --git a/tools/esp8266/defines.h b/tools/esp8266/defines.h index 4939da5b..9dda73d5 100644 --- a/tools/esp8266/defines.h +++ b/tools/esp8266/defines.h @@ -15,7 +15,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 1 -#define VERSION_PATCH 9 +#define VERSION_PATCH 10 //------------------------------------- diff --git a/tools/esp8266/hm1200Decode.h b/tools/esp8266/hm1200Decode.h new file mode 100644 index 00000000..95b9aebc --- /dev/null +++ b/tools/esp8266/hm1200Decode.h @@ -0,0 +1,87 @@ +#ifndef __HM1200_DECODE__ +#define __HM1200_DECODE__ + +typedef struct { + double u; + double i; + double p; + uint16_t y_d; // yield day + double y_t; // yield total +} ch_t; + +typedef struct { + ch_t ch_dc[4]; + ch_t ch_ac; + double temp; + double pct; + double acFreq; +} hoyData_t; + +class hm1200Decode { + public: + hm1200Decode() { + memset(&mData, 0, sizeof(hoyData_t)); + } + ~hm1200Decode() {} + + void convert(uint8_t buf[], uint8_t len) { + switch(buf[0]) { + case 0x01: convCmd01(buf, len); break; + case 0x02: convCmd02(buf, len); break; + case 0x03: convCmd03(buf, len); break; + case 0x84: convCmd84(buf, len); break; + default: break; + } + } + + + hoyData_t mData; + + private: + void convCmd01(uint8_t buf[], uint8_t len) { + mData.ch_dc[0].u = ((buf[ 3] << 8) | buf[ 4]) / 10.0f; + mData.ch_dc[0].i = ((buf[ 5] << 8) | buf[ 6]) / 100.0f; + mData.ch_dc[1].i = ((buf[ 7] << 8) | buf[ 8]) / 100.0f; + mData.ch_dc[0].p = ((buf[ 9] << 8) | buf[10]) / 10.0f; + mData.ch_dc[1].p = ((buf[11] << 8) | buf[12]) / 10.0f; + mData.ch_dc[0].y_t = ((buf[13] << 24) | (buf[14] << 16) + | (buf[15] << 8) | buf[16]) / 1000.0f; + } + + + void convCmd02(uint8_t buf[], uint8_t len) { + mData.ch_dc[1].y_t = ((buf[ 1] << 24) | (buf[ 2] << 16) + | (buf[ 3] << 8) | buf[ 4]) / 1000.0f; + mData.ch_dc[0].y_d = ((buf[ 5] << 8) | buf[ 6]); + mData.ch_dc[1].y_d = ((buf[ 7] << 8) | buf[ 8]); + mData.ch_dc[1].u = ((buf[ 9] << 8) | buf[10]) / 10.0f; + mData.ch_dc[2].i = ((buf[11] << 8) | buf[12]) / 100.0f; + mData.ch_dc[3].i = ((buf[13] << 8) | buf[14]) / 100.0f; + mData.ch_dc[2].p = ((buf[15] << 8) | buf[16]) / 10.0f; + } + + + void convCmd03(uint8_t buf[], uint8_t len) { + mData.ch_dc[3].p = ((buf[ 1] << 8) | buf[ 2]) / 10.0f; + mData.ch_dc[2].y_t = ((buf[ 3] << 24) | (buf[4] << 16) + | (buf[ 5] << 8) | buf[ 6]) / 1000.0f; + mData.ch_dc[3].y_t = ((buf[ 7] << 24) | (buf[8] << 16) + | (buf[ 9] << 8) | buf[10]) / 1000.0f; + mData.ch_dc[2].y_d = ((buf[11] << 8) | buf[12]); + mData.ch_dc[3].y_d = ((buf[13] << 8) | buf[14]); + mData.ch_ac.u = ((buf[15] << 8) | buf[16]) / 10.0f; + } + + + void convCmd84(uint8_t buf[], uint8_t len) { + mData.acFreq = ((buf[ 1] << 8) | buf[ 2]) / 100.0f; + mData.ch_ac.p = ((buf[ 3] << 8) | buf[ 4]) / 10.0f; + mData.ch_ac.i = ((buf[ 7] << 8) | buf[ 8]) / 100.0f; + mData.pct = ((buf[ 9] << 8) | buf[10]) / 10.0f; + mData.temp = ((buf[11] << 8) | buf[12]) / 10.0f; + } + + // private member variables +}; + +#endif /*__HM1200_DECODE__*/ diff --git a/tools/esp8266/hoymiles.h b/tools/esp8266/hoymiles.h index 66b80c3c..883c2d1f 100644 --- a/tools/esp8266/hoymiles.h +++ b/tools/esp8266/hoymiles.h @@ -10,7 +10,7 @@ #define luint64_t long long unsigned int #define DEFAULT_RECV_CHANNEL 3 -#define MAX_RF_PAYLOAD_SIZE 32 +#define MAX_RF_PAYLOAD_SIZE 64 #define DTU_RADIO_ID ((uint64_t)0x1234567801ULL) #define DUMMY_RADIO_ID ((uint64_t)0xDEADBEEF01ULL) diff --git a/tools/esp8266/html/conv.bat b/tools/esp8266/html/conv.bat index 46587400..0627beeb 100644 --- a/tools/esp8266/html/conv.bat +++ b/tools/esp8266/html/conv.bat @@ -1,4 +1,5 @@ ..\tools\fileConv.exe index.html h\index_html.h index_html ..\tools\fileConv.exe setup.html h\setup_html.h setup_html +..\tools\fileConv.exe hoymiles.html h\hoymiles_html.h hoymiles_html ..\tools\fileConv.exe style.css h\style_css.h style_css pause diff --git a/tools/esp8266/html/h/hoymiles_html.h b/tools/esp8266/html/h/hoymiles_html.h new file mode 100644 index 00000000..8a9f1888 --- /dev/null +++ b/tools/esp8266/html/h/hoymiles_html.h @@ -0,0 +1 @@ +String hoymiles_html = "Index - {DEVICE}

AHOY - {DEVICE}

Home

Every 10 seconds the values are updated

© 2022

AHOY :: {VERSION}

"; diff --git a/tools/esp8266/html/h/index_html.h b/tools/esp8266/html/h/index_html.h index 8c2921e0..8a4b2c10 100644 --- a/tools/esp8266/html/h/index_html.h +++ b/tools/esp8266/html/h/index_html.h @@ -1 +1 @@ -String index_html = "Index - {DEVICE}

AHOY - {DEVICE}

Update

Setup
Reboot

Uptime:

Time:

Statistics:

© 2022

AHOY :: {VERSION}

"; +String index_html = "Index - {DEVICE}

AHOY - {DEVICE}

Hoymiles
Update

Setup
Reboot

Uptime:

Time:

Statistics:

© 2022

AHOY :: {VERSION}

"; diff --git a/tools/esp8266/html/h/style_css.h b/tools/esp8266/html/h/style_css.h index be1e0761..d885938b 100644 --- a/tools/esp8266/html/h/style_css.h +++ b/tools/esp8266/html/h/style_css.h @@ -1 +1 @@ -String style_css = "h1 { margin: 0; padding: 20pt; font-size: 22pt; color: #fff; background-color: #006ec0; display: block; text-transform: uppercase; } html, body { font-family: Arial; margin: 0; padding: 0; } p { text-align: justify; font-size: 13pt; } .des { font-size: 14pt; color: #006ec0; padding-bottom: 0px !important; } .fw { width: 60px; display: block; float: left; } .color { width: 50px; height: 50px; border: 1px solid #ccc; } .range { width: 300px; } a:link, a:visited { text-decoration: none; font-size: 13pt; color: #006ec0; } a:hover, a:focus { color: #f00; } #content { padding: 15px 15px 60px 15px; } #footer { position: fixed; bottom: 0px; height: 45px; background-color: #006ec0; width: 100%; } #footer p { color: #fff; padding-left: 20px; padding-right: 20px; font-size: 10pt !important; } #footer a { color: #fff; } #footer a:hover { color: #f00; } div.content { background-color: #fff; padding-bottom: 65px; overflow: hidden; } span.warn { display: inline-block; padding-left: 20px; color: #ff9900; font-style: italic; } input { padding: 10px; font-size: 13pt; } input.button { background-color: #006ec0; color: #fff; border: 0px; float: right; text-transform: uppercase; } input.cb { margin-bottom: 20px; } label { font-size: 14pt; } .left { float: left; } .right { float: right; } .inputWrp { position: relative; } .inputWrp .inputText { height: 35px; width: 90%; margin-bottom: 20px; border: 1px solid #ccc; border-top: none; border-right: none; } .inputWrp .floating_label { position: absolute; pointer-events: none; top: 20px; left: 10px; transition: 0.2s ease all; } .inputWrp input:focus ~ .floating_label, .inputWrp input:not(:focus):valid ~ .floating_label { top: 0px; left: 20px; font-size: 10px; color: blue; opacity: 1; } "; +String style_css = "h1 { margin: 0; padding: 20pt; font-size: 22pt; color: #fff; background-color: #006ec0; display: block; text-transform: uppercase; } html, body { font-family: Arial; margin: 0; padding: 0; } p { text-align: justify; font-size: 13pt; } .des { font-size: 14pt; color: #006ec0; padding-bottom: 0px !important; } .fw { width: 60px; display: block; float: left; } .color { width: 50px; height: 50px; border: 1px solid #ccc; } .range { width: 300px; } a:link, a:visited { text-decoration: none; font-size: 13pt; color: #006ec0; } a:hover, a:focus { color: #f00; } #content { padding: 15px 15px 60px 15px; } #footer { position: fixed; bottom: 0px; height: 45px; background-color: #006ec0; width: 100%; } #footer p { color: #fff; padding-left: 20px; padding-right: 20px; font-size: 10pt !important; } #footer a { color: #fff; } #footer a:hover { color: #f00; } div.content { background-color: #fff; padding-bottom: 65px; overflow: hidden; } span.warn { display: inline-block; padding-left: 20px; color: #ff9900; font-style: italic; } input { padding: 10px; font-size: 13pt; } input.button { background-color: #006ec0; color: #fff; border: 0px; float: right; text-transform: uppercase; } input.cb { margin-bottom: 20px; } label { font-size: 14pt; } .left { float: left; } .right { float: right; } .inputWrp { position: relative; } .inputWrp .inputText { height: 35px; width: 90%; margin-bottom: 20px; border: 1px solid #ccc; border-top: none; border-right: none; } .inputWrp .floating_label { position: absolute; pointer-events: none; top: 20px; left: 10px; transition: 0.2s ease all; } .inputWrp input:focus ~ .floating_label, .inputWrp input:not(:focus):valid ~ .floating_label { top: 0px; left: 20px; font-size: 10px; color: blue; opacity: 1; } div.module { display: block; width: 250px; height: 410px; background-color: #006ec0; display: inline-block; position: relative; margin-right: 20px; margin-bottom: 20px; } div.module .value, div.module .info, div.module .header { color: #fff; display: block; width: 100%; text-align: center; } div.module .unit { font-size: 19px; margin-left: 10px; } div.module .value { margin-top: 20px; font-size: 30px; } div.module .info { margin-top: 3px; font-size: 10px; } div.module .header { background-color: #003c80; padding: 10px 0 10px 0; } "; diff --git a/tools/esp8266/html/hoymiles.html b/tools/esp8266/html/hoymiles.html new file mode 100644 index 00000000..e7985edd --- /dev/null +++ b/tools/esp8266/html/hoymiles.html @@ -0,0 +1,42 @@ + + + + Index - {DEVICE} + + + + + + +

AHOY - {DEVICE}

+
+

Home

+
+

Every 10 seconds the values are updated

+
+ + + diff --git a/tools/esp8266/html/index.html b/tools/esp8266/html/index.html index 2cd0b1b6..b3efc003 100644 --- a/tools/esp8266/html/index.html +++ b/tools/esp8266/html/index.html @@ -30,6 +30,7 @@

AHOY - {DEVICE}

+ Hoymiles
Update

Setup
diff --git a/tools/esp8266/html/style.css b/tools/esp8266/html/style.css index 7d6daaa7..cfa6dad0 100644 --- a/tools/esp8266/html/style.css +++ b/tools/esp8266/html/style.css @@ -134,18 +134,55 @@ label { } .inputWrp .floating_label { - position: absolute; - pointer-events: none; - top: 20px; - left: 10px; - transition: 0.2s ease all; + position: absolute; + pointer-events: none; + top: 20px; + left: 10px; + transition: 0.2s ease all; } .inputWrp input:focus ~ .floating_label, .inputWrp input:not(:focus):valid ~ .floating_label { - top: 0px; - left: 20px; - font-size: 10px; - color: blue; - opacity: 1; + top: 0px; + left: 20px; + font-size: 10px; + color: blue; + opacity: 1; +} + +div.module { + display: block; + width: 250px; + height: 410px; + background-color: #006ec0; + display: inline-block; + position: relative; + margin-right: 20px; + margin-bottom: 20px; +} +div.module .value, div.module .info, div.module .header { + color: #fff; + display: block; + width: 100%; + text-align: center; +} + +div.module .unit { + font-size: 19px; + margin-left: 10px; +} + +div.module .value { + margin-top: 20px; + font-size: 30px; +} + +div.module .info { + margin-top: 3px; + font-size: 10px; +} + +div.module .header { + background-color: #003c80; + padding: 10px 0 10px 0; }