mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-05 13:15:54 +02:00
Merge pull request #85 from KG3RK3N/mqtt_discovery
MQTT Auto Discovery Unterstützung (ESP8266)
This commit is contained in:
commit
b7cfe3e6ca
6 changed files with 123 additions and 11 deletions
|
@ -16,6 +16,7 @@ This code can be compiled using Arduino. The settings were:
|
||||||
- Time Arduino Time library (TimeLib.h)
|
- Time Arduino Time library (TimeLib.h)
|
||||||
- RF24 Optimized high speed nRF24L01+ driver class documentation
|
- RF24 Optimized high speed nRF24L01+ driver class documentation
|
||||||
- PubSubClient A client library for MQTT messaging. By Nick O'Leary
|
- PubSubClient A client library for MQTT messaging. By Nick O'Leary
|
||||||
|
- ArduinoJson Arduino Json library
|
||||||
|
|
||||||
### Optional Configuration before compilation
|
### Optional Configuration before compilation
|
||||||
|
|
||||||
|
@ -65,3 +66,4 @@ For now the following inverters should work out of the box:
|
||||||
- `Time` 1.6.1
|
- `Time` 1.6.1
|
||||||
- `RF24` 1.4.2
|
- `RF24` 1.4.2
|
||||||
- `PubSubClient` 2.8
|
- `PubSubClient` 2.8
|
||||||
|
- `ArduinoJson` 6.19.4
|
|
@ -284,6 +284,7 @@ void app::loop(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
snprintf(val, 10, "%d", millis()/1000);
|
snprintf(val, 10, "%d", millis()/1000);
|
||||||
|
sendMqttDiscoveryConfig();
|
||||||
mMqtt.sendMsg("uptime", val);
|
mMqtt.sendMsg("uptime", val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -904,3 +905,74 @@ void app::updateCrc(void) {
|
||||||
DPRINTLN(DBG_DEBUG, F("new CRC: ") + String(crc, HEX));
|
DPRINTLN(DBG_DEBUG, F("new CRC: ") + String(crc, HEX));
|
||||||
mEep->write(ADDR_SETTINGS_CRC, crc);
|
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];
|
||||||
|
}
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <RF24.h>
|
#include <RF24.h>
|
||||||
#include <RF24_config.h>
|
#include <RF24_config.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
@ -73,6 +74,9 @@ class app : public Main {
|
||||||
|
|
||||||
void saveValues(bool webSend);
|
void saveValues(bool webSend);
|
||||||
void updateCrc(void);
|
void updateCrc(void);
|
||||||
|
void sendMqttDiscoveryConfig(void);
|
||||||
|
const char* getFieldDeviceClass(uint8_t fieldId);
|
||||||
|
const char* getFieldStateClass(uint8_t fieldId);
|
||||||
|
|
||||||
uint64_t Serial2u64(const char *val) {
|
uint64_t Serial2u64(const char *val) {
|
||||||
char tmp[3] = {0};
|
char tmp[3] = {0};
|
||||||
|
@ -116,6 +120,7 @@ class app : public Main {
|
||||||
uint16_t mMqttTicker;
|
uint16_t mMqttTicker;
|
||||||
uint16_t mMqttInterval;
|
uint16_t mMqttInterval;
|
||||||
bool mMqttActive;
|
bool mMqttActive;
|
||||||
|
bool mMqttConfigSendState[MAX_NUM_INVERTERS];
|
||||||
|
|
||||||
// serial
|
// serial
|
||||||
uint16_t mSerialTicker;
|
uint16_t mSerialTicker;
|
||||||
|
|
|
@ -56,6 +56,8 @@ typedef struct {
|
||||||
#define MQTT_TOPIC_LEN 32
|
#define MQTT_TOPIC_LEN 32
|
||||||
#define MQTT_INTERVAL_LEN 2 // uint16_t
|
#define MQTT_INTERVAL_LEN 2 // uint16_t
|
||||||
#define MQTT_PORT_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_ENABLE_LEN 1 // uint8_t
|
||||||
#define SER_DEBUG_LEN 1 // uint8_t
|
#define SER_DEBUG_LEN 1 // uint8_t
|
||||||
|
|
|
@ -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,
|
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};
|
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",
|
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
|
// 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};
|
enum {CALC_YT_CH0 = 0, CALC_YD_CH0, CALC_UDC_CH, CALC_PDC_CH0, CALC_EFF_CH0, CALC_IRR_CH};
|
||||||
|
|
|
@ -29,6 +29,7 @@ class mqtt {
|
||||||
DPRINTLN(DBG_VERBOSE, F("mqtt.h:setup"));
|
DPRINTLN(DBG_VERBOSE, F("mqtt.h:setup"));
|
||||||
mAddressSet = true;
|
mAddressSet = true;
|
||||||
mClient->setServer(broker, port);
|
mClient->setServer(broker, port);
|
||||||
|
mClient->setBufferSize(MQTT_MAX_PACKET_SIZE);
|
||||||
|
|
||||||
mPort = port;
|
mPort = port;
|
||||||
snprintf(mUser, MQTT_USER_LEN, "%s", user);
|
snprintf(mUser, MQTT_USER_LEN, "%s", user);
|
||||||
|
@ -38,14 +39,17 @@ class mqtt {
|
||||||
|
|
||||||
void sendMsg(const char *topic, const char *msg) {
|
void sendMsg(const char *topic, const char *msg) {
|
||||||
//DPRINTLN(DBG_VERBOSE, F("mqtt.h:sendMsg"));
|
//DPRINTLN(DBG_VERBOSE, F("mqtt.h:sendMsg"));
|
||||||
if(mAddressSet) {
|
|
||||||
char top[64];
|
char top[64];
|
||||||
snprintf(top, 64, "%s/%s", mTopic, topic);
|
snprintf(top, 64, "%s/%s", mTopic, topic);
|
||||||
|
sendMsg2(top, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendMsg2(const char *topic, const char *msg) {
|
||||||
|
if(mAddressSet) {
|
||||||
if(!mClient->connected())
|
if(!mClient->connected())
|
||||||
reconnect();
|
reconnect();
|
||||||
if(mClient->connected())
|
if(mClient->connected())
|
||||||
mClient->publish(top, msg);
|
mClient->publish(topic, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue