Browse Source

* pinout can be saved using the web setup

* html / css files are now located inside PROGMEM
* conversion of files is done with python script (OS independent, open source)
* moved as much as possible for now to the hm* modules - the app should only be the body
* successfully tested with HM1200
pull/8/head
lumapu 3 years ago
parent
commit
d195eee498
  1. 25
      tools/esp8266/README.md
  2. 136
      tools/esp8266/app.cpp
  3. 24
      tools/esp8266/app.h
  4. 13
      tools/esp8266/defines.h
  5. 4
      tools/esp8266/eep.h
  6. 7
      tools/esp8266/esp8266.ino
  7. 179
      tools/esp8266/hmRadio.h
  8. 8
      tools/esp8266/hmSystem.h
  9. 5
      tools/esp8266/html/conv.bat
  10. 28
      tools/esp8266/html/convert.py
  11. 5
      tools/esp8266/html/h/hoymiles_html.h
  12. 5
      tools/esp8266/html/h/index_html.h
  13. 5
      tools/esp8266/html/h/setup_html.h
  14. 5
      tools/esp8266/html/h/style_css.h
  15. 4
      tools/esp8266/main.cpp
  16. BIN
      tools/esp8266/tools/fileConv.exe

25
tools/esp8266/README.md

@ -1,12 +1,9 @@
## OVERVIEW
This code was tested on a ESP8266 - ESP-07 module. Many parts of the code are based on 'Hubi's code, which can be found here: <https://www.mikrocontroller.net/topic/525778?page=3#7033371>
This code is intended to run on a Wemos D1mini or similar. The code is based on 'Hubi's code, which can be found here: <https://www.mikrocontroller.net/topic/525778?page=3#7033371>
The NRF24L01+ radio module is connected to the standard SPI pins. Additional there are 3 pins, which can be set individual:
- IRQ - Pin 4
- CE - Pin 5
- CS - Pin 15
The NRF24L01+ radio module is connected to the standard SPI pins. Additional there are 3 pins, which can be set individual: CS, CE and IRQ
These pins can be changed from the /setup URL
## Compile
@ -29,14 +26,22 @@ This code can be compiled using Arduino. The settings were:
## Usage
Connect the ESP to power and to your serial console. The webinterface is currently only used for OTA and config.
The serial console will print all information which is send and received.
Connect the ESP to power and to your serial console. The webinterface has the following abilities:
- OTA Update (over the air update)
- Configuration (Wifi, inverter(s), Pinout, MQTT)
- visual display of the connected inverters / modules
- some statistics about communication (debug)
The serial console will print the converted values which were read out of the inverter(s)
## Known Issues
## Compatiblity
- only command 0x81 is received
For now the following inverters should work out of the box:
- HM600
- HM1200
## USED LIBRARIES

136
tools/esp8266/app.cpp

