Browse Source

Merge branch 'main' of https://github.com/grindylow/ahoy

pull/97/head
lumapu 2 years ago
parent
commit
c35c86210f
  1. 2
      tools/esp8266/README.md
  2. 72
      tools/esp8266/app.cpp
  3. 5
      tools/esp8266/app.h
  4. 2
      tools/esp8266/defines.h
  5. 29
      tools/esp8266/hmDefines.h
  6. 8
      tools/esp8266/mqtt.h
  7. 2
      tools/esp8266/platformio.ini
  8. 3
      tools/rpi/hoymiles/__init__.py

2
tools/esp8266/README.md

@ -16,6 +16,7 @@ This code can be compiled using Arduino. The settings were:
- Time Arduino Time library (TimeLib.h)
- RF24 Optimized high speed nRF24L01+ driver class documentation
- PubSubClient A client library for MQTT messaging. By Nick O'Leary
- ArduinoJson Arduino Json library
### Optional Configuration before compilation
@ -65,3 +66,4 @@ For now the following inverters should work out of the box:
- `Time` 1.6.1
- `RF24` 1.4.2
- `PubSubClient` 2.8
- `ArduinoJson` 6.19.4

72
tools/esp8266/app.cpp

@ -283,6 +283,7 @@ void app::loop(void) {
}
}
snprintf(val, 10, "%ld", millis()/1000);
sendMqttDiscoveryConfig();
mMqtt.sendMsg("uptime", val);
}
@ -900,3 +901,74 @@ void app::updateCrc(void) {
DPRINTLN(DBG_DEBUG, F("new CRC: ") + String(crc, HEX));
mEep->write(ADDR_SETTINGS_CRC, crc);
}
void app::sendMqttDiscoveryConfig(void) {
DPRINTLN(DBG_VERBOSE, F("app::sendMqttDiscoveryConfig"));
char stateTopic[64], discoveryTopic[64], buffer[512], name[32], uniq_id[32];
for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
Inverter<> *iv = mSys->getInverterByPos(id);
if(NULL != iv) {
if(iv->isAvailable(mTimestamp) && mMqttConfigSendState[id] != true) {
DynamicJsonDocument deviceDoc(128);
deviceDoc["name"] = iv->name;
deviceDoc["ids"] = String(iv->serial.u64, HEX);
deviceDoc["cu"] = F("http://") + String(WiFi.localIP().toString());
JsonObject deviceObj = deviceDoc.as<JsonObject>();
DynamicJsonDocument doc(384);
for(uint8_t i = 0; i < iv->listLen; i++) {
if (iv->assign[i].ch == CH0) {
snprintf(name, 32, "%s %s", iv->name, iv->getFieldName(i));
} else {
snprintf(name, 32, "%s CH%d %s", iv->name, iv->assign[i].ch, iv->getFieldName(i));
}
snprintf(stateTopic, 64, "%s/%s/ch%d/%s", mMqtt.getTopic(), iv->name, iv->assign[i].ch, iv->getFieldName(i));
snprintf(discoveryTopic, 64, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->name, iv->assign[i].ch, iv->getFieldName(i));
snprintf(uniq_id, 32, "ch%d_%s", iv->assign[i].ch, iv->getFieldName(i));
const char* devCls = getFieldDeviceClass(iv->assign[i].fieldId);
const char* stateCls = getFieldStateClass(iv->assign[i].fieldId);
doc["name"] = name;
doc["stat_t"] = stateTopic;
doc["unit_of_meas"] = iv->getUnit(i);
doc["uniq_id"] = String(iv->serial.u64, HEX) + "_" + uniq_id;
doc["dev"] = deviceObj;
doc["exp_aft"] = mMqttInterval + 5; // add 5 sec if connection is bad or ESP too slow
if (devCls != NULL) {
doc["dev_cla"] = devCls;
}
if (stateCls != NULL) {
doc["stat_cla"] = stateCls;
}
serializeJson(doc, buffer);
mMqtt.sendMsg2(discoveryTopic, buffer);
doc.clear();
yield();
}
mMqttConfigSendState[id] = true;
}
}
}
}
const char* app::getFieldDeviceClass(uint8_t fieldId) {
uint8_t pos = 0;
for(; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) {
if(deviceFieldAssignment[pos].fieldId == fieldId)
break;
}
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : deviceClasses[deviceFieldAssignment[pos].deviceClsId];
}
const char* app::getFieldStateClass(uint8_t fieldId) {
uint8_t pos = 0;
for(; pos < DEVICE_CLS_ASSIGN_LIST_LEN; pos++) {
if(deviceFieldAssignment[pos].fieldId == fieldId)
break;
}
return (pos >= DEVICE_CLS_ASSIGN_LIST_LEN) ? NULL : stateClasses[deviceFieldAssignment[pos].stateClsId];
}

5
tools/esp8266/app.h

