diff --git a/tools/esp8266/CircularBuffer.h b/tools/esp8266/CircularBuffer.h index ab29e96a..aca8e417 100644 --- a/tools/esp8266/CircularBuffer.h +++ b/tools/esp8266/CircularBuffer.h @@ -1,21 +1,21 @@ /* - CircularBuffer - An Arduino circular buffering library for arbitrary types. - - Created by Ivo Pullens, Emmission, 2014 -- www.emmission.nl - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + CircularBuffer - An Arduino circular buffering library for arbitrary types. + + Created by Ivo Pullens, Emmission, 2014 -- www.emmission.nl + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CircularBuffer_h @@ -26,133 +26,132 @@ #define RESTORE_IRQ interrupts() #else #define DISABLE_IRQ \ - uint8_t sreg = SREG; \ - cli(); + uint8_t sreg = SREG; \ + cli(); #define RESTORE_IRQ \ - SREG = sreg; + SREG = sreg; #endif -template class CircularBuffer -{ - public: - /** Constructor - * @param buffer Preallocated buffer of at least size records. - * @param size Number of records available in the buffer. - */ - CircularBuffer(T* buffer, const uint8_t size ) - : m_size(size), m_buff(buffer) - { - clear(); - } - - /** Clear all entries in the circular buffer. */ - void clear(void) - { - m_front = 0; - m_fill = 0; - } - - /** Test if the circular buffer is empty */ - inline bool empty(void) const - { - return !m_fill; - } - - /** Return the number of records stored in the buffer */ - inline uint8_t available(void) const - { - return m_fill; - } - - /** Test if the circular buffer is full */ - inline bool full(void) const - { - return m_fill == m_size; - } - - /** Aquire record on front of the buffer, for writing. - * After filling the record, it has to be pushed to actually - * add it to the buffer. - * @return Pointer to record, or NULL when buffer is full. - */ - T* getFront(void) const - { - DISABLE_IRQ; - T* f = NULL; - if (!full()) - f = get(m_front); - RESTORE_IRQ; - return f; - } - - /** Push record to front of the buffer - * @param record Record to push. If record was aquired previously (using getFront) its - * data will not be copied as it is already present in the buffer. - * @return True, when record was pushed successfully. - */ - bool pushFront(T* record) - { - bool ok = false; - DISABLE_IRQ; - if (!full()) - { - T* f = get(m_front); - if (f != record) - *f = *record; - m_front = (m_front+1) % m_size; - m_fill++; - ok = true; - } - RESTORE_IRQ; - return ok; - } - - /** Aquire record on back of the buffer, for reading. - * After reading the record, it has to be pop'ed to actually - * remove it from the buffer. - * @return Pointer to record, or NULL when buffer is empty. - */ - T* getBack(void) const - { - T* b = NULL; - DISABLE_IRQ; - if (!empty()) - b = get(back()); - RESTORE_IRQ; - return b; - } - - /** Remove record from back of the buffer. - * @return True, when record was pop'ed successfully. - */ - bool popBack(void) - { - bool ok = false; - DISABLE_IRQ; - if (!empty()) - { - m_fill--; - ok = true; - } - RESTORE_IRQ; - return ok; - } - - protected: - inline T * get(const uint8_t idx) const - { - return &(m_buff[idx]); - } - inline uint8_t back(void) const - { - return (m_front - m_fill + m_size) % m_size; - } - - const uint8_t m_size; // Total number of records that can be stored in the buffer. - T* const m_buff; // Ptr to buffer holding all records. - volatile uint8_t m_front; // Index of front element (not pushed yet). - volatile uint8_t m_fill; // Amount of records currently pushed. +template +class CircularBuffer { + + typedef BUFFERTYPE BufferType; + BufferType Buffer[BUFFERSIZE]; + + public: + CircularBuffer() : m_buff(Buffer) { + m_size = BUFFERSIZE; + clear(); + } + + /** Clear all entries in the circular buffer. */ + void clear(void) + { + m_front = 0; + m_fill = 0; + } + + /** Test if the circular buffer is empty */ + inline bool empty(void) const + { + return !m_fill; + } + + /** Return the number of records stored in the buffer */ + inline uint8_t available(void) const + { + return m_fill; + } + + /** Test if the circular buffer is full */ + inline bool full(void) const + { + return m_fill == m_size; + } + + /** Aquire record on front of the buffer, for writing. + * After filling the record, it has to be pushed to actually + * add it to the buffer. + * @return Pointer to record, or NULL when buffer is full. + */ + BUFFERTYPE* getFront(void) const + { + DISABLE_IRQ; + BUFFERTYPE* f = NULL; + if (!full()) + f = get(m_front); + RESTORE_IRQ; + return f; + } + + /** Push record to front of the buffer + * @param record Record to push. If record was aquired previously (using getFront) its + * data will not be copied as it is already present in the buffer. + * @return True, when record was pushed successfully. + */ + bool pushFront(BUFFERTYPE* record) + { + bool ok = false; + DISABLE_IRQ; + if (!full()) + { + BUFFERTYPE* f = get(m_front); + if (f != record) + *f = *record; + m_front = (m_front+1) % m_size; + m_fill++; + ok = true; + } + RESTORE_IRQ; + return ok; + } + + /** Aquire record on back of the buffer, for reading. + * After reading the record, it has to be pop'ed to actually + * remove it from the buffer. + * @return Pointer to record, or NULL when buffer is empty. + */ + BUFFERTYPE* getBack(void) const + { + BUFFERTYPE* b = NULL; + DISABLE_IRQ; + if (!empty()) + b = get(back()); + RESTORE_IRQ; + return b; + } + + /** Remove record from back of the buffer. + * @return True, when record was pop'ed successfully. + */ + bool popBack(void) + { + bool ok = false; + DISABLE_IRQ; + if (!empty()) + { + m_fill--; + ok = true; + } + RESTORE_IRQ; + return ok; + } + + protected: + inline BUFFERTYPE * get(const uint8_t idx) const + { + return &(m_buff[idx]); + } + inline uint8_t back(void) const + { + return (m_front - m_fill + m_size) % m_size; + } + + uint8_t m_size; // Total number of records that can be stored in the buffer. + BUFFERTYPE* const m_buff; + volatile uint8_t m_front; // Index of front element (not pushed yet). + volatile uint8_t m_fill; // Amount of records currently pushed. }; #endif // CircularBuffer_h diff --git a/tools/esp8266/app.cpp b/tools/esp8266/app.cpp index bf305e7d..aa05324a 100644 --- a/tools/esp8266/app.cpp +++ b/tools/esp8266/app.cpp @@ -4,12 +4,11 @@ #include "html/h/hoymiles_html.h" extern String setup_html; + +#define DUMMY_RADIO_ID ((uint64_t)0xDEADBEEF01ULL) + //----------------------------------------------------------------------------- app::app() : Main() { - mHoymiles = new hoymiles(); - mDecoder = new hm1200Decode(); - mBufCtrl = new CircularBuffer(mBuffer, PACKET_BUFFER_SIZE); - mSendCnt = 0; mSendTicker = new Ticker(); mFlagSend = false; @@ -19,6 +18,8 @@ app::app() : Main() { memset(mCmds, 0, sizeof(uint32_t)); memset(mChannelStat, 0, sizeof(uint32_t)); + + mSys = new HmSystemType(); } @@ -42,10 +43,12 @@ void app::setup(const char *ssid, const char *pwd, uint32_t timeout) { if(mSettingsValid) { uint16_t interval; + uint64_t invSerial; // hoymiles - mEep->read(ADDR_INV0_ADDR, mHoymiles->mAddrBytes, INV_ADDR_LEN); + mEep->read(ADDR_INV0_ADDR, &invSerial); mEep->read(ADDR_INV_INTERVAL, &interval); + mSys->addInverter("HM1200", invSerial, INV_TYPE_HM1200); if(interval < 1000) interval = 1000; @@ -74,10 +77,6 @@ void app::setup(const char *ssid, const char *pwd, uint32_t timeout) { mMqtt.sendMsg("version", mVersion); } - else { - memset(mHoymiles->mAddrBytes, 0, 6); - } - mHoymiles->serial2RadioId(); initRadio(); @@ -90,26 +89,32 @@ void app::setup(const char *ssid, const char *pwd, uint32_t timeout) { void app::loop(void) { Main::loop(); - if(!mBufCtrl->empty()) { + if(!mSys->BufCtrl.empty()) { uint8_t len, rptCnt; - NRF24_packet_t *p = mBufCtrl->getBack(); + packet_t *p = mSys->BufCtrl.getBack(); + //dumpBuf("RAW ", p->packet, MAX_RF_PAYLOAD_SIZE); - //mHoymiles->dumpBuf("RAW ", p->packet, MAX_RF_PAYLOAD_SIZE); - - if(mHoymiles->checkCrc(p->packet, &len, &rptCnt)) { + if(mSys->Radio.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); - - mDecoder->convert(&p->packet[11], len); + //Serial.println("CMD " + String(*cmd, HEX)); + //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++) { + if(iv->assign[i].cmdId == *cmd) + mSys->addValue(iv, i, &p->packet[11]); + } + } - if(p->packet[11] == 0x01) mCmds[0]++; - else if(p->packet[11] == 0x02) mCmds[1]++; - else if(p->packet[11] == 0x03) mCmds[2]++; - else if(p->packet[11] == 0x81) mCmds[3]++; - else if(p->packet[11] == 0x84) mCmds[4]++; - else mCmds[5]++; + if(*cmd == 0x01) mCmds[0]++; + else if(*cmd == 0x02) mCmds[1]++; + else if(*cmd == 0x03) mCmds[2]++; + else if(*cmd == 0x81) mCmds[3]++; + else if(*cmd == 0x84) mCmds[4]++; + else mCmds[5]++; if(p->sendCh == 23) mChannelStat[0]++; else if(p->sendCh == 40) mChannelStat[1]++; @@ -117,94 +122,72 @@ void app::loop(void) { else mChannelStat[3]++; } } - mBufCtrl->popBack(); + mSys->BufCtrl.popBack(); } if(mFlagSend) { mFlagSend = false; uint8_t size = 0; - //if((mSendCnt % 6) == 0) - size = mHoymiles->getTimePacket(mSendBuf, mTimestamp); - /*else if((mSendCnt % 6) == 1) - size = mHoymiles->getCmdPacket(mSendBuf, 0x15, 0x81); - else if((mSendCnt % 6) == 2) - size = mHoymiles->getCmdPacket(mSendBuf, 0x15, 0x80); - else if((mSendCnt % 6) == 3) - size = mHoymiles->getCmdPacket(mSendBuf, 0x15, 0x83); - else if((mSendCnt % 6) == 4) - size = mHoymiles->getCmdPacket(mSendBuf, 0x15, 0x82); - else if((mSendCnt % 6) == 5) - size = mHoymiles->getCmdPacket(mSendBuf, 0x15, 0x84);*/ + inverter_t *inv = mSys->getInverterByPos(0); + size = mSys->Radio.getTimePacket(&inv->radioId.u64, mSendBuf, mTimestamp); //Serial.println("sent packet: #" + String(mSendCnt)); //dumpBuf(mSendBuf, size); - sendPacket(mSendBuf, size); + sendPacket(inv, mSendBuf, size); mSendCnt++; } // mqtt - mMqtt.loop(); + //mMqtt.loop(); if(mMqttEvt) { mMqttEvt = false; - mMqtt.isConnected(true); + /*mMqtt.isConnected(true); char topic[20], val[10]; - for(uint8_t i = 0; i < 4; i++) { - for(uint8_t j = 0; j < 5; j++) { - switch(j) { - default: - sprintf(topic, "ch%d/%s", i, "voltage"); - sprintf(val, "%.3f", mDecoder->mData.ch_dc[i/2].u); - break; - case 1: - sprintf(topic, "ch%d/%s", i, "current"); - sprintf(val, "%.3f", mDecoder->mData.ch_dc[i].i); - break; - case 2: - sprintf(topic, "ch%d/%s", i, "power"); - sprintf(val, "%.3f", mDecoder->mData.ch_dc[i].p); - break; - case 3: - sprintf(topic, "ch%d/%s", i, "yield_day"); - sprintf(val, "%.3f", (double)mDecoder->mData.ch_dc[i].y_d); - break; - case 4: - sprintf(topic, "ch%d/%s", i, "yield"); - sprintf(val, "%.3f", mDecoder->mData.ch_dc[i].y_t); - break; + for(uint8_t id = 0; id < mSys->getNumInverters(); id++) { + inverter_t *iv = mSys->getInverterByPos(id); + if(NULL != iv) { + for(uint8_t i = 0; i < iv->listLen; i++) { + if(0.0f != mSys->getValue(iv, i)) { + sprintf(topic, "%s/ch%d/%s", iv->name, iv->assign[i].ch, fields[iv->assign[i].fieldId]); + sprintf(val, "%.3f", mSys->getValue(iv, i)); + mMqtt.sendMsg(topic, val); + delay(10); + } } - if(0 != strncmp("0.000", val, 5)) { - mMqtt.sendMsg(topic, val); - delay(10); + } + }*/ + + // Serial debug + char topic[20], val[10]; + for(uint8_t id = 0; id < mSys->getNumInverters(); id++) { + inverter_t *iv = mSys->getInverterByPos(id); + if(NULL != iv) { + for(uint8_t i = 0; i < iv->listLen; i++) { + //if(0.0f != mSys->getValue(iv, i)) { + sprintf(topic, "%s/ch%d/%s", iv->name, iv->assign[i].ch, mSys->getFieldName(iv, i)); + sprintf(val, "%.3f %s", mSys->getValue(iv, i), mSys->getUnit(iv, i)); + Serial.println(String(topic) + ": " + String(val)); + //} } } } - - sprintf(val, "%.3f", mDecoder->mData.ch_ac.u); - mMqtt.sendMsg("ac/voltage", val); - delay(10); - sprintf(val, "%.3f", mDecoder->mData.ch_ac.i); - mMqtt.sendMsg("ac/current", val); - delay(10); - sprintf(val, "%.3f", mDecoder->mData.temp); - mMqtt.sendMsg("temperature", val); - delay(10); } } //----------------------------------------------------------------------------- void app::handleIntr(void) { - uint8_t lostCnt = 0, pipe, len; - NRF24_packet_t *p; + uint8_t pipe, len; + packet_t *p; DISABLE_IRQ; while(mRadio->available(&pipe)) { - if(!mBufCtrl->full()) { - p = mBufCtrl->getFront(); + if(!mSys->BufCtrl.full()) { + p = mSys->BufCtrl.getFront(); memset(p->packet, 0xcc, MAX_RF_PAYLOAD_SIZE); p->sendCh = mSendChannel; len = mRadio->getPayloadSize(); @@ -212,13 +195,10 @@ void app::handleIntr(void) { len = MAX_RF_PAYLOAD_SIZE; mRadio->read(p->packet, len); - mBufCtrl->pushFront(p); - lostCnt = 0; + mSys->BufCtrl.pushFront(p); } else { bool tx_ok, tx_fail, rx_ready; - if(lostCnt < 255) - lostCnt++; mRadio->whatHappened(tx_ok, tx_fail, rx_ready); // reset interrupt status mRadio->flush_rx(); // drop the packet } @@ -254,27 +234,27 @@ void app::initRadio(void) { Serial.println("Radio Config:"); mRadio->printPrettyDetails(); - mSendChannel = mHoymiles->getDefaultChannel(); + mSendChannel = mSys->Radio.getDefaultChannel(); } //----------------------------------------------------------------------------- -void app::sendPacket(uint8_t buf[], uint8_t len) { +void app::sendPacket(inverter_t *inv, uint8_t buf[], uint8_t len) { DISABLE_IRQ; mRadio->stopListening(); #ifdef CHANNEL_HOP //if(mSendCnt % 6 == 0) - mSendChannel = mHoymiles->getNxtChannel(); + mSendChannel = mSys->Radio.getNxtChannel(); //else - // mSendChannel = mHoymiles->getLastChannel(); + // mSendChannel = mSys->Radio.getLastChannel(); #else - mSendChannel = mHoymiles->getDefaultChannel(); + mSendChannel = mSys->Radio.getDefaultChannel(); #endif mRadio->setChannel(mSendChannel); //Serial.println("CH: " + String(mSendChannel)); - mRadio->openWritingPipe(mHoymiles->mRadioId); + mRadio->openWritingPipe(inv->radioId.u64); mRadio->setCRCLength(RF24_CRC_16); mRadio->enableDynamicPayloads(); mRadio->setAutoAck(true); @@ -329,13 +309,32 @@ void app::showSetup(void) { // PWD will be left at the default value (for protection) // -> the PWD will only be changed if it does not match the placeholder "{PWD}" - char addr[20] = {0}; - sprintf(addr, "%02X:%02X:%02X:%02X:%02X:%02X", mHoymiles->mAddrBytes[0], mHoymiles->mAddrBytes[1], mHoymiles->mAddrBytes[2], mHoymiles->mAddrBytes[3], mHoymiles->mAddrBytes[4], mHoymiles->mAddrBytes[5]); - html.replace("{INV0_ADDR}", String(addr)); - html.replace("{DEVICE}", String(mDeviceName)); html.replace("{VERSION}", String(mVersion)); + String inv; + inverter_t *pInv; + for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { + pInv = mSys->getInverterByPos(i); + inv += "

