mirror of
https://github.com/lumapu/ahoy.git
synced 2025-04-29 10:16:21 +02:00
* new structure
* slim definitions of fields and units * prepared multi inverter setup (not finished now)
This commit is contained in:
parent
58d79beb8c
commit
d0731f7065
17 changed files with 813 additions and 672 deletions
|
@ -1,21 +1,21 @@
|
|||
/*
|
||||
CircularBuffer - An Arduino circular buffering library for arbitrary types.
|
||||
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.
|
||||
Created by Ivo Pullens, Emmission, 2014 -- www.emmission.nl
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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
|
||||
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 T> 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();
|
||||
}
|
||||
template <class BUFFERTYPE, uint8_t BUFFERSIZE>
|
||||
class CircularBuffer {
|
||||
|
||||
/** Clear all entries in the circular buffer. */
|
||||
void clear(void)
|
||||
{
|
||||
m_front = 0;
|
||||
m_fill = 0;
|
||||
}
|
||||
typedef BUFFERTYPE BufferType;
|
||||
BufferType Buffer[BUFFERSIZE];
|
||||
|
||||
/** Test if the circular buffer is empty */
|
||||
inline bool empty(void) const
|
||||
{
|
||||
return !m_fill;
|
||||
}
|
||||
public:
|
||||
CircularBuffer() : m_buff(Buffer) {
|
||||
m_size = BUFFERSIZE;
|
||||
clear();
|
||||
}
|
||||
|
||||
/** Return the number of records stored in the buffer */
|
||||
inline uint8_t available(void) const
|
||||
{
|
||||
return m_fill;
|
||||
}
|
||||
/** Clear all entries in the circular buffer. */
|
||||
void clear(void)
|
||||
{
|
||||
m_front = 0;
|
||||
m_fill = 0;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
/** Test if the circular buffer is empty */
|
||||
inline bool empty(void) const
|
||||
{
|
||||
return !m_fill;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
/** Return the number of records stored in the buffer */
|
||||
inline uint8_t available(void) const
|
||||
{
|
||||
return m_fill;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
/** Test if the circular buffer is full */
|
||||
inline bool full(void) const
|
||||
{
|
||||
return m_fill == 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.
|
||||
/** 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
|
||||
|
|
|
@ -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);
|
||||
//Serial.println("CMD " + String(*cmd, HEX));
|
||||
//dumpBuf("Payload ", p->packet, len);
|
||||
|
||||
mDecoder->convert(&p->packet[11], 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 += "<p class=\"subdes\">Inverter "+ String(i) + "</p>";
|
||||
|
||||
inv += "<label for=\"inv" + String(i) + "Addr\">Address</label>";
|
||||
inv += "<input type=\"text\" class=\"text\" name=\"inv" + String(i) + "Addr\" value=\"";
|
||||
inv += (NULL != pInv) ? String(mSys->getSerial(pInv), HEX) : "";
|
||||
inv += "\"/>";
|
||||
|
||||
inv += "<label for=\"inv" + String(i) + "Name\">Name</label>";
|
||||
inv += "<input type=\"text\" class=\"text\" name=\"inv" + String(i) + "Name\" value=\"";
|
||||
inv += (NULL != pInv) ? String(pInv->name) : "";
|
||||
inv += "\"/>";
|
||||
|
||||
inv += "<label for=\"inv" + String(i) + "Type\">Type</label>";
|
||||
inv += "<input type=\"text\" class=\"text\" name=\"inv" + String(i) + "Name\" value=\"";
|
||||
inv += (NULL != pInv) ? String(pInv->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 modHtml = "<pre>";
|
||||
|
||||
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;
|
||||
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 += "<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>";
|
||||
}*/
|
||||
|
||||
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 += "<span class=\"unit\">" + unit[j] + "</span></span>";
|
||||
modHtml += "<span class=\"info\">" + info[j] + "</span>";
|
||||
}
|
||||
modHtml += "</div>";
|
||||
}
|
||||
|
||||
modHtml += "</pre>";
|
||||
|
||||
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) {
|
|||
"<p>Error while saving</p></body></html>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -8,12 +8,15 @@
|
|||
#include "main.h"
|
||||
|
||||
#include "CircularBuffer.h"
|
||||
#include "hoymiles.h"
|
||||
#include "hm1200Decode.h"
|
||||
#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 HmSystem<RadioType, BufferType, MAX_NUM_INVERTERS, double> 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<NRF24_packet_t> *mBufCtrl;
|
||||
NRF24_packet_t mBuffer[PACKET_BUFFER_SIZE];
|
||||
packet_t mBuffer[PACKET_BUFFER_SIZE];
|
||||
HmSystemType *mSys;
|
||||
|
||||
|
||||
Ticker *mSendTicker;
|
||||
|
|
38
tools/esp8266/debug.h
Normal file
38
tools/esp8266/debug.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
#ifndef __DEBUG_H__
|
||||
#define __DEBUG_H__
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define DPRINT(str)
|
||||
#else
|
||||
|
||||
#ifndef DSERIAL
|
||||
#define DSERIAL Serial
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
inline void DPRINT(T str) { DSERIAL.print(str); }
|
||||
template <class T>
|
||||
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__*/
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
#include "eep.h"
|
||||
#include <EEPROM.h>
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
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();
|
||||
}
|
|
@ -2,29 +2,132 @@
|
|||
#define __EEP_H__
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <EEPROM.h>
|
||||
|
||||
class eep {
|
||||
public:
|
||||
eep();
|
||||
~eep();
|
||||
eep() {
|
||||
EEPROM.begin(500);
|
||||
}
|
||||
~eep() {
|
||||
EEPROM.end();
|
||||
}
|
||||
|
||||
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);
|
||||
void read(uint32_t addr, char *str, uint8_t length) {
|
||||
for(uint8_t i = 0; i < length; i ++) {
|
||||
*(str++) = (char)EEPROM.read(addr++);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
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__*/
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <DNSServer.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <Ticker.h>
|
||||
|
||||
#include <ESP8266HTTPUpdateServer.h>
|
||||
#include "app.h"
|
||||
|
||||
app myApp;
|
||||
|
@ -22,4 +31,3 @@ void loop() {
|
|||
ICACHE_RAM_ATTR void handleIntr(void) {
|
||||
myApp.handleIntr();
|
||||
}
|
||||
|
||||
|
|
|
@ -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__*/
|
111
tools/esp8266/hmInverters.h
Normal file
111
tools/esp8266/hmInverters.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
#ifndef __HM_INVERTERS_H__
|
||||
#define __HM_INVERTERS_H__
|
||||
|
||||
#include "debug.h"
|
||||
#include <cstdint>
|
||||
|
||||
// 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__*/
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef __HOYMILES_H__
|
||||
#define __HOYMILES_H__
|
||||
#ifndef __RADIO_H__
|
||||
#define __RADIO_H__
|
||||
|
||||
#include <RF24.h>
|
||||
#include <RF24_config.h>
|
||||
|
@ -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 <uint8_t CE_PIN, uint8_t CS_PIN, uint8_t IRQ_PIN, uint64_t DTU_ID=DTU_RADIO_ID>
|
||||
class HmRadio {
|
||||
public:
|
||||
hoymiles() {
|
||||
serial2RadioId();
|
||||
calcDtuIdCrc();
|
||||
HmRadio() {
|
||||
pinMode(IRQ_PIN, INPUT_PULLUP);
|
||||
//attachInterrupt(digitalPinToInterrupt(IRQ_PIN), handleIntr, FALLING);
|
||||
|
||||
mChannels[0] = 23;
|
||||
mChannels[1] = 40;
|
||||
mChannels[2] = 61;
|
||||
mChannels[3] = 75;
|
||||
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__*/
|
149
tools/esp8266/hmSystem.h
Normal file
149
tools/esp8266/hmSystem.h
Normal file
|
@ -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 RADIO, class BUFFER, uint8_t MAX_INVERTER, class RECORDTYPE=float>
|
||||
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__*/
|
|
@ -1 +1 @@
|
|||
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><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"ssid\" value=\"{SSID}\" required/><span class=\"floating_label\">SSID</span></div><div class=\"inputWrp\"><input type=\"password\" class=\"inputText\" name=\"pwd\" value=\"{PWD}\" required/><span class=\"floating_label\">PASSWORD</span></div><p class=\"des\">Device Host Name</p><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"device\" value=\"{DEVICE}\" required/><span class=\"floating_label\">DEVICE NAME</span></div><p class=\"des\">Inverter</p><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"inv0Addr\" value=\"{INV0_ADDR}\" required/><span class=\"floating_label\">INVERTER 0 ADDRESS (eg. 11:22:33:44:55:66)</span></div><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"invInterval\" value=\"{INV_INTERVAL}\" required/><span class=\"floating_label\">INTERVAL (ms)</span></div><p class=\"des\">MQTT</p><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"mqttAddr\" value=\"{MQTT_ADDR}\" required/><span class=\"floating_label\">BROKER (Server IP)</span></div><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"mqttUser\" value=\"{MQTT_USER}\"/><span class=\"floating_label\">USERNAME (optional)</span></div><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"mqttPwd\" value=\"{MQTT_PWD}\"/><span class=\"floating_label\">PASSWORD (optional)</span></div><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"mqttTopic\" value=\"{MQTT_TOPIC}\" required/><span class=\"floating_label\">TOPIC</span></div><div class=\"inputWrp\"><input type=\"text\" class=\"inputText\" name=\"mqttInterval\" value=\"{MQTT_INTERVAL}\" required/><span class=\"floating_label\">INTERVAL (ms)</span></div><input type=\"checkbox\" class=\"cb\" name=\"reboot\"/><label for=\"reboot\">Reboot device after successful save</label><input type=\"submit\" value=\"save\" class=\"button\" /></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>";
|
||||
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/><label for=\"invInterval\">Interval (ms)</label><input type=\"text\" class=\"text\" name=\"invInterval\" value=\"{INV_INTERVAL}\"/><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\"> </p><input type=\"checkbox\" class=\"cb\" name=\"reboot\"/><label for=\"reboot\">Reboot device after successful save</label><input type=\"submit\" value=\"save\" class=\"button\" /></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>";
|
||||
|
|
|
@ -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; } ";
|
||||
|
|
|
@ -14,57 +14,32 @@
|
|||
</p>
|
||||
<form method="post" action="/save">
|
||||
<p class="des">WiFi</p>
|
||||
<div class="inputWrp">
|
||||
<input type="text" class="inputText" name="ssid" value="{SSID}" required/>
|
||||
<span class="floating_label">SSID</span>
|
||||
</div>
|
||||
<div class="inputWrp">
|
||||
<input type="password" class="inputText" name="pwd" value="{PWD}" required/>
|
||||
<span class="floating_label">PASSWORD</span>
|
||||
</div>
|
||||
|
||||
|
||||
<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>
|
||||
<div class="inputWrp">
|
||||
<input type="text" class="inputText" name="device" value="{DEVICE}" required/>
|
||||
<span class="floating_label">DEVICE NAME</span>
|
||||
</div>
|
||||
|
||||
<label for="device">Device Name</label>
|
||||
<input type="text" class="text" name="device" value="{DEVICE}"/>
|
||||
|
||||
<p class="des">Inverter</p>
|
||||
<div class="inputWrp">
|
||||
<input type="text" class="inputText" name="inv0Addr" value="{INV0_ADDR}" required/>
|
||||
<span class="floating_label">INVERTER 0 ADDRESS (eg. 11:22:33:44:55:66)</span>
|
||||
</div>
|
||||
<div class="inputWrp">
|
||||
<input type="text" class="inputText" name="invInterval" value="{INV_INTERVAL}" required/>
|
||||
<span class="floating_label">INTERVAL (ms)</span>
|
||||
</div>
|
||||
|
||||
{INVERTERS}<br/>
|
||||
<label for="invInterval">Interval (ms)</label>
|
||||
<input type="text" class="text" name="invInterval" value="{INV_INTERVAL}"/>
|
||||
|
||||
<p class="des">MQTT</p>
|
||||
<div class="inputWrp">
|
||||
<input type="text" class="inputText" name="mqttAddr" value="{MQTT_ADDR}" required/>
|
||||
<span class="floating_label">BROKER (Server IP)</span>
|
||||
</div>
|
||||
<div class="inputWrp">
|
||||
<input type="text" class="inputText" name="mqttUser" value="{MQTT_USER}"/>
|
||||
<span class="floating_label">USERNAME (optional)</span>
|
||||
</div>
|
||||
<div class="inputWrp">
|
||||
<input type="text" class="inputText" name="mqttPwd" value="{MQTT_PWD}"/>
|
||||
<span class="floating_label">PASSWORD (optional)</span>
|
||||
</div>
|
||||
<div class="inputWrp">
|
||||
<input type="text" class="inputText" name="mqttTopic" value="{MQTT_TOPIC}" required/>
|
||||
<span class="floating_label">TOPIC</span>
|
||||
</div>
|
||||
<div class="inputWrp">
|
||||
<input type="text" class="inputText" name="mqttInterval" value="{MQTT_INTERVAL}" required/>
|
||||
<span class="floating_label">INTERVAL (ms)</span>
|
||||
</div>
|
||||
|
||||
<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"> </p>
|
||||
<input type="checkbox" class="cb" name="reboot"/>
|
||||
<label for="reboot">Reboot device after successful save</label>
|
||||
<input type="submit" value="save" class="button" />
|
||||
|
|
|
@ -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,26 +80,23 @@ 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: 7px;
|
||||
font-size: 13pt;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 10px;
|
||||
font-size: 13pt;
|
||||
input.text, input.password {
|
||||
width: 70%;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10px;
|
||||
/*float: right;*/
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
input.button {
|
||||
|
@ -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;
|
||||
|
|
|
@ -51,7 +51,7 @@ class mqtt {
|
|||
}
|
||||
|
||||
char *getPwd(void) {
|
||||
return mUser;
|
||||
return mPwd;
|
||||
}
|
||||
|
||||
char *getTopic(void) {
|
||||
|
|
Loading…
Add table
Reference in a new issue