Implement s0 counter and enhance onewire logic. Improve MQTT HA Autodiscover for temp sensor and s0 counter

This commit is contained in:
Rob Roos 2023-01-23 20:00:27 +01:00
parent 836ef33811
commit 9199e43b8b
17 changed files with 386 additions and 80 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -528,6 +528,18 @@ void doAutoConfigure(bool bForcaAll = false){
}
//===========================================================================================
bool doAutoConfigureMsgid(byte OTid)
{
String cfgSensorId = "" ;
// check if foney dataid is called to do autoconfigure for temp sensors, call configsensors instead
if (OTid == OTGWdallasdataid) {
MQTTDebugTf(PSTR("Sending auto configuration for temp sensors %d\r\n"), OTid);
configSensors() ;
return true;
}
else return doAutoConfigureMsgid(OTid, cfgSensorId);
}
bool doAutoConfigureMsgid(byte OTid, String cfgSensorId )
{
bool _result = false;
@ -590,6 +602,9 @@ bool doAutoConfigureMsgid(byte OTid)
/// node
sTopic.replace("%node_id%", CSTR(NodeId));
/// SensorId
sTopic.replace("%sensor_id%", CSTR(cfgSensorId));
MQTTDebugf("[%s]\r\n", CSTR(sTopic));
/// ----------------------
@ -598,6 +613,9 @@ bool doAutoConfigureMsgid(byte OTid)
/// node
sMsg.replace("%node_id%", CSTR(NodeId));
/// SensorId
sTopic.replace("%sensor_id%", CSTR(cfgSensorId));
/// hostname
sMsg.replace("%hostname%", CSTR(settingHostname));
@ -632,6 +650,27 @@ bool doAutoConfigureMsgid(byte OTid)
return _result;
}
void sensorAutoConfigure(byte dataid, bool finishflag , String cfgSensorId = "") {
// Special version of Autoconfigure for sensors
// dataid is a foney id, not used by OT
// check wheter MQTT topic needs to be configured
// cfgNodeId can be set to alternate NodeId to allow for multiple temperature sensors, should normally be NodeId
// When finishflag is true, check on dataid is already done and complete the config. On false do the config and leave completion to caller
if(getMQTTConfigDone(dataid)==false or !finishflag) {
MQTTDebugTf(PSTR("Need to set MQTT config for sensor id(%d)\r\n"),dataid);
bool success = doAutoConfigureMsgid(dataid,cfgSensorId);
if(success) {
MQTTDebugTf(PSTR("Successfully sent MQTT config for sensor id(%d)\r\n"),dataid);
if (finishflag) setMQTTConfigDone(dataid);
} else {
MQTTDebugTf(PSTR("Not able to complete MQTT configuration for sensor id(%d)\r\n"),dataid);
}
} else {
// MQTTDebugTf("No need to set MQTT config for sensor id(%d)\r\n",dataid);
}
}
/***************************************************************************

View File

@ -447,6 +447,9 @@ enum OpenThermMessageID {
{ 132, OT_READ , ot_u8u8, "RemehaServicemessage", "Remeha Servicemessage", "" },
{ 133, OT_READ , ot_u8u8, "RemehaDetectionConnectedSCU", "Remeha detection connected SCUs", "" },
// all data ids are not defined above are resevered for future use
// A foney id is used for sensors on GPIO ports,
// 245 for counter and
// 246 for Dallas temperature sensors
};
#define OT_MSGID_MAX 133

View File

@ -1487,7 +1487,7 @@ void processOT(const char *buf, int len){
if (is_value_valid(OTdata, OTlookupitem) && settingMQTTenable ) {
if(getMQTTConfigDone(OTdata.id)==false) {
MQTTDebugTf(PSTR("Need to set MQTT config for message %s (%d)\r\n"), OTlookupitem.label, OTdata.id);
bool success = doAutoConfigureMsgid(OTdata.id);
bool success = doAutoConfigureMsgid(OTdata.id, NodeId);
if(success) {
MQTTDebugTf(PSTR("Successfully sent MQTT config for message %s (%d)\r\n"), OTlookupitem.label, OTdata.id);
setMQTTConfigDone(OTdata.id);

View File

@ -19,6 +19,8 @@
#include "safeTimers.h"
#include "src/libraries/OTGWSerial/OTGWSerial.h" // Bron Schelte's Serial class - it upgrades and more
#include "OTGW-Core.h" // Core code for this firmware
#include <OneWire.h> // required for Dallas sensor library
#include <DallasTemperature.h> // Miles Burton's - Arduino Dallas library
//OTGW Nodoshop hardware definitions
#define I2CSCL D1
@ -124,8 +126,36 @@ bool settingLEDblink = true;
// GPIO Sensor Settings
bool settingGPIOSENSORSenabled = false;
int8_t settingGPIOSENSORSpin = 10;
int16_t settingGPIOSENSORSinterval = 5;
int8_t settingGPIOSENSORSpin = 13; // GPIO 13 = D7, GPIO 10 = SDIO 3
int16_t settingGPIOSENSORSinterval = 20; // Interval time to read out temp and send to MQ
byte OTGWdallasdataid = 246; // foney dataid to be used to do autoconfigure for temp sensors
int DallasrealDeviceCount = 0; // Total temperature devices found on the bus
#define MAXDALLASDEVICES 16 // maximum number of devices on the bus
// Define structure to store temperature device addresses found on bus with their latest tempC value
struct
{
int id;
DeviceAddress addr;
float tempC;
time_t lasttime;
} DallasrealDevice[MAXDALLASDEVICES];
// prototype to allow use in restAPI.ino
char* getDallasAddress(DeviceAddress deviceAddress);
// S0 Counter Settings and variables with global scope
bool settingS0COUNTERenabled = false;
uint8_t settingS0COUNTERpin = 12; // GPIO 12 = D6, preferred, can be any pin with Interupt support
uint16_t settingS0COUNTERdebouncetime = 80; // Depending on S0 switch a debouncetime should be tailored
uint16_t settingS0COUNTERpulsekw = 1000; // Most S0 counters have 1000 pulses per kW, but this can be different
uint16_t settingS0COUNTERinterval = 60; // Sugggested measurement reporting interval
uint16_t OTGWs0pulseCount; // Number of S0 pulses in measurement interval
uint32_t OTGWs0pulseCountTot = 0; // Number of S0 pulses since start of measurement
float OTGWs0powerkw = 0 ; // Calculated kW actual consumption based on time between last pulses and settings
time_t OTGWs0lasttime = 0; // Last time S0 counters have been read
byte OTGWs0dataid = 245; // foney dataid to be used to do autoconfigure for counter
//boot commands
bool settingOTGWcommandenable = false;
@ -135,6 +165,7 @@ String settingOTGWcommands = "";
bool bDebugOTmsg = true;
bool bDebugRestAPI = false;
bool bDebugMQTT = false;
bool bDebugSensors = false;
//GPIO Output Settings
bool settingMyDEBUG = false;

View File

@ -41,6 +41,7 @@
#define OFF HIGH
DECLARE_TIMER_SEC(timerpollsensor, settingGPIOSENSORSinterval, CATCH_UP_MISSED_TICKS);
DECLARE_TIMER_SEC(timers0counter, settingS0COUNTERinterval, CATCH_UP_MISSED_TICKS);
//=====================================================================
void setup() {
@ -100,7 +101,7 @@ void setup() {
// Setup the OTGW PIC
resetOTGW(); // reset the OTGW pic
startOTGWstream(); // start port 25238
initSensors(); // init DS18B20
// initSensors(); // init DS18B20 (after MQ is up! )
initOutputs();
WatchDogEnabled(1); // turn on watchdog
@ -111,6 +112,8 @@ void setup() {
setLed(LED2, OFF);
sendMQTTuptime();
sendMQTTversioninfo();
initS0Count(); // init S0 counter
initSensors(); // init DS18B20 (after MQ is up!)
}
//=====================================================================
@ -317,6 +320,7 @@ void loop()
DECLARE_TIMER_MIN(timer24h, 1440, CATCH_UP_MISSED_TICKS);
if (DUE(timerpollsensor)) pollSensors(); // poll the temperature sensors connected to 2wire gpio pin
if (DUE(timers0counter)) sendS0Counters(); // poll the s0 counter connected to gpio pin when due
if (DUE(timer5min)) do5minevent();
if (DUE(timer60s)) doTaskEvery60s();
if (DUE(timer30s)) doTaskEvery30s();

View File

@ -45,7 +45,9 @@ The features of this Nodosop OpenTherm Gateware ESP8266 based firmware are:
- integration with any MQTT based Home Automation solution, like Domoticz (plugin available) & OpenHAB
- reliable OTGW PIC upgrades (v0.6.0+), to the latest firmware available at http://otgw.tclcode.com/download.html
- cleaner RestAPI's for Telegraf OTmonitor integration
- readout Dallas-type temperture sensors (eg. DS18B20) connected to GPIO
- readout Dallas-type temperture sensors (eg. DS18B20) connected to GPIO, added automatic Home Assistant Discovery
- readout S0 output counter and timing from kWh meter connected to configurable GPIO
- Enhance Home Assistant discovery for Dallas sensors and S0 output counter
**Warning: Never flash your OTGW PIC firmware through wifi using OTmonitor application, you can brick your OTGW PIC. Instead use the buildin PIC firmware upgrade feature (based on code by Schelte Bron)**
@ -58,6 +60,7 @@ Looking for the documentation, go here (work in progress): <br> https://github.
| Version | Release notes |
|-|-|
| 0.10.0 | Readout S0 output from configurable GPIO, interupt rtn added for this, enhanced Dallas-type sensor logic (autoconfigure, code cleanup)|
| 0.9.6 | Bugfix: bitwise not bytewise AND operation for ASF flags OEM codes |
| 0.9.5 | Improved: WebUI improved by community<br>Bugfix: Device Online status indicator for Home Assistant<br>Improved: Update of 5.x series (pic16f88) firmwares, preparing for 6.x (pic16f1847) updates.<br>Bugfix: Prevent spamming OTGW firmware website in case of rebootloop<br>Added: Unique useragent|
| 0.9.4 | Update: New firmware included gateway version 5.3 for PIC P16F88.<br>Update: Preventing >5.x PIC firmwares to be detected, incompatible (for now)|

BIN
data/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -778,6 +778,15 @@
,[ "gpiosensorsenabled", "GPIO Sensors Enabled"]
,[ "gpiosensorsinterval", "GPIO Publish Interval (sec)"]
,[ "gpiosensorspin", "GPIO pin # (SD3 = GPIO10 => 10)"]
,[ "numberofsensors", "Number of temperature sensors"]
,[ "s0counterenabled", "S0 Counter Enabled"]
,[ "s0counterinterval", "S0 Counter Interval (sec)"]
,[ "s0counterpin", "S0 Counter pin # (D6 = GPIO12 => 12)"]
,[ "s0counterdebouncetime", "S0 Counter debouncetime (mS)"]
,[ "s0counterpulsekw", "S0 pulses per kW"]
,[ "s0powerkw", "S0 actual power (kW)"]
,[ "s0intervalcount", "S0 interval pulses"]
,[ "s0totalcount", "S0 total pulses"]
,[ "mqttotmessage", "MQTT OT msg Enable"]
,[ "otgwcommandenable", "OTGW Boot Command Enabled"]
,[ "otgwcommands", "OTGW Boot Command"]

View File

@ -152,3 +152,10 @@
36 ; %homeassistant%/sensor/%node_id%/ElectricalCurrentBurnerFlame/config ; {"avty_t": "%mqtt_pub_topic%", "dev": {"identifiers": "%node_id%", "manufacturer": "Schelte Bron", "model": "otgw-nodo", "name": "OpenTherm Gateway (%hostname%)", "sw_version": "%version%"}, "uniq_id": "%node_id%-ElectricalCurrentBurnerFlame", "name": "%hostname%_ElectricalCurrentBurnerFlame", "stat_t": "%mqtt_pub_topic%/ElectricalCurrentBurnerFlame", "unit_of_measurement": "uA", "value_template": "{{ value }}" }
// split
115 ; %homeassistant%/sensor/%node_id%/OEMDiagnosticCode/config ; {"avty_t": "%mqtt_pub_topic%", "dev": {"identifiers": "%node_id%", "manufacturer": "Schelte Bron", "model": "otgw-nodo", "name": "OpenTherm Gateway (%hostname%)", "sw_version": "%version%"}, "uniq_id": "%node_id%-OEMDiagnosticCode", "name": "%hostname%_OEMDiagnosticCode", "stat_t": "%mqtt_pub_topic%/OEMDiagnosticCode", "unit_of_measurement": "", "value_template": "{{ value }}" }
// S0 counter special purpose foney dataid
245 ; %homeassistant%/sensor/%node_id%/s0pulsecount/config ; {"avty_t": "%mqtt_pub_topic%", "dev": {"identifiers": "%node_id%", "manufacturer": "Schelte Bron", "model": "otgw-nodo", "name": "OpenTherm Gateway (%hostname%)", "sw_version": "%version%"}, "uniq_id": "%node_id%-s0pulsecount", "name": "%hostname%_S0_Pulse_Count", "stat_t": "%mqtt_pub_topic%/s0pulsecount", "unit_of_measurement": "", "value_template": "{{ value }}" }
245 ; %homeassistant%/sensor/%node_id%/s0pulsecounttot/config ; {"avty_t": "%mqtt_pub_topic%", "dev": {"identifiers": "%node_id%", "manufacturer": "Schelte Bron", "model": "otgw-nodo", "name": "OpenTherm Gateway (%hostname%)", "sw_version": "%version%"}, "uniq_id": "%node_id%-s0pulsecounttot", "name": "%hostname%_S0_Pulse_Count_Total", "stat_t": "%mqtt_pub_topic%/s0pulsecounttot", "unit_of_measurement": "", "value_template": "{{ value }}" }
245 ; %homeassistant%/sensor/%node_id%/s0pulsetime/config ; {"avty_t": "%mqtt_pub_topic%", "dev": {"identifiers": "%node_id%", "manufacturer": "Schelte Bron", "model": "otgw-nodo", "name": "OpenTherm Gateway (%hostname%)", "sw_version": "%version%"}, "uniq_id": "%node_id%-s0pulsetime", "name": "%hostname%_S0_Pulse_Time", "stat_t": "%mqtt_pub_topic%/s0pulsetime", "unit_of_measurement": "mS", "value_template": "{{ value }}" }
245 ; %homeassistant%/sensor/%node_id%/s0powerkw/config ; {"avty_t": "%mqtt_pub_topic%", "dev": {"identifiers": "%node_id%", "manufacturer": "Schelte Bron", "model": "otgw-nodo", "name": "OpenTherm Gateway (%hostname%)", "sw_version": "%version%"}, "uniq_id": "%node_id%-s0powerkw", "name": "%hostname%_S0_Power_kw", "stat_t": "%mqtt_pub_topic%/s0powerkw", "device_class": "power","state_class": "measurement","unit_of_measurement": "kW", "value_template": "{{ value }}" }
// Dallas temperature sensor special purpose foney dataid
246 ; %homeassistant%/sensor/%node_id%/%sensor_id%/config ; {"avty_t": "%mqtt_pub_topic%", "dev": {"identifiers": "%node_id%", "manufacturer": "Schelte Bron", "model": "otgw-nodo", "name": "OpenTherm Gateway (%hostname%)", "sw_version": "%version%"}, "uniq_id": "%node_id%-%sensor_id%", "name": "%hostname%_%sensor_id%", "stat_t": "%mqtt_pub_topic%/%sensor_id%", "state_class" : "measurement", "unit_of_measurement": "°C", "value_template": "{{ value }}" }

View File

@ -7,9 +7,10 @@ void handleDebug(){
case 'h':
Debugln();
Debugln(F("---===[ Debug Help Menu ]===---"));
Debugln(F("1) Toggle verbose debug logging - OT message parsing"));
Debugln(F("2) Toggle verbose debug logging - API handeling"));
Debugln(F("3) Toggle verbose debug logging - MQTT module"));
Debugf(PSTR("1) Toggle debuglog - OT message parsing: %s\r\n"), CBOOLEAN(bDebugOTmsg));
Debugf(PSTR("2) Toggle debuglog - API handeling: %s\r\n"), CBOOLEAN(bDebugRestAPI));
Debugf(PSTR("3) Toggle debuglog - MQTT module: %s\r\n"), CBOOLEAN(bDebugMQTT));
Debugf(PSTR("4) Toggle debuglog - Sensor modules: %s\r\n"), CBOOLEAN(bDebugSensors));
Debugln(F("q) Force read settings"));
Debugln(F("m) Force MQTT discovery"));
Debugln(F("r) Reconnect wifi, telnet, otgwstream and mqtt"));
@ -68,6 +69,10 @@ void handleDebug(){
bDebugMQTT = !bDebugMQTT;
DebugTf(PSTR("\r\nDebug MQTT: %s\r\n"), CBOOLEAN(bDebugMQTT));
break;
case '4':
bDebugSensors = !bDebugSensors;
DebugTf(PSTR("\r\nDebug Sensors: %s\r\n"), CBOOLEAN(bDebugSensors));
break;
case 'b':
DebugTln(F("Blink led 1"));
blinkLED(LED1, 5, 500);

View File

@ -200,6 +200,7 @@ bool updateRebootLog(String text)
uint32_t errorCode = -1;
//waitforNTPsync();
loopNTP(); // make sure time is up to date (improved error logging)
struct rst_info *rtc_info = system_get_rst_info();

View File

@ -255,6 +255,7 @@ void sendTelegraf()
void sendOTmonitor()
{
time_t now = time(nullptr); // needed for Dallas sensor display
RESTDebugTln(F("sending OT monitor values ...\r"));
sendStartJsonObj("otmonitor");
@ -300,7 +301,22 @@ void sendOTmonitor()
sendJsonOTmonObj("chwaterpressure", OTcurrentSystemState.CHPressure, "bar", msglastupdated[OT_CHPressure]);
sendJsonOTmonObj("oemdiagnosticcode", OTcurrentSystemState.OEMDiagnosticCode, "", msglastupdated[OT_OEMDiagnosticCode]);
sendJsonOTmonObj("oemfaultcode", OTcurrentSystemState.ASFflags & 0xFF, "", msglastupdated[OT_ASFflags]);
if (settingS0COUNTERenabled)
{
sendJsonOTmonObj("s0powerkw", formatFloat(OTGWs0powerkw,3) , "kW", OTGWs0lasttime);
sendJsonOTmonObj("s0intervalcount", OTGWs0pulseCount , "", OTGWs0lasttime);
sendJsonOTmonObj("s0totalcount", OTGWs0pulseCountTot , "", OTGWs0lasttime);
}
if (settingGPIOSENSORSenabled)
{
sendJsonOTmonObj("numberofsensors", DallasrealDeviceCount , "", int(now));
for (int i = 0; i < DallasrealDeviceCount; i++) {
const char * strDeviceAddress = getDallasAddress(DallasrealDevice[i].addr);
sendJsonOTmonObj(strDeviceAddress, formatFloat(DallasrealDevice[i].tempC,1) , "°C", DallasrealDevice[i].lasttime);
}
}
sendEndJsonObj("otmonitor");
} // sendOTmonitor()
@ -424,6 +440,11 @@ void sendDeviceSettings()
sendJsonSettingObj("gpiosensorsenabled", settingGPIOSENSORSenabled, "b");
sendJsonSettingObj("gpiosensorspin", settingGPIOSENSORSpin, "i", 0, 16);
sendJsonSettingObj("gpiosensorsinterval", settingGPIOSENSORSinterval, "i", 5, 65535);
sendJsonSettingObj("s0counterenabled", settingS0COUNTERenabled, "b");
sendJsonSettingObj("s0counterpin", settingS0COUNTERpin, "i", 1, 16);
sendJsonSettingObj("s0counterdebouncetime", settingS0COUNTERdebouncetime, "i", 0, 1000);
sendJsonSettingObj("s0counterpulsekw", settingS0COUNTERpulsekw, "i", 1, 5000);
sendJsonSettingObj("s0counterinterval", settingS0COUNTERinterval, "i", 5, 65535);
sendJsonSettingObj("gpiooutputsenabled", settingGPIOOUTPUTSenabled, "b");
sendJsonSettingObj("gpiooutputspin", settingGPIOOUTPUTSpin, "i", 0, 16);
sendJsonSettingObj("gpiooutputstriggerbit", settingGPIOOUTPUTStriggerBit, "i", 0,16);

127
s0PulseCount.ino Normal file
View File

@ -0,0 +1,127 @@
/*
***************************************************************************
** Program : s0PulseCount
** Version : v0.10.0
**
** Copyright (c) 2021-2023 Rob Roos / Robert van Breemen
** based on Framework ESP8266 from Willem Aandewiel
**
** TERMS OF USE: MIT License. See bottom of file.
***************************************************************************
Functionality to measure number of pulses from a S0 output, eg from an energy consumption meter.
The S0 port is to be connected in a NO mode, with pulse closing contact pulling a configurable pin to Low.
MQ interface is enabled with Home Assistant AutoConfigure, for this the OTGW function is reused by using a foney dataid (245)
// S0 Counter Settings and variables with global scope, to be defined in xx.h
bool settingS0COUNTERenabled = false;
uint8_t settingS0COUNTERpin = 12; // GPIO 12 = D6, preferred, can be any pin with Interupt support
uint16_t settingS0COUNTERdebouncetime = 80; // Depending on S0 switch a debouncetime should be tailored
uint16_t settingS0COUNTERpulsekw = 1000; // Most S0 counters have 1000 pulses per kW, but this can be different
uint16_t settingS0COUNTERinterval = 60; // Sugggested measurement reporting interval
uint16_t OTGWs0pulseCount; // Number of S0 pulses in measurement interval
uint32_t OTGWs0pulseCountTot = 0; // Number of S0 pulses since start of measurement
float OTGWs0powerkw = 0 ; // Calculated kW actual consumption based on time between last pulses and settings
time_t OTGWs0lasttime = 0; // Last time S0 counters have been read
byte OTGWs0dataid = 245; // Foney dataid to be used to do autoconfigure, align value with mqttha.cfg contents
*/
#include <Arduino.h>
//-----------------------------------------------------------------------------------------------------------
volatile uint8_t pulseCount = 0; // Number of pulses, used to measure energy.
volatile uint32_t last_pulse_duration = 0; // Duration of the time between last pulses
//-----------------------------------------------------------------------------------------------------------
void IRAM_ATTR IRQcounter() {
static uint32_t last_interrupt_time = 0;
volatile uint32_t interrupt_time;
interrupt_time = millis() ;
// If interrupts come faster than debouncetime, assume it's a bounce and ignore
if (interrupt_time - last_interrupt_time > settingS0COUNTERdebouncetime)
{
pulseCount++;
last_pulse_duration = interrupt_time - last_interrupt_time ;
}
last_interrupt_time = interrupt_time;
}
void initS0Count()
{
if (!settingS0COUNTERenabled) return;
//------------------------------------------------------------------------------------------------------------------------------
pinMode(settingS0COUNTERpin, INPUT_PULLUP); // Set interrupt pulse counting pin as input (Dig 3 / INT1)
OTGWs0pulseCount=0; // Make sure pulse count starts at zero
attachInterrupt(digitalPinToInterrupt(settingS0COUNTERpin), IRQcounter, FALLING) ;
if (bDebugSensors) DebugTf(PSTR("*** S0PulseCounter initialized on GPIO[%d] )\r\n"), settingS0COUNTERpin) ;
} //end SETUP
void sendS0Counters()
{
time_t now = time(nullptr);
if (!settingS0COUNTERenabled) return;
if (pulseCount != 0 ) {
noInterrupts();
OTGWs0pulseCount = pulseCount;
OTGWs0pulseCountTot = OTGWs0pulseCountTot + pulseCount;
pulseCount=0;
interrupts();
OTGWs0powerkw = (float) 3600000 / (float)settingS0COUNTERpulsekw / (float)last_pulse_duration ;
if (bDebugSensors) DebugTf(PSTR("*** S0PulseCount(%d) S0PulseCountTot(%d)\r\n"), OTGWs0pulseCount, OTGWs0pulseCountTot) ;
if (bDebugSensors) DebugTf(PSTR("*** S0LastPulsetime(%d) S0Pulsekw:(%4.3f) \r\n"), last_pulse_duration, OTGWs0powerkw) ;
OTGWs0lasttime = int(now) ;
if (settingMQTTenable ) {
sensorAutoConfigure(OTGWs0dataid, true , "" ) ; // Configure S0 sensor with the
s0sendMQ() ;
}
}
}
void s0sendMQ()
{
//Build string for MQTT
char _msg[15]{0};
char _topic[50]{0};
snprintf(_topic, sizeof _topic, "s0pulsecount");
snprintf(_msg, sizeof _msg, "%d", OTGWs0pulseCount);
sendMQTTData(_topic, _msg);
snprintf(_topic, sizeof _topic, "s0pulsecounttot");
snprintf(_msg, sizeof _msg, "%d", OTGWs0pulseCountTot);
sendMQTTData(_topic, _msg);
snprintf(_topic, sizeof _topic, "s0pulsetime");
snprintf(_msg, sizeof _msg, "%d", last_pulse_duration);
sendMQTTData(_topic, _msg);
snprintf(_topic, sizeof _topic, "s0powerkw");
snprintf(_msg, sizeof _msg, "%4.3f", OTGWs0powerkw);
sendMQTTData(_topic, _msg);
}
/***************************************************************************
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
****************************************************************************
*/

