mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-10 07:26:38 +02:00
* added HM1200 decoder
* added HTML to visualize the read values
This commit is contained in:
parent
a7add69719
commit
8bfbd8d45b
12 changed files with 232 additions and 16 deletions
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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
Normal file
87
tools/esp8266/hm1200Decode.h
Normal file
|
@ -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__*/
|
|
@ -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,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
Normal file
1
tools/esp8266/html/h/hoymiles_html.h
Normal file
|
@ -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\">© 2022</p><p class=\"right\">AHOY :: {VERSION}</p></div></body></html>";
|
|
@ -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\">© 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\">© 2022</p><p class=\"right\">AHOY :: {VERSION}</p></div></body></html>";
|
||||
|
|
|
@ -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
Normal file
42
tools/esp8266/html/hoymiles.html
Normal file
|
@ -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">© 2022</p>
|
||||
<p class="right">AHOY :: {VERSION}</p>
|
||||
</div>
|
||||
</body>
|
||||
</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/>
|
||||
|
|
|
@ -149,3 +149,40 @@ label {
|
|||
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…
Add table
Add a link
Reference in a new issue