@ -1,15 +1,12 @@
#include "app.h"
#include "html/h/index_html.h"
#include "html/h/setup_html.h"
#include "html/h/hoymiles_html.h"
extern String setup_html;
#define DUMMY_RADIO_ID ((uint64_t)0xDEADBEEF01ULL)
//-----------------------------------------------------------------------------
app::app() : Main() {
mSendCnt = 0;
mSendTicker = new Ticker();
mFlagSend = false;
@ -93,7 +90,7 @@ void app::setup(const char *ssid, const char *pwd, uint32_t timeout) {
mMqtt.sendMsg("version", mVersion);
}
initRadio();
mSys->setup();
if(!mSettingsValid)
Serial.println("Warn: your settings are not valid! check [IP]/setup");
@ -107,15 +104,15 @@ void app::loop(void) {
if(!mSys->BufCtrl.empty()) {
uint8_t len, rptCnt;
packet_t *p = mSys->BufCtrl.getBack();
//dumpBuf("RAW ", p->packet, MAX_RF_PAYLOAD_SIZE);
//mSys->Radio.dumpBuf("RAW ", p->packet, MAX_RF_PAYLOAD_SIZE);
if(mSys->Radio.checkCrc(p->packet, &len, &rptCnt)) {
// process buffer only on first occurrence
if((0 != len) && (0 == rptCnt)) {
uint8_t *cmd = &p->packet[11];
//Serial.println("CMD " + String(*cmd, HEX));
//dumpBuf("Payload ", p->packet, len);
//mSys->Radio.dumpBuf("Payload ", p->packet, len);
uint8_t *cmd = &p->packet[11];
inverter_t *iv = mSys->findInverter(&p->packet[3]);
if(NULL != iv) {
for(uint8_t i = 0; i < iv->listLen; i++) {
@ -142,31 +139,11 @@ void app::loop(void) {
if(mFlagSend) {
mFlagSend = false;
uint8_t size = 0;
inverter_t *inv;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) {
inv = mSys->getInverterByPos(i);
if(NULL != inv) {
//if((mSendCnt % 6) == 0)
size = mSys->Radio.getTimePacket(&inv->radioId.u64, mSendBuf, mTimestamp);
/*else if((mSendCnt % 6) == 1)
size = mSys->Radio.getCmdPacket(&inv->radioId.u64, mSendBuf, 0x15, 0x81);
else if((mSendCnt % 6) == 2)
size = mSys->Radio.getCmdPacket(&inv->radioId.u64, mSendBuf, 0x15, 0x80);
else if((mSendCnt % 6) == 3)
size = mSys->Radio.getCmdPacket(&inv->radioId.u64, mSendBuf, 0x15, 0x83);
else if((mSendCnt % 6) == 4)
size = mSys->Radio.getCmdPacket(&inv->radioId.u64, mSendBuf, 0x15, 0x82);
else if((mSendCnt % 6) == 5)
size = mSys->Radio.getCmdPacket(&inv->radioId.u64, mSendBuf, 0x15, 0x84);*/
//Serial.println("sent packet: #" + String(mSendCnt));
//dumpBuf("SEN ", mSendBuf, size);
sendPacket(inv, mSendBuf, size);
mSendCnt++;
mSys->Radio.sendTimePacket(inv->radioId.u64, mTimestamp);
delay(20);
}
}
@ -213,100 +190,7 @@ void app::loop(void) {
//-----------------------------------------------------------------------------
void app::handleIntr(void) {
uint8_t pipe, len;
packet_t *p;
DISABLE_IRQ;
while(mRadio->available(&pipe)) {
if(!mSys->BufCtrl.full()) {
p = mSys->BufCtrl.getFront();
memset(p->packet, 0xcc, MAX_RF_PAYLOAD_SIZE);
p->sendCh = mSendChannel;
len = mRadio->getPayloadSize();
if(len > MAX_RF_PAYLOAD_SIZE)
len = MAX_RF_PAYLOAD_SIZE;
mRadio->read(p->packet, len);
mSys->BufCtrl.pushFront(p);
}
else {
bool tx_ok, tx_fail, rx_ready;
mRadio->whatHappened(tx_ok, tx_fail, rx_ready); // reset interrupt status
mRadio->flush_rx(); // drop the packet
}
}
RESTORE_IRQ;
}
//-----------------------------------------------------------------------------
void app::initRadio(void) {
mRadio = new RF24(RF24_CE_PIN, RF24_CS_PIN);
mRadio->begin();
mRadio->setAutoAck(false);
mRadio->setRetries(0, 0);
mRadio->setChannel(DEFAULT_RECV_CHANNEL);
mRadio->setDataRate(RF24_250KBPS);
mRadio->disableCRC();
mRadio->setAutoAck(false);
mRadio->setPayloadSize(MAX_RF_PAYLOAD_SIZE);
mRadio->setAddressWidth(5);
mRadio->openReadingPipe(1, DTU_RADIO_ID);
// enable only receiving interrupts
mRadio->maskIRQ(true, true, false);
// Use lo PA level, as a higher level will disturb CH340 serial usb adapter
mRadio->setPALevel(RF24_PA_MAX);
mRadio->startListening();
Serial.println("Radio Config:");
mRadio->printPrettyDetails();
mSendChannel = mSys->Radio.getDefaultChannel();
}
//-----------------------------------------------------------------------------
void app::sendPacket(inverter_t *inv, uint8_t buf[], uint8_t len) {
DISABLE_IRQ;
mRadio->stopListening();
#ifdef CHANNEL_HOP
//if(mSendCnt % 6 == 0)
mSendChannel = mSys->Radio.getNxtChannel();
//else
// mSendChannel = mSys->Radio.getLastChannel();
#else
mSendChannel = mSys->Radio.getDefaultChannel();
#endif
mRadio->setChannel(mSendChannel);
//Serial.println("CH: " + String(mSendChannel));
mRadio->openWritingPipe(inv->radioId.u64);
mRadio->setCRCLength(RF24_CRC_16);
mRadio->enableDynamicPayloads();
mRadio->setAutoAck(true);
mRadio->setRetries(3, 15);
mRadio->write(buf, len);
// Try to avoid zero payload acks (has no effect)
mRadio->openWritingPipe(DUMMY_RADIO_ID); // TODO: why dummy radio id?
mRadio->setAutoAck(false);
mRadio->setRetries(0, 0);
mRadio->disableDynamicPayloads();
mRadio->setCRCLength(RF24_CRC_DISABLED);
mRadio->setChannel(DEFAULT_RECV_CHANNEL);
mRadio->startListening();
RESTORE_IRQ;
mSys->Radio.handleIntr();
}
@ -324,7 +208,7 @@ void app::mqttTicker(void) {
//-----------------------------------------------------------------------------
void app::showIndex(void) {
String html = index_html;
String html = FPSTR(index_html);
html.replace("{DEVICE}", mDeviceName);
html.replace("{VERSION}", mVersion);
mWeb->send(200, "text/html", html);
@ -337,7 +221,7 @@ void app::showSetup(void) {
uint16_t interval;
String html = setup_html;
String html = FPSTR(setup_html);
html.replace("{SSID}", mStationSsid);
// PWD will be left at the default value (for protection)
// -> the PWD will only be changed if it does not match the placeholder "{PWD}"
@ -455,7 +339,7 @@ void app::showCmdStatistics(void) {
//-----------------------------------------------------------------------------
void app::showHoymiles(void) {
String html = hoymiles_html;
String html = FPSTR(hoymiles_html);
html.replace("{DEVICE}", mDeviceName);
html.replace("{VERSION}", mVersion);
mWeb->send(200, "text/html", html);

24
tools/esp8266/app.h

@ -11,8 +11,8 @@
#include "hmSystem.h"
#include "mqtt.h"
typedef HmRadio<RF24_CE_PIN, RF24_CS_PIN, RF24_IRQ_PIN> RadioType;
typedef CircularBuffer<packet_t, PACKET_BUFFER_SIZE> BufferType;
typedef HmRadio<RF24_CE_PIN, RF24_CS_PIN, RF24_IRQ_PIN, BufferType> RadioType;
typedef HmSystem<RadioType, BufferType, MAX_NUM_INVERTERS, float> HmSystemType;
const char* const wemosPins[] = {"D3 (GPIO0)", "TX (GPIO1)", "D4 (GPIO2)", "RX (GPIO3)",
@ -31,10 +31,11 @@ class app : public Main {
void loop(void);
void handleIntr(void);
private:
void initRadio(void);
void sendPacket(inverter_t *inv, uint8_t data[], uint8_t length);
uint8_t getIrqPin(void) {
return mSys->Radio.pinIrq;
}
private:
void sendTicker(void);
void mqttTicker(void);
@ -49,15 +50,6 @@ class app : public Main {
void saveValues(bool webSend);
void updateCrc(void);
void dumpBuf(const char *info, uint8_t buf[], uint8_t len) {
Serial.print(String(info));
for(uint8_t i = 0; i < len; i++) {
Serial.print(buf[i], HEX);
Serial.print(" ");
}
Serial.println();
}
uint64_t Serial2u64(const char *val) {
char tmp[3] = {0};
uint64_t ret = 0ULL;
@ -76,16 +68,10 @@ class app : public Main {
uint8_t mState;
bool mKeyPressed;
RF24 *mRadio;
packet_t mBuffer[PACKET_BUFFER_SIZE];
HmSystemType *mSys;
Ticker *mSendTicker;
uint32_t mSendCnt;
uint8_t mSendBuf[MAX_RF_PAYLOAD_SIZE];
bool mFlagSend;
uint8_t mSendChannel;
uint32_t mCmds[6];
uint32_t mChannelStat[4];

13
tools/esp8266/defines.h

@ -6,9 +6,8 @@
// PINOUT
//-------------------------------------
#define RF24_CS_PIN 15
#define RF24_CE_PIN 2 //5
#define RF24_IRQ_PIN 0 //4
#define RF24_CE_PIN 2
#define RF24_IRQ_PIN 0
//-------------------------------------
@ -17,6 +16,7 @@
#define PACKET_BUFFER_SIZE 30
#define MAX_NUM_INVERTERS 3
#define MAX_NAME_LENGTH 16
#define MAX_RF_PAYLOAD_SIZE 64
#define LIVEDATA_VISUALIZED // show live data pv-module wise or as dump
@ -28,6 +28,13 @@
#define VERSION_PATCH 4
//-------------------------------------
typedef struct {
uint8_t sendCh;
uint8_t packet[MAX_RF_PAYLOAD_SIZE];
} packet_t;
//-------------------------------------
// EEPROM
//-------------------------------------

4
tools/esp8266/eep.h

@ -36,7 +36,7 @@ class eep {
*value = (EEPROM.read(addr++));
}
void read(uint32_t addr, uint8_t data[], uint8_t length) {
void read(uint32_t addr, uint8_t data[], uint16_t length) {
for(uint8_t i = 0; i < length; i ++) {
*(data++) = EEPROM.read(addr++);
}
@ -77,7 +77,7 @@ class eep {
EEPROM.commit();
}
void write(uint32_t addr, uint8_t data[], uint8_t length) {
void write(uint32_t addr, uint8_t data[], uint16_t length) {
for(uint8_t i = 0; i < length; i ++) {
EEPROM.write(addr++, data[i]);
}

7
tools/esp8266/esp8266.ino

@ -13,12 +13,11 @@ app myApp;
//-----------------------------------------------------------------------------
void setup() {
// TODO: move to HmRadio
pinMode(RF24_IRQ_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(RF24_IRQ_PIN), handleIntr, FALLING);
// AP name, password, timeout
myApp.setup("ESP AHOY", "esp_8266", 15);
// TODO: move to HmRadio
attachInterrupt(digitalPinToInterrupt(myApp.getIrqPin()), handleIntr, FALLING);
}

179
tools/esp8266/hmRadio.h

@ -8,9 +8,9 @@
//#define CHANNEL_HOP // switch between channels or use static channel to send
#define DEFAULT_RECV_CHANNEL 3
#define MAX_RF_PAYLOAD_SIZE 64
#define DTU_RADIO_ID ((uint64_t)0x1234567801ULL)
#define DUMMY_RADIO_ID ((uint64_t)0xDEADBEEF01ULL)
//-----------------------------------------------------------------------------
@ -38,17 +38,14 @@
//-----------------------------------------------------------------------------
// HM Radio class
//-----------------------------------------------------------------------------
template <uint8_t CE_PIN, uint8_t CS_PIN, uint8_t IRQ_PIN, uint64_t DTU_ID=DTU_RADIO_ID>
template <uint8_t CE_PIN, uint8_t CS_PIN, uint8_t IRQ_PIN, class BUFFER, uint64_t DTU_ID=DTU_RADIO_ID>
class HmRadio {
public:
HmRadio() {
//pinMode(IRQ_PIN, INPUT_PULLUP);
//attachInterrupt(digitalPinToInterrupt(IRQ_PIN), handleIntr, FALLING);
mSendChan[0] = 23;
mSendChan[1] = 40;
mSendChan[2] = 61;
mSendChan[3] = 75;
HmRadio() : mNrf24(CE_PIN, CS_PIN) {
mChanOut[0] = 23;
mChanOut[1] = 40;
mChanOut[2] = 61;
mChanOut[3] = 75;
mChanIdx = 1;
calcDtuCrc();
@ -56,47 +53,106 @@ class HmRadio {
pinCs = CS_PIN;
pinCe = CE_PIN;
pinIrq = IRQ_PIN;
mSendCnt = 0;
}
~HmRadio() {}
void setup(BUFFER *ctrl) {
//Serial.println("HmRadio::setup, pins: " + String(pinCs) + ", " + String(pinCe) + ", " + String(pinIrq));
pinMode(pinIrq, INPUT_PULLUP);
mBufCtrl = ctrl;
mNrf24.begin(pinCe, pinCs);
mNrf24.setAutoAck(false);
mNrf24.setRetries(0, 0);
mNrf24.setChannel(DEFAULT_RECV_CHANNEL);
mNrf24.setDataRate(RF24_250KBPS);
mNrf24.disableCRC();
mNrf24.setAutoAck(false);
mNrf24.setPayloadSize(MAX_RF_PAYLOAD_SIZE);
mNrf24.setAddressWidth(5);
mNrf24.openReadingPipe(1, DTU_RADIO_ID);
// enable only receiving interrupts
mNrf24.maskIRQ(true, true, false);
// Use lo PA level, as a higher level will disturb CH340 serial usb adapter
mNrf24.setPALevel(RF24_PA_MAX);
mNrf24.startListening();
Serial.println("Radio Config:");
mNrf24.printPrettyDetails();
mSendChannel = getDefaultChannel();
}
void handleIntr(void) {
uint8_t pipe, len;
packet_t *p;
DISABLE_IRQ;
while(mNrf24.available(&pipe)) {
if(!mBufCtrl->full()) {
p = mBufCtrl->getFront();
memset(p->packet, 0xcc, MAX_RF_PAYLOAD_SIZE);
p->sendCh = mSendChannel;
len = mNrf24.getPayloadSize();
if(len > MAX_RF_PAYLOAD_SIZE)
len = MAX_RF_PAYLOAD_SIZE;
mNrf24.read(p->packet, len);
mBufCtrl->pushFront(p);
}
else {
bool tx_ok, tx_fail, rx_ready;
mNrf24.whatHappened(tx_ok, tx_fail, rx_ready); // reset interrupt status
mNrf24.flush_rx(); // drop the packet
}
}
RESTORE_IRQ;
}
uint8_t getDefaultChannel(void) {
return mSendChan[2];
return mChanOut[2];
}
uint8_t getLastChannel(void) {
return mSendChan[mChanIdx];
return mChanOut[mChanIdx];
}
uint8_t getNxtChannel(void) {
if(++mChanIdx >= 4)
mChanIdx = 0;
return mSendChan[mChanIdx];
return mChanOut[mChanIdx];
}
uint8_t getTimePacket(const uint64_t *invId, uint8_t buf[], uint32_t ts) {
getCmdPacket(invId, buf, 0x15, 0x80, false);
buf[10] = 0x0b; // cid
buf[11] = 0x00;
CP_U32_LittleEndian(&buf[12], ts);
buf[19] = 0x05;
void sendTimePacket(uint64_t invId, uint32_t ts) {
sendCmdPacket(invId, 0x15, 0x80, false);
mSendBuf[10] = 0x0b; // cid
mSendBuf[11] = 0x00;
CP_U32_LittleEndian(&mSendBuf[12], ts);
mSendBuf[19] = 0x05;
uint16_t crc = crc16(&buf[10], 14);
buf[24] = (crc >> 8) & 0xff;
buf[25] = (crc ) & 0xff;
buf[26] = crc8(buf, 26);
uint16_t crc = crc16(&mSendBuf[10], 14);
mSendBuf[24] = (crc >> 8) & 0xff;
mSendBuf[25] = (crc ) & 0xff;
mSendBuf[26] = crc8(mSendBuf, 26);
return 27;
sendPacket(invId, mSendBuf, 27);
}
uint8_t getCmdPacket(const uint64_t *invId, uint8_t buf[], uint8_t mid, uint8_t cmd, bool calcCrc = true) {
memset(buf, 0, MAX_RF_PAYLOAD_SIZE);
buf[0] = mid; // message id
CP_U32_BigEndian(&buf[1], ((*invId) >> 8));
CP_U32_BigEndian(&buf[5], (DTU_ID >> 8));
buf[9] = cmd;
if(calcCrc)
buf[10] = crc8(buf, 10);
return 11;
void sendCmdPacket(uint64_t invId, uint8_t mid, uint8_t cmd, bool calcCrc = true) {
memset(mSendBuf, 0, MAX_RF_PAYLOAD_SIZE);
mSendBuf[0] = mid; // message id
CP_U32_BigEndian(&mSendBuf[1], (invId >> 8));
CP_U32_BigEndian(&mSendBuf[5], (DTU_ID >> 8));
mSendBuf[9] = cmd;
if(calcCrc) {
mSendBuf[10] = crc8(mSendBuf, 10);
sendPacket(invId, mSendBuf, 11);
}
}
bool checkCrc(uint8_t buf[], uint8_t *len, uint8_t *rptCnt) {
@ -126,6 +182,44 @@ class HmRadio {
uint8_t pinIrq;
private:
void sendPacket(uint64_t invId, uint8_t buf[], uint8_t len) {
//Serial.println("sent packet: #" + String(mSendCnt));
//dumpBuf("SEN ", buf, len);
DISABLE_IRQ;
mNrf24.stopListening();
#ifdef CHANNEL_HOP
mSendChannel = getNxtChannel();
#else
mSendChannel = getDefaultChannel();
#endif
mNrf24.setChannel(mSendChannel);
//Serial.println("CH: " + String(mSendChannel));
mNrf24.openWritingPipe(invId); // TODO: deprecated
mNrf24.setCRCLength(RF24_CRC_16);
mNrf24.enableDynamicPayloads();
mNrf24.setAutoAck(true);
mNrf24.setRetries(3, 15);
mNrf24.write(buf, len);
// Try to avoid zero payload acks (has no effect)
mNrf24.openWritingPipe(DUMMY_RADIO_ID); // TODO: why dummy radio id?, deprecated
mNrf24.setAutoAck(false);
mNrf24.setRetries(0, 0);
mNrf24.disableDynamicPayloads();
mNrf24.setCRCLength(RF24_CRC_DISABLED);
mNrf24.setChannel(DEFAULT_RECV_CHANNEL);
mNrf24.startListening();
RESTORE_IRQ;
mSendCnt++;
}
void calcDtuCrc(void) {
uint64_t addr = DTU_RADIO_ID;
uint8_t tmp[5];
@ -136,11 +230,26 @@ class HmRadio {
mDtuIdCrc = crc16nrf24(tmp, BIT_CNT(5));
}
uint8_t mSendChan[4];
void dumpBuf(const char *info, uint8_t buf[], uint8_t len) {
Serial.print(String(info));
for(uint8_t i = 0; i < len; i++) {
Serial.print(buf[i], HEX);
Serial.print(" ");
}
Serial.println();
}
uint8_t mChanOut[4];
uint8_t mChanIdx;
uint16_t mDtuIdCrc;
uint16_t mLastCrc;
uint8_t mRptCnt;
RF24 mNrf24;
uint8_t mSendChannel;
BUFFER *mBufCtrl;
uint32_t mSendCnt;
uint8_t mSendBuf[MAX_RF_PAYLOAD_SIZE];
};
#endif /*__RADIO_H__*/

8
tools/esp8266/hmSystem.h

@ -4,10 +4,6 @@
#include "hmInverters.h"
#include "hmRadio.h"
typedef struct {
uint8_t sendCh;
uint8_t packet[MAX_RF_PAYLOAD_SIZE];
} packet_t;
template <class RADIO, class BUFFER, uint8_t MAX_INVERTER, class RECORDTYPE=float>
@ -25,6 +21,10 @@ class HmSystem {
// TODO: cleanup
}
void setup() {
Radio.setup(&BufCtrl);
}
inverter_t *addInverter(const char *name, uint64_t serial, uint8_t type) {
if(MAX_INVERTER <= mNumInv) {
DPRINT("max number of inverters reached!");

5
tools/esp8266/html/conv.bat

@ -1,5 +0,0 @@
..\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

28
tools/esp8266/html/convert.py

@ -0,0 +1,28 @@
import re
def convert2Header(inFile):
outName = "h/" + inFile.replace(".", "_") + ".h"
fileType = inFile.split(".")[1]
f = open(inFile, "r")
data = f.read().replace('\n', '')
f.close()
if fileType == "html":
data = re.sub(r"\>\s+\<", '><', data) # whitespaces between xml tags
data = re.sub(r"(\;|\}|\>|\{)\s+", r'\1', data) # whitespaces inner javascript
data = re.sub(r"\"", '\\\"', data) # escape quotation marks
else:
data = re.sub(r"(\;|\}|\:|\{)\s+", r'\1', data) # whitespaces inner css
define = inFile.split(".")[0].upper()
f = open(outName, "w")
f.write("#ifndef __{}_H__\n".format(define))
f.write("#define __{}_H__\n".format(define))
f.write("const char {}[] PROGMEM = \"{}\";\n".format(inFile.replace(".", "_"), data))
f.write("#endif /*__{}_H__*/\n".format(define))
f.close()
convert2Header("index.html")
convert2Header("setup.html")
convert2Header("hoymiles.html")
convert2Header("style.css")

5
tools/esp8266/html/h/hoymiles_html.h

@ -1 +1,4 @@
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>";
#ifndef __HOYMILES_H__
#define __HOYMILES_H__
const char hoymiles_html[] PROGMEM = "<!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>";
#endif /*__HOYMILES_H__*/

5
tools/esp8266/html/h/index_html.h

@ -1 +1,4 @@
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('/mqttstate', 'mqtt')\", 2000); 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\">MQTT: </span><span id=\"mqtt\"></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>";
#ifndef __INDEX_H__
#define __INDEX_H__
const char index_html[] PROGMEM = "<!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('/mqttstate', 'mqtt')\", 2000);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\">MQTT: </span><span id=\"mqtt\"></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>";
#endif /*__INDEX_H__*/

5
tools/esp8266/html/h/setup_html.h

@ -1 +1,4 @@
String setup_html = "<!doctype html><html><head><title>Setup - {DEVICE}</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"></head><body><h1>Setup</h1><div id=\"setup\" class=\"content\"><div id=\"content\"><p> Enter the credentials to your prefered WiFi station. After rebooting the device tries to connect with this information. </p><form method=\"post\" action=\"/save\"><p class=\"des\">WiFi</p><label for=\"ssid\">SSID</label><input type=\"text\" class=\"text\" name=\"ssid\" value=\"{SSID}\"/><label for=\"pwd\">Password</label><input type=\"password\" class=\"text\" name=\"pwd\" value=\"{PWD}\"/><p class=\"des\">Device Host Name</p><label for=\"device\">Device Name</label><input type=\"text\" class=\"text\" name=\"device\" value=\"{DEVICE}\"/><p class=\"des\">Inverter</p> {INVERTERS}<br/><p class=\"subdes\">General</p><label for=\"invInterval\">Interval (ms)</label><input type=\"text\" class=\"text\" name=\"invInterval\" value=\"{INV_INTERVAL}\"/><p class=\"des\">Pinout</p> {PINOUT} <p class=\"des\">MQTT</p><label for=\"mqttAddr\">Broker / Server IP</label><input type=\"text\" class=\"text\" name=\"mqttAddr\" value=\"{MQTT_ADDR}\"/><label for=\"mqttUser\">Username (optional)</label><input type=\"text\" class=\"text\" name=\"mqttUser\" value=\"{MQTT_USER}\"/><label for=\"mqttPwd\">Password (optional)</label><input type=\"text\" class=\"text\" name=\"mqttPwd\" value=\"{MQTT_PWD}\"/><label for=\"mqttTopic\">Topic</label><input type=\"text\" class=\"text\" name=\"mqttTopic\" value=\"{MQTT_TOPIC}\"/><label for=\"mqttInterval\">Interval (seconds)</label><input type=\"text\" class=\"text\" name=\"mqttInterval\" value=\"{MQTT_INTERVAL}\"/><p class=\"des\">&nbsp;</p><input type=\"checkbox\" class=\"cb\" name=\"reboot\"/><label for=\"reboot\">Reboot device after successful save</label><input type=\"submit\" value=\"save\" class=\"btn\" /></form></div></div><div id=\"footer\"><p class=\"left\"><a href=\"/\">Home</a></p><p class=\"left\"><a href=\"/update\">Update Firmware</a></p><p class=\"right\">AHOY - {VERSION}</p></div></body></html>";
#ifndef __SETUP_H__
#define __SETUP_H__
const char setup_html[] PROGMEM = "<!doctype html><html><head><title>Setup - {DEVICE}</title><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"></head><body><h1>Setup</h1><div id=\"setup\" class=\"content\"><div id=\"content\"><p>Enter the credentials to your prefered WiFi station. After rebooting the device tries to connect with this information. </p><form method=\"post\" action=\"/save\"><p class=\"des\">WiFi</p><label for=\"ssid\">SSID</label><input type=\"text\" class=\"text\" name=\"ssid\" value=\"{SSID}\"/><label for=\"pwd\">Password</label><input type=\"password\" class=\"text\" name=\"pwd\" value=\"{PWD}\"/><p class=\"des\">Device Host Name</p><label for=\"device\">Device Name</label><input type=\"text\" class=\"text\" name=\"device\" value=\"{DEVICE}\"/><p class=\"des\">Inverter</p>{INVERTERS}<br/><p class=\"subdes\">General</p><label for=\"invInterval\">Interval (ms)</label><input type=\"text\" class=\"text\" name=\"invInterval\" value=\"{INV_INTERVAL}\"/><p class=\"des\">Pinout (Wemos)</p>{PINOUT}<p class=\"des\">MQTT</p><label for=\"mqttAddr\">Broker / Server IP</label><input type=\"text\" class=\"text\" name=\"mqttAddr\" value=\"{MQTT_ADDR}\"/><label for=\"mqttUser\">Username (optional)</label><input type=\"text\" class=\"text\" name=\"mqttUser\" value=\"{MQTT_USER}\"/><label for=\"mqttPwd\">Password (optional)</label><input type=\"text\" class=\"text\" name=\"mqttPwd\" value=\"{MQTT_PWD}\"/><label for=\"mqttTopic\">Topic</label><input type=\"text\" class=\"text\" name=\"mqttTopic\" value=\"{MQTT_TOPIC}\"/><label for=\"mqttInterval\">Interval (seconds)</label><input type=\"text\" class=\"text\" name=\"mqttInterval\" value=\"{MQTT_INTERVAL}\"/><p class=\"des\">&nbsp;</p><input type=\"checkbox\" class=\"cb\" name=\"reboot\"/><label for=\"reboot\">Reboot device after successful save</label><input type=\"submit\" value=\"save\" class=\"btn\" /></form></div></div><div id=\"footer\"><p class=\"left\"><a href=\"/\">Home</a></p><p class=\"left\"><a href=\"/update\">Update Firmware</a></p><p class=\"right\">AHOY - {VERSION}</p></div></body></html>";
#endif /*__SETUP_H__*/

5
tools/esp8266/html/h/style_css.h

@ -1 +1,4 @@
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 { margin-top: 35px; font-size: 14pt; color: #006ec0; } .subdes { font-size: 13pt; color: #006ec0; margin-left: 7px; } .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; } div.content { background-color: #fff; padding-bottom: 65px; overflow: hidden; } input, select { padding: 7px; font-size: 13pt; } input.text, select { width: 70%; box-sizing: border-box; margin-bottom: 10px; border: 1px solid #ccc; } input.btn { background-color: #006ec0; color: #fff; border: 0px; float: right; text-transform: uppercase; } input.cb { margin-bottom: 20px; } label { width: 20%; display: inline-block; font-size: 12pt; padding-right: 10px; margin-left: 10px; } .left { float: left; } .right { float: right; } div.ch { width: 250px; height: 410px; background-color: #006ec0; display: inline-block; margin-right: 20px; margin-bottom: 20px; } div.ch .value, div.ch .info, div.ch .head { color: #fff; display: block; width: 100%; text-align: center; } div.ch .unit { font-size: 19px; margin-left: 10px; } div.ch .value { margin-top: 20px; font-size: 30px; } div.ch .info { margin-top: 3px; font-size: 10px; } div.ch .head { background-color: #003c80; padding: 10px 0 10px 0; } ";
#ifndef __STYLE_H__
#define __STYLE_H__
const char style_css[] PROGMEM = "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 {margin-top:35px;font-size:14pt;color:#006ec0;}.subdes {font-size:13pt;color:#006ec0;margin-left:7px;}.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;}div.content {background-color:#fff;padding-bottom:65px;overflow:hidden;}input, select {padding:7px;font-size:13pt;}input.text, select {width:70%;box-sizing:border-box;margin-bottom:10px;border:1px solid #ccc;}input.btn {background-color:#006ec0;color:#fff;border:0px;float:right;text-transform:uppercase;}input.cb {margin-bottom:20px;}label {width:20%;display:inline-block;font-size:12pt;padding-right:10px;margin-left:10px;}.left {float:left;}.right {float:right;}div.ch {width:250px;height:410px;background-color:#006ec0;display:inline-block;margin-right:20px;margin-bottom:20px;}div.ch .value, div.ch .info, div.ch .head {color:#fff;display:block;width:100%;text-align:center;}div.ch .unit {font-size:19px;margin-left:10px;}div.ch .value {margin-top:20px;font-size:30px;}div.ch .info {margin-top:3px;font-size:10px;}div.ch .head {background-color:#003c80;padding:10px 0 10px 0;}";
#endif /*__STYLE_H__*/

4
tools/esp8266/main.cpp

@ -160,7 +160,7 @@ bool Main::setupStation(uint32_t timeout) {
//-----------------------------------------------------------------------------
void Main::showSetup(void) {
String html = setup_html;
String html = FPSTR(setup_html);
html.replace("{SSID}", mStationSsid);
// PWD will be left at the default value (for protection)
// -> the PWD will only be changed if it does not match the default "{PWD}"
@ -173,7 +173,7 @@ void Main::showSetup(void) {
//-----------------------------------------------------------------------------
void Main::showCss(void) {
mWeb->send(200, "text/css", style_css);
mWeb->send(200, "text/css", FPSTR(style_css));
}

BIN
tools/esp8266/tools/fileConv.exe

Binary file not shown.
Loading…
Cancel
Save