View File

@ -1,32 +1,42 @@
/*
** Program : output_ext.ino
** Version : v0.9.5
** Version : v0.10.0
**
** Copyright (c) 2021-2023 Robert van den Breemen
** Contributed by Sjorsjuhmaniac
** Modified by Rob Roos to enable MQ autoconfigure and cleanup
**
** TERMS OF USE: MIT License. See bottom of file.
**
** most code shamelessly copied from Miles Burton's - Arduino Dallas library
** example 'Multiple'
*/
#include <OneWire.h>
#include <DallasTemperature.h>
//prototype
char* getDallasAddress(DeviceAddress deviceAddress);
// GPIO where the DS18B20 is connected to
// Data wire is plugged TO GPIO 10
// #define ONE_WIRE_BUS 10
// To be included in OTGW-firmware.h
// #include <OneWire.h>
// #include <DallasTemperature.h>
// GPIO Sensor Settings
// bool settingGPIOSENSORSenabled = false;
// int8_t settingGPIOSENSORSpin = 10;
// int16_t settingGPIOSENSORSinterval = 20; // Interval time to read out temp and send to MQ
// byte OTGWdallasdataid = 246; // foney dataid to be used to do autoconfigure for temp sensors
// int DallasrealDeviceCount = 0; // Total temperature devices found on the bus
// #define MAXDALLASDEVICES 16 // maximum number of devices on the bus
//
// // Define structure to store temperature device addresses found on bus with their latest tempC value
// struct
// {
// int id;
// DeviceAddress addr;
// float tempC;
// time_t lasttime;
// } DallasrealDevice[MAXDALLASDEVICES];
//
// prototype to allow use in restAPI.ino
// char* getDallasAddress(DeviceAddress deviceAddress);
// Number of temperature devices found
int numberOfDevices;
// We'll use this variable to store a found device address
DeviceAddress tempDeviceAddress;
// Setup a oneWire instance to communicate with any OneWire devices
// needs a PIN to init correctly, pin is changed when we initSensors()
// this still may cause problems though because we this configures the pin already
@ -38,11 +48,11 @@ OneWire oneWire(settingGPIOSENSORSpin);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
// Initialise the oneWire bus on the GPIO pin
void initSensors() {
if (!settingGPIOSENSORSenabled) return;
DebugTf(PSTR("init GPIO Sensors on GPIO%d...\r\n"), settingGPIOSENSORSpin);
if (bDebugSensors)DebugTf(PSTR("init GPIO Temperature sensors on GPIO%d...\r\n"), settingGPIOSENSORSpin);
oneWire.begin(settingGPIOSENSORSpin);
@ -52,81 +62,92 @@ void initSensors() {
// Grab a count of devices on the wire
numberOfDevices = sensors.getDeviceCount();
DebugTf(PSTR("Found %d device(s)\r\n"), numberOfDevices);
int realDeviceCount = 0;
// Loop through each device, print out address
DallasrealDeviceCount = 0; // To determine the total found real temp sensors
if (numberOfDevices > MAXDALLASDEVICES)
{
DebugTf(PSTR("***ERR More (%d) sensor devices found than allowed(%d) on the bus\r\n"), numberOfDevices, MAXDALLASDEVICES);
numberOfDevices = MAXDALLASDEVICES ; // limit to max number of devices
}
if (bDebugSensors) DebugTf(PSTR("Sensors: Found %d device(s)\r\n"), numberOfDevices);
// Loop through each device, check if it is real temp sensor
for (int i = 0; i < numberOfDevices; i++)
{
// Search the wire for address
if (sensors.getAddress(tempDeviceAddress, i))
if (sensors.getAddress(DallasrealDevice[i].addr, i))
{
//TODO: get real device address, push data to mqtt topic.
DebugTf(PSTR("Device address %u device(s)\r\n"), (unsigned int) tempDeviceAddress);
DebugFlush();
realDeviceCount++;
if (bDebugSensors) DebugTf(PSTR("Device address %u device(s)\r\n"), (unsigned int) DallasrealDevice[i].addr);
DallasrealDevice[i].id = DallasrealDeviceCount ;
DallasrealDevice[i].tempC = 0 ;
DallasrealDevice[i].lasttime = 0 ;
DallasrealDeviceCount++;
}
else
{
DebugTf(PSTR("Found ghost device %d but could not detect address. Check power and cabling\r\n"), i);
DebugTf(PSTR("***ERR Found ghost device %d but could not detect address. Check power and cabling\r\n"), i);
}
}
if (numberOfDevices < 1 or realDeviceCount < 1)
if (numberOfDevices < 1 or DallasrealDeviceCount < 1)
{
DebugTln("No Sensors Found, disabled GPIO Sensors! Reboot node to search again.");
DebugTln(PSTR("***ERR No Sensors Found, disabled GPIO Sensors! Reboot node to search again."));
settingGPIOSENSORSenabled = false;
return;
}
}
int pollSensors()
// Send the sensor device address to MQ for Autoconfigure
void configSensors()
{
if (!settingGPIOSENSORSenabled) return 1;
// Setup a oneWire instance to communicate with any OneWire devices
// oneWire.begin(settingGPIOSENSORSpin);
if (settingMQTTenable) {
if (bDebugSensors) DebugTf(PSTR("Sensor Device MQ configuration started \r\n"));
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
if (numberOfDevices < 1)
{
DebugTln("No Sensors Found, please reboot the node to search for sensors");
return 1;
for (int i = 0; i < DallasrealDeviceCount ; i++)
{
// Now configure the MQ interface, it will return immediatly when already configured
const char * strDeviceAddress = getDallasAddress(DallasrealDevice[i].addr);
if (bDebugSensors) DebugTf(PSTR("Sensor Device MQ configuration for device no[%d] addr[%s] \r\n"), i, strDeviceAddress);
sensorAutoConfigure(OTGWdallasdataid, false, strDeviceAddress) ; // Configure sensor with the Dallas Deviceaddress
}
// after last sensor set the ConfigDone flag
setMQTTConfigDone(OTGWdallasdataid);
}
// DebugTln("start polling sensors");
} // configSensors()
void pollSensors()
{
time_t now = time(nullptr);
if (!settingGPIOSENSORSenabled) return;
sensors.requestTemperatures(); // Send the command to get temperatures
// Loop through each device, print out temperature data
for (int i = 0; i < numberOfDevices; i++)
// check if HA Autoconfigure must be performed (initial or as repeat for HA reboot)
if (settingMQTTenable && getMQTTConfigDone(OTGWdallasdataid)==false) configSensors() ;
// Loop through each real device, store temperature data and send to MQ
for (int i = 0; i < DallasrealDeviceCount; i++)
{
// Search the wire for address
if (sensors.getAddress(tempDeviceAddress, i))
{
// Output the device ID
// Print the data
const char * strDeviceAddress = getDallasAddress(tempDeviceAddress);
float tempC = sensors.getTempC(tempDeviceAddress);
DebugTf(PSTR("Device: %s, TempC: %f\r\n"), strDeviceAddress, tempC);
//Build string for MQTT
// Convert device address to string
const char * strDeviceAddress = getDallasAddress(DallasrealDevice[i].addr);
// Store the C temp in struc to allow it to be shown on Homepage through restAPI.ino
DallasrealDevice[i].tempC = sensors.getTempC(DallasrealDevice[i].addr);
DallasrealDevice[i].lasttime = int(now) ;
if (settingMQTTenable ) {
//Build string for MQTT, rse sendMQTTData for this
// ref MQTTPubNamespace = settingMQTTtopTopic + "/value/" + strDeviceAddress ;
char _msg[15]{0};
char _topic[50]{0};
snprintf(_topic, sizeof _topic, "otgw-firmware/sensors/%s", strDeviceAddress);
snprintf(_msg, sizeof _msg, "%f", tempC);
snprintf(_topic, sizeof _topic, "%s", strDeviceAddress);
snprintf(_msg, sizeof _msg, "%4.1f", DallasrealDevice[i].tempC);
// DebugTf(PSTR("Topic: %s -- Payload: %s\r\n"), _topic, _msg);
DebugFlush();
if (bDebugSensors) DebugFlush();
// sendMQTTData(_topic, _msg);
sendMQTTData(_topic, _msg);
// Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
}
}
delay(100);
// DebugTln("end polling sensors");
DebugFlush();
return 0;
}
// function to print a device address

View File

@ -47,6 +47,11 @@ void writeSettings(bool show)
root["GPIOSENSORSenabled"] = settingGPIOSENSORSenabled;
root["GPIOSENSORSpin"] = settingGPIOSENSORSpin;
root["GPIOSENSORSinterval"] = settingGPIOSENSORSinterval;
root["S0COUNTERenabled"] = settingS0COUNTERenabled;
root["S0COUNTERpin"] = settingS0COUNTERpin;
root["S0COUNTERdebouncetime"] = settingS0COUNTERdebouncetime;
root["S0COUNTERpulsekw"] = settingS0COUNTERpulsekw;
root["S0COUNTERinterval"] = settingS0COUNTERinterval;
root["OTGWcommandenable"] = settingOTGWcommandenable;
root["OTGWcommands"] = settingOTGWcommands;
root["GPIOOUTPUTSenabled"] = settingGPIOOUTPUTSenabled;
@ -117,6 +122,12 @@ void readSettings(bool show)
settingGPIOSENSORSpin = doc["GPIOSENSORSpin"] | settingGPIOSENSORSpin;
settingGPIOSENSORSinterval = doc["GPIOSENSORSinterval"] | settingGPIOSENSORSinterval;
CHANGE_INTERVAL_SEC(timerpollsensor, settingGPIOSENSORSinterval, CATCH_UP_MISSED_TICKS);
settingS0COUNTERenabled = doc["S0COUNTERenabled"] | settingS0COUNTERenabled;
settingS0COUNTERpin = doc["S0COUNTERpin"] | settingS0COUNTERpin;
settingS0COUNTERdebouncetime = doc["S0COUNTERdebouncetime"] | settingS0COUNTERdebouncetime;
settingS0COUNTERpulsekw = doc["S0COUNTERpulsekw"] | settingS0COUNTERpulsekw;
settingS0COUNTERinterval = doc["S0COUNTERinterval"] | settingS0COUNTERinterval;
CHANGE_INTERVAL_SEC(timers0counter, settingS0COUNTERinterval, CATCH_UP_MISSED_TICKS);
settingOTGWcommandenable = doc["OTGWcommandenable"] | settingOTGWcommandenable;
settingOTGWcommands = doc["OTGWcommands"].as<String>();
if (settingOTGWcommands=="null") settingOTGWcommands = "";
@ -148,6 +159,11 @@ void readSettings(bool show)
Debugf("GPIO Sensors : %s\r\n", CBOOLEAN(settingGPIOSENSORSenabled));
Debugf("GPIO Sen. Pin : %d\r\n", settingGPIOSENSORSpin);
Debugf("GPIO Interval : %d\r\n", settingGPIOSENSORSinterval);
Debugf("S0 Counter : %s\r\n", CBOOLEAN(settingS0COUNTERenabled));
Debugf("S0 Counter Pin : %d\r\n", settingS0COUNTERpin);
Debugf("S0 Counter Debouncetime:%d\r\n", settingS0COUNTERdebouncetime);
Debugf("S0 Counter Pulses/kw : %d\r\n", settingS0COUNTERpulsekw);
Debugf("S0 Counter Interval : %d\r\n", settingS0COUNTERinterval);
Debugf("OTGW boot cmd enabled : %s\r\n", CBOOLEAN(settingOTGWcommandenable));
Debugf("OTGW boot cmd : %s\r\n", CSTR(settingOTGWcommands));
Debugf("GPIO Outputs : %s\r\n", CBOOLEAN(settingGPIOOUTPUTSenabled));
@ -234,6 +250,25 @@ void updateSetting(const char *field, const char *newValue)
settingGPIOSENSORSinterval = atoi(newValue);
CHANGE_INTERVAL_SEC(timerpollsensor, settingGPIOSENSORSinterval, CATCH_UP_MISSED_TICKS);
}
if (strcasecmp(field, "S0COUNTERenabled") == 0)
{
settingS0COUNTERenabled = EVALBOOLEAN(newValue);
Debugln();
DebugTf(PSTR("Need reboot before S0 Counter starts counting on pin GPIO%d!\r\n\n"), settingS0COUNTERpin);
}
if (strcasecmp(field, "S0COUNTERpin") == 0)
{
settingS0COUNTERpin = atoi(newValue);
Debugln();
DebugTf(PSTR("Need reboot before S0 Counter will use new pin GPIO%d!\r\n\n"), settingS0COUNTERpin);
}
if (strcasecmp(field, "S0COUNTERdebouncetime") == 0) settingS0COUNTERdebouncetime = atoi(newValue);
if (strcasecmp(field, "S0COUNTERpulsekw") == 0) settingS0COUNTERpulsekw = atoi(newValue);
if (strcasecmp(field, "S0COUNTERinterval") == 0) {
settingS0COUNTERinterval = atoi(newValue);
CHANGE_INTERVAL_SEC(timers0counter, settingS0COUNTERinterval, CATCH_UP_MISSED_TICKS);
}
if (strcasecmp(field, "OTGWcommandenable")==0) settingOTGWcommandenable = EVALBOOLEAN(newValue);
if (strcasecmp(field, "OTGWcommands")==0) settingOTGWcommands = String(newValue);
if (strcasecmp(field, "GPIOOUTPUTSenabled") == 0)

View File

@ -1,16 +1,16 @@
//The version number conforms to semver.org format
#define _VERSION_MAJOR 0
#define _VERSION_MINOR 9
#define _VERSION_PATCH 6
#define _VERSION_BUILD 1952
#define _VERSION_GITHASH "e218a09"
#define _VERSION_MINOR 10
#define _VERSION_PATCH 0
#define _VERSION_BUILD 1953
#define _VERSION_GITHASH "0000000"
#define _VERSION_PRERELEASE beta
#define _VERSION_DATE "22-01-2023"
#define _VERSION_TIME "23:11:17"
#define _SEMVER_CORE "0.9.6"
#define _SEMVER_BUILD "0.9.6+1952"
#define _SEMVER_GITHASH "0.9.6+e218a09"
#define _SEMVER_FULL "0.9.6-beta+e218a09"
#define _SEMVER_NOBUILD "0.9.6-beta (22-01-2023)"
#define _VERSION "0.9.6-beta+e218a09 (22-01-2023)"
#define _VERSION_DATE "23/01/2023"
#define _VERSION_TIME "19:52:17"
#define _SEMVER_CORE "0.10.0"
#define _SEMVER_BUILD "0.10.0+1953"
#define _SEMVER_GITHASH "0.10.0+0000000"
#define _SEMVER_FULL "0.10.0 beta (23/01/2023)"
#define _SEMVER_NOBUILD "0.10.0 (23/01/2023)"
#define _VERSION "0.10.0+1953 (23/01/2023)"
//The version information is created automatically, more information here: https://github.com/rvdbreemen/autoinc-semver