@ -9,6 +9,7 @@
#include "main.h"
#include <RF24.h>
#include <RF24_config.h>
#include <ArduinoJson.h>
#include "defines.h"
@ -73,6 +74,9 @@ class app : public Main {
void saveValues(bool webSend);
void updateCrc(void);
void sendMqttDiscoveryConfig(void);
const char* getFieldDeviceClass(uint8_t fieldId);
const char* getFieldStateClass(uint8_t fieldId);
uint64_t Serial2u64(const char *val) {
char tmp[3] = {0};
@ -116,6 +120,7 @@ class app : public Main {
uint16_t mMqttTicker;
uint16_t mMqttInterval;
bool mMqttActive;
bool mMqttConfigSendState[MAX_NUM_INVERTERS];
// serial
uint16_t mSerialTicker;

2
tools/esp8266/defines.h

@ -56,6 +56,8 @@ typedef struct {
#define MQTT_TOPIC_LEN 32
#define MQTT_INTERVAL_LEN 2 // uint16_t
#define MQTT_PORT_LEN 2 // uint16_t
#define MQTT_DISCOVERY_PREFIX "homeassistant"
#define MQTT_MAX_PACKET_SIZE 384
#define SER_ENABLE_LEN 1 // uint8_t
#define SER_DEBUG_LEN 1 // uint8_t

29
tools/esp8266/hmDefines.h

@ -25,8 +25,35 @@ const char* const units[] = {"V", "A", "W", "Wh", "kWh", "Hz", "°C", "%"};
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, FLD_EFF, FLD_IRR};
const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
"U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct", "Effiency", "Irradiation"};
"U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct", "Efficiency", "Irradiation"};
// mqtt discovery device classes
enum {DEVICE_CLS_NONE = 0, DEVICE_CLS_CURRENT, DEVICE_CLS_ENERGY, DEVICE_CLS_PWR, DEVICE_CLS_VOLTAGE, DEVICE_CLS_FREQ, DEVICE_CLS_TEMP};
const char* const deviceClasses[] = {0, "current", "energy", "power", "voltage", "frequency", "temperature"};
enum {STATE_CLS_NONE = 0, STATE_CLS_MEASUREMENT, STATE_CLS_TOTAL_INCREASING};
const char* const stateClasses[] = {0, "measurement", "total_increasing"};
typedef struct {
uint8_t fieldId; // field id
uint8_t deviceClsId; // device class
uint8_t stateClsId; // state class
} byteAssign_fieldDeviceClass;
const byteAssign_fieldDeviceClass deviceFieldAssignment[] = {
{FLD_UDC, DEVICE_CLS_VOLTAGE, STATE_CLS_MEASUREMENT},
{FLD_IDC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT},
{FLD_PDC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT},
{FLD_YD, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING},
{FLD_YW, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING},
{FLD_YT, DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING},
{FLD_UAC, DEVICE_CLS_VOLTAGE, STATE_CLS_MEASUREMENT},
{FLD_IAC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT},
{FLD_PAC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT},
{FLD_F, DEVICE_CLS_FREQ, STATE_CLS_NONE},
{FLD_T, DEVICE_CLS_TEMP, STATE_CLS_MEASUREMENT},
{FLD_PCT, DEVICE_CLS_NONE, STATE_CLS_NONE},
{FLD_EFF, DEVICE_CLS_NONE, STATE_CLS_NONE},
{FLD_IRR, DEVICE_CLS_NONE, STATE_CLS_NONE}
};
#define DEVICE_CLS_ASSIGN_LIST_LEN (sizeof(deviceFieldAssignment) / sizeof(byteAssign_fieldDeviceClass))
// indices to calculation functions, defined in hmInverter.h
enum {CALC_YT_CH0 = 0, CALC_YD_CH0, CALC_UDC_CH, CALC_PDC_CH0, CALC_EFF_CH0, CALC_IRR_CH};

8
tools/esp8266/mqtt.h

@ -27,6 +27,7 @@ class mqtt {
DPRINTLN(DBG_VERBOSE, F("mqtt.h:setup"));
mAddressSet = true;
mClient->setServer(broker, port);
mClient->setBufferSize(MQTT_MAX_PACKET_SIZE);
mPort = port;
snprintf(mUser, MQTT_USER_LEN, "%s", user);
@ -36,14 +37,17 @@ class mqtt {
void sendMsg(const char *topic, const char *msg) {
//DPRINTLN(DBG_VERBOSE, F("mqtt.h:sendMsg"));
if(mAddressSet) {
char top[64];
snprintf(top, 64, "%s/%s", mTopic, topic);
sendMsg2(top, msg);
}
void sendMsg2(const char *topic, const char *msg) {
if(mAddressSet) {
if(!mClient->connected())
reconnect();
if(mClient->connected())
mClient->publish(top, msg);
mClient->publish(topic, msg);
}
}

2
tools/esp8266/platformio.ini

@ -13,4 +13,4 @@ lib_deps =
RF24
PubSubClient
Time
ESP Async WebServer
ArduinoJson

3
tools/rpi/hoymiles/__init__.py

@ -263,8 +263,7 @@ class InverterPacketFragment:
c_datetime = self.time_rx.strftime("%Y-%m-%d %H:%M:%S.%f")
size = len(self.frame)
channel = f' channel {self.ch_rx}' if self.ch_rx else ''
raw = " ".join([f"{b:02x}" for b in self.frame])
return f"{c_datetime} Received {size} bytes{channel}: {raw}"
return f"{c_datetime} Received {size} bytes{channel}: {hexify_payload(self.frame)}"
class HoymilesNRF:
"""Hoymiles NRF24 Interface"""

Loading…
Cancel
Save