Browse Source

* added HM1200 decoder

* added HTML to visualize the read values
pull/8/head
lumapu 3 years ago
parent
commit
8bfbd8d45b
  1. 47
      tools/esp8266/app.cpp
  2. 4
      tools/esp8266/app.h
  3. 2
      tools/esp8266/defines.h
  4. 87
      tools/esp8266/hm1200Decode.h
  5. 2
      tools/esp8266/hoymiles.h
  6. 1
      tools/esp8266/html/conv.bat
  7. 1
      tools/esp8266/html/h/hoymiles_html.h
  8. 2
      tools/esp8266/html/h/index_html.h
  9. 2
      tools/esp8266/html/h/style_css.h
  10. 42
      tools/esp8266/html/hoymiles.html
  11. 1
      tools/esp8266/html/index.html
  12. 57
      tools/esp8266/html/style.css

47
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 += "<div class=\"module\"><span class=\"header\">CHANNEL " + String(i) + "</span>";
for(uint8_t j = 0; j < 5; j++) {
modHtml += "<span class=\"value\">";
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 += "<span class=\"unit\">" + unit[j] + "</span></span>";
modHtml += "<span class=\"info\">" + info[j] + "</span>";
}
modHtml += "</div>";
}
mWeb->send(200, "text/html", modHtml);
}
//-----------------------------------------------------------------------------
void app::saveValues(bool webSend = true) {
Main::saveValues(false); // general configuration

4
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<NRF24_packet_t> *mBufCtrl;
NRF24_packet_t mBuffer[PACKET_BUFFER_SIZE];

2
tools/esp8266/defines.h

@ -15,7 +15,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 1
#define VERSION_PATCH 9
#define VERSION_PATCH 10
//-------------------------------------

87
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__*/

2
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)

1
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

1
tools/esp8266/html/h/hoymiles_html.h

@ -0,0 +1 @@
String hoymiles_html = "<!doctype html><html><head><title>Index - {DEVICE}</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><script type=\"text/javascript\"> getAjax('/livedata', 'livedata') window.setInterval(\"getAjax('/livedata', 'livedata')\", 10000); function getAjax(url, resid) { var http = null; http = new XMLHttpRequest(); if(http != null) { http.open(\"GET\", url, true); http.onreadystatechange = print; http.send(null); } function print() { if(http.readyState == 4) { document.getElementById(resid).innerHTML = http.responseText; } } } </script><style type=\"text/css\"></style></head><body><h1>AHOY - {DEVICE}</h1><div id=\"content\" class=\"content\"><p><a href=\"/\">Home</a><br/></p><div id=\"livedata\"></div><p>Every 10 seconds the values are updated</p></div><div id=\"footer\"><p class=\"left\">&copy 2022</p><p class=\"right\">AHOY :: {VERSION}</p></div></body></html>";

2
tools/esp8266/html/h/index_html.h

@ -1 +1 @@
String index_html = "<!doctype html><html><head><title>Index - {DEVICE}</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><script type=\"text/javascript\"> window.setInterval(\"getAjax('/uptime', 'uptime')\", 1000); window.setInterval(\"getAjax('/time', 'time')\", 1000); window.setInterval(\"getAjax('/cmdstat', 'cmds')\", 2000); function getAjax(url, resid) { var http = null; http = new XMLHttpRequest(); if(http != null) { http.open(\"GET\", url, true); http.onreadystatechange = print; http.send(null); } function print() { if(http.readyState == 4) { document.getElementById(resid).innerHTML = http.responseText; } } } </script></head><body><h1>AHOY - {DEVICE}</h1><div id=\"content\" class=\"content\"><p><a href=\"/update\">Update</a><br/><br/><a href=\"/setup\">Setup</a><br/><a href=\"/reboot\">Reboot</a></p><p><span class=\"des\">Uptime: </span><span id=\"uptime\"></span></p><p><span class=\"des\">Time: </span><span id=\"time\"></span></p><p><span class=\"des\">Statistics: </span><pre id=\"cmds\"></pre></p></div><div id=\"footer\"><p class=\"left\">&copy 2022</p><p class=\"right\">AHOY :: {VERSION}</p></div></body></html>";
String index_html = "<!doctype html><html><head><title>Index - {DEVICE}</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><script type=\"text/javascript\"> window.setInterval(\"getAjax('/uptime', 'uptime')\", 1000); window.setInterval(\"getAjax('/time', 'time')\", 1000); window.setInterval(\"getAjax('/cmdstat', 'cmds')\", 2000); function getAjax(url, resid) { var http = null; http = new XMLHttpRequest(); if(http != null) { http.open(\"GET\", url, true); http.onreadystatechange = print; http.send(null); } function print() { if(http.readyState == 4) { document.getElementById(resid).innerHTML = http.responseText; } } } </script></head><body><h1>AHOY - {DEVICE}</h1><div id=\"content\" class=\"content\"><p><a href=\"/hoymiles\">Hoymiles</a><br/><a href=\"/update\">Update</a><br/><br/><a href=\"/setup\">Setup</a><br/><a href=\"/reboot\">Reboot</a></p><p><span class=\"des\">Uptime: </span><span id=\"uptime\"></span></p><p><span class=\"des\">Time: </span><span id=\"time\"></span></p><p><span class=\"des\">Statistics: </span><pre id=\"cmds\"></pre></p></div><div id=\"footer\"><p class=\"left\">&copy 2022</p><p class=\"right\">AHOY :: {VERSION}</p></div></body></html>";

2
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; } ";

42
tools/esp8266/html/hoymiles.html

@ -0,0 +1,42 @@
<!doctype html>
<html>
<head>
<title>Index - {DEVICE}</title>
<link rel="stylesheet" type="text/css" href="style.css"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript">
getAjax('/livedata', 'livedata')
window.setInterval("getAjax('/livedata', 'livedata')", 10000);
function getAjax(url, resid) {
var http = null;
http = new XMLHttpRequest();
if(http != null) {
http.open("GET", url, true);
http.onreadystatechange = print;
http.send(null);
}
function print() {
if(http.readyState == 4) {
document.getElementById(resid).innerHTML = http.responseText;
}
}
}
</script>
<style type="text/css">
</style>
</head>
<body>
<h1>AHOY - {DEVICE}</h1>
<div id="content" class="content">
<p><a href="/">Home</a><br/></p>
<div id="livedata"></div>
<p>Every 10 seconds the values are updated</p>
</div>
<div id="footer">
<p class="left">&copy 2022</p>
<p class="right">AHOY :: {VERSION}</p>
</div>
</body>
</html>

1
tools/esp8266/html/index.html

@ -30,6 +30,7 @@
<h1>AHOY - {DEVICE}</h1>
<div id="content" class="content">
<p>
<a href="/hoymiles">Hoymiles</a><br/>
<a href="/update">Update</a><br/>
<br/>
<a href="/setup">Setup</a><br/>

57
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;
}

Loading…
Cancel
Save