Inverter "+ String(i) + "

"; + + inv += ""; + inv += "getSerial(pInv), HEX) : ""; + inv += "\"/>"; + + inv += ""; + inv += "name) : ""; + inv += "\"/>"; + + inv += ""; + inv += "type) : ""; + inv += "\"/>"; + } + html.replace("{INVERTERS}", String(inv)); + if(mSettingsValid) { mEep->read(ADDR_INV_INTERVAL, &interval); html.replace("{INV_INTERVAL}", String(interval)); @@ -402,28 +401,46 @@ void app::showHoymiles(void) { //----------------------------------------------------------------------------- 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; + String modHtml = "
";
+
+    char topic[20], val[10];
+    for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
+        inverter_t *iv = mSys->getInverterByPos(id);
+        if(NULL != iv) {
+            /*uint8_t modNum;
+            switch(iv->type) {
+                default:              modNum = 1; break;
+                case INV_TYPE_HM600:  modNum = 2; break;
+                case INV_TYPE_HM1200: modNum = 4; break;
+            }
+
+            for(uint8_t mod = 1; mod <= modNum; mod ++) {
+                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 += "
"; + }*/ + + for(uint8_t i = 0; i < iv->listLen; i++) { + sprintf(topic, "%s/ch%d/%s", iv->name, iv->assign[i].ch, mSys->getFieldName(iv, i)); + sprintf(val, "%.3f %s", mSys->getValue(iv, i), mSys->getUnit(iv, i)); + modHtml += String(topic) + ": " + String(val) + "\n"; } - modHtml += "" + unit[j] + ""; - modHtml += "" + info[j] + ""; } - modHtml += "
"; } + modHtml += ""; + mWeb->send(200, "text/html", modHtml); } @@ -443,20 +460,24 @@ void app::saveValues(bool webSend = true) { if(mWeb->args() > 0) { char *p; - char addr[20] = {0}; + char buf[20] = {0}; uint8_t i = 0; uint16_t interval; - // hoymiles - memset(mHoymiles->mAddrBytes, 0, 6); - mWeb->arg("inv0Addr").toCharArray(addr, 20); - p = strtok(addr, ":"); - while(NULL != p) { - mHoymiles->mAddrBytes[i++] = strtol(p, NULL, 16); - p = strtok(NULL, ":"); + // inverter + serial_u addr; + mWeb->arg("inv0Addr").toCharArray(buf, 20); + addr.u64 = Serial2u64(buf); + mSys->updateSerial(mSys->getInverterByPos(0), addr.u64); + + for(uint8_t i = 0; i < 8; i++) { + Serial.print(String(addr.b[i], HEX) + " "); } + Serial.println(); + Serial.println("addr: " + String(addr.u64, HEX)); + interval = mWeb->arg("invInterval").toInt(); - mEep->write(ADDR_INV0_ADDR, mHoymiles->mAddrBytes, INV_ADDR_LEN); + mEep->write(ADDR_INV0_ADDR, addr.u64); mEep->write(ADDR_INV_INTERVAL, interval); @@ -465,9 +486,9 @@ void app::saveValues(bool webSend = true) { char mqttUser[MQTT_USER_LEN]; char mqttPwd[MQTT_PWD_LEN]; char mqttTopic[MQTT_TOPIC_LEN]; - mWeb->arg("mqttAddr").toCharArray(addr, 20); + mWeb->arg("mqttAddr").toCharArray(buf, 20); i = 0; - p = strtok(addr, "."); + p = strtok(buf, "."); while(NULL != p) { mqttAddr[i++] = atoi(p); p = strtok(NULL, "."); @@ -495,14 +516,3 @@ void app::saveValues(bool webSend = true) { "

Error while saving

"); } } - - -//----------------------------------------------------------------------------- -void app::dumpBuf(uint8_t buf[], uint8_t len) { - for(uint8_t i = 0; i < len; i ++) { - if((i % 8 == 0) && (i != 0)) - Serial.println(); - Serial.print(String(buf[i], HEX) + " "); - } - Serial.println(); -} diff --git a/tools/esp8266/app.h b/tools/esp8266/app.h index 627cdb1e..80aba5f6 100644 --- a/tools/esp8266/app.h +++ b/tools/esp8266/app.h @@ -8,12 +8,15 @@ #include "main.h" #include "CircularBuffer.h" -#include "hoymiles.h" -#include "hm1200Decode.h" +#include "hmSystem.h" #include "mqtt.h" +typedef HmRadio RadioType; +typedef CircularBuffer BufferType; +typedef HmSystem HmSystemType; + class app : public Main { public: app(); @@ -25,7 +28,7 @@ class app : public Main { private: void initRadio(void); - void sendPacket(uint8_t data[], uint8_t length); + void sendPacket(inverter_t *inv, uint8_t data[], uint8_t length); void sendTicker(void); void mqttTicker(void); @@ -39,16 +42,35 @@ class app : public Main { void showMqtt(void); void saveValues(bool webSend); - void dumpBuf(uint8_t buf[], uint8_t len); + + 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; + uint64_t u64; + for(uint8_t i = 0; i < 6; i++) { + tmp[0] = val[i*2]; + tmp[1] = val[i*2 + 1]; + u64 = strtol(tmp, NULL, 16); + ret |= (u64 << ((5-i) << 3)); + } + return ret; + } uint8_t mState; bool mKeyPressed; RF24 *mRadio; - hoymiles *mHoymiles; - hm1200Decode *mDecoder; - CircularBuffer *mBufCtrl; - NRF24_packet_t mBuffer[PACKET_BUFFER_SIZE]; + packet_t mBuffer[PACKET_BUFFER_SIZE]; + HmSystemType *mSys; Ticker *mSendTicker; diff --git a/tools/esp8266/debug.h b/tools/esp8266/debug.h new file mode 100644 index 00000000..11e8e071 --- /dev/null +++ b/tools/esp8266/debug.h @@ -0,0 +1,38 @@ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#ifdef NDEBUG + #define DPRINT(str) +#else + +#ifndef DSERIAL + #define DSERIAL Serial +#endif + + template + inline void DPRINT(T str) { DSERIAL.print(str); } + template + inline void DPRINTLN(T str) { DPRINT(str); DPRINT(F("\r\n")); } + inline void DHEX(uint8_t b) { + if( b<0x10 ) DSERIAL.print('0'); + DSERIAL.print(b,HEX); + } + inline void DHEX(uint16_t b) { + if( b<0x10 ) DSERIAL.print(F("000")); + else if( b<0x100 ) DSERIAL.print(F("00")); + else if( b<0x1000 ) DSERIAL.print(F("0")); + DSERIAL.print(b,HEX); + } + inline void DHEX(uint32_t b) { + if( b<0x10 ) DSERIAL.print(F("0000000")); + else if( b<0x100 ) DSERIAL.print(F("000000")); + else if( b<0x1000 ) DSERIAL.print(F("00000")); + else if( b<0x10000 ) DSERIAL.print(F("0000")); + else if( b<0x100000 ) DSERIAL.print(F("000")); + else if( b<0x1000000 ) DSERIAL.print(F("00")); + else if( b<0x10000000 ) DSERIAL.print(F("0")); + DSERIAL.print(b,HEX); + } +#endif + +#endif /*__DEBUG_H__*/ diff --git a/tools/esp8266/defines.h b/tools/esp8266/defines.h index b965cccb..adc4ac80 100644 --- a/tools/esp8266/defines.h +++ b/tools/esp8266/defines.h @@ -10,12 +10,20 @@ #define RF24_CS_PIN 15 + +//------------------------------------- +// CONFIGURATION - COMPILE TIME +//------------------------------------- +#define PACKET_BUFFER_SIZE 30 +#define MAX_NUM_INVERTERS 3 + + //------------------------------------- // VERSION //------------------------------------- #define VERSION_MAJOR 0 -#define VERSION_MINOR 1 -#define VERSION_PATCH 12 +#define VERSION_MINOR 2 +#define VERSION_PATCH 1 //------------------------------------- @@ -26,7 +34,7 @@ #define DEVNAME_LEN 16 #define CRC_LEN 2 -#define INV_ADDR_LEN 6 +#define INV_ADDR_LEN 8 // uint64_t #define INV_INTERVAL_LEN 2 // uint16_t diff --git a/tools/esp8266/eep.cpp b/tools/esp8266/eep.cpp deleted file mode 100644 index 97a3c600..00000000 --- a/tools/esp8266/eep.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "eep.h" -#include - - -//----------------------------------------------------------------------------- -eep::eep() { - EEPROM.begin(500); -} - - -//----------------------------------------------------------------------------- -eep::~eep() { - EEPROM.end(); -} - - -//----------------------------------------------------------------------------- -void eep::read(uint32_t addr, char *str, uint8_t length) { - for(uint8_t i = 0; i < length; i ++) { - *(str++) = (char)EEPROM.read(addr++); - } -} - - -//----------------------------------------------------------------------------- -void eep::read(uint32_t addr, float *value) { - uint8_t *p = (uint8_t*)value; - for(uint8_t i = 0; i < 4; i ++) { - *(p++) = (uint8_t)EEPROM.read(addr++); - } -} - - -//----------------------------------------------------------------------------- -void eep::read(uint32_t addr, bool *value) { - uint8_t intVal = 0x00; - intVal = EEPROM.read(addr++); - *value = (intVal == 0x01); -} - - -//----------------------------------------------------------------------------- -void eep::read(uint32_t addr, uint8_t *value) { - *value = (EEPROM.read(addr++)); -} - - -//----------------------------------------------------------------------------- -void eep::read(uint32_t addr, uint8_t data[], uint8_t length) { - for(uint8_t i = 0; i < length; i ++) { - *(data++) = EEPROM.read(addr++); - } -} - - -//----------------------------------------------------------------------------- -void eep::read(uint32_t addr, uint16_t *value) { - *value = (EEPROM.read(addr++) << 8); - *value |= (EEPROM.read(addr++)); -} - - -//----------------------------------------------------------------------------- -void eep::read(uint32_t addr, uint32_t *value) { - *value = (EEPROM.read(addr++) << 24); - *value |= (EEPROM.read(addr++) << 16); - *value |= (EEPROM.read(addr++) << 8); - *value |= (EEPROM.read(addr++)); -} - - -//----------------------------------------------------------------------------- -void eep::write(uint32_t addr, const char *str, uint8_t length) { - for(uint8_t i = 0; i < length; i ++) { - EEPROM.write(addr++, str[i]); - } - EEPROM.commit(); -} - - -//----------------------------------------------------------------------------- -void eep::write(uint32_t addr, uint8_t data[], uint8_t length) { - for(uint8_t i = 0; i < length; i ++) { - EEPROM.write(addr++, data[i]); - } - EEPROM.commit(); -} - - -//----------------------------------------------------------------------------- -void eep::write(uint32_t addr, float value) { - uint8_t *p = (uint8_t*)&value; - for(uint8_t i = 0; i < 4; i ++) { - EEPROM.write(addr++, p[i]); - } - EEPROM.commit(); -} - - -//----------------------------------------------------------------------------- -void eep::write(uint32_t addr, bool value) { - uint8_t intVal = (value) ? 0x01 : 0x00; - EEPROM.write(addr++, intVal); - EEPROM.commit(); -} - - -//----------------------------------------------------------------------------- -void eep::write(uint32_t addr, uint8_t value) { - EEPROM.write(addr++, value); - EEPROM.commit(); -} - - -//----------------------------------------------------------------------------- -void eep::write(uint32_t addr, uint16_t value) { - EEPROM.write(addr++, (value >> 8) & 0xff); - EEPROM.write(addr++, (value ) & 0xff); - EEPROM.commit(); -} - - -//----------------------------------------------------------------------------- -void eep::write(uint32_t addr, uint32_t value) { - EEPROM.write(addr++, (value >> 24) & 0xff); - EEPROM.write(addr++, (value >> 16) & 0xff); - EEPROM.write(addr++, (value >> 8) & 0xff); - EEPROM.write(addr++, (value ) & 0xff); - EEPROM.commit(); -} diff --git a/tools/esp8266/eep.h b/tools/esp8266/eep.h index 36b03914..e38bc9fd 100644 --- a/tools/esp8266/eep.h +++ b/tools/esp8266/eep.h @@ -2,29 +2,132 @@ #define __EEP_H__ #include "Arduino.h" +#include class eep { public: - eep(); - ~eep(); - - void read(uint32_t addr, char *str, uint8_t length); - void read(uint32_t addr, float *value); - void read(uint32_t addr, bool *value); - void read(uint32_t addr, uint8_t *value); - void read(uint32_t addr, uint8_t data[], uint8_t length); - void read(uint32_t addr, uint16_t *value); - void read(uint32_t addr, uint32_t *value); - void write(uint32_t addr, const char *str, uint8_t length); - void write(uint32_t addr, uint8_t data[], uint8_t length); - void write(uint32_t addr, float value); - void write(uint32_t addr, bool value); - void write(uint32_t addr, uint8_t value); - void write(uint32_t addr, uint16_t value); - void write(uint32_t addr, uint32_t value); - - private: + eep() { + EEPROM.begin(500); + } + ~eep() { + EEPROM.end(); + } + void read(uint32_t addr, char *str, uint8_t length) { + for(uint8_t i = 0; i < length; i ++) { + *(str++) = (char)EEPROM.read(addr++); + } + } + + void read(uint32_t addr, float *value) { + uint8_t *p = (uint8_t*)value; + for(uint8_t i = 0; i < 4; i ++) { + *(p++) = (uint8_t)EEPROM.read(addr++); + } + } + + void read(uint32_t addr, bool *value) { + uint8_t intVal = 0x00; + intVal = EEPROM.read(addr++); + *value = (intVal == 0x01); + } + + void read(uint32_t addr, uint8_t *value) { + *value = (EEPROM.read(addr++)); + } + + void read(uint32_t addr, uint8_t data[], uint8_t length) { + for(uint8_t i = 0; i < length; i ++) { + *(data++) = EEPROM.read(addr++); + } + } + + void read(uint32_t addr, uint16_t *value) { + *value = (EEPROM.read(addr++) << 8); + *value |= (EEPROM.read(addr++)); + } + + void read(uint32_t addr, uint32_t *value) { + *value = (EEPROM.read(addr++) << 24); + *value |= (EEPROM.read(addr++) << 16); + *value |= (EEPROM.read(addr++) << 8); + *value |= (EEPROM.read(addr++)); + } + + void read(uint32_t addr, uint64_t *value) { + read(addr, (uint32_t *)value); + *value <<= 32; + uint32_t tmp; + read(addr+4, &tmp); + *value |= tmp; + /**value = (EEPROM.read(addr++) << 56); + *value |= (EEPROM.read(addr++) << 48); + *value |= (EEPROM.read(addr++) << 40); + *value |= (EEPROM.read(addr++) << 32); + *value |= (EEPROM.read(addr++) << 24); + *value |= (EEPROM.read(addr++) << 16); + *value |= (EEPROM.read(addr++) << 8); + *value |= (EEPROM.read(addr++));*/ + } + + void write(uint32_t addr, const char *str, uint8_t length) { + for(uint8_t i = 0; i < length; i ++) { + EEPROM.write(addr++, str[i]); + } + EEPROM.commit(); + } + + void write(uint32_t addr, uint8_t data[], uint8_t length) { + for(uint8_t i = 0; i < length; i ++) { + EEPROM.write(addr++, data[i]); + } + EEPROM.commit(); + } + + void write(uint32_t addr, float value) { + uint8_t *p = (uint8_t*)&value; + for(uint8_t i = 0; i < 4; i ++) { + EEPROM.write(addr++, p[i]); + } + EEPROM.commit(); + } + + void write(uint32_t addr, bool value) { + uint8_t intVal = (value) ? 0x01 : 0x00; + EEPROM.write(addr++, intVal); + EEPROM.commit(); + } + + void write(uint32_t addr, uint8_t value) { + EEPROM.write(addr++, value); + EEPROM.commit(); + } + + void write(uint32_t addr, uint16_t value) { + EEPROM.write(addr++, (value >> 8) & 0xff); + EEPROM.write(addr++, (value ) & 0xff); + EEPROM.commit(); + } + + void write(uint32_t addr, uint32_t value) { + EEPROM.write(addr++, (value >> 24) & 0xff); + EEPROM.write(addr++, (value >> 16) & 0xff); + EEPROM.write(addr++, (value >> 8) & 0xff); + EEPROM.write(addr++, (value ) & 0xff); + EEPROM.commit(); + } + + void write(uint64_t addr, uint64_t value) { + EEPROM.write(addr++, (value >> 56) & 0xff); + EEPROM.write(addr++, (value >> 48) & 0xff); + EEPROM.write(addr++, (value >> 40) & 0xff); + EEPROM.write(addr++, (value >> 32) & 0xff); + EEPROM.write(addr++, (value >> 24) & 0xff); + EEPROM.write(addr++, (value >> 16) & 0xff); + EEPROM.write(addr++, (value >> 8) & 0xff); + EEPROM.write(addr++, (value ) & 0xff); + EEPROM.commit(); + } }; #endif /*__EEP_H__*/ diff --git a/tools/esp8266/esp8266.ino b/tools/esp8266/esp8266.ino index 2dcc84c3..86c413d3 100644 --- a/tools/esp8266/esp8266.ino +++ b/tools/esp8266/esp8266.ino @@ -1,3 +1,12 @@ + +#include "Arduino.h" + +#include +#include +#include +#include + +#include #include "app.h" app myApp; @@ -22,4 +31,3 @@ void loop() { ICACHE_RAM_ATTR void handleIntr(void) { myApp.handleIntr(); } - diff --git a/tools/esp8266/hm1200Decode.h b/tools/esp8266/hm1200Decode.h deleted file mode 100644 index 95b9aebc..00000000 --- a/tools/esp8266/hm1200Decode.h +++ /dev/null @@ -1,87 +0,0 @@ -#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/hmInverters.h b/tools/esp8266/hmInverters.h new file mode 100644 index 00000000..e1f6d86c --- /dev/null +++ b/tools/esp8266/hmInverters.h @@ -0,0 +1,111 @@ +#ifndef __HM_INVERTERS_H__ +#define __HM_INVERTERS_H__ + +#include "debug.h" +#include + +// 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"}; + + + +// CH0 is default channel (freq, ac, temp) +enum {CH0 = 0, CH1, CH2, CH3, CH4}; +enum {CMD01 = 0x01, CMD02, CMD03, CMD83 = 0x83, CMD84}; + +enum {INV_TYPE_HM600 = 0, INV_TYPE_HM1200}; + + +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; + + +union serial_u { + uint64_t u64; + uint8_t b[8]; +}; + +typedef struct { + uint8_t id; // unique id + char name[20]; // 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 +} inverter_t; + + +/** + * indices are built for the buffer starting with cmd-id in first byte + * (complete payload in buffer) + * */ + +//------------------------------------- +// HM600, HM700 +//------------------------------------- +const byteAssign_t hm600assignment[] = { + { FLD_UDC, UNIT_V, CH1, CMD01, 14, 2, 10 }, + { FLD_IDC, UNIT_A, CH1, CMD01, 16, 2, 100 }, + { FLD_PDC, UNIT_W, CH1, CMD01, 18, 2, 10 }, + { FLD_UDC, UNIT_V, CH2, CMD01, 20, 2, 10 }, + { FLD_IDC, UNIT_A, CH2, CMD01, 22, 2, 100 }, + { FLD_PDC, UNIT_W, CH2, CMD01, 24, 2, 10 }, + { FLD_YW, UNIT_WH, CH0, CMD02, 12, 2, 1 }, + { FLD_YT, UNIT_WH, CH0, CMD02, 14, 4, 1 }, + { FLD_YD, UNIT_WH, CH1, CMD02, 18, 2, 1 }, + { FLD_YD, UNIT_WH, CH2, CMD02, 20, 2, 1 }, + { FLD_UAC, UNIT_V, CH0, CMD02, 22, 2, 10 }, + { FLD_F, UNIT_HZ, CH0, CMD02, 24, 2, 100 }, + { FLD_IAC, UNIT_A, CH0, CMD02, 26, 2, 10 }, + { FLD_T, UNIT_C, CH0, CMD83, 18, 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_IDC, UNIT_A, CH2, CMD01, 7, 2, 100 }, + { FLD_PDC, UNIT_W, CH1, CMD01, 9, 2, 10 }, + { FLD_PDC, UNIT_W, CH2, CMD01, 11, 2, 10 }, + { FLD_YT, UNIT_KWH, CH1, CMD01, 13, 4, 1000 }, + { FLD_YT, UNIT_KWH, CH2, CMD02, 1, 4, 1000 }, + { FLD_YD, UNIT_WH, CH1, CMD02, 5, 2, 1 }, + { FLD_YD, UNIT_WH, CH2, CMD02, 7, 2, 1 }, + { FLD_UDC, UNIT_V, CH2, CMD02, 9, 2, 10 }, + { FLD_IDC, UNIT_A, CH3, CMD02, 11, 2, 100 }, + { FLD_IDC, UNIT_A, CH4, CMD02, 13, 2, 100 }, + { FLD_PDC, UNIT_W, CH3, CMD02, 15, 2, 10 }, + { FLD_PDC, UNIT_W, CH4, CMD03, 1, 2, 10 }, + { FLD_YT, UNIT_KWH, CH3, CMD03, 3, 4, 1000 }, + { FLD_YT, UNIT_KWH, CH4, CMD03, 7, 4, 1000 }, + { FLD_YD, UNIT_WH, CH3, CMD03, 11, 2, 1 }, + { FLD_YD, UNIT_WH, CH4, CMD03, 13, 2, 1 }, + { FLD_UAC, UNIT_V, CH0, CMD03, 15, 2, 10 }, + { FLD_F, UNIT_HZ, CH0, CMD84, 1, 2, 100 }, + { FLD_PAC, UNIT_W, CH0, CMD84, 3, 2, 10 }, + { FLD_IAC, UNIT_A, CH0, CMD84, 7, 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_INVERTERS_H__*/ diff --git a/tools/esp8266/hoymiles.h b/tools/esp8266/hmRadio.h similarity index 57% rename from tools/esp8266/hoymiles.h rename to tools/esp8266/hmRadio.h index 883c2d1f..0f8e39a6 100644 --- a/tools/esp8266/hoymiles.h +++ b/tools/esp8266/hmRadio.h @@ -1,5 +1,5 @@ -#ifndef __HOYMILES_H__ -#define __HOYMILES_H__ +#ifndef __RADIO_H__ +#define __RADIO_H__ #include #include @@ -7,18 +7,15 @@ #define CHANNEL_HOP // switch between channels or use static channel to send -#define luint64_t long long unsigned int - #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) -#define PACKET_BUFFER_SIZE 30 +#define DTU_RADIO_ID ((uint64_t)0x1234567801ULL) //----------------------------------------------------------------------------- // MACROS +//----------------------------------------------------------------------------- #define CP_U32_LittleEndian(buf, v) ({ \ uint8_t *b = buf; \ b[0] = ((v >> 24) & 0xff); \ @@ -39,64 +36,38 @@ //----------------------------------------------------------------------------- -union uint64Bytes { - uint64_t ull; - uint8_t bytes[8]; -}; - -typedef struct { - uint8_t sendCh; - uint8_t packet[MAX_RF_PAYLOAD_SIZE]; -} NRF24_packet_t; - - +// HM Radio class //----------------------------------------------------------------------------- -class hoymiles { +template +class HmRadio { public: - hoymiles() { - serial2RadioId(); - calcDtuIdCrc(); - - mChannels[0] = 23; - mChannels[1] = 40; - mChannels[2] = 61; - mChannels[3] = 75; + HmRadio() { + pinMode(IRQ_PIN, INPUT_PULLUP); + //attachInterrupt(digitalPinToInterrupt(IRQ_PIN), handleIntr, FALLING); + + mSendChan[0] = 23; + mSendChan[1] = 40; + mSendChan[2] = 61; + mSendChan[3] = 75; mChanIdx = 1; - - mLastCrc = 0x0000; - mRptCnt = 0; } - - ~hoymiles() {} + ~HmRadio() {} uint8_t getDefaultChannel(void) { - return mChannels[2]; + return mSendChan[2]; } uint8_t getLastChannel(void) { - return mChannels[mChanIdx]; + return mSendChan[mChanIdx]; } uint8_t getNxtChannel(void) { if(++mChanIdx >= 4) mChanIdx = 0; - return mChannels[mChanIdx]; + return mSendChan[mChanIdx]; } - void serial2RadioId(void) { - uint64Bytes id; - - id.ull = 0ULL; - id.bytes[4] = mAddrBytes[5]; - id.bytes[3] = mAddrBytes[4]; - id.bytes[2] = mAddrBytes[3]; - id.bytes[1] = mAddrBytes[2]; - id.bytes[0] = 0x01; - - mRadioId = id.ull; - } - - uint8_t getTimePacket(uint8_t buf[], uint32_t ts) { - getCmdPacket(buf, 0x15, 0x80, false); + 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); @@ -110,11 +81,11 @@ class hoymiles { return 27; } - uint8_t getCmdPacket(uint8_t buf[], uint8_t mid, uint8_t cmd, bool calcCrc = true) { + 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], (mRadioId >> 8)); - CP_U32_BigEndian(&buf[5], (DTU_RADIO_ID >> 8)); + 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); @@ -144,35 +115,22 @@ class hoymiles { return valid; } - 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 mAddrBytes[6]; - luint64_t mRadioId; - - private: - void calcDtuIdCrc(void) { + protected: + void getDtuIdCrc(void) { uint64_t addr = DTU_RADIO_ID; - uint8_t dtuAddr[5]; + uint8_t tmp[5]; for(int8_t i = 4; i >= 0; i--) { - dtuAddr[i] = addr; + tmp[i] = addr; addr >>= 8; } - mDtuIdCrc = crc16nrf24(dtuAddr, BIT_CNT(5)); + mDtuIdCrc = crc16nrf24(tmp, BIT_CNT(5)); } - - uint8_t mChannels[4]; + uint8_t mSendChan[4]; uint8_t mChanIdx; uint16_t mDtuIdCrc; uint16_t mLastCrc; uint8_t mRptCnt; }; -#endif /*__HOYMILES_H__*/ +#endif /*__RADIO_H__*/ diff --git a/tools/esp8266/hmSystem.h b/tools/esp8266/hmSystem.h new file mode 100644 index 00000000..2e865ac6 --- /dev/null +++ b/tools/esp8266/hmSystem.h @@ -0,0 +1,149 @@ +#ifndef __HM_SYSTEM_H__ +#define __HM_SYSTEM_H__ + +#include "hmInverters.h" +#include "hmRadio.h" + +typedef struct { + uint8_t sendCh; + uint8_t packet[MAX_RF_PAYLOAD_SIZE]; +} packet_t; + + +template +class HmSystem { + public: + typedef RADIO RadioType; + RadioType Radio; + typedef BUFFER BufferType; + BufferType BufCtrl; + + HmSystem() { + mNumInv = 0; + } + ~HmSystem() { + // TODO: cleanup + } + + inverter_t *addInverter(const char *name, uint64_t serial, uint8_t type) { + if(MAX_INVERTER <= mNumInv) { + DPRINT("max number of inverters reached!"); + return NULL; + } + inverter_t *p = &mInverter[mNumInv]; + p->id = mNumInv++; + p->serial.u64 = serial; + p->type = type; + uint8_t len = strlen(name); + strncpy(p->name, name, (len > 20) ? 20 : len); + getAssignment(p); + toRadioId(p); + + if(NULL == p->assign) { + DPRINT("no assignment for type found!"); + return NULL; + } + else { + mRecord = new RECORDTYPE[p->listLen]; + memset(mRecord, 0, sizeof(RECORDTYPE) * p->listLen); + return p; + } + } + + inverter_t *findInverter(uint8_t buf[]) { + inverter_t *p; + for(uint8_t i = 0; i < mNumInv; i++) { + p = &mInverter[i]; + if((p->serial.b[3] == buf[0]) + && (p->serial.b[2] == buf[1]) + && (p->serial.b[1] == buf[2]) + && (p->serial.b[0] == buf[3])) + return p; + } + return NULL; + } + + inverter_t *getInverterByPos(uint8_t pos) { + if(mInverter[pos].serial.u64 != 0ULL) + return &mInverter[pos]; + else + return NULL; + } + + const char *getFieldName(inverter_t *p, uint8_t pos) { + return fields[p->assign[pos].fieldId]; + } + + const char *getUnit(inverter_t *p, uint8_t pos) { + return units[p->assign[pos].unitId]; + } + + uint64_t getSerial(inverter_t *p) { + return p->serial.u64; + } + + void updateSerial(inverter_t *p, uint64_t serial) { + p->serial.u64 = serial; + } + + uint8_t getChannel(inverter_t *p, uint8_t pos) { + return p->assign[pos].ch; + } + + uint8_t getCmdId(inverter_t *p, uint8_t pos) { + return p->assign[pos].cmdId; + } + + void addValue(inverter_t *p, uint8_t pos, uint8_t buf[]) { + uint8_t ptr = p->assign[pos].start; + uint8_t end = ptr + p->assign[pos].num; + uint16_t div = p->assign[pos].div; + + uint32_t val = 0; + do { + val <<= 8; + val |= buf[ptr]; + } while(++ptr != end); + + mRecord[pos] = (RECORDTYPE)(val) / (RECORDTYPE)(div); + } + + RECORDTYPE getValue(inverter_t *p, uint8_t pos) { + return mRecord[pos]; + } + + uint8_t getNumInverters(void) { + return mNumInv; + } + + private: + void toRadioId(inverter_t *p) { + p->radioId.u64 = 0ULL; + p->radioId.b[4] = p->serial.b[0]; + p->radioId.b[3] = p->serial.b[1]; + p->radioId.b[2] = p->serial.b[2]; + p->radioId.b[1] = p->serial.b[3]; + p->radioId.b[0] = 0x01; + } + + void getAssignment(inverter_t *p) { + if(INV_TYPE_HM600 == p->type) { + p->listLen = (uint8_t)(HM1200_LIST_LEN); + p->assign = (byteAssign_t*)hm600assignment; + } + else if(INV_TYPE_HM1200 == p->type) { + p->listLen = (uint8_t)(HM1200_LIST_LEN); + p->assign = (byteAssign_t*)hm1200assignment; + } + else { + p->listLen = 0; + p->assign = NULL; + } + } + + inverter_t mInverter[MAX_INVERTER]; // TODO: only one inverter supported!!! + uint8_t mNumInv; + RECORDTYPE *mRecord; +}; + +#endif /*__HM_SYSTEM_H__*/ diff --git a/tools/esp8266/html/h/setup_html.h b/tools/esp8266/html/h/setup_html.h index f34e8a0f..8dd9a2dc 100644 --- a/tools/esp8266/html/h/setup_html.h +++ b/tools/esp8266/html/h/setup_html.h @@ -1 +1 @@ -String setup_html = "Setup - {DEVICE}

Setup

Enter the credentials to your prefered WiFi station. After rebooting the device tries to connect with this information.

WiFi

SSID
PASSWORD

Device Host Name

DEVICE NAME

Inverter

INVERTER 0 ADDRESS (eg. 11:22:33:44:55:66)
INTERVAL (ms)

MQTT

BROKER (Server IP)
USERNAME (optional)
PASSWORD (optional)
TOPIC
INTERVAL (ms)

Home

Update Firmware

AHOY - {VERSION}

"; +String setup_html = "Setup - {DEVICE}

Setup

Enter the credentials to your prefered WiFi station. After rebooting the device tries to connect with this information.

WiFi

Device Host Name

Inverter

{INVERTERS}

MQTT

 

Home

Update Firmware

AHOY - {VERSION}

"; diff --git a/tools/esp8266/html/h/style_css.h b/tools/esp8266/html/h/style_css.h index d885938b..82532bc8 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; } 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; } "; +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 { padding: 7px; font-size: 13pt; } input.text, input.password { width: 70%; box-sizing: border-box; margin-bottom: 10px; /*float: right;*/ border: 1px solid #ccc; } input.button { 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.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/setup.html b/tools/esp8266/html/setup.html index 2e9a4db0..ec7a474a 100644 --- a/tools/esp8266/html/setup.html +++ b/tools/esp8266/html/setup.html @@ -14,57 +14,32 @@

WiFi

-
- - SSID -
-
- - PASSWORD -
- - + + + +

Device Host Name

-
- - DEVICE NAME -
- + +

Inverter

-
- - INVERTER 0 ADDRESS (eg. 11:22:33:44:55:66) -
-
- - INTERVAL (ms) -
- + {INVERTERS}
+ +

MQTT

-
- - BROKER (Server IP) -
-
- - USERNAME (optional) -
-
- - PASSWORD (optional) -
-
- - TOPIC -
-
- - INTERVAL (ms) -
- - + + + + + + + + + + + +

 

diff --git a/tools/esp8266/html/style.css b/tools/esp8266/html/style.css index cfa6dad0..573d415f 100644 --- a/tools/esp8266/html/style.css +++ b/tools/esp8266/html/style.css @@ -20,9 +20,15 @@ p { } .des { + margin-top: 35px; font-size: 14pt; color: #006ec0; - padding-bottom: 0px !important; +} + +.subdes { + font-size: 13pt; + color: #006ec0; + margin-left: 7px; } .fw { @@ -74,28 +80,25 @@ a:hover, a:focus { 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; + padding: 7px; font-size: 13pt; } +input.text, input.password { + width: 70%; + box-sizing: border-box; + margin-bottom: 10px; + /*float: right;*/ + border: 1px solid #ccc; +} + input.button { background-color: #006ec0; color: #fff; @@ -109,7 +112,11 @@ input.cb { } label { - font-size: 14pt; + width: 20%; + display: inline-block; + font-size: 12pt; + padding-right: 10px; + margin-left: 10px; } .left { @@ -120,36 +127,6 @@ label { 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; diff --git a/tools/esp8266/mqtt.h b/tools/esp8266/mqtt.h index fdc32303..b3247d14 100644 --- a/tools/esp8266/mqtt.h +++ b/tools/esp8266/mqtt.h @@ -51,7 +51,7 @@ class mqtt { } char *getPwd(void) { - return mUser; + return mPwd; } char *getTopic(void) {