diff --git a/main/User_config.h b/main/User_config.h
index 5d6081e..5760d66 100644
--- a/main/User_config.h
+++ b/main/User_config.h
@@ -496,4 +496,9 @@ int lowpowermode = DEFAULT_LOW_POWER_MODE;
# define LOG_LEVEL LOG_LEVEL_NOTICE
#endif
+/*-----------PLACEHOLDERS FOR OLED/LCD DISPLAY--------------*/
+// The real definitions are in config_M5.h / config_HELTEC.h
+#define displayPrint(...) // only print if not in low power mode
+#define lpDisplayPrint(...) // print in low power mode
+
#endif
diff --git a/main/ZboardHeltec.ino b/main/ZboardHeltec.ino
new file mode 100644
index 0000000..a28c7bb
--- /dev/null
+++ b/main/ZboardHeltec.ino
@@ -0,0 +1,258 @@
+/*
+ OpenMQTTGateway Addon - ESP8266 or Arduino program for home automation
+
+ Act as a wifi or ethernet gateway between your 433mhz/infrared IR signal and a MQTT broker
+ Send and receiving command by MQTT
+
+ HELTEC ESP32 LORA - SSD1306 / Onboard 0.96-inch 128*64 dot matrix OLED display
+
+ Copyright: (c)Florian ROBERT
+
+ Contributors:
+ - 1technophile
+ - NorthernMan54
+
+ This file is part of OpenMQTTGateway.
+
+ OpenMQTTGateway is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenMQTTGateway 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#include "User_config.h"
+#if defined(ZboardHELTEC)
+# include "ArduinoLog.h"
+# include "config_HELTEC.h"
+// # include "heltec.h"
+
+void logToLCD(bool display) {
+ display ? Log.begin(LOG_LEVEL_LCD, &Oled) : Log.begin(LOG_LEVEL, &Serial); // Log on LCD following LOG_LEVEL_LCD
+}
+
+void setupHELTEC() {
+ Log.trace(F("Setup HELTEC Display" CR));
+ Oled.begin();
+ Log.notice(F("Setup HELTEC Display end" CR));
+
+# if LOG_TO_LCD
+ Log.begin(LOG_LEVEL_LCD, &Oled); // Log on LCD following LOG_LEVEL_LCD
+# endif
+}
+
+void loopHELTEC() {
+ static int previousLogLevel;
+ int currentLogLevel = Log.getLastMsgLevel();
+ if (previousLogLevel != currentLogLevel && lowpowermode != 2) {
+ switch (currentLogLevel) {
+ case 1:
+ case 2:
+ // wakeScreen(NORMAL_LCD_BRIGHTNESS);
+ // M5.Lcd.fillScreen(TFT_RED); // FATAL, ERROR
+ // M5.Lcd.setTextColor(TFT_BLACK, TFT_RED);
+ break;
+ case 3:
+ // wakeScreen(NORMAL_LCD_BRIGHTNESS);
+ // M5.Lcd.fillScreen(TFT_ORANGE); // WARNING
+ // M5.Lcd.setTextColor(TFT_BLACK, TFT_ORANGE);
+ break;
+ default:
+ // wakeScreen(SLEEP_LCD_BRIGHTNESS);
+ // M5.Lcd.fillScreen(TFT_WHITE);
+ Oled.fillScreen(WHITE);
+ Oled.drawLogo((int)OLED_WIDTH * 0.24, (int)(OLED_WIDTH / 2) - OLED_WIDTH * 0.2, (int)(OLED_HEIGHT / 2) + OLED_HEIGHT * 0.2, true, true, true, true, true, true); // Name
+ break;
+ }
+ }
+ previousLogLevel = currentLogLevel;
+}
+
+void MQTTtoHELTEC(char* topicOri, JsonObject& HELTECdata) { // json object decoding
+ if (cmpToMainTopic(topicOri, subjectMQTTtoHELTECset)) {
+ Log.trace(F("MQTTtoHELTEC json set" CR));
+ // Log display set between HELTEC lcd (true) and serial monitor (false)
+ if (HELTECdata.containsKey("log-lcd")) {
+ bool displayOnLCD = HELTECdata["log-lcd"];
+ Log.notice(F("Set lcd log: %T" CR), displayOnLCD);
+ logToLCD(displayOnLCD);
+ }
+ }
+}
+
+// Simple print methonds
+
+void heltecPrint(char* line1, char* line2, char* line3) {
+ Oled.println(line1);
+ Oled.println(line2);
+ Oled.println(line3);
+ delay(2000);
+}
+
+void heltecPrint(char* line1, char* line2) {
+ Oled.println(line1);
+ Oled.println(line2);
+ delay(2000);
+}
+
+void heltecPrint(char* line1) {
+ Oled.println(line1);
+ delay(2000);
+}
+
+// This pattern was borrowed from HardwareSerial and modified to support the ssd1306 display
+
+OledSerial Oled(0); // Not sure about this, came from Hardwareserial
+OledSerial::OledSerial(int x) {
+ pinMode(RST_OLED, OUTPUT);
+ digitalWrite(RST_OLED, LOW);
+ delay(50);
+ digitalWrite(RST_OLED, HIGH);
+
+# if defined(WIFI_Kit_32) || defined(WIFI_LoRa_32) || defined(WIFI_LoRa_32_V2)
+ display = new SSD1306Wire(0x3c, SDA_OLED, SCL_OLED, GEOMETRY_128_64);
+# elif defined(Wireless_Stick)
+ display = new SSD1306Wire(0x3c, SDA_OLED, SCL_OLED, GEOMETRY_64_32);
+# endif
+} // Not sure about this, came from Hardwareserial
+
+void OledSerial::begin() {
+ // Heltec.begin(); // User OMG serial support
+ display->init();
+ display->flipScreenVertically();
+ display->setFont(ArialMT_Plain_10);
+
+ display->setColor(WHITE);
+ display->fillRect(0, 0, OLED_WIDTH, OLED_HEIGHT);
+ display->display();
+ displayIntro(OLED_WIDTH * 0.24, (OLED_WIDTH / 2) - OLED_WIDTH * 0.2, (OLED_HEIGHT / 2) + OLED_HEIGHT * 0.2);
+ display->setLogBuffer(OLED_TEXT_ROWS, OLED_TEXT_BUFFER);
+ delay(1000);
+}
+
+// Dummy virtual's from Serial
+
+int OledSerial::available(void) {
+}
+
+int OledSerial::peek(void) {
+}
+
+int OledSerial::read(void) {
+}
+
+void OledSerial::flush(void) {
+}
+
+void OledSerial::fillScreen(OLEDDISPLAY_COLOR color) {
+ display->clear();
+ display->setColor(color);
+ display->fillRect(0, 0, OLED_WIDTH, OLED_HEIGHT);
+}
+
+size_t OledSerial::write(uint8_t c) {
+ display->clear();
+ display->setColor(WHITE);
+ display->setFont(ArialMT_Plain_10);
+
+ display->write((char)c);
+ display->drawLogBuffer(0, 0);
+ display->display();
+ return 1;
+}
+
+size_t OledSerial::write(const uint8_t* buffer, size_t size) {
+ display->clear();
+ display->setColor(WHITE);
+ display->setFont(ArialMT_Plain_10);
+ while (size) {
+ display->write((char)*buffer++);
+ size--;
+ }
+ display->drawLogBuffer(0, 0);
+ display->display();
+ return size;
+}
+
+/*
+Display OpenMQTTGateway logo - borrowed from ZboardM5.ino and tweaked for ssd1306 display ( removed color and tweaked size/location )
+*/
+
+void OledSerial::displayIntro(int scale, int displayWidth, int displayHeight) {
+ drawLogo(scale, displayWidth, displayHeight, false, true, false, false, false, false); // Circle 2
+ drawLogo(scale, displayWidth, displayHeight, false, false, true, false, false, false); // Circle 3
+ drawLogo(scale, displayWidth, displayHeight, false, true, true, true, false, false); // Line 1
+ drawLogo(scale, displayWidth, displayHeight, false, true, true, false, true, false); // Line 2
+ drawLogo(scale, displayWidth, displayHeight, true, true, true, true, true, false); // Circle 1
+ drawLogo(scale, displayWidth, displayHeight, true, true, true, true, true, true); // Name
+}
+
+void OledSerial::drawLogo(int logoSize, int circle1X, int circle1Y, bool circle1, bool circle2, bool circle3, bool line1, bool line2, bool name) {
+ int circle1T = logoSize / 15;
+ int circle2T = logoSize / 25;
+ int circle3T = logoSize / 30;
+
+ int circle3Y = circle1Y - (logoSize * 1.2);
+ int circle3X = circle1X - (logoSize * 0.13);
+ int circle2X = circle1X - (logoSize * 1.05);
+ int circle2Y = circle1Y - (logoSize * 0.8);
+
+ if (line1) {
+ display->setColor(BLACK);
+ display->drawLine(circle1X - 2, circle1Y, circle2X - 2, circle2Y);
+ display->drawLine(circle1X - 1, circle1Y, circle2X - 1, circle2Y);
+ display->drawLine(circle1X, circle1Y, circle2X, circle2Y);
+ display->drawLine(circle1X + 1, circle1Y, circle2X + 1, circle2Y);
+ display->drawLine(circle1X + 2, circle1Y, circle2X + 2, circle2Y);
+ display->setColor(WHITE);
+ display->fillCircle(circle3X, circle3Y, logoSize / 4 - circle3T * 2); // , WHITE);
+ }
+ if (line2) {
+ display->setColor(BLACK);
+ display->drawLine(circle1X - 2, circle1Y, circle3X - 2, circle3Y);
+ display->drawLine(circle1X - 1, circle1Y, circle3X - 1, circle3Y);
+ display->drawLine(circle1X, circle1Y, circle3X, circle3Y);
+ display->drawLine(circle1X + 1, circle1Y, circle3X + 1, circle3Y);
+ display->setColor(WHITE);
+ display->fillCircle(circle2X, circle2Y, logoSize / 3 - circle2T * 2); // , WHITE);
+ }
+ if (circle1) {
+ display->setColor(WHITE);
+ display->fillCircle(circle1X, circle1Y, logoSize / 2); // , WHITE);
+ display->setColor(BLACK);
+ display->fillCircle(circle1X, circle1Y, logoSize / 2 - circle1T); // , TFT_GREEN);
+ display->setColor(WHITE);
+ display->fillCircle(circle1X, circle1Y, logoSize / 2 - circle1T * 2); // , WHITE);
+ }
+ if (circle2) {
+ display->setColor(WHITE);
+ display->fillCircle(circle2X, circle2Y, logoSize / 3); // , WHITE);
+ display->setColor(BLACK);
+ display->fillCircle(circle2X, circle2Y, logoSize / 3 - circle2T); // , TFT_ORANGE);
+ display->setColor(WHITE);
+ display->fillCircle(circle2X, circle2Y, logoSize / 3 - circle2T * 2); // , WHITE);
+ }
+ if (circle3) {
+ display->setColor(WHITE);
+ display->fillCircle(circle3X, circle3Y, logoSize / 4); // , WHITE);
+ display->setColor(BLACK);
+ display->fillCircle(circle3X, circle3Y, logoSize / 4 - circle3T); // , TFT_PINK);
+ display->setColor(WHITE);
+ display->fillCircle(circle3X, circle3Y, logoSize / 4 - circle3T * 2); // , WHITE);
+ }
+ if (name) {
+ display->setColor(BLACK);
+ display->drawString(circle1X + (circle1X * 0.27), circle1Y, "penMQTTGateway");
+ }
+ display->display();
+ delay(50);
+}
+
+#endif
\ No newline at end of file
diff --git a/main/config_HELTEC.h b/main/config_HELTEC.h
new file mode 100644
index 0000000..f2705a1
--- /dev/null
+++ b/main/config_HELTEC.h
@@ -0,0 +1,116 @@
+/*
+ OpenMQTTGateway Addon - ESP8266 or Arduino program for home automation
+
+ Act as a wifi or ethernet gateway between your 433mhz/infrared IR signal and a MQTT broker
+ Send and receiving command by MQTT
+
+ HELTEC ESP32 LORA - SSD1306 / Onboard 0.96-inch 128*64 dot matrix OLED display
+
+ Copyright: (c)Florian ROBERT
+
+ Contributors:
+ - 1technophile
+ - NorthernMan54
+
+ This file is part of OpenMQTTGateway.
+
+ OpenMQTTGateway is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenMQTTGateway 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifndef config_HELTEC_h
+#define config_HELTEC_h
+
+#include
+#include
+
+#include "SSD1306Wire.h"
+
+#ifdef ZboardHELTEC
+# define OLED_WIDTH 128
+# define OLED_HEIGHT 64
+# define OLED_TEXT_BUFFER 1000
+# define OLED_TEXT_ROWS 5
+#endif
+
+extern void setupHELTEC();
+extern void loopHELTEC();
+size_t OledPrint(String msg);
+
+/*-------------------DEFINE LOG LEVEL----------------------*/
+#ifndef LOG_LEVEL_LCD
+# define LOG_LEVEL_LCD LOG_LEVEL_WARNING // Default to only display Notice level messages
+#endif
+#ifndef LOG_TO_LCD
+# define LOG_TO_LCD false // Default to not display log messages on display
+#endif
+/*-------------------DEFINE MQTT TOPIC FOR CONFIG----------------------*/
+#define subjectMQTTtoHELTECset "/commands/MQTTtoHELTEC/config"
+
+// Simple construct for displaying message in lcd and oled displays
+
+#define displayPrint(...) \
+ if (lowpowermode < 2) heltecPrint(__VA_ARGS__) // only print if not in low power mode
+#define lpDisplayPrint(...) heltecPrint(__VA_ARGS__) // print in low power mode
+
+void heltecPrint(char*, char*, char*);
+void heltecPrint(char*, char*);
+void heltecPrint(char*);
+
+// This pattern was borrowed from HardwareSerial and modified to support the ssd1306 display
+
+class OledSerial : public Stream {
+private:
+ void displayIntro(int scale, int displayWidth, int displayHeight);
+
+public:
+ OledSerial(int);
+ void begin();
+ void drawLogo(int logoSize, int circle1X, int circle1Y, bool circle1, bool circle2, bool circle3, bool line1, bool line2, bool name);
+
+ SSD1306Wire* display;
+
+ int available(void); // Dummy functions
+ int peek(void); // Dummy functions
+ int read(void); // Dummy functions
+ void flush(void); // Dummy functions
+
+ void fillScreen(OLEDDISPLAY_COLOR); // fillScreen display and set color
+
+ size_t write(uint8_t);
+ size_t write(const uint8_t* buffer, size_t size);
+ inline size_t write(const char* buffer, size_t size) {
+ return write((uint8_t*)buffer, size);
+ }
+ inline size_t write(const char* s) {
+ return write((uint8_t*)s, strlen(s));
+ }
+ inline size_t write(unsigned long n) {
+ return write((uint8_t)n);
+ }
+ inline size_t write(long n) {
+ return write((uint8_t)n);
+ }
+ inline size_t write(unsigned int n) {
+ return write((uint8_t)n);
+ }
+ inline size_t write(int n) {
+ return write((uint8_t)n);
+ }
+
+protected:
+};
+
+extern OledSerial Oled;
+
+#endif
\ No newline at end of file
diff --git a/main/config_M5.h b/main/config_M5.h
index 1d1e01a..95deea9 100644
--- a/main/config_M5.h
+++ b/main/config_M5.h
@@ -65,4 +65,14 @@ extern void loopM5();
/*-------------------DEFINE MQTT TOPIC FOR CONFIG----------------------*/
#define subjectMQTTtoM5set "/commands/MQTTtoM5/config"
+// Simple print macro
+
+// Simple construct for displaying message in lcd and oled displays
+
+#define displayPrint(...) \
+ if (lowpowermode < 2) M5Print(__VA_ARGS__) // only print if not in low power mode
+#define lpDisplayPrint(...) M5Print(__VA_ARGS__) // print in low power mode
+
+void M5Print(char*, char* = "", char* = "");
+
#endif
\ No newline at end of file
diff --git a/main/main.ino b/main/main.ino
index abfd2b4..fe110fc 100644
--- a/main/main.ino
+++ b/main/main.ino
@@ -153,6 +153,9 @@ struct GfSun2000Data {};
#if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
# include "config_M5.h"
#endif
+#if defined(ZboardHELTEC)
+# include "config_HELTEC.h"
+#endif
#if defined(ZgatewayRS232)
# include "config_RS232.h"
#endif
@@ -524,10 +527,8 @@ void connectMQTT() {
#else
if (client.connect(gateway_name, mqtt_user, mqtt_pass, topic, will_QoS, will_Retain, will_Message)) {
#endif
-#if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
- if (lowpowermode < 2)
- M5Print("MQTT connected", "", "");
-#endif
+
+ displayPrint("MQTT connected");
Log.notice(F("Connected to broker" CR));
failure_number_mqtt = 0;
// Once connected, publish an announcement...
@@ -620,6 +621,10 @@ void setup() {
preferences.end();
# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
setupM5();
+# endif
+# if defined(ZboardHELTEC)
+ setupHELTEC();
+ modules.add(ZboardHELTEC);
# endif
Log.notice(F("OpenMQTTGateway Version: " OMG_VERSION CR));
# endif
@@ -873,9 +878,7 @@ void setOTA() {
# if defined(ZgatewayBT) && defined(ESP32)
stopProcessing();
# endif
-# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
- M5Print("OTA in progress", "", "");
-# endif
+ lpDisplayPrint("OTA in progress");
});
ArduinoOTA.onEnd([]() {
Log.trace(F("\nOTA done" CR));
@@ -884,9 +887,7 @@ void setOTA() {
# if defined(ZgatewayBT) && defined(ESP32)
startProcessing();
# endif
-# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
- M5Print("OTA done", "", "");
-# endif
+ lpDisplayPrint("OTA done");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Log.trace(F("Progress: %u%%\r" CR), (progress / (total / 100)));
@@ -1200,9 +1201,7 @@ void setup_wifimanager(bool reset_settings) {
{
# ifdef ESP32
if (lowpowermode < 2) {
-# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
- M5Print("Connect your phone to WIFI AP:", WifiManager_ssid, WifiManager_password);
-# endif
+ displayPrint("Connect your phone to WIFI AP:", WifiManager_ssid, WifiManager_password);
} else { // in case of low power mode we put the ESP to sleep again if we didn't get connected (typical in case the wifi is down)
# ifdef ZgatewayBT
lowPowerESP32();
@@ -1227,10 +1226,7 @@ void setup_wifimanager(bool reset_settings) {
digitalWrite(LED_INFO, !LED_INFO_ON);
}
-# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
- if (lowpowermode < 2)
- M5Print("Wifi connected", "", "");
-# endif
+ displayPrint("Wifi connected");
if (shouldSaveConfig) {
//read updated parameters
@@ -1525,6 +1521,11 @@ void loop() {
#if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
loopM5();
#endif
+
+// Function that doesn't need an active connection
+#if defined(ZboardHELTEC)
+ loopHELTEC();
+#endif
}
/**
@@ -1742,6 +1743,9 @@ void receivingMQTT(char* topicOri, char* datacallback) {
# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
MQTTtoM5(topicOri, jsondata);
# endif
+# if defined(ZboardHELTEC)
+ MQTTtoHELTEC(topicOri, jsondata);
+# endif
# ifdef ZactuatorONOFF
MQTTtoONOFF(topicOri, jsondata);
# endif
diff --git a/main/main.ino.cpp b/main/main.ino.cpp
new file mode 100644
index 0000000..51f44e2
--- /dev/null
+++ b/main/main.ino.cpp
@@ -0,0 +1,10278 @@
+# 1 "/var/folders/nk/2b522cbn5pdfsm1_n0zm50680000gp/T/tmp3fm37ml6"
+#include
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/main.ino"
+# 28 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/main.ino"
+#include "User_config.h"
+
+
+#if defined(ZgatewayRF) || defined(ZgatewayIR) || defined(ZgatewaySRFB) || defined(ZgatewayWeatherStation)
+
+struct ReceivedSignal {
+ SIGNAL_SIZE_UL_ULL value;
+ uint32_t time;
+};
+# if defined(ESP8266) || defined(ESP32) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__)
+ReceivedSignal receivedSignal[] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}};
+# else
+ReceivedSignal receivedSignal[] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
+# endif
+#define struct_size (sizeof(receivedSignal) / sizeof(ReceivedSignal))
+#endif
+
+#if defined(ESP8266) || defined(ESP32) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__)
+
+unsigned long timer_sys_measures = 0;
+#define ARDUINOJSON_USE_LONG_LONG 1
+#define ARDUINOJSON_ENABLE_STD_STRING 1
+#endif
+
+#include
+#include
+#include
+
+#include
+
+StaticJsonDocument modulesBuffer;
+JsonArray modules = modulesBuffer.to();
+
+#ifndef ZgatewayGFSunInverter
+
+
+
+struct GfSun2000Data {};
+#endif
+
+
+#if defined(ZgatewayRF) || defined(ZgatewayRF2) || defined(ZgatewayPilight) || defined(ZactuatorSomfy) || defined(ZgatewayRTL_433)
+# include "config_RF.h"
+#endif
+#ifdef ZgatewayWeatherStation
+# include "config_WeatherStation.h"
+#endif
+#ifdef ZgatewayGFSunInverter
+# include "config_GFSunInverter.h"
+#endif
+#ifdef ZgatewayLORA
+# include "config_LORA.h"
+#endif
+#ifdef ZgatewaySRFB
+# include "config_SRFB.h"
+#endif
+#ifdef ZgatewayBT
+# include "config_BT.h"
+#endif
+#ifdef ZgatewayIR
+# include "config_IR.h"
+#endif
+#ifdef Zgateway2G
+# include "config_2G.h"
+#endif
+#ifdef ZactuatorONOFF
+# include "config_ONOFF.h"
+#endif
+#ifdef ZsensorINA226
+# include "config_INA226.h"
+#endif
+#ifdef ZsensorHCSR501
+# include "config_HCSR501.h"
+#endif
+#ifdef ZsensorADC
+# include "config_ADC.h"
+#endif
+#ifdef ZsensorBH1750
+# include "config_BH1750.h"
+#endif
+#ifdef ZsensorTSL2561
+# include "config_TSL2561.h"
+#endif
+#ifdef ZsensorBME280
+# include "config_BME280.h"
+#endif
+#ifdef ZsensorHTU21
+# include "config_HTU21.h"
+#endif
+#ifdef ZsensorAHTx0
+# include "config_AHTx0.h"
+#endif
+#ifdef ZsensorHCSR04
+# include "config_HCSR04.h"
+#endif
+#ifdef ZsensorDHT
+# include "config_DHT.h"
+#endif
+#ifdef ZsensorSHTC3
+# include "config_SHTC3.h"
+#endif
+#ifdef ZsensorDS1820
+# include "config_DS1820.h"
+#endif
+#ifdef ZgatewayRFM69
+# include "config_RFM69.h"
+#endif
+#ifdef ZsensorGPIOInput
+# include "config_GPIOInput.h"
+#endif
+#ifdef ZsensorGPIOKeyCode
+# include "config_GPIOKeyCode.h"
+#endif
+#ifdef ZmqttDiscovery
+# include "config_mqttDiscovery.h"
+#endif
+#ifdef ZactuatorFASTLED
+# include "config_FASTLED.h"
+#endif
+#ifdef ZactuatorPWM
+# include "config_PWM.h"
+#endif
+#ifdef ZactuatorSomfy
+# include "config_Somfy.h"
+#endif
+#if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
+# include "config_M5.h"
+#endif
+#if defined(ZboardHELTEC)
+# include "config_HELTEC.h"
+#endif
+#if defined(ZgatewayRS232)
+# include "config_RS232.h"
+#endif
+
+
+void setupTLS(bool self_signed = false, uint8_t index = 0);
+
+
+void callback(char* topic, byte* payload, unsigned int length);
+
+char mqtt_user[parameters_size + 1] = MQTT_USER;
+char mqtt_pass[parameters_size + 1] = MQTT_PASS;
+char mqtt_server[parameters_size + 1] = MQTT_SERVER;
+char mqtt_port[6] = MQTT_PORT;
+char mqtt_topic[parameters_size + 1] = Base_Topic;
+char gateway_name[parameters_size + 1] = Gateway_Name;
+#ifdef USE_MAC_AS_GATEWAY_NAME
+#undef WifiManager_ssid
+#undef ota_hostname
+#define MAC_NAME_MAX_LEN 30
+char WifiManager_ssid[MAC_NAME_MAX_LEN];
+char ota_hostname[MAC_NAME_MAX_LEN];
+#endif
+bool connectedOnce = false;
+bool justReconnected = false;
+int failure_number_ntwk = 0;
+int failure_number_mqtt = 0;
+#ifdef ZmqttDiscovery
+bool disc = true;
+#endif
+unsigned long timer_led_measures = 0;
+static void* eClient = nullptr;
+#if defined(ESP8266) || defined(ESP32)
+static bool mqtt_secure = MQTT_SECURE_DEFAULT;
+static uint8_t mqtt_ss_index = MQTT_SECURE_SELF_SIGNED_INDEX_DEFAULT;
+static String mqtt_cert = "";
+static String ota_server_cert = "";
+#endif
+
+#ifdef ESP32
+# include
+# include
+# include
+# include
+# include
+
+# ifdef ESP32_ETHERNET
+# include
+void WiFiEvent(WiFiEvent_t event);
+static bool esp32EthConnected = false;
+# endif
+
+# include
+# include
+WiFiMulti wifiMulti;
+# include
+# include
+Preferences preferences;
+# ifdef MDNS_SD
+# include
+# endif
+
+#elif defined(ESP8266)
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+X509List caCert;
+# if MQTT_SECURE_SELF_SIGNED_CLIENT
+X509List* pClCert = nullptr;
+PrivateKey* pClKey = nullptr;
+# endif
+ESP8266WiFiMulti wifiMulti;
+# ifdef MDNS_SD
+# include
+# endif
+
+#else
+# include
+#endif
+
+#define convertTemp_CtoF(c) ((c * 1.8) + 32)
+#define convertTemp_FtoC(f) ((f - 32) * 5 / 9)
+
+
+PubSubClient client;
+void revert_hex_data(const char* in, char* out, int l);
+char* ip2CharArray(IPAddress ip);
+bool to_bool(String const& s);
+void pub(const char* topicori, const char* payload, bool retainFlag);
+void pub(const char* topicori, JsonObject& data);
+void pub(const char* topicori, const char* payload);
+void pub_custom_topic(const char* topic, JsonObject& data, boolean retain);
+void pubMQTT(const char* topic, const char* payload);
+void pubMQTT(const char* topic, const char* payload, bool retainFlag);
+void pubMQTT(String topic, const char* payload);
+void pubMQTT(const char* topic, unsigned long payload);
+void pubMQTT(const char* topic, unsigned long long payload);
+void pubMQTT(const char* topic, String payload);
+void pubMQTT(String topic, String payload);
+void pubMQTT(String topic, int payload);
+void pubMQTT(String topic, unsigned long long payload);
+void pubMQTT(String topic, float payload);
+void pubMQTT(const char* topic, float payload);
+void pubMQTT(const char* topic, int payload);
+void pubMQTT(const char* topic, unsigned int payload);
+void pubMQTT(const char* topic, long payload);
+void pubMQTT(const char* topic, double payload);
+void pubMQTT(String topic, unsigned long payload);
+bool cmpToMainTopic(const char* topicOri, const char* toAdd);
+void connectMQTT();
+void setup();
+bool wifi_reconnect_bypass();
+void setOTA();
+void setupTLS(bool self_signed, uint8_t index);
+void setup_wifi();
+void saveConfigCallback();
+void checkButton();
+void checkButton();
+void eraseAndRestart();
+void saveMqttConfig();
+void setup_wifimanager(bool reset_settings);
+void setup_ethernet_esp32();
+void setup_ethernet();
+void connectMQTTmdns();
+void loop();
+unsigned long uptime();
+void stateMeasures();
+void storeSignalValue(SIGNAL_SIZE_UL_ULL MQTTvalue);
+int getMin();
+bool isAduplicateSignal(SIGNAL_SIZE_UL_ULL value);
+void receivingMQTT(char* topicOri, char* datacallback);
+void MQTTHttpsFWUpdate(char* topicOri, JsonObject& HttpsFwUpdateData);
+void MQTTtoSYS(char* topicOri, JsonObject& SYSdata);
+String toString(uint64_t input);
+String toString(uint32_t input);
+void watchdogReboot(byte reason);
+void setupFASTLED();
+int animation_step(int duration, int steps);
+int animation_step_count(int duration, int steps);
+void FASTLEDLoop();
+boolean FASTLEDtoMQTT();
+void MQTTtoFASTLED(char* topicOri, JsonObject& jsonData);
+void MQTTtoFASTLED(char* topicOri, char* datacallback);
+void Fire2012WithPalette();
+void MQTTtoONOFF(char* topicOri, JsonObject& ONOFFdata);
+void MQTTtoONOFF(char* topicOri, char* datacallback);
+void ActuatorButtonTrigger();
+void setupPWM();
+static float perceptualToLinear(float perceptual, int channelIdx);
+void PWMLoop();
+boolean PWMtoMQTT();
+void MQTTtoPWM(char* topicOri, JsonObject& jsonData);
+void setupSomfy();
+void MQTTtoSomfy(char* topicOri, JsonObject& jsonData);
+void logToLCD(bool display);
+void setupHELTEC();
+void loopHELTEC();
+void MQTTtoHELTEC(char* topicOri, JsonObject& HELTECdata);
+void heltecPrint(char* line1, char* line2, char* line3);
+void heltecPrint(char* line1, char* line2);
+void heltecPrint(char* line1);
+void logToLCD(bool display);
+void setBrightness(int brightness);
+void setupM5();
+void sleepScreen();
+void wakeScreen(int brightness);
+void loopM5();
+void MQTTtoM5(char* topicOri, JsonObject& M5data);
+void displayIntro(int i, int X, int Y);
+void drawLogo(int logoSize, int circle1X, int circle1Y, bool circle1, bool circle2, bool circle3, bool line1, bool line2, bool name);
+void M5Print(char* line1, char* line2, char* line3);
+void setup2G();
+void setupGSM(bool deleteSMS);
+void signalStrengthAnalysis();
+bool _2GtoMQTT();
+void MQTTto2G(char* topicOri, char* datacallback);
+void MQTTto2G(char* topicOri, JsonObject& SMSdata);
+void BTConfig_init();
+void BTConfig_load();
+JsonObject& getBTJsonObject(const char* json, bool haPresenceEnabled);
+void pubBT(JsonObject& data);
+void emptyBTQueue();
+bool updateWorB(JsonObject& BTdata, bool isWhite);
+void createOrUpdateDevice(const char* mac, uint8_t flags, int model, int mac_type);
+void dumpDevices();
+void strupp(char* beg);
+void DT24Discovery(const char* mac, const char* sensorModel_id);
+void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel);
+void MHO_C401Discovery(const char* mac, const char* sensorModel);
+void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel);
+void XMWSDJ04MMCDiscovery(const char* mac, const char* sensorModel);
+void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel);
+void MHO_C401Discovery(const char* mac, const char* sensorModel);
+void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel);
+void DT24Discovery(const char* mac, const char* sensorModel_id);
+void XMWSDJ04MMCDiscovery(const char* mac, const char* sensorModel_id);
+void procBLETask(void* pvParameters);
+void BLEscan();
+void BLEconnect();
+void stopProcessing();
+void startProcessing();
+void coreTask(void* pvParameters);
+void lowPowerESP32();
+void deepSleep(uint64_t time_in_us);
+void changelowpowermode(int newLowPowerMode);
+void setupBT();
+bool BTtoMQTT();
+void RemoveJsonPropertyIf(JsonObject& obj, const char* key, bool condition);
+boolean valid_service_data(const char* data, int size);
+void launchBTDiscovery();
+void PublishDeviceData(JsonObject& BLEdata, bool processBLEData);
+void process_bledata(JsonObject& BLEdata);
+void hass_presence(JsonObject& HomePresence);
+void BTforceScan();
+void immediateBTAction(void* pvParameters);
+void startBTActionTask();
+void MQTTtoBTAction(JsonObject& BTdata);
+void MQTTtoBT(char* topicOri, JsonObject& BTdata);
+void GFSunInverterDataHandler(GfSun2000Data data);
+void GFSunInverterErrorHandler(int errorId, char* errorMessage);
+void setupGFSunInverter();
+void ZgatewayGFSunInverterMQTT();
+uint64_t getUInt64fromHex(char const* str);
+void setupIR();
+void IRtoMQTT();
+void MQTTtoIR(char* topicOri, JsonObject& IRdata);
+void setupLORA();
+void LORAtoMQTT();
+void MQTTtoLORA(char* topicOri, JsonObject& LORAdata);
+void MQTTtoLORA(char* topicOri, char* LORAdata);
+void pilightCallback(const String& protocol, const String& message, int status,
+ size_t repeats, const String& deviceID);
+void setupPilight();
+void savePilightConfig();
+void loadPilightConfig();
+void PilighttoMQTT();
+void MQTTtoPilight(char* topicOri, JsonObject& Pilightdata);
+extern void disablePilightReceive();
+extern void enablePilightReceive();
+static char* dec2binWzerofill(unsigned long Dec, unsigned int bitLength);
+void RFtoMQTTdiscovery(SIGNAL_SIZE_UL_ULL MQTTvalue);
+void setupRF();
+void RFtoMQTT();
+void MQTTtoRF(char* topicOri, char* datacallback);
+void MQTTtoRF(char* topicOri, JsonObject& RFdata);
+void disableRFReceive();
+void enableRFReceive();
+void setupRF2();
+void RF2toMQTTdiscovery(JsonObject& data);
+void RF2toMQTT();
+void rf2Callback(unsigned int period, unsigned long address, unsigned long groupBit, unsigned long unit, unsigned long switchType);
+void MQTTtoRF2(char* topicOri, char* datacallback);
+void MQTTtoRF2(char* topicOri, JsonObject& RF2data);
+void disableRF2Receive();
+void enableRF2Receive();
+uint32_t gc_checksum();
+void eeprom_setup();
+void setupRFM69(void);
+bool RFM69toMQTT(void);
+void MQTTtoRFM69(char* topicOri, char* datacallback);
+void MQTTtoRFM69(char* topicOri, JsonObject& RFM69data);
+void setupRS232();
+void RS232toMQTT();
+void MQTTtoRS232(char* topicOri, JsonObject& RS232data);
+void rtl_433_Callback(char* message);
+void setupRTL_433();
+void RTL_433Loop();
+extern void MQTTtoRTL_433(char* topicOri, JsonObject& RTLdata);
+extern void enableRTLreceive();
+extern void disableRTLreceive();
+extern int getRTLMinimumRSSI();
+extern int getRTLCurrentRSSI();
+extern int getRTLMessageCount();
+void setupSRFB();
+void _rfbSend(byte* message);
+void _rfbSend(byte* message, int times);
+bool SRFBtoMQTT();
+void _rfbDecode();
+void _rfbAck();
+bool _rfbToArray(const char* in, byte* out);
+bool _rfbToChar(byte* in, char* out);
+void MQTTtoSRFB(char* topicOri, char* datacallback);
+void MQTTtoSRFB(char* topicOri, JsonObject& SRFBdata);
+void PairedDeviceAdded(byte newID);
+void setupWeatherStation();
+void sendWindSpeedData(byte id, float wind_speed, byte battery_status);
+void sendRainData(byte id, float rain_volume, byte battery_status);
+void sendWindData(byte id, int wind_direction, float wind_gust, byte battery_status);
+void sendTemperatureData(byte id, float temperature, int humidity, byte battery_status);
+void ZgatewayWeatherStationtoMQTT();
+String getMacAddress();
+String getUniqueId(String name, String sufix);
+void createDiscoveryFromList(const char* mac,
+ const char* sensorList[][9],
+ int sensorCount,
+ const char* device_name,
+ const char* device_manufacturer,
+ const char* device_model);
+void announceDeviceTrigger(bool use_gateway_info, char* topic, char* type, char* subtype, char* unique_id, char* device_name, char* device_manufacturer, char* device_model, char* device_mac);
+void createDiscovery(const char* sensor_type,
+ const char* st_topic, const char* s_name, const char* unique_id,
+ const char* availability_topic, const char* device_class, const char* value_template,
+ const char* payload_on, const char* payload_off, const char* unit_of_meas,
+ int off_delay,
+ const char* payload_available, const char* payload_not_avalaible, bool gateway_entity, const char* cmd_topic,
+ const char* device_name, const char* device_manufacturer, const char* device_model, const char* device_mac, bool retainCmd,
+ const char* state_class, const char* state_off, const char* state_on);
+void pubMqttDiscovery();
+void setupADC();
+void MeasureADC();
+void setupZsensorAHTx0();
+void MeasureAHTTempHum();
+void setupZsensorBH1750();
+void MeasureLightIntensity();
+void setupZsensorBME280();
+void MeasureTempHumAndPressure();
+void setupDHT();
+void MeasureTempAndHum();
+void setupZsensorDS1820();
+void pubOneWire_HADiscovery();
+void MeasureDS1820Temp();
+void setupGPIOInput();
+void MeasureGPIOInput();
+void setupGPIOKeyCode();
+void MeasureGPIOKeyCode();
+void setupHCSR04();
+void MeasureDistance();
+void setupHCSR501();
+void MeasureHCSR501();
+void setupZsensorHTU21();
+void MeasureTempHum();
+void setupINA226();
+void MeasureINA226();
+static void writeRegister(byte reg, word value);
+static word readRegister(byte reg);
+void setupSHTC3();
+void MeasureTempAndHum();
+void displaySensorDetails(void);
+void setupZsensorTSL2561();
+void MeasureLightIntensityTSL2561();
+#line 249 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/main.ino"
+void revert_hex_data(const char* in, char* out, int l) {
+
+ int i = l - 2, j = 0;
+ while (i != -2) {
+ if (i % 2 == 0)
+ out[j] = in[i + 1];
+ else
+ out[j] = in[i - 1];
+ j++;
+ i--;
+ }
+ out[l - 1] = '\0';
+}
+
+
+
+
+
+long value_from_hex_data(const char* service_data, int offset, int data_length, bool reverse, bool canBeNegative = true) {
+ char data[data_length + 1];
+ memcpy(data, &service_data[offset], data_length);
+ data[data_length] = '\0';
+ long value;
+ if (reverse) {
+
+ char rev_data[data_length + 1];
+ revert_hex_data(data, rev_data, data_length + 1);
+ value = strtol(rev_data, NULL, 16);
+ } else {
+ value = strtol(data, NULL, 16);
+ }
+ if (value > 65000 && data_length <= 4 && canBeNegative)
+ value = value - 65535;
+ Log.trace(F("value %D" CR), value);
+ return value;
+}
+
+char* ip2CharArray(IPAddress ip) {
+ static char a[16];
+ sprintf(a, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+ return a;
+}
+
+bool to_bool(String const& s) {
+ return s != "0";
+}
+# 303 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/main.ino"
+void pub(const char* topicori, const char* payload, bool retainFlag) {
+ String topic = String(mqtt_topic) + String(gateway_name) + String(topicori);
+ pubMQTT(topic.c_str(), payload, retainFlag);
+}
+
+
+
+
+
+
+
+void pub(const char* topicori, JsonObject& data) {
+ String dataAsString = "";
+ serializeJson(data, dataAsString);
+ Log.notice(F("Send on %s msg %s" CR), topicori, dataAsString.c_str());
+ digitalWrite(LED_SEND_RECEIVE, LED_SEND_RECEIVE_ON);
+ String topic = String(mqtt_topic) + String(gateway_name) + String(topicori);
+#if valueAsATopic
+# ifdef ZgatewayPilight
+ String value = data["value"];
+ String protocol = data["protocol"];
+ if (value != "null" && value != 0) {
+ topic = topic + "/" + protocol + "/" + value;
+ }
+# else
+ SIGNAL_SIZE_UL_ULL value = data["value"];
+ if (value != 0) {
+ topic = topic + "/" + toString(value);
+ }
+# endif
+#endif
+
+#if jsonPublishing
+ Log.trace(F("jsonPubl - ON" CR));
+ pubMQTT(topic, dataAsString.c_str());
+#endif
+
+#if simplePublishing
+ Log.trace(F("simplePub - ON" CR));
+
+ for (JsonPair p : data) {
+# if defined(ESP8266)
+ yield();
+# endif
+ if (p.value().is() && strcmp(p.key().c_str(), "rssi") != 0) {
+ if (strcmp(p.key().c_str(), "value") == 0) {
+ pubMQTT(topic, p.value().as());
+ } else {
+ pubMQTT(topic + "/" + String(p.key().c_str()), p.value().as());
+ }
+ } else if (p.value().is()) {
+ pubMQTT(topic + "/" + String(p.key().c_str()), p.value().as());
+ } else if (p.value().is()) {
+ pubMQTT(topic + "/" + String(p.key().c_str()), p.value().as());
+ } else if (p.value().is()) {
+ pubMQTT(topic + "/" + String(p.key().c_str()), p.value().as());
+ }
+ }
+#endif
+}
+
+
+
+
+
+
+
+void pub(const char* topicori, const char* payload) {
+ String topic = String(mqtt_topic) + String(gateway_name) + String(topicori);
+ pubMQTT(topic, payload);
+}
+# 382 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/main.ino"
+void pub_custom_topic(const char* topic, JsonObject& data, boolean retain) {
+ String buffer = "";
+ serializeJson(data, buffer);
+ pubMQTT(topic, buffer.c_str(), retain);
+}
+
+
+
+
+
+
+
+void pubMQTT(const char* topic, const char* payload) {
+ pubMQTT(topic, payload, false);
+}
+# 405 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/main.ino"
+void pubMQTT(const char* topic, const char* payload, bool retainFlag) {
+ if (client.connected()) {
+ Log.trace(F("[ OMG->MQTT ] topic: %s msg: %s " CR), topic, payload);
+#if AWS_IOT
+ client.publish(topic, payload);
+#else
+ client.publish(topic, payload, retainFlag);
+#endif
+ } else {
+ Log.warning(F("Client not connected, aborting thes publication" CR));
+ }
+}
+
+void pubMQTT(String topic, const char* payload) {
+ pubMQTT(topic.c_str(), payload);
+}
+
+void pubMQTT(const char* topic, unsigned long payload) {
+ char val[11];
+ sprintf(val, "%lu", payload);
+ pubMQTT(topic, val);
+}
+
+void pubMQTT(const char* topic, unsigned long long payload) {
+ char val[21];
+ sprintf(val, "%llu", payload);
+ pubMQTT(topic, val);
+}
+
+void pubMQTT(const char* topic, String payload) {
+ pubMQTT(topic, payload.c_str());
+}
+
+void pubMQTT(String topic, String payload) {
+ pubMQTT(topic.c_str(), payload.c_str());
+}
+
+void pubMQTT(String topic, int payload) {
+ char val[12];
+ sprintf(val, "%d", payload);
+ pubMQTT(topic.c_str(), val);
+}
+
+void pubMQTT(String topic, unsigned long long payload) {
+ char val[21];
+ sprintf(val, "%llu", payload);
+ pubMQTT(topic.c_str(), val);
+}
+
+void pubMQTT(String topic, float payload) {
+ char val[12];
+ dtostrf(payload, 3, 1, val);
+ pubMQTT(topic.c_str(), val);
+}
+
+void pubMQTT(const char* topic, float payload) {
+ char val[12];
+ dtostrf(payload, 3, 1, val);
+ pubMQTT(topic, val);
+}
+
+void pubMQTT(const char* topic, int payload) {
+ char val[12];
+ sprintf(val, "%d", payload);
+ pubMQTT(topic, val);
+}
+
+void pubMQTT(const char* topic, unsigned int payload) {
+ char val[12];
+ sprintf(val, "%u", payload);
+ pubMQTT(topic, val);
+}
+
+void pubMQTT(const char* topic, long payload) {
+ char val[11];
+ sprintf(val, "%ld", payload);
+ pubMQTT(topic, val);
+}
+
+void pubMQTT(const char* topic, double payload) {
+ char val[16];
+ sprintf(val, "%f", payload);
+ pubMQTT(topic, val);
+}
+
+void pubMQTT(String topic, unsigned long payload) {
+ char val[11];
+ sprintf(val, "%lu", payload);
+ pubMQTT(topic.c_str(), val);
+}
+
+bool cmpToMainTopic(const char* topicOri, const char* toAdd) {
+
+
+ if (strncmp(topicOri, mqtt_topic, strlen(mqtt_topic)) != 0)
+ return false;
+
+ topicOri += strlen(mqtt_topic);
+
+ if (strncmp(topicOri, gateway_name, strlen(gateway_name)) != 0)
+ return false;
+ topicOri += strlen(gateway_name);
+ if (strncmp(topicOri, toAdd, strlen(toAdd)) != 0)
+ return false;
+ return true;
+}
+
+void connectMQTT() {
+#ifndef ESPWifiManualSetup
+# if defined(ESP8266) || defined(ESP32)
+ checkButton();
+# endif
+#endif
+
+ Log.warning(F("MQTT connection..." CR));
+ char topic[mqtt_topic_max_size];
+ strcpy(topic, mqtt_topic);
+ strcat(topic, gateway_name);
+ strcat(topic, will_Topic);
+ client.setBufferSize(mqtt_max_packet_size);
+#if AWS_IOT
+ if (client.connect(gateway_name, mqtt_user, mqtt_pass)) {
+#else
+ if (client.connect(gateway_name, mqtt_user, mqtt_pass, topic, will_QoS, will_Retain, will_Message)) {
+#endif
+
+ displayPrint("MQTT connected");
+ Log.notice(F("Connected to broker" CR));
+ failure_number_mqtt = 0;
+
+ pub(will_Topic, Gateway_AnnouncementMsg, will_Retain);
+
+ pub(version_Topic, OMG_VERSION, will_Retain);
+
+ char topic2[mqtt_topic_max_size];
+ strcpy(topic2, mqtt_topic);
+ strcat(topic2, gateway_name);
+ strcat(topic2, subjectMQTTtoX);
+ if (client.subscribe(topic2)) {
+#ifdef ZgatewayRF
+ client.subscribe(subjectMultiGTWRF);
+#endif
+#ifdef ZgatewayIR
+ client.subscribe(subjectMultiGTWIR);
+#endif
+#ifdef MQTT_HTTPS_FW_UPDATE
+ client.subscribe(subjectFWUpdate);
+#endif
+ Log.trace(F("Subscription OK to the subjects %s" CR), topic2);
+ }
+ } else {
+ failure_number_mqtt++;
+ Log.warning(F("failure_number_mqtt: %d" CR), failure_number_mqtt);
+ Log.warning(F("failed, rc=%d" CR), client.state());
+#if defined(ESP32)
+ if (mqtt_secure)
+ Log.warning(F("failed, ssl error code=%d" CR), ((WiFiClientSecure*)eClient)->lastError(nullptr, 0));
+#elif defined(ESP8266)
+ if (mqtt_secure)
+ Log.warning(F("failed, ssl error code=%d" CR), ((WiFiClientSecure*)eClient)->getLastSSLError());
+#endif
+ digitalWrite(LED_ERROR, LED_ERROR_ON);
+ delay(2000);
+ digitalWrite(LED_ERROR, !LED_ERROR_ON);
+ delay(5000);
+ if (failure_number_mqtt > maxRetryWatchDog) {
+ watchdogReboot(1);
+ }
+ }
+}
+
+
+void callback(char* topic, byte* payload, unsigned int length) {
+
+
+
+ Log.trace(F("Hey I got a callback %s" CR), topic);
+
+ byte* p = (byte*)malloc(length + 1);
+
+ memcpy(p, payload, length);
+
+ p[length] = '\0';
+
+ if ((strstr(topic, subjectMultiGTWKey) != NULL) ||
+ (strstr(topic, subjectGTWSendKey) != NULL) ||
+ (strstr(topic, subjectFWUpdate) != NULL))
+ receivingMQTT(topic, (char*)p);
+
+
+ free(p);
+}
+
+void setup() {
+
+ Serial.begin(SERIAL_BAUD);
+ Log.begin(LOG_LEVEL, &Serial);
+ Log.notice(F(CR "************* WELCOME TO OpenMQTTGateway **************" CR));
+
+
+ pinMode(LED_SEND_RECEIVE, OUTPUT);
+ pinMode(LED_INFO, OUTPUT);
+ pinMode(LED_ERROR, OUTPUT);
+ digitalWrite(LED_SEND_RECEIVE, !LED_SEND_RECEIVE_ON);
+ digitalWrite(LED_INFO, !LED_INFO_ON);
+ digitalWrite(LED_ERROR, !LED_ERROR_ON);
+
+#if defined(ESP8266) || defined(ESP32)
+# ifdef ESP8266
+# ifndef ZgatewaySRFB
+ Serial.end();
+ Serial.begin(SERIAL_BAUD, SERIAL_8N1, SERIAL_TX_ONLY);
+# endif
+# elif ESP32
+ preferences.begin(Gateway_Short_Name, false);
+ lowpowermode = preferences.getUInt("lowpowermode", DEFAULT_LOW_POWER_MODE);
+ preferences.end();
+# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
+ setupM5();
+# endif
+# if defined(ZboardHELTEC)
+ setupHELTEC();
+ modules.add(ZboardHELTEC);
+# endif
+ Log.notice(F("OpenMQTTGateway Version: " OMG_VERSION CR));
+# endif
+
+# ifdef USE_MAC_AS_GATEWAY_NAME
+ String s = WiFi.macAddress();
+ sprintf(gateway_name, "%.2s%.2s%.2s%.2s%.2s%.2s",
+ s.c_str(), s.c_str() + 3, s.c_str() + 6, s.c_str() + 9, s.c_str() + 12, s.c_str() + 15);
+ snprintf(WifiManager_ssid, MAC_NAME_MAX_LEN, "%s_%s", Gateway_Short_Name, gateway_name);
+ strcpy(ota_hostname, WifiManager_ssid);
+ Log.notice(F("OTA Hostname: %s.local" CR), ota_hostname);
+# endif
+
+# ifdef ESP32_ETHERNET
+ setup_ethernet_esp32();
+# else
+# if defined(ESPWifiManualSetup)
+ setup_wifi();
+# else
+ setup_wifimanager(false);
+# endif
+ Log.trace(F("OpenMQTTGateway mac: %s" CR), WiFi.macAddress().c_str());
+ Log.trace(F("OpenMQTTGateway ip: %s" CR), WiFi.localIP().toString().c_str());
+# endif
+
+ setOTA();
+#else
+
+
+ Serial.begin(SERIAL_BAUD);
+
+ setup_ethernet();
+#endif
+
+#if defined(ESP8266) || defined(ESP32)
+ if (mqtt_secure) {
+ eClient = new WiFiClientSecure;
+ setupTLS(MQTT_SECURE_SELF_SIGNED, mqtt_ss_index);
+ } else {
+ eClient = new WiFiClient;
+ }
+#else
+ eClient = new EthernetClient;
+#endif
+ client.setClient(*(Client*)eClient);
+
+#if defined(MDNS_SD) && (defined(ESP8266) || defined(ESP32))
+ Log.trace(F("Connecting to MQTT by mDNS without MQTT hostname" CR));
+ connectMQTTmdns();
+#else
+ uint16_t port = strtol(mqtt_port, NULL, 10);
+ Log.trace(F("Port: %l" CR), port);
+ Log.trace(F("Mqtt server: %s" CR), mqtt_server);
+ client.setServer(mqtt_server, port);
+#endif
+
+ client.setCallback(callback);
+
+ delay(1500);
+
+#ifdef ZactuatorONOFF
+ pinMode(ACTUATOR_ONOFF_GPIO, OUTPUT);
+ digitalWrite(ACTUATOR_ONOFF_GPIO, ACTUATOR_ONOFF_DEFAULT);
+#endif
+#ifdef ZsensorBME280
+ setupZsensorBME280();
+ modules.add(ZsensorBME280);
+#endif
+#ifdef ZsensorHTU21
+ setupZsensorHTU21();
+ modules.add(ZsensorHTU21);
+#endif
+#ifdef ZsensorAHTx0
+ setupZsensorAHTx0();
+ modules.add(ZsensorAHTx0);
+#endif
+#ifdef ZsensorBH1750
+ setupZsensorBH1750();
+ modules.add(ZsensorBH1750);
+#endif
+#ifdef ZsensorTSL2561
+ setupZsensorTSL2561();
+ modules.add(ZsensorTSL2561);
+#endif
+#ifdef Zgateway2G
+ setup2G();
+ modules.add(Zgateway2G);
+#endif
+#ifdef ZgatewayIR
+ setupIR();
+ modules.add(ZgatewayIR);
+#endif
+#ifdef ZgatewayLORA
+ setupLORA();
+ modules.add(ZgatewayLORA);
+#endif
+#ifdef ZgatewayRF
+ setupRF();
+ modules.add(ZgatewayRF);
+#define ACTIVE_RECEIVER ACTIVE_RF
+#endif
+#ifdef ZgatewayRF2
+ setupRF2();
+ modules.add(ZgatewayRF2);
+# ifdef ACTIVE_RECEIVER
+#undef ACTIVE_RECEIVER
+# endif
+#define ACTIVE_RECEIVER ACTIVE_RF2
+#endif
+#ifdef ZgatewayPilight
+ setupPilight();
+ modules.add(ZgatewayPilight);
+ disablePilightReceive();
+# ifdef ACTIVE_RECEIVER
+#undef ACTIVE_RECEIVER
+# endif
+#define ACTIVE_RECEIVER ACTIVE_PILIGHT
+#endif
+#ifdef ZgatewayWeatherStation
+ setupWeatherStation();
+ modules.add(ZgatewayWeatherStation);
+#endif
+#ifdef ZgatewayGFSunInverter
+ setupGFSunInverter();
+ modules.add(ZgatewayGFSunInverter);
+#endif
+#ifdef ZgatewaySRFB
+ setupSRFB();
+ modules.add(ZgatewaySRFB);
+#endif
+#ifdef ZgatewayBT
+ setupBT();
+ modules.add(ZgatewayBT);
+#endif
+#ifdef ZgatewayRFM69
+ setupRFM69();
+ modules.add(ZgatewayRFM69);
+#endif
+#ifdef ZsensorINA226
+ setupINA226();
+ modules.add(ZsensorINA226);
+#endif
+#ifdef ZsensorHCSR501
+ setupHCSR501();
+ modules.add(ZsensorHCSR501);
+#endif
+#ifdef ZsensorHCSR04
+ setupHCSR04();
+ modules.add(ZsensorHCSR04);
+#endif
+#ifdef ZsensorGPIOInput
+ setupGPIOInput();
+ modules.add(ZsensorGPIOInput);
+#endif
+#ifdef ZsensorGPIOKeyCode
+ setupGPIOKeyCode();
+ modules.add(ZsensorGPIOKeyCode);
+#endif
+#ifdef ZactuatorFASTLED
+ setupFASTLED();
+ modules.add(ZactuatorFASTLED);
+#endif
+#ifdef ZactuatorPWM
+ setupPWM();
+ modules.add(ZactuatorPWM);
+#endif
+#ifdef ZactuatorSomfy
+ setupSomfy();
+ modules.add(ZactuatorSomfy);
+#endif
+#ifdef ZsensorDS1820
+ setupZsensorDS1820();
+ modules.add(ZsensorDS1820);
+#endif
+#ifdef ZsensorADC
+ setupADC();
+ modules.add(ZsensorADC);
+#endif
+#ifdef ZsensorDHT
+ setupDHT();
+ modules.add(ZsensorDHT);
+#endif
+#ifdef ZgatewayRS232
+ setupRS232();
+ modules.add(ZgatewayRS232);
+#endif
+#ifdef ZsensorSHTC3
+ setupSHTC3();
+#endif
+#ifdef ZgatewayRTL_433
+ setupRTL_433();
+ modules.add(ZgatewayRTL_433);
+# ifdef ACTIVE_RECEIVER
+#undef ACTIVE_RECEIVER
+# endif
+#define ACTIVE_RECEIVER ACTIVE_RTL
+#endif
+#if defined(ZgatewayRTL_433) || defined(ZgatewayRF) || defined(ZgatewayPilight) || defined(ZgatewayRF2)
+# ifdef DEFAULT_RECEIVER
+ activeReceiver = DEFAULT_RECEIVER;
+# else
+ activeReceiver = ACTIVE_RECEIVER;
+# endif
+ enableActiveReceiver(true);
+#endif
+ Log.trace(F("mqtt_max_packet_size: %d" CR), mqtt_max_packet_size);
+
+#ifndef ARDUINO_AVR_UNO
+ char jsonChar[100];
+ serializeJson(modules, jsonChar, measureJson(modules) + 1);
+ Log.notice(F("OpenMQTTGateway modules: %s" CR), jsonChar);
+#endif
+ Log.notice(F("************** Setup OpenMQTTGateway end **************" CR));
+}
+
+#if defined(ESP8266) || defined(ESP32)
+
+bool wifi_reconnect_bypass() {
+ uint8_t wifi_autoreconnect_cnt = 0;
+# ifdef ESP32
+ while (WiFi.status() != WL_CONNECTED && wifi_autoreconnect_cnt < maxConnectionRetryWifi) {
+# else
+ while (WiFi.waitForConnectResult() != WL_CONNECTED && wifi_autoreconnect_cnt < maxConnectionRetryWifi) {
+# endif
+ Log.notice(F("Attempting Wifi connection with saved AP: %d" CR), wifi_autoreconnect_cnt);
+ WiFi.begin();
+ delay(500);
+ wifi_autoreconnect_cnt++;
+ }
+ if (wifi_autoreconnect_cnt < maxConnectionRetryWifi) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void setOTA() {
+
+ ArduinoOTA.setPort(ota_port);
+
+
+ ArduinoOTA.setHostname(ota_hostname);
+
+
+ ArduinoOTA.setPassword(ota_password);
+
+ ArduinoOTA.onStart([]() {
+ Log.trace(F("Start OTA, lock other functions" CR));
+ digitalWrite(LED_SEND_RECEIVE, LED_SEND_RECEIVE_ON);
+ digitalWrite(LED_ERROR, LED_ERROR_ON);
+# if defined(ZgatewayBT) && defined(ESP32)
+ stopProcessing();
+# endif
+ lpDisplayPrint("OTA in progress");
+ });
+ ArduinoOTA.onEnd([]() {
+ Log.trace(F("\nOTA done" CR));
+ digitalWrite(LED_SEND_RECEIVE, !LED_SEND_RECEIVE_ON);
+ digitalWrite(LED_ERROR, !LED_ERROR_ON);
+# if defined(ZgatewayBT) && defined(ESP32)
+ startProcessing();
+# endif
+ lpDisplayPrint("OTA done");
+ });
+ ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
+ Log.trace(F("Progress: %u%%\r" CR), (progress / (total / 100)));
+ });
+ ArduinoOTA.onError([](ota_error_t error) {
+ digitalWrite(LED_SEND_RECEIVE, !LED_SEND_RECEIVE_ON);
+ digitalWrite(LED_ERROR, !LED_ERROR_ON);
+# if defined(ZgatewayBT) && defined(ESP32)
+ startProcessing();
+# endif
+ Serial.printf("Error[%u]: ", error);
+ if (error == OTA_AUTH_ERROR)
+ Log.error(F("Auth Failed" CR));
+ else if (error == OTA_BEGIN_ERROR)
+ Log.error(F("Begin Failed" CR));
+ else if (error == OTA_CONNECT_ERROR)
+ Log.error(F("Connect Failed" CR));
+ else if (error == OTA_RECEIVE_ERROR)
+ Log.error(F("Receive Failed" CR));
+ else if (error == OTA_END_ERROR)
+ Log.error(F("End Failed" CR));
+ });
+ ArduinoOTA.begin();
+}
+
+void setupTLS(bool self_signed, uint8_t index) {
+ configTime(0, 0, NTP_SERVER);
+ WiFiClientSecure* sClient = (WiFiClientSecure*)eClient;
+# if MQTT_SECURE_SELF_SIGNED
+ if (self_signed) {
+ Log.notice(F("Using self signed cert index %u" CR), index);
+# if defined(ESP32)
+ sClient->setCACert(certs_array[index].server_cert);
+# if MQTT_SECURE_SELF_SIGNED_CLIENT
+ sClient->setCertificate(certs_array[index].client_cert);
+ sClient->setPrivateKey(certs_array[index].client_key);
+# endif
+# elif defined(ESP8266)
+ caCert.append(certs_array[index].server_cert);
+ sClient->setTrustAnchors(&caCert);
+ sClient->setBufferSizes(512, 512);
+# if MQTT_SECURE_SELF_SIGNED_CLIENT
+ if (pClCert != nullptr) {
+ delete pClCert;
+ }
+ if (pClKey != nullptr) {
+ delete pClKey;
+ }
+ pClCert = new X509List(certs_array[index].client_cert);
+ pClKey = new PrivateKey(certs_array[index].client_key);
+ sClient->setClientRSACert(pClCert, pClKey);
+# endif
+# endif
+ } else
+# endif
+ {
+ if (mqtt_cert.length() > 0) {
+# if defined(ESP32)
+ sClient->setCACert(mqtt_cert.c_str());
+ } else {
+ sClient->setCACert(certificate);
+ }
+# elif defined(ESP8266)
+ caCert.append(mqtt_cert.c_str());
+ } else {
+ caCert.append(certificate);
+ }
+ sClient->setTrustAnchors(&caCert);
+ sClient->setBufferSizes(512, 512);
+# endif
+ }
+}
+#endif
+
+#if defined(ESPWifiManualSetup)
+void setup_wifi() {
+ WiFi.mode(WIFI_STA);
+ wifiMulti.addAP(wifi_ssid, wifi_password);
+ Log.trace(F("Connecting to %s" CR), wifi_ssid);
+# ifdef wifi_ssid1
+ wifiMulti.addAP(wifi_ssid1, wifi_password1);
+ Log.trace(F("Connecting to %s" CR), wifi_ssid1);
+# endif
+ delay(10);
+
+
+
+# ifdef NetworkAdvancedSetup
+ IPAddress ip_adress(ip);
+ IPAddress gateway_adress(gateway);
+ IPAddress subnet_adress(subnet);
+ IPAddress dns_adress(Dns);
+ if (!WiFi.config(ip_adress, gateway_adress, subnet_adress, dns_adress)) {
+ Log.error(F("Wifi STA Failed to configure" CR));
+ }
+
+# endif
+
+ while (wifiMulti.run() != WL_CONNECTED) {
+ delay(500);
+ Log.trace(F("." CR));
+ failure_number_ntwk++;
+# if defined(ESP32) && defined(ZgatewayBT)
+ if (lowpowermode) {
+ if (failure_number_ntwk > maxConnectionRetryWifi) {
+ lowPowerESP32();
+ }
+ } else {
+ if (failure_number_ntwk > maxRetryWatchDog) {
+ watchdogReboot(2);
+ }
+ }
+# else
+ if (failure_number_ntwk > maxRetryWatchDog) {
+ watchdogReboot(2);
+ }
+# endif
+ }
+ Log.notice(F("WiFi ok with manual config credentials" CR));
+}
+
+#elif defined(ESP8266) || defined(ESP32)
+
+WiFiManager wifiManager;
+
+
+bool shouldSaveConfig = false;
+
+
+
+void saveConfigCallback() {
+ Log.trace(F("Should save config" CR));
+ shouldSaveConfig = true;
+}
+
+# ifdef TRIGGER_GPIO
+void checkButton() {
+# if defined(INPUT_GPIO) && defined(ZsensorGPIOInput) && INPUT_GPIO == TRIGGER_GPIO
+ MeasureGPIOInput();
+# else
+
+ if (digitalRead(TRIGGER_GPIO) == LOW) {
+
+ delay(50);
+ if (digitalRead(TRIGGER_GPIO) == LOW) {
+ Log.trace(F("Trigger button Pressed" CR));
+
+ delay(3000);
+ if (digitalRead(TRIGGER_GPIO) == LOW) {
+ Log.trace(F("Button Held" CR));
+ Log.notice(F("Erasing ESP Config, restarting" CR));
+ setup_wifimanager(true);
+ }
+ }
+ }
+# endif
+}
+# else
+void checkButton() {}
+# endif
+
+void eraseAndRestart() {
+ Log.trace(F("Formatting requested, result: %d" CR), SPIFFS.format());
+
+# if defined(ESP8266)
+ WiFi.disconnect(true);
+ wifiManager.resetSettings();
+ delay(5000);
+ ESP.reset();
+# else
+ nvs_flash_erase();
+ ESP.restart();
+# endif
+}
+
+void saveMqttConfig() {
+ Log.trace(F("saving config" CR));
+ DynamicJsonDocument json(512 + ota_server_cert.length() + mqtt_cert.length());
+ json["mqtt_server"] = mqtt_server;
+ json["mqtt_port"] = mqtt_port;
+ json["mqtt_user"] = mqtt_user;
+ json["mqtt_pass"] = mqtt_pass;
+ json["mqtt_topic"] = mqtt_topic;
+ json["gateway_name"] = gateway_name;
+ json["mqtt_broker_secure"] = mqtt_secure;
+ json["mqtt_broker_cert"] = mqtt_cert;
+ json["mqtt_ss_index"] = mqtt_ss_index;
+ json["ota_server_cert"] = ota_server_cert;
+
+ File configFile = SPIFFS.open("/config.json", "w");
+ if (!configFile) {
+ Log.error(F("failed to open config file for writing" CR));
+ }
+
+ serializeJsonPretty(json, Serial);
+ serializeJson(json, configFile);
+ configFile.close();
+}
+
+void setup_wifimanager(bool reset_settings) {
+# ifdef TRIGGER_GPIO
+ pinMode(TRIGGER_GPIO, INPUT_PULLUP);
+# endif
+ delay(10);
+ WiFi.mode(WIFI_STA);
+
+ if (reset_settings)
+ eraseAndRestart();
+
+
+ Log.trace(F("mounting FS..." CR));
+
+ if (SPIFFS.begin()) {
+ Log.trace(F("mounted file system" CR));
+ } else {
+ Log.warning(F("failed to mount FS -> formating" CR));
+ SPIFFS.format();
+ if (SPIFFS.begin())
+ Log.trace(F("mounted file system after formating" CR));
+ }
+ if (SPIFFS.exists("/config.json")) {
+
+ Log.trace(F("reading config file" CR));
+ File configFile = SPIFFS.open("/config.json", "r");
+ if (configFile) {
+ Log.trace(F("opened config file" CR));
+ DynamicJsonDocument json(configFile.size() * 2);
+ auto error = deserializeJson(json, configFile);
+ if (error) {
+ Log.error(F("deserialize config failed: %s, buffer capacity: %u" CR), error.c_str(), json.capacity());
+ }
+ serializeJsonPretty(json, Serial);
+ if (!json.isNull()) {
+ Log.trace(F("\nparsed json, size: %u" CR), json.memoryUsage());
+ if (json.containsKey("mqtt_server"))
+ strcpy(mqtt_server, json["mqtt_server"]);
+ if (json.containsKey("mqtt_port"))
+ strcpy(mqtt_port, json["mqtt_port"]);
+ if (json.containsKey("mqtt_user"))
+ strcpy(mqtt_user, json["mqtt_user"]);
+ if (json.containsKey("mqtt_pass"))
+ strcpy(mqtt_pass, json["mqtt_pass"]);
+ if (json.containsKey("mqtt_topic"))
+ strcpy(mqtt_topic, json["mqtt_topic"]);
+ if (json.containsKey("mqtt_broker_secure"))
+ mqtt_secure = json["mqtt_broker_secure"].as();
+ if (json.containsKey("mqtt_broker_cert"))
+ mqtt_cert = json["mqtt_broker_cert"].as();
+ if (json.containsKey("mqtt_ss_index"))
+ mqtt_ss_index = json["mqtt_ss_index"].as();
+ if (json.containsKey("gateway_name"))
+ strcpy(gateway_name, json["gateway_name"]);
+ if (json.containsKey("ota_server_cert"))
+ ota_server_cert = json["ota_server_cert"].as();
+ } else {
+ Log.warning(F("failed to load json config" CR));
+ }
+ configFile.close();
+ }
+ }
+
+ wifiManager.setDebugOutput(WM_DEBUG_LEVEL);
+
+
+
+
+# ifndef WIFIMNG_HIDE_MQTT_CONFIG
+ WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, parameters_size);
+ WiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 6);
+ WiFiManagerParameter custom_mqtt_user("user", "mqtt user", mqtt_user, parameters_size);
+ WiFiManagerParameter custom_mqtt_pass("pass", "mqtt pass", mqtt_pass, parameters_size);
+ WiFiManagerParameter custom_mqtt_topic("topic", "mqtt base topic", mqtt_topic, mqtt_topic_max_size);
+ WiFiManagerParameter custom_mqtt_secure("secure", "mqtt secure", "1", 1, mqtt_secure ? "type=\"checkbox\" checked" : "type=\"checkbox\"");
+ WiFiManagerParameter custom_mqtt_cert("cert", "mqtt broker cert", mqtt_cert.c_str(), 2048);
+ WiFiManagerParameter custom_gateway_name("name", "gateway name", gateway_name, parameters_size);
+# endif
+
+
+
+ wifiManager.setConnectTimeout(WifiManager_TimeOut);
+
+ wifiManager.setConfigPortalTimeout(WifiManager_ConfigPortalTimeOut);
+
+
+ wifiManager.setSaveConfigCallback(saveConfigCallback);
+
+
+# ifdef NetworkAdvancedSetup
+ Log.trace(F("Adv wifi cfg" CR));
+ IPAddress gateway_adress(gateway);
+ IPAddress subnet_adress(subnet);
+ IPAddress ip_adress(ip);
+ wifiManager.setSTAStaticIPConfig(ip_adress, gateway_adress, subnet_adress);
+# endif
+
+# ifndef WIFIMNG_HIDE_MQTT_CONFIG
+
+ wifiManager.addParameter(&custom_mqtt_server);
+ wifiManager.addParameter(&custom_mqtt_port);
+ wifiManager.addParameter(&custom_mqtt_user);
+ wifiManager.addParameter(&custom_mqtt_pass);
+ wifiManager.addParameter(&custom_mqtt_secure);
+ wifiManager.addParameter(&custom_mqtt_cert);
+ wifiManager.addParameter(&custom_gateway_name);
+ wifiManager.addParameter(&custom_mqtt_topic);
+# endif
+
+ wifiManager.setMinimumSignalQuality(MinimumWifiSignalQuality);
+
+ if (!wifi_reconnect_bypass())
+ {
+# ifdef ESP32
+ if (lowpowermode < 2) {
+ displayPrint("Connect your phone to WIFI AP:", WifiManager_ssid, WifiManager_password);
+ } else {
+# ifdef ZgatewayBT
+ lowPowerESP32();
+# endif
+ }
+# endif
+
+ digitalWrite(LED_ERROR, LED_ERROR_ON);
+ digitalWrite(LED_INFO, LED_INFO_ON);
+ Log.notice(F("Connect your phone to WIFI AP: %s with PWD: %s" CR), WifiManager_ssid, WifiManager_password);
+
+
+
+ if (!wifiManager.autoConnect(WifiManager_ssid, WifiManager_password)) {
+ Log.warning(F("failed to connect and hit timeout" CR));
+ delay(3000);
+
+ watchdogReboot(3);
+ delay(5000);
+ }
+ digitalWrite(LED_ERROR, !LED_ERROR_ON);
+ digitalWrite(LED_INFO, !LED_INFO_ON);
+ }
+
+ displayPrint("Wifi connected");
+
+ if (shouldSaveConfig) {
+
+# ifndef WIFIMNG_HIDE_MQTT_CONFIG
+ strcpy(mqtt_server, custom_mqtt_server.getValue());
+ strcpy(mqtt_port, custom_mqtt_port.getValue());
+ strcpy(mqtt_user, custom_mqtt_user.getValue());
+ strcpy(mqtt_pass, custom_mqtt_pass.getValue());
+ strcpy(mqtt_topic, custom_mqtt_topic.getValue());
+ strcpy(gateway_name, custom_gateway_name.getValue());
+ mqtt_secure = *custom_mqtt_secure.getValue();
+
+ int cert_len = strlen(custom_mqtt_cert.getValue());
+ if (cert_len) {
+ char* cert_in = (char*)custom_mqtt_cert.getValue();
+ while (*cert_in == ' ' || *cert_in == '\t') {
+ cert_in++;
+ }
+
+ char* cert_begin = cert_in;
+ while (*cert_in != NULL) {
+ if (*cert_in == ' ' && (strncmp((cert_in + 1), "CERTIFICATE", 11) != 0)) {
+ *cert_in = '\n';
+ }
+ cert_in++;
+ }
+
+ mqtt_cert = cert_begin;
+ }
+# endif
+
+ saveMqttConfig();
+ }
+}
+# ifdef ESP32_ETHERNET
+void setup_ethernet_esp32() {
+ bool ethBeginSuccess = false;
+ WiFi.onEvent(WiFiEvent);
+# ifdef NetworkAdvancedSetup
+ Log.trace(F("Adv eth cfg" CR));
+ ETH.config(ip, gateway, subnet, Dns);
+ ethBeginSuccess = ETH.begin();
+# else
+ Log.trace(F("Spl eth cfg" CR));
+ ethBeginSuccess = ETH.begin();
+# endif
+ Log.trace(F("Connecting to Ethernet" CR));
+ while (!esp32EthConnected) {
+ delay(500);
+ Log.trace(F("." CR));
+ }
+}
+
+void WiFiEvent(WiFiEvent_t event) {
+ switch (event) {
+ case SYSTEM_EVENT_ETH_START:
+ Log.trace(F("Ethernet Started" CR));
+ ETH.setHostname(gateway_name);
+ break;
+ case SYSTEM_EVENT_ETH_CONNECTED:
+ Log.notice(F("Ethernet Connected" CR));
+ break;
+ case SYSTEM_EVENT_ETH_GOT_IP:
+ Log.trace(F("OpenMQTTGateway MAC: %s" CR), ETH.macAddress().c_str());
+ Log.trace(F("OpenMQTTGateway IP: %s" CR), ETH.localIP().toString().c_str());
+ Log.trace(F("OpenMQTTGateway link speed: %d Mbps" CR), ETH.linkSpeed());
+ esp32EthConnected = true;
+ break;
+ case SYSTEM_EVENT_ETH_DISCONNECTED:
+ Log.error(F("Ethernet Disconnected" CR));
+ esp32EthConnected = false;
+ break;
+ case SYSTEM_EVENT_ETH_STOP:
+ Log.error(F("Ethernet Stopped" CR));
+ esp32EthConnected = false;
+ break;
+ default:
+ break;
+ }
+}
+# endif
+#else
+void setup_ethernet() {
+# ifdef NetworkAdvancedSetup
+ Log.trace(F("Adv eth cfg" CR));
+ Ethernet.begin(mac, ip, Dns, gateway, subnet);
+# else
+ Log.trace(F("Spl eth cfg" CR));
+ Ethernet.begin(mac, ip);
+# endif
+ if (Ethernet.hardwareStatus() == EthernetNoHardware) {
+ Log.error(F("Ethernet shield was not found." CR));
+ } else {
+ Log.trace(F("ip: %s " CR), Ethernet.localIP());
+ }
+}
+#endif
+
+#if defined(MDNS_SD) && (defined(ESP8266) || defined(ESP32))
+void connectMQTTmdns() {
+ Log.trace(F("Browsing for MQTT service" CR));
+ int n = MDNS.queryService("mqtt", "tcp");
+ if (n == 0) {
+ Log.error(F("no services found" CR));
+ } else {
+ Log.trace(F("%d service(s) found" CR), n);
+ for (int i = 0; i < n; ++i) {
+ Log.trace(F("Service %d %s found" CR), i, MDNS.hostname(i).c_str());
+ Log.trace(F("IP %s Port %d" CR), MDNS.IP(i).toString().c_str(), MDNS.port(i));
+ }
+ if (n == 1) {
+ Log.trace(F("One MQTT server found setting parameters" CR));
+ client.setServer(MDNS.IP(0), int(MDNS.port(0)));
+ } else {
+ Log.error(F("Several MQTT servers found, please deactivate mDNS and set your default server" CR));
+ }
+ }
+}
+#endif
+
+void loop() {
+#ifndef ESPWifiManualSetup
+# if defined(ESP8266) || defined(ESP32)
+ checkButton();
+# endif
+#endif
+
+ unsigned long now = millis();
+
+
+ if (now > (timer_led_measures + (TimeLedON * 1000))) {
+ timer_led_measures = millis();
+ digitalWrite(LED_INFO, !LED_INFO_ON);
+ digitalWrite(LED_SEND_RECEIVE, !LED_SEND_RECEIVE_ON);
+ }
+
+#if defined(ESP8266) || defined(ESP32)
+# ifdef ESP32_ETHERNET
+ if (esp32EthConnected) {
+# else
+ if (WiFi.status() == WL_CONNECTED) {
+# endif
+ ArduinoOTA.handle();
+#else
+ if ((Ethernet.hardwareStatus() != EthernetW5100 && Ethernet.linkStatus() == LinkON) || (Ethernet.hardwareStatus() == EthernetW5100)) {
+#endif
+ failure_number_ntwk = 0;
+ if (client.connected()) {
+ digitalWrite(LED_INFO, LED_INFO_ON);
+ failure_number_ntwk = 0;
+
+ client.loop();
+
+#ifdef ZmqttDiscovery
+ if (disc) {
+ if (!connectedOnce || (discovery_republish_on_reconnect && !justReconnected)) {
+
+
+ pubMqttDiscovery();
+ }
+ }
+#endif
+ connectedOnce = true;
+ justReconnected = true;
+#if defined(ESP8266) || defined(ESP32) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__)
+ if (now > (timer_sys_measures + (TimeBetweenReadingSYS * 1000)) || !timer_sys_measures) {
+ timer_sys_measures = millis();
+ stateMeasures();
+ }
+#endif
+#ifdef ZsensorBME280
+ MeasureTempHumAndPressure();
+#endif
+#ifdef ZsensorHTU21
+ MeasureTempHum();
+#endif
+#ifdef ZsensorAHTx0
+ MeasureAHTTempHum();
+#endif
+#ifdef ZsensorHCSR04
+ MeasureDistance();
+#endif
+#ifdef ZsensorBH1750
+ MeasureLightIntensity();
+#endif
+#ifdef ZsensorTSL2561
+ MeasureLightIntensityTSL2561();
+#endif
+#ifdef ZsensorDHT
+ MeasureTempAndHum();
+#endif
+#ifdef ZsensorSHTC3
+ MeasureTempAndHum();
+#endif
+#ifdef ZsensorDS1820
+ MeasureDS1820Temp();
+#endif
+#ifdef ZsensorINA226
+ MeasureINA226();
+#endif
+#ifdef ZsensorHCSR501
+ MeasureHCSR501();
+#endif
+#ifdef ZsensorGPIOInput
+ MeasureGPIOInput();
+#endif
+#ifdef ZsensorGPIOKeyCode
+ MeasureGPIOKeyCode();
+#endif
+#ifdef ZsensorADC
+ MeasureADC();
+#endif
+#ifdef ZgatewayLORA
+ LORAtoMQTT();
+#endif
+#ifdef ZgatewayRF
+ RFtoMQTT();
+#endif
+#ifdef ZgatewayRF2
+ RF2toMQTT();
+#endif
+#ifdef ZgatewayWeatherStation
+ ZgatewayWeatherStationtoMQTT();
+#endif
+#ifdef ZgatewayGFSunInverter
+ ZgatewayGFSunInverterMQTT();
+#endif
+#ifdef ZgatewayPilight
+ PilighttoMQTT();
+#endif
+#ifdef ZgatewayBT
+# ifdef ZmqttDiscovery
+ if (disc)
+ launchBTDiscovery();
+# endif
+# ifndef ESP32
+ if (BTtoMQTT())
+ Log.trace(F("BTtoMQTT OK" CR));
+# else
+ emptyBTQueue();
+# endif
+#endif
+#ifdef ZgatewaySRFB
+ SRFBtoMQTT();
+#endif
+#ifdef ZgatewayIR
+ IRtoMQTT();
+#endif
+#ifdef Zgateway2G
+ if (_2GtoMQTT())
+ Log.trace(F("2GtoMQTT OK" CR));
+#endif
+#ifdef ZgatewayRFM69
+ if (RFM69toMQTT())
+ Log.trace(F("RFM69toMQTT OK" CR));
+#endif
+#ifdef ZgatewayRS232
+ RS232toMQTT();
+#endif
+#ifdef ZactuatorFASTLED
+ FASTLEDLoop();
+#endif
+#ifdef ZactuatorPWM
+ PWMLoop();
+#endif
+#ifdef ZgatewayRTL_433
+ RTL_433Loop();
+#endif
+ } else {
+
+ justReconnected = false;
+ connectMQTT();
+ }
+ } else {
+
+ justReconnected = false;
+ Log.warning(F("Network disconnected:" CR));
+ digitalWrite(LED_ERROR, LED_ERROR_ON);
+ delay(2000);
+ digitalWrite(LED_ERROR, !LED_ERROR_ON);
+ delay(2000);
+#if defined(ESP8266) || defined(ESP32) && !defined(ESP32_ETHERNET)
+# ifdef ESP32
+ WiFi.reconnect();
+# endif
+ Log.warning(F("wifi" CR));
+#else
+ Log.warning(F("ethernet" CR));
+#endif
+ }
+
+#if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
+ loopM5();
+#endif
+
+
+#if defined(ZboardHELTEC)
+ loopHELTEC();
+#endif
+}
+
+
+
+
+unsigned long uptime() {
+ static unsigned long lastUptime = 0;
+ static unsigned long uptimeAdd = 0;
+ unsigned long uptime = millis() / 1000 + uptimeAdd;
+ if (uptime < lastUptime) {
+ uptime += 4294967;
+ uptimeAdd += 4294967;
+ }
+ lastUptime = uptime;
+ return uptime;
+}
+
+#if defined(ESP8266) || defined(ESP32) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__)
+void stateMeasures() {
+ StaticJsonDocument jsonBuffer;
+ JsonObject SYSdata = jsonBuffer.to();
+ SYSdata["uptime"] = uptime();
+ SYSdata["version"] = OMG_VERSION;
+ Log.trace(F("retrieving value of system characteristics Uptime (s):%u" CR), uptime());
+# if defined(ESP8266) || defined(ESP32)
+ uint32_t freeMem;
+ freeMem = ESP.getFreeHeap();
+ SYSdata["freemem"] = freeMem;
+ SYSdata["mqttport"] = mqtt_port;
+ SYSdata["mqttsecure"] = mqtt_secure;
+# ifdef ESP32
+ SYSdata["freestack"] = uxTaskGetStackHighWaterMark(NULL);
+# endif
+# ifdef ESP32_ETHERNET
+ SYSdata["mac"] = (char*)ETH.macAddress().c_str();
+ SYSdata["ip"] = ip2CharArray(ETH.localIP());
+ ETH.fullDuplex() ? SYSdata["fd"] = (bool)"true" : SYSdata["fd"] = (bool)"false";
+ SYSdata["linkspeed"] = (int)ETH.linkSpeed();
+# else
+ long rssi = WiFi.RSSI();
+ SYSdata["rssi"] = rssi;
+ String SSID = WiFi.SSID();
+ SYSdata["SSID"] = SSID;
+ String BSSID = WiFi.BSSIDstr();
+ SYSdata["BSSID"] = BSSID;
+ SYSdata["ip"] = ip2CharArray(WiFi.localIP());
+ String mac = WiFi.macAddress();
+ SYSdata["mac"] = (char*)mac.c_str();
+# endif
+# endif
+# ifdef ZgatewayBT
+# ifdef ESP32
+ SYSdata["lowpowermode"] = (int)lowpowermode;
+ SYSdata["btqblck"] = btQueueBlocked;
+ SYSdata["btqsum"] = btQueueLengthSum;
+ SYSdata["btqsnd"] = btQueueLengthCount;
+ SYSdata["btqavg"] = (btQueueLengthCount > 0 ? btQueueLengthSum / (float)btQueueLengthCount : 0);
+# endif
+ SYSdata["interval"] = BTConfig.BLEinterval;
+ SYSdata["scanbcnct"] = BTConfig.BLEscanBeforeConnect;
+ SYSdata["scnct"] = scanCount;
+# endif
+# ifdef ZboardM5STACK
+ M5.Power.begin();
+ SYSdata["m5battlevel"] = (int8_t)M5.Power.getBatteryLevel();
+ SYSdata["m5ischarging"] = (bool)M5.Power.isCharging();
+ SYSdata["m5ischargefull"] = (bool)M5.Power.isChargeFull();
+# endif
+# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5TOUGH)
+ M5.Axp.EnableCoulombcounter();
+ SYSdata["m5batvoltage"] = (float)M5.Axp.GetBatVoltage();
+ SYSdata["m5batcurrent"] = (float)M5.Axp.GetBatCurrent();
+ SYSdata["m5vinvoltage"] = (float)M5.Axp.GetVinVoltage();
+ SYSdata["m5vincurrent"] = (float)M5.Axp.GetVinCurrent();
+ SYSdata["m5vbusvoltage"] = (float)M5.Axp.GetVBusVoltage();
+ SYSdata["m5vbuscurrent"] = (float)M5.Axp.GetVBusCurrent();
+ SYSdata["m5tempaxp"] = (float)M5.Axp.GetTempInAXP192();
+ SYSdata["m5batpower"] = (float)M5.Axp.GetBatPower();
+ SYSdata["m5batchargecurrent"] = (float)M5.Axp.GetBatChargeCurrent();
+ SYSdata["m5apsvoltage"] = (float)M5.Axp.GetAPSVoltage();
+# endif
+# if defined(ZgatewayRF) || defined(ZgatewayPilight) || defined(ZgatewayRTL_433) || defined(ZgatewayRF2)
+ SYSdata["actRec"] = (int)activeReceiver;
+# endif
+# ifdef ZradioCC1101
+ SYSdata["mhz"] = (float)receiveMhz;
+# endif
+# if defined(ZgatewayRTL_433)
+ if (activeReceiver == ACTIVE_RTL) {
+ SYSdata["RTLminRssi"] = (int)getRTLMinimumRSSI();
+ SYSdata["RTLRssi"] = (int)getRTLCurrentRSSI();
+ SYSdata["RTLCnt"] = (int)getRTLMessageCount();
+ }
+# endif
+ SYSdata["modules"] = modules;
+ pub(subjectSYStoMQTT, SYSdata);
+}
+#endif
+
+#if defined(ZgatewayRF) || defined(ZgatewayIR) || defined(ZgatewaySRFB) || defined(ZgatewayWeatherStation)
+
+
+
+void storeSignalValue(SIGNAL_SIZE_UL_ULL MQTTvalue) {
+ unsigned long now = millis();
+
+ int o = getMin();
+ Log.trace(F("Min ind: %d" CR), o);
+
+ receivedSignal[o].value = MQTTvalue;
+ receivedSignal[o].time = now;
+
+
+ Log.trace(F("store code : %u / %u" CR), (unsigned long)receivedSignal[o].value, receivedSignal[o].time);
+ Log.trace(F("Col: val/timestamp" CR));
+ for (int i = 0; i < struct_size; i++) {
+ Log.trace(F("mem code : %u / %u" CR), (unsigned long)receivedSignal[i].value, receivedSignal[i].time);
+ }
+}
+
+
+
+
+int getMin() {
+ unsigned int minimum = receivedSignal[0].time;
+ int minindex = 0;
+ for (int i = 1; i < struct_size; i++) {
+ if (receivedSignal[i].time < minimum) {
+ minimum = receivedSignal[i].time;
+ minindex = i;
+ }
+ }
+ return minindex;
+}
+
+
+
+
+bool isAduplicateSignal(SIGNAL_SIZE_UL_ULL value) {
+ Log.trace(F("isAdupl?" CR));
+ for (int i = 0; i < struct_size; i++) {
+ if (receivedSignal[i].value == value) {
+ unsigned long now = millis();
+ if (now - receivedSignal[i].time < time_avoid_duplicate) {
+ Log.notice(F("no pub. dupl" CR));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+#endif
+
+void receivingMQTT(char* topicOri, char* datacallback) {
+ StaticJsonDocument jsonBuffer;
+ JsonObject jsondata = jsonBuffer.to();
+ auto error = deserializeJson(jsonBuffer, datacallback);
+ if (error) {
+ Log.error(F("deserialize MQTT data failed: %s" CR), error.c_str());
+ return;
+ }
+
+#if defined(ZgatewayRF) || defined(ZgatewayIR) || defined(ZgatewaySRFB) || defined(ZgatewayWeatherStation)
+ if (strstr(topicOri, subjectMultiGTWKey) != NULL) {
+ SIGNAL_SIZE_UL_ULL data = jsondata.isNull() ? STRTO_UL_ULL(datacallback, NULL, 10) : jsondata["value"];
+ if (data != 0 && !isAduplicateSignal(data)) {
+ storeSignalValue(data);
+ }
+ }
+#endif
+
+ if (!jsondata.isNull()) {
+
+ String buffer = "";
+ serializeJson(jsondata, buffer);
+ Log.notice(F("[ MQTT->OMG ]: %s" CR), buffer.c_str());
+
+#ifdef ZgatewayPilight
+ MQTTtoPilight(topicOri, jsondata);
+#endif
+#ifdef ZgatewayRTL_433
+ MQTTtoRTL_433(topicOri, jsondata);
+#endif
+#if jsonReceiving
+# ifdef ZgatewayLORA
+ MQTTtoLORA(topicOri, jsondata);
+# endif
+# ifdef ZgatewayRF
+ MQTTtoRF(topicOri, jsondata);
+# endif
+# ifdef ZgatewayRF2
+ MQTTtoRF2(topicOri, jsondata);
+# endif
+# ifdef Zgateway2G
+ MQTTto2G(topicOri, jsondata);
+# endif
+# ifdef ZgatewaySRFB
+ MQTTtoSRFB(topicOri, jsondata);
+# endif
+# ifdef ZgatewayIR
+ MQTTtoIR(topicOri, jsondata);
+# endif
+# ifdef ZgatewayRFM69
+ MQTTtoRFM69(topicOri, jsondata);
+# endif
+# ifdef ZgatewayBT
+ MQTTtoBT(topicOri, jsondata);
+# endif
+# ifdef ZactuatorFASTLED
+ MQTTtoFASTLED(topicOri, jsondata);
+# endif
+# ifdef ZactuatorPWM
+ MQTTtoPWM(topicOri, jsondata);
+# endif
+# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
+ MQTTtoM5(topicOri, jsondata);
+# endif
+# if defined(ZboardHELTEC)
+ MQTTtoHELTEC(topicOri, jsondata);
+# endif
+# ifdef ZactuatorONOFF
+ MQTTtoONOFF(topicOri, jsondata);
+# endif
+# ifdef ZactuatorSomfy
+ MQTTtoSomfy(topicOri, jsondata);
+# endif
+# ifdef ZgatewayRS232
+ MQTTtoRS232(topicOri, jsondata);
+# endif
+# ifdef MQTT_HTTPS_FW_UPDATE
+ MQTTHttpsFWUpdate(topicOri, jsondata);
+# endif
+#endif
+ digitalWrite(LED_SEND_RECEIVE, LED_SEND_RECEIVE_ON);
+
+ MQTTtoSYS(topicOri, jsondata);
+ } else {
+#if simpleReceiving
+# ifdef ZgatewayLORA
+ MQTTtoLORA(topicOri, datacallback);
+# endif
+# ifdef ZgatewayRF
+ MQTTtoRF(topicOri, datacallback);
+# endif
+# ifdef ZgatewayRF315
+ MQTTtoRF315(topicOri, datacallback);
+# endif
+# ifdef ZgatewayRF2
+ MQTTtoRF2(topicOri, datacallback);
+# endif
+# ifdef Zgateway2G
+ MQTTto2G(topicOri, datacallback);
+# endif
+# ifdef ZgatewaySRFB
+ MQTTtoSRFB(topicOri, datacallback);
+# endif
+# ifdef ZgatewayRFM69
+ MQTTtoRFM69(topicOri, datacallback);
+# endif
+# ifdef ZactuatorFASTLED
+ MQTTtoFASTLED(topicOri, datacallback);
+# endif
+#endif
+#ifdef ZactuatorONOFF
+ MQTTtoONOFF(topicOri, datacallback);
+#endif
+ }
+}
+
+#ifdef MQTT_HTTPS_FW_UPDATE
+# ifdef ESP32
+# include "zzHTTPUpdate.h"
+# elif ESP8266
+# include
+# endif
+void MQTTHttpsFWUpdate(char* topicOri, JsonObject& HttpsFwUpdateData) {
+ if (strstr(topicOri, subjectFWUpdate) != NULL) {
+ const char* version = HttpsFwUpdateData["version"];
+ if (version && ((strlen(version) != strlen(OMG_VERSION)) || strcmp(version, OMG_VERSION) != 0)) {
+ const char* url = HttpsFwUpdateData["url"];
+ if (url) {
+ if (!strstr((url + (strlen(url) - 5)), ".bin")) {
+ Log.error(F("Invalid firmware extension" CR));
+ return;
+ }
+ } else {
+ Log.error(F("Invalid URL" CR));
+ return;
+ }
+
+# if MQTT_HTTPS_FW_UPDATE_USE_PASSWORD > 0
+ const char* pwd = HttpsFwUpdateData["password"];
+ if (pwd) {
+ if (strcmp(pwd, ota_password) != 0) {
+ Log.error(F("Invalid OTA password" CR));
+ return;
+ }
+ } else {
+ Log.error(F("No password sent" CR));
+ return;
+ }
+# endif
+
+ Log.warning(F("Starting firmware update" CR));
+ digitalWrite(LED_SEND_RECEIVE, LED_SEND_RECEIVE_ON);
+ digitalWrite(LED_ERROR, LED_ERROR_ON);
+
+# if defined(ZgatewayBT) && defined(ESP32)
+ stopProcessing();
+# endif
+
+ const char* ota_cert = HttpsFwUpdateData["server_cert"];
+ if (!ota_cert) {
+ if (ota_server_cert.length() > 0) {
+ Log.error(F("using stored cert" CR));
+ ota_cert = ota_server_cert.c_str();
+ } else {
+ Log.error(F("using config cert" CR));
+ ota_cert = OTAserver_cert;
+ }
+ }
+
+ t_httpUpdate_return result = HTTP_UPDATE_FAILED;
+ if (strstr(url, "http:")) {
+ WiFiClient update_client;
+# ifdef ESP32
+ httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
+ result = httpUpdate.update(update_client, url);
+# elif ESP8266
+ ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
+ result = ESPhttpUpdate.update(update_client, url);
+# endif
+
+ } else {
+ WiFiClientSecure update_client;
+ if (mqtt_secure) {
+ client.disconnect();
+ update_client = *(WiFiClientSecure*)eClient;
+ } else {
+ configTime(0, 0, NTP_SERVER);
+ time_t now = time(nullptr);
+ uint8_t count = 0;
+ Log.trace(F("Waiting for NTP time sync" CR));
+ while ((now < 8 * 3600 * 2) && count++ < 60) {
+ delay(500);
+ now = time(nullptr);
+ }
+
+ if (count >= 60) {
+ Log.error(F("Unable to update - invalid time" CR));
+# if defined(ZgatewayBT) && defined(ESP32)
+ startProcessing();
+# endif
+ return;
+ }
+ }
+
+# ifdef ESP32
+ update_client.setCACert(ota_cert);
+ update_client.setTimeout(12);
+ httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
+ httpUpdate.rebootOnUpdate(false);
+ result = httpUpdate.update(update_client, url);
+# elif ESP8266
+ caCert.append(ota_cert);
+ update_client.setTrustAnchors(&caCert);
+ update_client.setTimeout(12000);
+ ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
+ ESPhttpUpdate.rebootOnUpdate(false);
+ result = ESPhttpUpdate.update(update_client, url);
+# endif
+ }
+
+ switch (result) {
+ case HTTP_UPDATE_FAILED:
+# ifdef ESP32
+ Log.error(F("HTTP_UPDATE_FAILED Error (%d): %s\n" CR), httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
+# elif ESP8266
+ Log.error(F("HTTP_UPDATE_FAILED Error (%d): %s\n" CR), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
+# endif
+ break;
+
+ case HTTP_UPDATE_NO_UPDATES:
+ Log.notice(F("HTTP_UPDATE_NO_UPDATES" CR));
+ break;
+
+ case HTTP_UPDATE_OK:
+ Log.notice(F("HTTP_UPDATE_OK" CR));
+ ota_server_cert = ota_cert;
+# ifndef ESPWifiManualSetup
+ saveMqttConfig();
+# endif
+# ifdef ESP32
+ ESP.restart();
+# elif ESP8266
+ ESP.reset();
+# endif
+ break;
+ }
+
+ digitalWrite(LED_SEND_RECEIVE, !LED_SEND_RECEIVE_ON);
+ digitalWrite(LED_ERROR, !LED_ERROR_ON);
+
+# if defined(ZgatewayBT) && defined(ESP32)
+ startProcessing();
+# endif
+ }
+ }
+}
+#endif
+
+void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoSYSset)) {
+ Log.trace(F("MQTTtoSYS json" CR));
+#if defined(ESP8266) || defined(ESP32)
+ if (SYSdata.containsKey("cmd")) {
+ const char* cmd = SYSdata["cmd"];
+ Log.notice(F("Command: %s" CR), cmd);
+ if (strstr(cmd, restartCmd) != NULL) {
+# if defined(ESP8266)
+ ESP.reset();
+# else
+ ESP.restart();
+# endif
+ } else if (strstr(cmd, eraseCmd) != NULL) {
+# ifndef ESPWifiManualSetup
+ setup_wifimanager(true);
+# endif
+ } else if (strstr(cmd, statusCmd) != NULL) {
+ stateMeasures();
+ }
+ }
+
+ if (SYSdata.containsKey("wifi_ssid") && SYSdata.containsKey("wifi_pass")) {
+# if defined(ZgatewayBT) && defined(ESP32)
+ stopProcessing();
+# endif
+ String prev_ssid = WiFi.SSID();
+ String prev_pass = WiFi.psk();
+ client.disconnect();
+ WiFi.disconnect(true);
+
+ Log.warning(F("Attempting connection to new AP" CR));
+ WiFi.begin((const char*)SYSdata["wifi_ssid"], (const char*)SYSdata["wifi_pass"]);
+ WiFi.waitForConnectResult();
+
+ if (WiFi.status() != WL_CONNECTED) {
+ Log.error(F("Failed to connect to new AP; falling back" CR));
+ WiFi.disconnect(true);
+ WiFi.begin(prev_ssid.c_str(), prev_pass.c_str());
+ if (WiFi.waitForConnectResult() != WL_CONNECTED) {
+# if defined(ESP8266)
+ ESP.reset();
+# else
+ ESP.restart();
+# endif
+ }
+ }
+# if defined(ZgatewayBT) && defined(ESP32)
+ startProcessing();
+# endif
+ }
+
+# ifdef MQTTsetMQTT
+ if (SYSdata.containsKey("mqtt_user") && SYSdata.containsKey("mqtt_pass")) {
+ bool update_server = false;
+ bool secure_connect = SYSdata["mqtt_secure"].as();
+ void* prev_client = nullptr;
+ bool use_ss_cert = SYSdata.containsKey("mqtt_cert_index");
+ uint8_t cert_index = mqtt_ss_index;
+
+ if (SYSdata.containsKey("mqtt_server") && SYSdata.containsKey("mqtt_port")) {
+ if (!SYSdata.containsKey("mqtt_secure")) {
+ Log.error(F("mqtt_server provided without mqtt_secure defined - ignoring command" CR));
+ return;
+ }
+# if MQTT_SECURE_SELF_SIGNED
+ if (use_ss_cert) {
+ cert_index = SYSdata["mqtt_cert_index"].as();
+ if (cert_index >= sizeof(certs_array) / sizeof(ss_certs)) {
+ Log.error(F("mqtt_cert_index invalid - ignoring command" CR));
+ return;
+ }
+ }
+# endif
+
+# if defined(ZgatewayBT) && defined(ESP32)
+ stopProcessing();
+# endif
+ client.disconnect();
+ update_server = true;
+ if (secure_connect != mqtt_secure) {
+ prev_client = eClient;
+ if (!mqtt_secure) {
+ eClient = new WiFiClientSecure;
+ } else {
+ Log.warning(F("Switching to unsecure MQTT broker" CR));
+ eClient = new WiFiClient;
+ }
+
+ client.setClient(*(Client*)eClient);
+ }
+
+ if (secure_connect) {
+ setupTLS(use_ss_cert, cert_index);
+ }
+
+ client.setServer(SYSdata["mqtt_server"].as(), SYSdata["mqtt_port"].as());
+ } else {
+# if defined(ZgatewayBT) && defined(ESP32)
+ stopProcessing();
+# endif
+ client.disconnect();
+ }
+
+ String prev_user = mqtt_user;
+ String prev_pass = mqtt_pass;
+ strcpy(mqtt_user, SYSdata["mqtt_user"]);
+ strcpy(mqtt_pass, SYSdata["mqtt_pass"]);
+
+ connectMQTT();
+
+ if (client.connected()) {
+ if (update_server) {
+ strcpy(mqtt_server, SYSdata["mqtt_server"]);
+ strcpy(mqtt_port, SYSdata["mqtt_port"]);
+ mqtt_ss_index = cert_index;
+ if (prev_client != nullptr) {
+ mqtt_secure = !mqtt_secure;
+ delete prev_client;
+ }
+ }
+# ifndef ESPWifiManualSetup
+ saveMqttConfig();
+# endif
+ } else {
+ if (update_server) {
+ if (prev_client != nullptr) {
+ delete eClient;
+ eClient = prev_client;
+ client.setClient(*(Client*)eClient);
+ }
+ uint16_t port = strtol(mqtt_port, NULL, 10);
+ client.setServer(mqtt_server, port);
+ }
+ strcpy(mqtt_user, prev_user.c_str());
+ strcpy(mqtt_pass, prev_pass.c_str());
+ if (mqtt_secure) {
+ setupTLS(MQTT_SECURE_SELF_SIGNED, mqtt_ss_index);
+ }
+ connectMQTT();
+ }
+# if defined(ZgatewayBT) && defined(ESP32)
+ startProcessing();
+# endif
+ }
+# endif
+ if (SYSdata.containsKey("mqtt_topic") || SYSdata.containsKey("gateway_name")) {
+ if (SYSdata.containsKey("mqtt_topic")) {
+ strncpy(mqtt_topic, SYSdata["mqtt_topic"], parameters_size);
+ }
+ if (SYSdata.containsKey("gateway_name")) {
+ strncpy(gateway_name, SYSdata["gateway_name"], parameters_size);
+ }
+# ifndef ESPWifiManualSetup
+ saveMqttConfig();
+# endif
+ client.disconnect();
+ }
+#endif
+
+#ifdef ZmqttDiscovery
+ if (SYSdata.containsKey("discovery")) {
+ if (SYSdata["discovery"].is()) {
+ disc = SYSdata["discovery"];
+ if (disc)
+ pubMqttDiscovery();
+ } else {
+ Log.error(F("Discovery command not a boolean" CR));
+ }
+ Log.notice(F("Discovery state: %T" CR), disc);
+ }
+#endif
+ }
+}
+
+#if valueAsATopic && !defined(ZgatewayPilight)
+# if defined(ESP32) || defined(ESP8266) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__)
+String toString(uint64_t input) {
+ String result = "";
+ uint8_t base = 10;
+
+ do {
+ char c = input % base;
+ input /= base;
+
+ if (c < 10)
+ c += '0';
+ else
+ c += 'A' - 10;
+ result = c + result;
+ } while (input);
+ return result;
+}
+
+# else
+
+String toString(uint32_t input) {
+ String result = String(input);
+
+ return result;
+}
+# endif
+#endif
+# 2151 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/main.ino"
+void watchdogReboot(byte reason) {
+ Log.warning(F("Rebooting for reason code %d" CR), reason);
+#if defined(ESP32)
+ ESP.restart();
+#elif defined(ESP8266)
+ ESP.reset();
+#else
+#endif
+}
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZactuatorFASTLED.ino"
+# 23 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZactuatorFASTLED.ino"
+#include "User_config.h"
+
+#ifdef ZactuatorFASTLED
+
+# include
+
+enum LEDState {
+ OFF,
+ FIRE,
+ GENERAL
+};
+LEDState currentLEDState;
+long lastUpdate = 0;
+long currentUpdate = 0;
+CRGB leds[FASTLED_NUM_LEDS];
+CRGB ledColorBlink[FASTLED_NUM_LEDS];
+bool blinkLED[FASTLED_NUM_LEDS];
+const long blinkInterval = 300;
+const long fireUpdate = 10;
+CRGBPalette16 gPal;
+
+void setupFASTLED() {
+ Log.notice(F("FASTLED_DATA_GPIO: %d" CR), FASTLED_DATA_GPIO);
+ Log.notice(F("FASTLED_NUM_LEDS: %d" CR), FASTLED_NUM_LEDS);
+ Log.trace(F("ZactuatorFASTLED setup done " CR));
+ FastLED.addLeds(leds, FASTLED_NUM_LEDS);
+}
+
+
+int animation_step(int duration, int steps) {
+ int currentStep = ((currentUpdate % duration) / ((float)duration)) * steps;
+ return currentStep;
+}
+
+
+int animation_step_count(int duration, int steps) {
+ long lastAnimationNumber = lastUpdate / duration;
+ long currentAnimationNumber = currentUpdate / duration;
+ int lastStep = ((lastUpdate % duration) / ((float)duration)) * steps;
+ int currentStep = ((currentUpdate % duration) / ((float)duration)) * steps;
+
+ return currentStep - lastStep + (currentAnimationNumber - lastAnimationNumber) * steps;
+}
+
+void FASTLEDLoop() {
+ lastUpdate = currentUpdate;
+ currentUpdate = millis();
+
+ if (currentLEDState == GENERAL) {
+ for (int i = 0; i < FASTLED_NUM_LEDS; i++) {
+ int count = animation_step_count(blinkInterval, 2);
+ int step = animation_step(blinkInterval, 2);
+
+ if (count > 0) {
+ if (blinkLED[i]) {
+ if (step == 0) {
+ leds[i] = ledColorBlink[i];
+ } else {
+ ledColorBlink[i] = leds[i];
+ leds[i] = CRGB::Black;
+ }
+ }
+ }
+ }
+ } else if (currentLEDState == FIRE) {
+ int count = animation_step_count(fireUpdate, 1);
+ if (count > 0) {
+
+ Fire2012WithPalette();
+ }
+ }
+ FastLED.show();
+}
+
+boolean FASTLEDtoMQTT() {
+ return false;
+}
+# if jsonReceiving
+void MQTTtoFASTLED(char* topicOri, JsonObject& jsonData) {
+ currentLEDState = GENERAL;
+
+
+
+ if (cmpToMainTopic(topicOri, subjectMQTTtoFASTLEDsetled)) {
+ Log.trace(F("MQTTtoFASTLED JSON analysis" CR));
+ int ledNr = jsonData["led"];
+ Log.notice(F("Led numero: %d" CR), ledNr);
+ const char* color = jsonData["hex"];
+ Log.notice(F("Color hex: %s" CR), color);
+
+ long number = (long)strtol(color, NULL, 16);
+ bool blink = jsonData["blink"];
+ if (ledNr <= FASTLED_NUM_LEDS) {
+ Log.notice(F("Blink: %d" CR), blink);
+ blinkLED[ledNr] = blink;
+ leds[ledNr] = number;
+ }
+ }
+}
+# endif
+
+# if simpleReceiving
+void MQTTtoFASTLED(char* topicOri, char* datacallback) {
+ Log.trace(F("MQTTtoFASTLED: " CR));
+ currentLEDState = GENERAL;
+ long number = 0;
+ if (cmpToMainTopic(topicOri, subjectMQTTtoFASTLED)) {
+ number = (long)strtol(&datacallback[1], NULL, 16);
+ Log.notice(F("Number: %l" CR), number);
+ for (int i = 0; i < FASTLED_NUM_LEDS; i++) {
+ leds[i] = number;
+ }
+ FastLED.show();
+ } else if (cmpToMainTopic(topicOri, subjectMQTTtoFASTLEDsetbrightness)) {
+ number = (long)strtol(&datacallback[1], NULL, 16);
+ Log.notice(F("Number: %l" CR), number);
+ FastLED.setBrightness(number);
+ FastLED.show();
+ } else if (cmpToMainTopic(topicOri, subjectMQTTtoFASTLEDsetanimation)) {
+ String payload = datacallback;
+ Log.notice(F("Datacallback: %s" CR), datacallback);
+ if (strstr(datacallback, "fire") != NULL) {
+ currentLEDState = FIRE;
+ gPal = HeatColors_p;
+ } else {
+ currentLEDState = OFF;
+ }
+ } else {
+ currentLEDState = OFF;
+ }
+ if (currentLEDState == OFF) {
+ for (int i = 0; i < FASTLED_NUM_LEDS; i++) {
+ leds[i] = CRGB::Black;
+ }
+ }
+}
+# endif
+# 191 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZactuatorFASTLED.ino"
+#define COOLING 55
+
+
+
+
+#define SPARKING 120
+bool gReverseDirection = false;
+
+void Fire2012WithPalette() {
+
+ static byte heat[FASTLED_NUM_LEDS];
+
+
+ for (int i = 0; i < FASTLED_NUM_LEDS; i++) {
+ heat[i] = qsub8(heat[i], random8(0, ((COOLING * 10) / FASTLED_NUM_LEDS) + 2));
+ }
+
+
+ for (int k = FASTLED_NUM_LEDS - 1; k >= 2; k--) {
+ heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
+ }
+
+
+ if (random8() < SPARKING) {
+ int y = random8(7);
+ heat[y] = qadd8(heat[y], random8(160, 255));
+ }
+
+
+ for (int j = 0; j < FASTLED_NUM_LEDS; j++) {
+
+
+ byte colorindex = scale8(heat[j], 240);
+ CRGB color = ColorFromPalette(gPal, colorindex);
+ int pixelnumber;
+ if (gReverseDirection) {
+ pixelnumber = (FASTLED_NUM_LEDS - 1) - j;
+ } else {
+ pixelnumber = j;
+ }
+ leds[pixelnumber] = color;
+ }
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZactuatorONOFF.ino"
+# 29 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZactuatorONOFF.ino"
+#include "User_config.h"
+
+#ifdef ZactuatorONOFF
+
+# if jsonReceiving
+void MQTTtoONOFF(char* topicOri, JsonObject& ONOFFdata) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoONOFF)) {
+ Log.trace(F("MQTTtoONOFF json data analysis" CR));
+ int boolSWITCHTYPE = ONOFFdata["cmd"] | 99;
+ int gpio = ONOFFdata["gpio"] | ACTUATOR_ONOFF_GPIO;
+ if (boolSWITCHTYPE != 99) {
+ Log.notice(F("MQTTtoONOFF boolSWITCHTYPE ok: %d" CR), boolSWITCHTYPE);
+ Log.notice(F("GPIO number: %d" CR), gpio);
+ pinMode(gpio, OUTPUT);
+ digitalWrite(gpio, boolSWITCHTYPE);
+
+ pub(subjectGTWONOFFtoMQTT, ONOFFdata);
+ } else {
+ if (ONOFFdata["cmd"] == "high_pulse") {
+ Log.notice(F("MQTTtoONOFF high_pulse ok" CR));
+ Log.notice(F("GPIO number: %d" CR), gpio);
+ int pulselength = ONOFFdata["pulse_length"] | 500;
+ Log.notice(F("Pulse length: %d ms" CR), pulselength);
+ pinMode(gpio, OUTPUT);
+ digitalWrite(gpio, HIGH);
+ delay(pulselength);
+ digitalWrite(gpio, LOW);
+ } else if (ONOFFdata["cmd"] == "low_pulse") {
+ Log.notice(F("MQTTtoONOFF low_pulse ok" CR));
+ Log.notice(F("GPIO number: %d" CR), gpio);
+ int pulselength = ONOFFdata["pulse_length"] | 500;
+ Log.notice(F("Pulse length: %d ms" CR), pulselength);
+ pinMode(gpio, OUTPUT);
+ digitalWrite(gpio, LOW);
+ delay(pulselength);
+ digitalWrite(gpio, HIGH);
+ } else {
+ Log.error(F("MQTTtoONOFF failed json read" CR));
+ }
+ }
+ }
+}
+# endif
+
+# if simpleReceiving
+void MQTTtoONOFF(char* topicOri, char* datacallback) {
+ if ((cmpToMainTopic(topicOri, subjectMQTTtoONOFF))) {
+ Log.trace(F("MQTTtoONOFF" CR));
+ char* endptr = NULL;
+ long gpio = strtol(datacallback, &endptr, 10);
+ if (datacallback == endptr)
+ gpio = ACTUATOR_ONOFF_GPIO;
+
+ Log.notice(F("GPIO number: %d" CR), gpio);
+ pinMode(gpio, OUTPUT);
+
+ bool ON = false;
+ if (strstr(topicOri, ONKey) != NULL)
+ ON = true;
+ if (strstr(topicOri, OFFKey) != NULL)
+ ON = false;
+
+ digitalWrite(gpio, ON);
+
+ char b = ON;
+ pub(subjectGTWONOFFtoMQTT, &b);
+ }
+}
+# endif
+
+void ActuatorButtonTrigger() {
+ uint8_t level = !digitalRead(ACTUATOR_ONOFF_GPIO);
+ char* level_string = "ON";
+ if (level != ACTUATOR_ON) {
+ level_string = "OFF";
+ }
+ Log.trace(F("Actuator triggered %s by button" CR), level_string);
+ digitalWrite(ACTUATOR_ONOFF_GPIO, level);
+ pub(subjectGTWONOFFtoMQTT, level_string);
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZactuatorPWM.ino"
+# 68 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZactuatorPWM.ino"
+#include "User_config.h"
+
+#ifdef ZactuatorPWM
+
+# include "config_PWM.h"
+
+static long previousUpdateTime = 0;
+static long currentUpdateTime = 0;
+
+static const char* channelJsonKeys[] = PWM_CHANNEL_NAMES;
+static const int channelPins[] = PWM_CHANNEL_PINS;
+static const int kNumChannels = sizeof(channelPins) / sizeof(int);
+
+
+static float currentValues[kNumChannels] = {};
+static float fadeStartValues[kNumChannels] = {};
+static float targetValues[kNumChannels] = {};
+
+static long fadeStartUpdateTime[kNumChannels] = {};
+static long fadeEndUpdateTime[kNumChannels] = {};
+static bool fadeIsComplete = false;
+
+
+static float calibrationMinLinear[kNumChannels];
+static float calibrationMaxLinear[kNumChannels];
+static float calibrationGamma[kNumChannels];
+
+# if defined(ESP32)
+
+
+static const int kNumDutyCycleBits = 16;
+# elif defined(ESP8266)
+
+
+static const int kNumDutyCycleBits = 10;
+# else
+
+
+
+static const int kNumDutyCycleBits = 8;
+# endif
+
+static const float kUNormToDutyCycle = (float)((1 << kNumDutyCycleBits) - 1);
+
+void setupPWM() {
+# 126 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZactuatorPWM.ino"
+ for (int i = 0; i < kNumChannels; ++i) {
+# if defined(ESP32)
+
+
+
+ ledcSetup(i, 625.0, kNumDutyCycleBits);
+ ledcAttachPin(channelPins[i], i);
+# endif
+ calibrationMinLinear[i] = 0.f;
+ calibrationMaxLinear[i] = 1.f;
+ calibrationGamma[i] = PWM_DEFAULT_GAMMA;
+ }
+
+ Log.trace(F("ZactuatorPWM setup done " CR));
+}
+
+
+
+static float perceptualToLinear(float perceptual, int channelIdx) {
+ return pow(perceptual, calibrationGamma[channelIdx]);
+}
+
+
+void PWMLoop() {
+ previousUpdateTime = currentUpdateTime;
+ currentUpdateTime = millis();
+
+ if (fadeIsComplete) {
+ return;
+ }
+
+ fadeIsComplete = true;
+ for (int i = 0; i < kNumChannels; ++i) {
+
+ long totalFadeDuration = fadeEndUpdateTime[i] - fadeStartUpdateTime[i];
+ float fadeLerpValue = 1.f;
+ if (totalFadeDuration > 0) {
+ fadeLerpValue = (float)(currentUpdateTime - fadeStartUpdateTime[i]) / (float)totalFadeDuration;
+ }
+ if (fadeLerpValue >= 1.f) {
+ currentValues[i] = targetValues[i];
+ } else {
+ currentValues[i] = ((targetValues[i] - fadeStartValues[i]) * fadeLerpValue) + fadeStartValues[i];
+ fadeIsComplete = false;
+ }
+ }
+
+
+
+ for (int i = 0; i < kNumChannels; ++i) {
+ float linear = perceptualToLinear(currentValues[i], i);
+
+
+ if (linear > 0.f) {
+
+ linear = (linear * (calibrationMaxLinear[i] - calibrationMinLinear[i])) + calibrationMinLinear[i];
+ }
+
+ long dutyCycle = (long)(linear * kUNormToDutyCycle);
+# if defined(ESP32)
+ ledcWrite(i, dutyCycle);
+# else
+ analogWrite(channelPins[i], dutyCycle);
+# endif
+
+ }
+}
+
+boolean PWMtoMQTT() {
+ return false;
+}
+
+# if jsonReceiving
+void MQTTtoPWM(char* topicOri, JsonObject& jsonData) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoPWMset)) {
+ Log.trace(F("MQTTtoPWM JSON analysis" CR));
+
+ int modifiedChannelBits = 0;
+ for (int i = 0; i < kNumChannels; ++i) {
+ fadeStartValues[i] = currentValues[i];
+ JsonVariant value = jsonData[channelJsonKeys[i]];
+ if (!value.isNull()) {
+ float targetValue = value.as();
+ targetValue = std::min(targetValue, 1.f);
+ targetValue = std::max(targetValue, 0.f);
+ targetValues[i] = targetValue;
+
+
+ fadeStartUpdateTime[i] = currentUpdateTime;
+ fadeEndUpdateTime[i] = currentUpdateTime;
+
+ modifiedChannelBits |= (1 << i);
+ }
+ }
+
+ JsonVariant fade = jsonData["fade"];
+ if (!fade.isNull()) {
+
+ long endUpdateTime = currentUpdateTime + (long)(fade.as() * (1000.f));
+
+ for (int i = 0; i < kNumChannels; ++i) {
+ if (modifiedChannelBits & (1 << i)) {
+ fadeEndUpdateTime[i] = endUpdateTime;
+ }
+ }
+ }
+ fadeIsComplete = false;
+ } else if (cmpToMainTopic(topicOri, subjectMQTTtoPWMcalibrate)) {
+
+ for (int i = 0; i < kNumChannels; ++i) {
+ char key[64];
+ snprintf(key, sizeof(key), "gamma-%s", channelJsonKeys[i]);
+ JsonVariant value = jsonData[key];
+ if (!value.isNull()) {
+ float gamma = value.as();
+
+ gamma = std::min(gamma, 4.f);
+ gamma = std::max(gamma, 0.5f);
+ calibrationGamma[i] = gamma;
+ }
+ snprintf(key, sizeof(key), "min-%s", channelJsonKeys[i]);
+ value = jsonData[key];
+ if (!value.isNull()) {
+ calibrationMinLinear[i] = perceptualToLinear(value.as(), i);
+ }
+ snprintf(key, sizeof(key), "max-%s", channelJsonKeys[i]);
+ value = jsonData[key];
+ if (!value.isNull()) {
+ calibrationMaxLinear[i] = perceptualToLinear(value.as(), i);
+ }
+ }
+ }
+}
+# endif
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZactuatorSomfy.ino"
+# 24 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZactuatorSomfy.ino"
+#include "User_config.h"
+
+#ifdef ZactuatorSomfy
+
+# include
+# include
+# include
+
+# ifdef ZradioCC1101
+# include
+# endif
+
+void setupSomfy() {
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.Init();
+# endif
+ pinMode(RF_EMITTER_GPIO, OUTPUT);
+ digitalWrite(RF_EMITTER_GPIO, LOW);
+
+# if defined(ESP32)
+ if (!EEPROM.begin(max(4, SOMFY_REMOTE_NUM * 2))) {
+ Log.error(F("failed to initialise EEPROM" CR));
+ }
+# elif defined(ESP8266)
+ EEPROM.begin(max(4, SOMFY_REMOTE_NUM * 2));
+# endif
+
+ Log.trace(F("ZactuatorSomfy setup done " CR));
+}
+
+# if jsonReceiving
+void MQTTtoSomfy(char* topicOri, JsonObject& jsonData) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoSomfy)) {
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.SetTx(CC1101_FREQUENCY_SOMFY);
+# endif
+ Log.trace(F("MQTTtoSomfy json data analysis" CR));
+
+ const int remoteIndex = jsonData["remote"];
+ if (remoteIndex >= SOMFY_REMOTE_NUM) {
+ Log.warning(F("ZactuatorSomfy remote does not exist" CR));
+ return;
+ }
+ const String commandData = jsonData["command"];
+ const Command command = getSomfyCommand(commandData);
+
+ const int repeat = jsonData["repeat"] | 4;
+
+ EEPROMRollingCodeStorage rollingCodeStorage(EEPROM_ADDRESS_START + remoteIndex * 2);
+ SomfyRemote somfyRemote(RF_EMITTER_GPIO, somfyRemotes[remoteIndex], &rollingCodeStorage);
+ somfyRemote.sendCommand(command, repeat);
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.SetRx(receiveMhz);
+# endif
+ }
+}
+# endif
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZboardHeltec.ino"
+# 31 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZboardHeltec.ino"
+#include "User_config.h"
+#if defined(ZboardHELTEC)
+# include "ArduinoLog.h"
+# include "config_HELTEC.h"
+
+
+void logToLCD(bool display) {
+ display ? Log.begin(LOG_LEVEL_LCD, &Oled) : Log.begin(LOG_LEVEL, &Serial);
+}
+
+void setupHELTEC() {
+ Log.trace(F("Setup HELTEC Display" CR));
+ Oled.begin();
+ Log.notice(F("Setup HELTEC Display end" CR));
+
+# if LOG_TO_LCD
+ Log.begin(LOG_LEVEL_LCD, &Oled);
+# endif
+}
+
+void loopHELTEC() {
+ static int previousLogLevel;
+ int currentLogLevel = Log.getLastMsgLevel();
+ if (previousLogLevel != currentLogLevel && lowpowermode != 2) {
+ switch (currentLogLevel) {
+ case 1:
+ case 2:
+
+
+
+ break;
+ case 3:
+
+
+
+ break;
+ default:
+
+
+ Oled.fillScreen(WHITE);
+ Oled.drawLogo((int)OLED_WIDTH * 0.24, (int)(OLED_WIDTH / 2) - OLED_WIDTH * 0.2, (int)(OLED_HEIGHT / 2) + OLED_HEIGHT * 0.2, true, true, true, true, true, true);
+ break;
+ }
+ }
+ previousLogLevel = currentLogLevel;
+}
+
+void MQTTtoHELTEC(char* topicOri, JsonObject& HELTECdata) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoHELTECset)) {
+ Log.trace(F("MQTTtoHELTEC json set" CR));
+
+ if (HELTECdata.containsKey("log-lcd")) {
+ bool displayOnLCD = HELTECdata["log-lcd"];
+ Log.notice(F("Set lcd log: %T" CR), displayOnLCD);
+ logToLCD(displayOnLCD);
+ }
+ }
+}
+
+
+
+void heltecPrint(char* line1, char* line2, char* line3) {
+ Oled.println(line1);
+ Oled.println(line2);
+ Oled.println(line3);
+ delay(2000);
+}
+
+void heltecPrint(char* line1, char* line2) {
+ Oled.println(line1);
+ Oled.println(line2);
+ delay(2000);
+}
+
+void heltecPrint(char* line1) {
+ Oled.println(line1);
+ delay(2000);
+}
+
+
+
+OledSerial Oled(0);
+OledSerial::OledSerial(int x) {
+ pinMode(RST_OLED, OUTPUT);
+ digitalWrite(RST_OLED, LOW);
+ delay(50);
+ digitalWrite(RST_OLED, HIGH);
+
+# if defined(WIFI_Kit_32) || defined(WIFI_LoRa_32) || defined(WIFI_LoRa_32_V2)
+ display = new SSD1306Wire(0x3c, SDA_OLED, SCL_OLED, GEOMETRY_128_64);
+# elif defined(Wireless_Stick)
+ display = new SSD1306Wire(0x3c, SDA_OLED, SCL_OLED, GEOMETRY_64_32);
+# endif
+}
+
+void OledSerial::begin() {
+
+ display->init();
+ display->flipScreenVertically();
+ display->setFont(ArialMT_Plain_10);
+
+ display->setColor(WHITE);
+ display->fillRect(0, 0, OLED_WIDTH, OLED_HEIGHT);
+ display->display();
+ displayIntro(OLED_WIDTH * 0.24, (OLED_WIDTH / 2) - OLED_WIDTH * 0.2, (OLED_HEIGHT / 2) + OLED_HEIGHT * 0.2);
+ display->setLogBuffer(OLED_TEXT_ROWS, OLED_TEXT_BUFFER);
+ delay(1000);
+}
+
+
+
+int OledSerial::available(void) {
+}
+
+int OledSerial::peek(void) {
+}
+
+int OledSerial::read(void) {
+}
+
+void OledSerial::flush(void) {
+}
+
+void OledSerial::fillScreen(OLEDDISPLAY_COLOR color) {
+ display->clear();
+ display->setColor(color);
+ display->fillRect(0, 0, OLED_WIDTH, OLED_HEIGHT);
+}
+
+size_t OledSerial::write(uint8_t c) {
+ display->clear();
+ display->setColor(WHITE);
+ display->setFont(ArialMT_Plain_10);
+
+ display->write((char)c);
+ display->drawLogBuffer(0, 0);
+ display->display();
+ return 1;
+}
+
+size_t OledSerial::write(const uint8_t* buffer, size_t size) {
+ display->clear();
+ display->setColor(WHITE);
+ display->setFont(ArialMT_Plain_10);
+ while (size) {
+ display->write((char)*buffer++);
+ size--;
+ }
+ display->drawLogBuffer(0, 0);
+ display->display();
+ return size;
+}
+
+
+
+
+
+void OledSerial::displayIntro(int scale, int displayWidth, int displayHeight) {
+ drawLogo(scale, displayWidth, displayHeight, false, true, false, false, false, false);
+ drawLogo(scale, displayWidth, displayHeight, false, false, true, false, false, false);
+ drawLogo(scale, displayWidth, displayHeight, false, true, true, true, false, false);
+ drawLogo(scale, displayWidth, displayHeight, false, true, true, false, true, false);
+ drawLogo(scale, displayWidth, displayHeight, true, true, true, true, true, false);
+ drawLogo(scale, displayWidth, displayHeight, true, true, true, true, true, true);
+}
+
+void OledSerial::drawLogo(int logoSize, int circle1X, int circle1Y, bool circle1, bool circle2, bool circle3, bool line1, bool line2, bool name) {
+ int circle1T = logoSize / 15;
+ int circle2T = logoSize / 25;
+ int circle3T = logoSize / 30;
+
+ int circle3Y = circle1Y - (logoSize * 1.2);
+ int circle3X = circle1X - (logoSize * 0.13);
+ int circle2X = circle1X - (logoSize * 1.05);
+ int circle2Y = circle1Y - (logoSize * 0.8);
+
+ if (line1) {
+ display->setColor(BLACK);
+ display->drawLine(circle1X - 2, circle1Y, circle2X - 2, circle2Y);
+ display->drawLine(circle1X - 1, circle1Y, circle2X - 1, circle2Y);
+ display->drawLine(circle1X, circle1Y, circle2X, circle2Y);
+ display->drawLine(circle1X + 1, circle1Y, circle2X + 1, circle2Y);
+ display->drawLine(circle1X + 2, circle1Y, circle2X + 2, circle2Y);
+ display->setColor(WHITE);
+ display->fillCircle(circle3X, circle3Y, logoSize / 4 - circle3T * 2);
+ }
+ if (line2) {
+ display->setColor(BLACK);
+ display->drawLine(circle1X - 2, circle1Y, circle3X - 2, circle3Y);
+ display->drawLine(circle1X - 1, circle1Y, circle3X - 1, circle3Y);
+ display->drawLine(circle1X, circle1Y, circle3X, circle3Y);
+ display->drawLine(circle1X + 1, circle1Y, circle3X + 1, circle3Y);
+ display->setColor(WHITE);
+ display->fillCircle(circle2X, circle2Y, logoSize / 3 - circle2T * 2);
+ }
+ if (circle1) {
+ display->setColor(WHITE);
+ display->fillCircle(circle1X, circle1Y, logoSize / 2);
+ display->setColor(BLACK);
+ display->fillCircle(circle1X, circle1Y, logoSize / 2 - circle1T);
+ display->setColor(WHITE);
+ display->fillCircle(circle1X, circle1Y, logoSize / 2 - circle1T * 2);
+ }
+ if (circle2) {
+ display->setColor(WHITE);
+ display->fillCircle(circle2X, circle2Y, logoSize / 3);
+ display->setColor(BLACK);
+ display->fillCircle(circle2X, circle2Y, logoSize / 3 - circle2T);
+ display->setColor(WHITE);
+ display->fillCircle(circle2X, circle2Y, logoSize / 3 - circle2T * 2);
+ }
+ if (circle3) {
+ display->setColor(WHITE);
+ display->fillCircle(circle3X, circle3Y, logoSize / 4);
+ display->setColor(BLACK);
+ display->fillCircle(circle3X, circle3Y, logoSize / 4 - circle3T);
+ display->setColor(WHITE);
+ display->fillCircle(circle3X, circle3Y, logoSize / 4 - circle3T * 2);
+ }
+ if (name) {
+ display->setColor(BLACK);
+ display->drawString(circle1X + (circle1X * 0.27), circle1Y, "penMQTTGateway");
+ }
+ display->display();
+ delay(50);
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZboardM5.ino"
+# 29 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZboardM5.ino"
+#include "User_config.h"
+
+#if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5STACK) || defined(ZboardM5TOUGH)
+# ifdef ZboardM5STICKC
+# include
+# endif
+# ifdef ZboardM5STICKCP
+# include
+# endif
+# ifdef ZboardM5STACK
+# include
+# endif
+# ifdef ZboardM5TOUGH
+# include
+# endif
+void logToLCD(bool display) {
+ display ? Log.begin(LOG_LEVEL_LCD, &M5.Lcd) : Log.begin(LOG_LEVEL, &Serial);
+}
+
+void setBrightness(int brightness) {
+# if defined(ZboardM5STACK)
+ M5.Lcd.setBrightness(brightness * 2);
+# endif
+# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5TOUGH)
+ (!brightness) ? M5.Axp.ScreenBreath(0) : M5.Axp.ScreenBreath(7 + (int)brightness * 0.08);
+# endif
+}
+
+void setupM5() {
+ Log.notice(F("Setup M5" CR));
+ pinMode(SLEEP_BUTTON, INPUT);
+
+
+
+ Log.notice(F("Low power set to: %d" CR), lowpowermode);
+ switch (lowpowermode)
+ {
+ case 0:
+ wakeScreen(NORMAL_LCD_BRIGHTNESS);
+ M5.Lcd.fillScreen(WHITE);
+ displayIntro(M5.Lcd.width() * 0.25, (M5.Lcd.width() / 2) + M5.Lcd.width() * 0.12, (M5.Lcd.height() / 2) + M5.Lcd.height() * 0.2);
+# if LOG_TO_LCD
+ Log.begin(LOG_LEVEL_LCD, &M5.Lcd);
+# endif
+ break;
+ case 1:
+ wakeScreen(SLEEP_LCD_BRIGHTNESS);
+ M5.Lcd.fillScreen(WHITE);
+# if LOG_TO_LCD
+ Log.begin(LOG_LEVEL_LCD, &M5.Lcd);
+# endif
+ break;
+ case 2:
+ M5.begin(false, false, false);
+ break;
+ }
+ Log.notice(F("Setup M5 end" CR));
+}
+
+void sleepScreen() {
+ Log.trace(F("Screen going to sleep" CR));
+# if defined(ZboardM5STACK)
+ M5.begin(false, false, false);
+# endif
+# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5TOUGH)
+ M5.Axp.ScreenBreath(0);
+ M5.Axp.SetLDO2(false);
+# endif
+}
+
+void wakeScreen(int brightness) {
+ Log.trace(F("Screen wake up" CR));
+ M5.begin();
+ M5.Lcd.setCursor(0, 0, (M5.Lcd.height() > 200) ? 4 : 2);
+ M5.Lcd.setTextSize(1);
+ M5.Lcd.setRotation(1);
+ setBrightness(brightness);
+}
+
+void loopM5() {
+ static int previousBtnState;
+ int currentBtnState = digitalRead(SLEEP_BUTTON);
+ if (currentBtnState != previousBtnState && currentBtnState == 0) {
+ int newlowpowermode = lowpowermode;
+ (lowpowermode == 2) ? newlowpowermode = 0 : newlowpowermode = newlowpowermode + 1;
+ changelowpowermode(newlowpowermode);
+ }
+ previousBtnState = currentBtnState;
+ static int previousLogLevel;
+ int currentLogLevel = Log.getLastMsgLevel();
+ if (previousLogLevel != currentLogLevel && lowpowermode != 2) {
+ switch (currentLogLevel) {
+ case 1:
+ case 2:
+ wakeScreen(NORMAL_LCD_BRIGHTNESS);
+ M5.Lcd.fillScreen(TFT_RED);
+ M5.Lcd.setTextColor(TFT_BLACK, TFT_RED);
+ break;
+ case 3:
+ wakeScreen(NORMAL_LCD_BRIGHTNESS);
+ M5.Lcd.fillScreen(TFT_ORANGE);
+ M5.Lcd.setTextColor(TFT_BLACK, TFT_ORANGE);
+ break;
+ default:
+ wakeScreen(SLEEP_LCD_BRIGHTNESS);
+ M5.Lcd.fillScreen(TFT_WHITE);
+ drawLogo(M5.Lcd.width() * 0.1875, (M5.Lcd.width() / 2) - M5.Lcd.width() * 0.24, M5.Lcd.height() * 0.5, true, true, true, true, true, true);
+ break;
+ }
+ }
+ previousLogLevel = currentLogLevel;
+}
+
+void MQTTtoM5(char* topicOri, JsonObject& M5data) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoM5set)) {
+ Log.trace(F("MQTTtoM5 json set" CR));
+
+ if (M5data.containsKey("log-lcd")) {
+ bool displayOnLCD = M5data["log-lcd"];
+ Log.notice(F("Set lcd log: %T" CR), displayOnLCD);
+ logToLCD(displayOnLCD);
+ }
+ }
+}
+
+void displayIntro(int i, int X, int Y) {
+ Log.trace(F("Intro display on screen" CR));
+ drawLogo(i, X, Y, false, true, false, false, false, false);
+ delay(50);
+ drawLogo(i, X, Y, false, false, true, false, false, false);
+ delay(50);
+ drawLogo(i, X, Y, false, true, false, false, false, false);
+ delay(50);
+ drawLogo(i, X, Y, false, true, true, false, false, false);
+ delay(50);
+ drawLogo(i, X, Y, false, true, true, true, false, false);
+ delay(50);
+ drawLogo(i, X, Y, false, true, true, false, true, false);
+ delay(50);
+ drawLogo(i, X, Y, false, true, true, true, true, false);
+ delay(50);
+ drawLogo(i, X, Y, true, true, true, true, true, false);
+}
+
+void drawLogo(int logoSize, int circle1X, int circle1Y, bool circle1, bool circle2, bool circle3, bool line1, bool line2, bool name) {
+ int circle1T = logoSize / 15;
+ int circle2T = logoSize / 25;
+ int circle3T = logoSize / 30;
+
+ int circle3Y = circle1Y - (logoSize * 1.2);
+ int circle3X = circle1X - (logoSize * 0.13);
+ int circle2X = circle1X - (logoSize * 1.05);
+ int circle2Y = circle1Y - (logoSize * 0.8);
+
+ if (line1) {
+ M5.Lcd.drawLine(circle1X - 2, circle1Y, circle2X - 2, circle2Y, BLUE);
+ M5.Lcd.drawLine(circle1X - 1, circle1Y, circle2X - 1, circle2Y, BLUE);
+ M5.Lcd.drawLine(circle1X, circle1Y, circle2X, circle2Y, BLUE);
+ M5.Lcd.drawLine(circle1X + 1, circle1Y, circle2X + 1, circle2Y, BLUE);
+ M5.Lcd.drawLine(circle1X + 2, circle1Y, circle2X + 2, circle2Y, BLUE);
+ M5.Lcd.fillCircle(circle3X, circle3Y, logoSize / 4 - circle3T * 2, WHITE);
+ }
+ if (line2) {
+ M5.Lcd.drawLine(circle1X - 2, circle1Y, circle3X - 2, circle3Y, BLUE);
+ M5.Lcd.drawLine(circle1X - 1, circle1Y, circle3X - 1, circle3Y, BLUE);
+ M5.Lcd.drawLine(circle1X, circle1Y, circle3X, circle3Y, BLUE);
+ M5.Lcd.drawLine(circle1X + 1, circle1Y, circle3X + 1, circle3Y, BLUE);
+ M5.Lcd.fillCircle(circle2X, circle2Y, logoSize / 3 - circle2T * 2, WHITE);
+ }
+ M5.Lcd.fillCircle(circle1X, circle1Y, logoSize / 2 - circle1T * 2, WHITE);
+ if (circle1) {
+ M5.Lcd.fillCircle(circle1X, circle1Y, logoSize / 2, WHITE);
+ M5.Lcd.fillCircle(circle1X, circle1Y, logoSize / 2 - circle1T, TFT_GREEN);
+ M5.Lcd.fillCircle(circle1X, circle1Y, logoSize / 2 - circle1T * 2, WHITE);
+ }
+ if (circle2) {
+ M5.Lcd.fillCircle(circle2X, circle2Y, logoSize / 3, WHITE);
+ M5.Lcd.fillCircle(circle2X, circle2Y, logoSize / 3 - circle2T, TFT_ORANGE);
+ M5.Lcd.fillCircle(circle2X, circle2Y, logoSize / 3 - circle2T * 2, WHITE);
+ }
+ if (circle3) {
+ M5.Lcd.fillCircle(circle3X, circle3Y, logoSize / 4, WHITE);
+ M5.Lcd.fillCircle(circle3X, circle3Y, logoSize / 4 - circle3T, TFT_PINK);
+ M5.Lcd.fillCircle(circle3X, circle3Y, logoSize / 4 - circle3T * 2, WHITE);
+ }
+ if (name) {
+ M5.Lcd.setTextColor(BLUE);
+ M5.Lcd.drawString("penMQTTGateway", circle1X + (circle1X * 0.27), circle1Y, (M5.Lcd.height() > 200) ? 4 : 2);
+ }
+}
+
+void M5Print(char* line1, char* line2, char* line3) {
+ if (lowpowermode == 2) digitalWrite(LED_INFO, LED_INFO_ON);
+ wakeScreen(NORMAL_LCD_BRIGHTNESS);
+ M5.Lcd.fillScreen(TFT_WHITE);
+ drawLogo(M5.Lcd.width() * 0.1875, (M5.Lcd.width() / 2) - M5.Lcd.width() * 0.24, M5.Lcd.height() * 0.5, true, true, true, true, true, true);
+ M5.Lcd.setTextColor(BLUE);
+ M5.Lcd.drawString(line1, 5, M5.Lcd.height() * 0.7, 1);
+ M5.Lcd.drawString(line2, 5, M5.Lcd.height() * 0.8, 1);
+ M5.Lcd.drawString(line3, 5, M5.Lcd.height() * 0.9, 1);
+ delay(2000);
+ digitalWrite(LED_INFO, !LED_INFO_ON);
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/Zgateway2G.ino"
+# 28 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/Zgateway2G.ino"
+#include "User_config.h"
+
+#ifdef Zgateway2G
+
+# include
+# include
+
+
+A6lib A6l(_2G_TX_GPIO, _2G_RX_GPIO);
+
+int unreadSMSLocs[50] = {0};
+int unreadSMSNum = 0;
+SMSmessage sms;
+
+void setup2G() {
+ Log.notice(F("_2G_TX_GPIO: %d " CR), _2G_TX_GPIO);
+ Log.notice(F("_2G_RX_GPIO: %d " CR), _2G_RX_GPIO);
+ setupGSM(false);
+ Log.trace(F("Zgateway2G setup done " CR));
+}
+
+void setupGSM(bool deleteSMS) {
+ Log.trace(F("Init 2G module: %d" CR), _2G_PWR_GPIO);
+ delay(1000);
+
+ A6l.powerCycle(_2G_PWR_GPIO);
+ Log.notice(F("waiting for network connection at bd: %d" CR), _2G_MODULE_BAUDRATE);
+ A6l.blockUntilReady(_2G_MODULE_BAUDRATE);
+ Log.notice(F("A6/A7 gsm ready" CR));
+ signalStrengthAnalysis();
+ delay(1000);
+
+ if (deleteSMS) {
+ if (A6l.deleteSMS(1, 4) == A6_OK) {
+ Log.notice(F("delete SMS OK" CR));
+ } else {
+ Log.error(F("delete SMS KO" CR));
+ }
+ }
+}
+
+void signalStrengthAnalysis() {
+ int signalStrength = 0;
+ signalStrength = A6l.getSignalStrength();
+ Log.trace(F("Signal strength: %d" CR), signalStrength);
+ if (signalStrength < _2G_MIN_SIGNAL || signalStrength > _2G_MAX_SIGNAL) {
+ Log.trace(F("Signal too low restart the module" CR));
+ setupGSM(false);
+ }
+}
+
+bool _2GtoMQTT() {
+
+ unreadSMSNum = A6l.getUnreadSMSLocs(unreadSMSLocs, 512);
+ Log.trace(F("Creating SMS buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject SMSdata = jsonBuffer.to();
+ for (int i = 0; i < unreadSMSNum; i++) {
+ Log.notice(F("New message at index: %d" CR), unreadSMSNum);
+ sms = A6l.readSMS(unreadSMSLocs[i]);
+ SMSdata["message"] = (const char*)sms.message.c_str();
+ SMSdata["date"] = (const char*)sms.date.c_str();
+ SMSdata["phone"] = (const char*)sms.number.c_str();
+ A6l.deleteSMS(unreadSMSLocs[i]);
+ Log.trace(F("Adv data 2GtoMQTT" CR));
+ pub(subject2GtoMQTT, SMSdata);
+ return true;
+ }
+ return false;
+}
+# if simpleReceiving
+void MQTTto2G(char* topicOri, char* datacallback) {
+ String data = datacallback;
+ String topic = topicOri;
+
+ if (cmpToMainTopic(topicOri, subjectMQTTto2G)) {
+ Log.trace(F("MQTTto2G data analysis" CR));
+
+ String phone_number = "";
+ int pos0 = topic.lastIndexOf(_2GPhoneKey);
+ if (pos0 != -1) {
+ pos0 = pos0 + strlen(_2GPhoneKey);
+ phone_number = topic.substring(pos0);
+ Log.notice(F("MQTTto2G phone: %s" CR), (char*)phone_number.c_str());
+ Log.notice(F("MQTTto2G sms: %s" CR), (char*)data.c_str());
+ if (A6l.sendSMS(phone_number, data) == A6_OK) {
+ Log.notice(F("SMS OK" CR));
+
+ pub(subjectGTW2GtoMQTT, "SMS OK");
+ } else {
+ Log.error(F("SMS KO" CR));
+
+ pub(subjectGTW2GtoMQTT, "SMS KO");
+ }
+ } else {
+ Log.error(F("MQTTto2G Fail reading phone number" CR));
+ }
+ }
+}
+# endif
+
+# if jsonReceiving
+void MQTTto2G(char* topicOri, JsonObject& SMSdata) {
+ if (cmpToMainTopic(topicOri, subjectMQTTto2G)) {
+ const char* sms = SMSdata["message"];
+ const char* phone = SMSdata["phone"];
+ Log.trace(F("MQTTto2G json data analysis" CR));
+ if (sms && phone) {
+ Log.notice(F("MQTTto2G phone: %s" CR), phone);
+ Log.notice(F("MQTTto2G sms: %s" CR), sms);
+ if (A6l.sendSMS(String(phone), String(sms)) == A6_OK) {
+ Log.notice(F("SMS OK" CR));
+
+ pub(subjectGTW2GtoMQTT, "SMS OK");
+ } else {
+ Log.error(F("SMS KO" CR));
+ pub(subjectGTW2GtoMQTT, "SMS KO");
+ }
+ } else {
+ Log.error(F("MQTTto2G failed json read" CR));
+ }
+ }
+}
+# endif
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayBLEConnect.ino"
+#ifdef ESP32
+# include "User_config.h"
+# ifdef ZgatewayBT
+# include "ArduinoJson.h"
+# include "ArduinoLog.h"
+# include "ZgatewayBLEConnect.h"
+
+#define convertTemp_CtoF(c) ((c * 1.8) + 32)
+
+extern bool ProcessLock;
+extern std::vector devices;
+
+NimBLERemoteCharacteristic* zBLEConnect::getCharacteristic(const NimBLEUUID& service,
+ const NimBLEUUID& characteristic) {
+ BLERemoteCharacteristic* pRemoteCharacteristic = nullptr;
+ if (!m_pClient) {
+ Log.error(F("No BLE client" CR));
+ } else if (!m_pClient->isConnected() && !m_pClient->connect()) {
+ Log.error(F("Connect to: %s failed" CR), m_pClient->getPeerAddress().toString().c_str());
+ } else {
+ BLERemoteService* pRemoteService = m_pClient->getService(service);
+ if (!pRemoteService) {
+ Log.notice(F("Failed to find service UUID: %s" CR), service.toString().c_str());
+ } else {
+ Log.trace(F("Found service: %s" CR), service.toString().c_str());
+ Log.trace(F("Client isConnected, freeHeap: %d" CR), ESP.getFreeHeap());
+ pRemoteCharacteristic = pRemoteService->getCharacteristic(characteristic);
+ if (!pRemoteCharacteristic) {
+ Log.notice(F("Failed to find characteristic UUID: %s" CR), characteristic.toString().c_str());
+ }
+ }
+ }
+
+ return pRemoteCharacteristic;
+}
+
+bool zBLEConnect::writeData(BLEAction* action) {
+ NimBLERemoteCharacteristic* pChar = getCharacteristic(action->service, action->characteristic);
+ if (pChar && (pChar->canWrite() || pChar->canWriteNoResponse())) {
+ switch (action->value_type) {
+ case BLE_VAL_HEX: {
+ int len = action->value.length();
+ if (len % 2) {
+ Log.error(F("Invalid HEX value length" CR));
+ return false;
+ }
+
+ std::vector buf;
+ for (auto i = 0; i < len; i += 2) {
+ std::string temp = action->value.substr(i, 2);
+ buf.push_back((uint8_t)strtoul(temp.c_str(), nullptr, 16));
+ }
+ return pChar->writeValue((const uint8_t*)&buf[0], buf.size(), !pChar->canWriteNoResponse());
+ }
+ case BLE_VAL_INT:
+ return pChar->writeValue(strtol(action->value.c_str(), nullptr, 0), !pChar->canWriteNoResponse());
+ case BLE_VAL_FLOAT:
+ return pChar->writeValue(strtod(action->value.c_str(), nullptr), !pChar->canWriteNoResponse());
+ default:
+ return pChar->writeValue(action->value, !pChar->canWriteNoResponse());
+ }
+ }
+ return false;
+}
+
+bool zBLEConnect::readData(BLEAction* action) {
+ NimBLERemoteCharacteristic* pChar = getCharacteristic(action->service, action->characteristic);
+
+ if (pChar && pChar->canRead()) {
+ action->value = pChar->readValue();
+ if (action->value != "") {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool zBLEConnect::processActions(std::vector& actions) {
+ bool result = false;
+ if (actions.size() > 0) {
+ for (auto& it : actions) {
+ if (NimBLEAddress(it.addr) == m_pClient->getPeerAddress()) {
+ JsonObject BLEresult = getBTJsonObject();
+ BLEresult["id"] = it.addr;
+ BLEresult["service"] = it.service.toString();
+ BLEresult["characteristic"] = it.characteristic.toString();
+
+ if (it.write) {
+ Log.trace(F("processing BLE write" CR));
+ BLEresult["write"] = it.value;
+ result = writeData(&it);
+ } else {
+ Log.trace(F("processing BLE read" CR));
+ result = readData(&it);
+ if (result) {
+ switch (it.value_type) {
+ case BLE_VAL_HEX: {
+ char* pHex = NimBLEUtils::buildHexData(nullptr, (uint8_t*)it.value.c_str(), it.value.length());
+ BLEresult["read"] = pHex;
+ free(pHex);
+ break;
+ }
+ case BLE_VAL_INT: {
+ int ival = *(int*)it.value.data();
+ BLEresult["read"] = ival;
+ break;
+ }
+ case BLE_VAL_FLOAT: {
+ float fval = *(double*)it.value.data();
+ BLEresult["read"] = fval;
+ break;
+ }
+ default:
+ BLEresult["read"] = it.value.c_str();
+ break;
+ }
+ }
+ }
+
+ it.complete = result;
+ BLEresult["success"] = result;
+ if (result || it.ttl <= 1) {
+ pubBT(BLEresult);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+
+void LYWSD03MMC_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
+ if (m_taskHandle == nullptr) {
+ return;
+ }
+ if (!ProcessLock) {
+ Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str());
+
+ if (length == 5) {
+ Log.trace(F("Device identified creating BLE buffer" CR));
+ JsonObject BLEdata = getBTJsonObject();
+ String mac_address = m_pClient->getPeerAddress().toString().c_str();
+ mac_address.toUpperCase();
+ for (std::vector::iterator it = devices.begin(); it != devices.end(); ++it) {
+ BLEdevice* p = *it;
+ if ((strcmp(p->macAdr, (char*)mac_address.c_str()) == 0)) {
+ if (p->sensorModel_id == BLEconectable::id::LYWSD03MMC)
+ BLEdata["model"] = "LYWSD03MMC";
+ else if (p->sensorModel_id == BLEconectable::id::MHO_C401)
+ BLEdata["model"] = "MHO-C401";
+ }
+ }
+ BLEdata["id"] = (char*)mac_address.c_str();
+ Log.trace(F("Device identified in CB: %s" CR), (char*)mac_address.c_str());
+ BLEdata["tempc"] = (float)((pData[0] | (pData[1] << 8)) * 0.01);
+ BLEdata["tempf"] = (float)(convertTemp_CtoF((pData[0] | (pData[1] << 8)) * 0.01));
+ BLEdata["hum"] = (float)(pData[2]);
+ BLEdata["volt"] = (float)(((pData[4] * 256) + pData[3]) / 1000.0);
+ BLEdata["batt"] = (float)(((((pData[4] * 256) + pData[3]) / 1000.0) - 2.1) * 100);
+
+ pubBT(BLEdata);
+ } else {
+ Log.notice(F("Invalid notification data" CR));
+ return;
+ }
+ } else {
+ Log.trace(F("Callback process canceled by processLock" CR));
+ }
+
+ xTaskNotifyGive(m_taskHandle);
+}
+
+void LYWSD03MMC_connect::publishData() {
+ NimBLEUUID serviceUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6");
+ NimBLEUUID charUUID("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6");
+ NimBLERemoteCharacteristic* pChar = getCharacteristic(serviceUUID, charUUID);
+
+ if (pChar && pChar->canNotify()) {
+ Log.trace(F("Registering notification" CR));
+ if (pChar->subscribe(true, std::bind(&LYWSD03MMC_connect::notifyCB, this,
+ std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3, std::placeholders::_4))) {
+ m_taskHandle = xTaskGetCurrentTaskHandle();
+ if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(BLE_CNCT_TIMEOUT)) == pdFALSE) {
+ m_taskHandle = nullptr;
+ }
+ } else {
+ Log.notice(F("Failed registering notification" CR));
+ }
+ }
+}
+
+
+void DT24_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
+ if (m_taskHandle == nullptr) {
+ return;
+ }
+
+ if (!ProcessLock) {
+ Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str());
+ if (length == 20) {
+ m_data.assign(pData, pData + length);
+ return;
+ } else if (m_data.size() == 20 && length == 16) {
+ m_data.insert(m_data.end(), pData, pData + length);
+
+
+
+
+ Log.trace(F("Device identified creating BLE buffer" CR));
+ JsonObject& BLEdata = getBTJsonObject();
+ String mac_address = m_pClient->getPeerAddress().toString().c_str();
+ mac_address.toUpperCase();
+ BLEdata["model"] = "DT24";
+ BLEdata["id"] = (char*)mac_address.c_str();
+ Log.trace(F("Device identified in CB: %s" CR), (char*)mac_address.c_str());
+ BLEdata["volt"] = (float)(((m_data[4] * 256 * 256) + (m_data[5] * 256) + m_data[6]) / 10.0);
+ BLEdata["current"] = (float)(((m_data[7] * 256 * 256) + (m_data[8] * 256) + m_data[9]) / 1000.0);
+ BLEdata["power"] = (float)(((m_data[10] * 256 * 256) + (m_data[11] * 256) + m_data[12]) / 10.0);
+ BLEdata["energy"] = (float)(((m_data[13] * 256 * 256 * 256) + (m_data[14] * 256 * 256) + (m_data[15] * 256) + m_data[16]) / 100.0);
+ BLEdata["price"] = (float)(((m_data[17] * 256 * 256) + (m_data[18] * 256) + m_data[19]) / 100.0);
+ BLEdata["tempc"] = (float)(m_data[24] * 256) + m_data[25];
+ BLEdata["tempf"] = (float)(convertTemp_CtoF((m_data[24] * 256) + m_data[25]));
+
+ pubBT(BLEdata);
+ } else {
+ Log.notice(F("Invalid notification data" CR));
+ return;
+ }
+ } else {
+ Log.trace(F("Callback process canceled by processLock" CR));
+ }
+
+ xTaskNotifyGive(m_taskHandle);
+}
+
+void DT24_connect::publishData() {
+ NimBLEUUID serviceUUID("ffe0");
+ NimBLEUUID charUUID("ffe1");
+ NimBLERemoteCharacteristic* pChar = getCharacteristic(serviceUUID, charUUID);
+
+ if (pChar && pChar->canNotify()) {
+ Log.trace(F("Registering notification" CR));
+ if (pChar->subscribe(true, std::bind(&DT24_connect::notifyCB, this,
+ std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3, std::placeholders::_4))) {
+ m_taskHandle = xTaskGetCurrentTaskHandle();
+ if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(BLE_CNCT_TIMEOUT)) == pdFALSE) {
+ m_taskHandle = nullptr;
+ }
+ } else {
+ Log.notice(F("Failed registering notification" CR));
+ }
+ }
+}
+
+
+void HHCCJCY01HHCC_connect::publishData() {
+ NimBLEUUID serviceUUID("00001204-0000-1000-8000-00805f9b34fb");
+ NimBLEUUID charUUID("00001a00-0000-1000-8000-00805f9b34fb");
+ NimBLEUUID charUUID2("00001a02-0000-1000-8000-00805f9b34fb");
+ NimBLERemoteCharacteristic* pChar = getCharacteristic(serviceUUID, charUUID);
+
+ if (pChar) {
+ Log.trace(F("Read mode" CR));
+ uint8_t buf[2] = {0xA0, 0x1F};
+ pChar->writeValue(buf, 2, true);
+ int batteryValue = -1;
+ NimBLERemoteCharacteristic* pChar2 = getCharacteristic(serviceUUID, charUUID2);
+ if (pChar2) {
+ std::string value;
+ value = pChar2->readValue();
+ const char* val2 = value.c_str();
+ batteryValue = val2[0];
+ JsonObject& BLEdata = getBTJsonObject();
+ String mac_address = m_pClient->getPeerAddress().toString().c_str();
+ mac_address.toUpperCase();
+ BLEdata["model"] = "HHCCJCY01HHCC";
+ BLEdata["id"] = (char*)mac_address.c_str();
+ BLEdata["batt"] = (int)batteryValue;
+ pubBT(BLEdata);
+ } else {
+ Log.notice(F("Failed getting characteristic" CR));
+ }
+ } else {
+ Log.notice(F("Failed getting characteristic" CR));
+ }
+}
+
+
+void XMWSDJ04MMC_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
+ if (m_taskHandle == nullptr) {
+ return;
+ }
+ if (!ProcessLock) {
+ Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str());
+
+ if (length == 6) {
+ Log.trace(F("Device identified creating BLE buffer" CR));
+ JsonObject BLEdata = getBTJsonObject();
+ String mac_address = m_pClient->getPeerAddress().toString().c_str();
+ mac_address.toUpperCase();
+ BLEdata["model"] = "XMWSDJ04MMC";
+ BLEdata["id"] = (char*)mac_address.c_str();
+ Log.trace(F("Device identified in CB: %s" CR), (char*)mac_address.c_str());
+ BLEdata["tempc"] = (float)((pData[0] | (pData[1] << 8)) * 0.1);
+ BLEdata["tempf"] = (float)(convertTemp_CtoF((pData[0] | (pData[1] << 8)) * 0.1));
+ BLEdata["hum"] = (float)((pData[2] | (pData[3] << 8)) * 0.1);
+ BLEdata["volt"] = (float)((pData[4] | (pData[5] << 8)) / 1000.0);
+ BLEdata["batt"] = (float)((((pData[4] | (pData[5] << 8)) / 1000.0) - 2.1) * 100);
+
+ pubBT(BLEdata);
+ } else {
+ Log.notice(F("Invalid notification data" CR));
+ return;
+ }
+ } else {
+ Log.trace(F("Callback process canceled by processLock" CR));
+ }
+
+ xTaskNotifyGive(m_taskHandle);
+}
+
+void XMWSDJ04MMC_connect::publishData() {
+ NimBLEUUID serviceUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6");
+ NimBLEUUID charUUID("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6");
+ NimBLERemoteCharacteristic* pChar = getCharacteristic(serviceUUID, charUUID);
+
+ if (pChar && pChar->canNotify()) {
+ Log.trace(F("Registering notification" CR));
+ if (pChar->subscribe(true, std::bind(&XMWSDJ04MMC_connect::notifyCB, this,
+ std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3, std::placeholders::_4))) {
+ m_taskHandle = xTaskGetCurrentTaskHandle();
+ if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(BLE_CNCT_TIMEOUT)) == pdFALSE) {
+ m_taskHandle = nullptr;
+ }
+ } else {
+ Log.notice(F("Failed registering notification" CR));
+ }
+ }
+}
+
+
+void SBS1_connect::notifyCB(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
+ if (m_taskHandle == nullptr) {
+ return;
+ }
+ if (!ProcessLock) {
+ Log.trace(F("Callback from %s characteristic" CR), pChar->getUUID().toString().c_str());
+
+ if (length) {
+ m_notifyVal = *pData;
+ } else {
+ Log.notice(F("Invalid notification data" CR));
+ return;
+ }
+ } else {
+ Log.trace(F("Callback process canceled by processLock" CR));
+ }
+
+ xTaskNotifyGive(m_taskHandle);
+}
+
+bool SBS1_connect::processActions(std::vector& actions) {
+ NimBLEUUID serviceUUID("cba20d00-224d-11e6-9fb8-0002a5d5c51b");
+ NimBLEUUID charUUID("cba20002-224d-11e6-9fb8-0002a5d5c51b");
+ NimBLEUUID notifyCharUUID("cba20003-224d-11e6-9fb8-0002a5d5c51b");
+ static byte ON[] = {0x57, 0x01, 0x01};
+ static byte OFF[] = {0x57, 0x01, 0x02};
+
+ bool result = false;
+ if (actions.size() > 0) {
+ for (auto& it : actions) {
+ if (NimBLEAddress(it.addr) == m_pClient->getPeerAddress()) {
+ NimBLERemoteCharacteristic* pChar = getCharacteristic(serviceUUID, charUUID);
+ NimBLERemoteCharacteristic* pNotifyChar = getCharacteristic(serviceUUID, notifyCharUUID);
+
+ if (it.write && pChar && pNotifyChar) {
+ Log.trace(F("processing Switchbot %s" CR), it.value.c_str());
+ if (pNotifyChar->subscribe(true,
+ std::bind(&SBS1_connect::notifyCB,
+ this, std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3, std::placeholders::_4),
+ true)) {
+ if (it.value == "on") {
+ result = pChar->writeValue(ON, 3, false);
+ } else {
+ result = pChar->writeValue(OFF, 3, false);
+ }
+
+ if (result) {
+ m_taskHandle = xTaskGetCurrentTaskHandle();
+ if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(BLE_CNCT_TIMEOUT)) == pdFALSE) {
+ m_taskHandle = nullptr;
+ }
+ result = m_notifyVal == 0x01;
+ }
+ }
+ }
+
+ it.complete = result;
+ if (result || it.ttl <= 1) {
+ JsonObject BLEresult = getBTJsonObject();
+ BLEresult["id"] = it.addr;
+ BLEresult["state"] = result ? it.value : it.value == "on" ? "off" : "on";
+ pubBT(BLEresult);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+# endif
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayBT.ino"
+# 31 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayBT.ino"
+#include "User_config.h"
+
+#ifdef ZgatewayBT
+
+# include "FreeRTOS.h"
+SemaphoreHandle_t semaphoreCreateOrUpdateDevice;
+SemaphoreHandle_t semaphoreBLEOperation;
+QueueHandle_t BLEQueue;
+
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+
+# include
+
+# include "ZgatewayBLEConnect.h"
+# include "soc/timer_group_reg.h"
+# include "soc/timer_group_struct.h"
+
+using namespace std;
+
+
+BTConfig_s BTConfig;
+
+#define device_flags_init 0 << 0
+#define device_flags_isDisc 1 << 0
+#define device_flags_isWhiteL 1 << 1
+#define device_flags_isBlackL 1 << 2
+#define device_flags_connect 1 << 3
+
+TheengsDecoder decoder;
+
+struct decompose {
+ int start;
+ int len;
+ bool reverse;
+};
+
+vector BLEactions;
+
+vector devices;
+int newDevices = 0;
+
+static BLEdevice NO_DEVICE_FOUND = {{0},
+ 0,
+ false,
+ false,
+ false,
+ false,
+ TheengsDecoder::BLE_ID_NUM::UNKNOWN_MODEL};
+static bool oneWhite = false;
+
+void BTConfig_init() {
+ BTConfig.bleConnect = AttemptBLEConnect;
+ BTConfig.BLEinterval = TimeBtwRead;
+ BTConfig.BLEscanBeforeConnect = ScanBeforeConnect;
+ BTConfig.pubOnlySensors = PublishOnlySensors;
+ BTConfig.presenceEnable = HassPresence;
+ BTConfig.presenceTopic = subjectHomePresence;
+ BTConfig.presenceUseBeaconUuid = useBeaconUuidForPresence;
+ BTConfig.minRssi = MinimumRSSI;
+ BTConfig.extDecoderEnable = UseExtDecoder;
+ BTConfig.extDecoderTopic = MQTTDecodeTopic;
+ BTConfig.filterConnectable = BLE_FILTER_CONNECTABLE;
+ BTConfig.pubKnownServiceData = pubKnownBLEServiceData;
+ BTConfig.pubUnknownServiceData = pubUnknownBLEServiceData;
+ BTConfig.pubKnownManufData = pubBLEManufacturerData;
+ BTConfig.pubUnknownManufData = pubUnknownBLEManufacturerData;
+ BTConfig.pubServiceDataUUID = pubBLEServiceUUID;
+ BTConfig.pubBeaconUuidForTopic = useBeaconUuidForTopic;
+ BTConfig.ignoreWBlist = false;
+}
+
+template
+void BTConfig_update(JsonObject& data, const char* key, T& var);
+template
+void BTConfig_update(JsonObject& data, const char* key, T& var) {
+ if (data.containsKey(key)) {
+ if (var != data[key].as()) {
+ var = data[key].as();
+ Log.notice(F("BT config %s changed: %s" CR), key, data[key].as());
+ } else {
+ Log.notice(F("BT config %s unchanged: %s" CR), key, data[key].as());
+ }
+ }
+}
+
+void BTConfig_fromJson(JsonObject& BTdata, bool startup = false) {
+
+ BTConfig_update(BTdata, "bleconnect", BTConfig.bleConnect);
+
+ if (BTdata.containsKey("interval") && BTdata["interval"] != 0)
+ BTConfig_update(BTdata, "interval", BTConfig.BLEinterval);
+
+ BTConfig_update(BTdata, "scanbcnct", BTConfig.BLEscanBeforeConnect);
+
+ BTConfig_update(BTdata, "onlysensors", BTConfig.pubOnlySensors);
+
+ BTConfig_update(BTdata, "hasspresence", BTConfig.presenceEnable);
+
+ BTConfig_update(BTdata, "presenceTopic", BTConfig.presenceTopic);
+
+ BTConfig_update(BTdata, "presenceUseBeaconUuid", BTConfig.presenceUseBeaconUuid);
+
+ BTConfig_update(BTdata, "minrssi", BTConfig.minRssi);
+
+ BTConfig_update(BTdata, "extDecoderEnable", BTConfig.extDecoderEnable);
+
+ BTConfig_update(BTdata, "extDecoderTopic", BTConfig.extDecoderTopic);
+
+ BTConfig_update(BTdata, "filterConnectable", BTConfig.filterConnectable);
+
+ BTConfig_update(BTdata, "pubKnownServiceData", BTConfig.pubKnownServiceData);
+
+ BTConfig_update(BTdata, "pubUnknownServiceData", BTConfig.pubUnknownServiceData);
+
+ BTConfig_update(BTdata, "pubKnownManufData", BTConfig.pubKnownManufData);
+
+ BTConfig_update(BTdata, "pubUnknownManufData", BTConfig.pubUnknownManufData);
+
+ BTConfig_update(BTdata, "pubServiceDataUUID", BTConfig.pubServiceDataUUID);
+
+ BTConfig_update(BTdata, "pubBeaconUuidForTopic", BTConfig.pubBeaconUuidForTopic);
+
+ BTConfig_update(BTdata, "ignoreWBlist", (BTConfig.ignoreWBlist));
+
+ StaticJsonDocument jsonBuffer;
+ JsonObject jo = jsonBuffer.to();
+ jo["bleconnect"] = BTConfig.bleConnect;
+ jo["interval"] = BTConfig.BLEinterval;
+ jo["scanbcnct"] = BTConfig.BLEscanBeforeConnect;
+ jo["onlysensors"] = BTConfig.pubOnlySensors;
+ jo["hasspresence"] = BTConfig.presenceEnable;
+ jo["presenceTopic"] = BTConfig.presenceTopic;
+ jo["presenceUseBeaconUuid"] = BTConfig.presenceUseBeaconUuid;
+ jo["minrssi"] = -abs(BTConfig.minRssi);
+ jo["extDecoderEnable"] = BTConfig.extDecoderEnable;
+ jo["extDecoderTopic"] = BTConfig.extDecoderTopic;
+ jo["filterConnectable"] = BTConfig.filterConnectable;
+ jo["pubKnownServiceData"] = BTConfig.pubKnownServiceData;
+ jo["pubUnknownServiceData"] = BTConfig.pubUnknownServiceData;
+ jo["pubKnownManufData"] = BTConfig.pubKnownManufData;
+ jo["pubUnknownManufData"] = BTConfig.pubUnknownManufData;
+ jo["pubServiceDataUUID"] = BTConfig.pubServiceDataUUID;
+ jo["pubBeaconUuidForTopic"] = BTConfig.pubBeaconUuidForTopic;
+ jo["ignoreWBlist"] = BTConfig.ignoreWBlist;
+
+ if (startup) {
+ Log.notice(F("BT config: "));
+ serializeJsonPretty(jsonBuffer, Serial);
+ Serial.println();
+ return;
+ }
+ pub("/commands/BTtoMQTT/config", jo);
+
+ if (BTdata.containsKey("erase") && BTdata["erase"].as()) {
+
+ preferences.begin(Gateway_Short_Name, false);
+ preferences.remove("BTConfig");
+ preferences.end();
+ Log.notice(F("BT config erased" CR));
+ return;
+ }
+
+ if (BTdata.containsKey("save") && BTdata["save"].as()) {
+
+ String conf = "";
+ serializeJson(jsonBuffer, conf);
+ preferences.begin(Gateway_Short_Name, false);
+ preferences.putString("BTConfig", conf);
+ preferences.end();
+ Log.notice(F("BT config saved" CR));
+ }
+}
+
+void BTConfig_load() {
+ StaticJsonDocument jsonBuffer;
+ preferences.begin(Gateway_Short_Name, true);
+ auto error = deserializeJson(jsonBuffer, preferences.getString("BTConfig", "{}"));
+ preferences.end();
+ Log.notice(F("BT config loaded" CR));
+ if (error) {
+ Log.error(F("BT config deserialization failed: %s, buffer capacity: %u" CR), error.c_str(), jsonBuffer.capacity());
+ return;
+ }
+ if (jsonBuffer.isNull()) {
+ Log.warning(F("BT config is null" CR));
+ return;
+ }
+ JsonObject jo = jsonBuffer.as();
+ BTConfig_fromJson(jo, true);
+ Log.notice(F("BT config loaded" CR));
+}
+
+void pubBTMainCore(JsonObject& data, bool haPresenceEnabled = true) {
+ if (abs((int)data["rssi"] | 0) < abs(BTConfig.minRssi) && data.containsKey("id")) {
+ String topic = data["id"].as();
+ topic.replace(":", "");
+ if (BTConfig.pubBeaconUuidForTopic && !BTConfig.extDecoderEnable && data.containsKey("model_id") && data["model_id"].as() == "IBEACON")
+ topic = data["uuid"].as();
+ if (BTConfig.extDecoderEnable && !data.containsKey("model"))
+ topic = BTConfig.extDecoderTopic;
+ topic = subjectBTtoMQTT + String("/") + topic;
+ pub((char*)topic.c_str(), data);
+ }
+ if (haPresenceEnabled && data.containsKey("distance")) {
+ if (data.containsKey("servicedatauuid"))
+ data.remove("servicedatauuid");
+ if (data.containsKey("servicedata"))
+ data.remove("servicedata");
+ if (BTConfig.presenceUseBeaconUuid && data.containsKey("model_id") && data["model_id"].as() == "IBEACON") {
+ data["mac"] = data["id"];
+ data["id"] = data["uuid"];
+ }
+ String topic = String(mqtt_topic) + BTConfig.presenceTopic + String(gateway_name);
+ Log.trace(F("Pub HA Presence %s" CR), topic.c_str());
+ pub_custom_topic((char*)topic.c_str(), data, false);
+ }
+}
+
+class JsonBundle {
+public:
+ StaticJsonDocument buffer;
+ JsonObject object;
+ bool haPresence;
+
+ JsonObject& createObject(const char* json = NULL, bool haPresenceEnabled = true) {
+ buffer.clear();
+ haPresence = haPresenceEnabled;
+ object = buffer.to();
+
+ if (json != nullptr) {
+ auto error = deserializeJson(buffer, json);
+ if (error) {
+ Log.error(F("deserialize object failed: %s" CR), error.c_str());
+ }
+ }
+ return object;
+ }
+};
+
+void PublishDeviceData(JsonObject& BLEdata, bool processBLEData = true);
+
+static TaskHandle_t xCoreTaskHandle;
+static TaskHandle_t xProcBLETaskHandle;
+
+atomic_int forceBTScan;
+
+JsonBundle jsonBTBufferQueue[BTQueueSize];
+atomic_int jsonBTBufferQueueNext, jsonBTBufferQueueLast;
+int btQueueBlocked = 0;
+int btQueueLengthSum = 0;
+int btQueueLengthCount = 0;
+
+JsonObject& getBTJsonObject(const char* json, bool haPresenceEnabled) {
+ int next, last;
+ for (bool blocked = false;;) {
+ next = atomic_load_explicit(&jsonBTBufferQueueNext, ::memory_order_seq_cst);
+ last = atomic_load_explicit(&jsonBTBufferQueueLast, ::memory_order_seq_cst);
+ if ((2 * BTQueueSize + last - next) % (2 * BTQueueSize) != BTQueueSize) break;
+ if (!blocked) {
+ blocked = true;
+ btQueueBlocked++;
+ }
+ delay(1);
+ }
+ return jsonBTBufferQueue[last % BTQueueSize].createObject(json, haPresenceEnabled);
+}
+
+
+void pubBT(JsonObject& data) {
+ int last = atomic_load_explicit(&jsonBTBufferQueueLast, ::memory_order_seq_cst);
+ atomic_store_explicit(&jsonBTBufferQueueLast, (last + 1) % (2 * BTQueueSize), ::memory_order_seq_cst);
+}
+
+
+void emptyBTQueue() {
+ for (bool first = true;;) {
+ int next = atomic_load_explicit(&jsonBTBufferQueueNext, ::memory_order_seq_cst);
+ int last = atomic_load_explicit(&jsonBTBufferQueueLast, ::memory_order_seq_cst);
+ if (last == next) break;
+ if (first) {
+ int diff = (2 * BTQueueSize + last - next) % (2 * BTQueueSize);
+ btQueueLengthSum += diff;
+ btQueueLengthCount++;
+ first = false;
+ }
+ JsonBundle& bundle = jsonBTBufferQueue[next % BTQueueSize];
+ pubBTMainCore(bundle.object, bundle.haPresence);
+ atomic_store_explicit(&jsonBTBufferQueueNext, (next + 1) % (2 * BTQueueSize), ::memory_order_seq_cst);
+ vTaskDelay(1);
+ }
+}
+
+bool ProcessLock = false;
+
+void createOrUpdateDevice(const char* mac, uint8_t flags, int model, int mac_type = 0);
+
+BLEdevice* getDeviceByMac(const char* mac);
+BLEdevice* getDeviceByMac(const char* mac) {
+ Log.trace(F("getDeviceByMac %s" CR), mac);
+
+ for (vector::iterator it = devices.begin(); it != devices.end(); ++it) {
+ if ((strcmp((*it)->macAdr, mac) == 0)) {
+ return *it;
+ }
+ }
+ return &NO_DEVICE_FOUND;
+}
+
+bool updateWorB(JsonObject& BTdata, bool isWhite) {
+ Log.trace(F("update WorB" CR));
+ const char* jsonKey = isWhite ? "white-list" : "black-list";
+
+ int size = BTdata[jsonKey].size();
+ if (size == 0)
+ return false;
+
+ for (int i = 0; i < size; i++) {
+ const char* mac = BTdata[jsonKey][i];
+ createOrUpdateDevice(mac, (isWhite ? device_flags_isWhiteL : device_flags_isBlackL),
+ TheengsDecoder::BLE_ID_NUM::UNKNOWN_MODEL);
+ }
+
+ return true;
+}
+
+void createOrUpdateDevice(const char* mac, uint8_t flags, int model, int mac_type) {
+ if (xSemaphoreTake(semaphoreCreateOrUpdateDevice, pdMS_TO_TICKS(30000)) == pdFALSE) {
+ Log.error(F("Semaphore NOT taken" CR));
+ return;
+ }
+
+ BLEdevice* device = getDeviceByMac(mac);
+ if (device == &NO_DEVICE_FOUND) {
+ Log.trace(F("add %s" CR), mac);
+
+ device = new BLEdevice();
+ strcpy(device->macAdr, mac);
+ device->isDisc = flags & device_flags_isDisc;
+ device->isWhtL = flags & device_flags_isWhiteL;
+ device->isBlkL = flags & device_flags_isBlackL;
+ device->connect = flags & device_flags_connect;
+ device->macType = mac_type;
+ device->sensorModel_id = model;
+ devices.push_back(device);
+ newDevices++;
+ } else {
+ Log.trace(F("update %s" CR), mac);
+
+ device->macType = mac_type;
+
+ if (flags & device_flags_isDisc) {
+ device->isDisc = true;
+ }
+
+ if (flags & device_flags_connect) {
+ device->connect = true;
+ }
+
+ if (model != TheengsDecoder::BLE_ID_NUM::UNKNOWN_MODEL) {
+ device->sensorModel_id = model;
+ }
+
+ if (flags & device_flags_isWhiteL || flags & device_flags_isBlackL) {
+ device->isWhtL = flags & device_flags_isWhiteL;
+ device->isBlkL = flags & device_flags_isBlackL;
+ }
+ }
+
+
+ oneWhite = oneWhite || device->isWhtL;
+
+ xSemaphoreGive(semaphoreCreateOrUpdateDevice);
+}
+
+#define isWhite(device) device->isWhtL
+#define isBlack(device) device->isBlkL
+#define isDiscovered(device) device->isDisc
+
+void dumpDevices() {
+ for (vector::iterator it = devices.begin(); it != devices.end(); ++it) {
+ BLEdevice* p = *it;
+ Log.trace(F("macAdr %s" CR), p->macAdr);
+ Log.trace(F("macType %d" CR), p->macType);
+ Log.trace(F("isDisc %d" CR), p->isDisc);
+ Log.trace(F("isWhtL %d" CR), p->isWhtL);
+ Log.trace(F("isBlkL %d" CR), p->isBlkL);
+ Log.trace(F("connect %d" CR), p->connect);
+ Log.trace(F("sensorModel_id %d" CR), p->sensorModel_id);
+ }
+}
+
+void strupp(char* beg) {
+ while ((*beg = toupper(*beg)))
+ ++beg;
+}
+
+# ifdef ZmqttDiscovery
+void DT24Discovery(const char* mac, const char* sensorModel_id) {
+#define DT24parametersCount 6
+ Log.trace(F("DT24Discovery" CR));
+ const char* DT24sensor[DT24parametersCount][9] = {
+ {"sensor", "volt", mac, "power", jsonVolt, "", "", "V", stateClassMeasurement},
+ {"sensor", "amp", mac, "power", jsonCurrent, "", "", "A", stateClassMeasurement},
+ {"sensor", "watt", mac, "power", jsonPower, "", "", "W", stateClassMeasurement},
+ {"sensor", "watt-hour", mac, "power", jsonEnergy, "", "", "kWh", stateClassMeasurement},
+ {"sensor", "price", mac, "", jsonMsg, "", "", "", stateClassNone},
+ {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement}
+
+ };
+
+ createDiscoveryFromList(mac, DT24sensor, DT24parametersCount, "DT24", "ATorch", sensorModel_id);
+}
+
+void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel) {
+#define LYWSD03MMCparametersCount 4
+ Log.trace(F("LYWSD03MMCDiscovery" CR));
+ const char* LYWSD03MMCsensor[LYWSD03MMCparametersCount][9] = {
+ {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement},
+ {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement},
+ {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement},
+ {"sensor", "hum", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement}
+
+ };
+
+ createDiscoveryFromList(mac, LYWSD03MMCsensor, LYWSD03MMCparametersCount, "LYWSD03MMC", "Xiaomi", sensorModel);
+}
+
+void MHO_C401Discovery(const char* mac, const char* sensorModel) {
+#define MHO_C401parametersCount 4
+ Log.trace(F("MHO_C401Discovery" CR));
+ const char* MHO_C401sensor[MHO_C401parametersCount][9] = {
+ {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement},
+ {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement},
+ {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement},
+ {"sensor", "hum", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement}
+
+ };
+
+ createDiscoveryFromList(mac, MHO_C401sensor, MHO_C401parametersCount, "MHO_C401", "Xiaomi", sensorModel);
+}
+
+void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel) {
+#define HHCCJCY01HHCCparametersCount 5
+ Log.trace(F("HHCCJCY01HHCCDiscovery" CR));
+ const char* HHCCJCY01HHCCsensor[HHCCJCY01HHCCparametersCount][9] = {
+ {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement},
+ {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement},
+ {"sensor", "lux", mac, "illuminance", jsonLux, "", "", "lx", stateClassMeasurement},
+ {"sensor", "fer", mac, "", jsonFer, "", "", "µS/cm", stateClassMeasurement},
+ {"sensor", "moi", mac, "", jsonMoi, "", "", "%", stateClassMeasurement}
+
+ };
+
+ createDiscoveryFromList(mac, HHCCJCY01HHCCsensor, HHCCJCY01HHCCparametersCount, "HHCCJCY01HHCC", "Xiaomi", sensorModel);
+}
+
+void XMWSDJ04MMCDiscovery(const char* mac, const char* sensorModel) {
+#define XMWSDJ04MMCparametersCount 4
+ Log.trace(F("XMWSDJ04MMCDiscovery" CR));
+ const char* XMWSDJ04MMCsensor[XMWSDJ04MMCparametersCount][9] = {
+ {"sensor", "batt", mac, "battery", jsonBatt, "", "", "%", stateClassMeasurement},
+ {"sensor", "volt", mac, "", jsonVolt, "", "", "V", stateClassMeasurement},
+ {"sensor", "temp", mac, "temperature", jsonTempc, "", "", "°C", stateClassMeasurement},
+ {"sensor", "hum", mac, "humidity", jsonHum, "", "", "%", stateClassMeasurement}
+
+ };
+
+ createDiscoveryFromList(mac, XMWSDJ04MMCsensor, XMWSDJ04MMCparametersCount, "XMWSDJ04MMC", "Xiaomi", sensorModel);
+}
+
+# else
+void LYWSD03MMCDiscovery(const char* mac, const char* sensorModel) {}
+void MHO_C401Discovery(const char* mac, const char* sensorModel) {}
+void HHCCJCY01HHCCDiscovery(const char* mac, const char* sensorModel) {}
+void DT24Discovery(const char* mac, const char* sensorModel_id) {}
+void XMWSDJ04MMCDiscovery(const char* mac, const char* sensorModel_id) {}
+# endif
+# 525 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayBT.ino"
+static int taskCore = 0;
+
+class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
+ void onResult(BLEAdvertisedDevice* advertisedDevice) {
+ if (xQueueSend(BLEQueue, &advertisedDevice, 0) != pdTRUE) {
+ Log.error(F("BLEQueue full" CR));
+ }
+ }
+};
+
+std::string convertServiceData(std::string deviceServiceData) {
+ int serviceDataLength = (int)deviceServiceData.length();
+ char spr[2 * serviceDataLength + 1];
+ for (int i = 0; i < serviceDataLength; i++) sprintf(spr + 2 * i, "%.2x", (unsigned char)deviceServiceData[i]);
+ spr[2 * serviceDataLength] = 0;
+ Log.trace("Converted service data (%d) to %s" CR, serviceDataLength, spr);
+ return spr;
+}
+
+void procBLETask(void* pvParameters) {
+ BLEAdvertisedDevice* advertisedDevice = nullptr;
+
+ for (;;) {
+ xQueueReceive(BLEQueue, &advertisedDevice, portMAX_DELAY);
+
+ if (!ProcessLock) {
+ Log.trace(F("Creating BLE buffer" CR));
+ JsonObject& BLEdata = getBTJsonObject();
+ String mac_adress = advertisedDevice->getAddress().toString().c_str();
+ mac_adress.toUpperCase();
+ BLEdata["id"] = (char*)mac_adress.c_str();
+ BLEdata["mac_type"] = advertisedDevice->getAddress().getType();
+ Log.notice(F("Device detected: %s" CR), (char*)mac_adress.c_str());
+ BLEdevice* device = getDeviceByMac(BLEdata["id"].as());
+
+ if (BTConfig.filterConnectable && device->connect) {
+ Log.notice(F("Filtered connectable device" CR));
+ continue;
+ }
+
+ if (BTConfig.ignoreWBlist || ((!oneWhite || isWhite(device)) && !isBlack(device))) {
+ if (advertisedDevice->haveName())
+ BLEdata["name"] = (char*)advertisedDevice->getName().c_str();
+ if (advertisedDevice->haveManufacturerData()) {
+ char* manufacturerdata = BLEUtils::buildHexData(NULL, (uint8_t*)advertisedDevice->getManufacturerData().data(), advertisedDevice->getManufacturerData().length());
+ BLEdata["manufacturerdata"] = manufacturerdata;
+ free(manufacturerdata);
+ }
+ if (advertisedDevice->haveRSSI())
+ BLEdata["rssi"] = (int)advertisedDevice->getRSSI();
+ if (advertisedDevice->haveTXPower())
+ BLEdata["txpower"] = (int8_t)advertisedDevice->getTXPower();
+ if (advertisedDevice->haveRSSI() && !BTConfig.pubOnlySensors && BTConfig.presenceEnable) {
+ hass_presence(BLEdata);
+ }
+ if (advertisedDevice->haveServiceData()) {
+ int serviceDataCount = advertisedDevice->getServiceDataCount();
+ Log.trace(F("Get services data number: %d" CR), serviceDataCount);
+ for (int j = 0; j < serviceDataCount; j++) {
+ std::string service_data = convertServiceData(advertisedDevice->getServiceData(j));
+ Log.trace(F("Service data: %s" CR), service_data.c_str());
+ BLEdata["servicedata"] = (char*)service_data.c_str();
+ std::string serviceDatauuid = advertisedDevice->getServiceDataUUID(j).toString();
+ Log.trace(F("Service data UUID: %s" CR), (char*)serviceDatauuid.c_str());
+ BLEdata["servicedatauuid"] = (char*)serviceDatauuid.c_str();
+ process_bledata(BLEdata);
+ }
+
+ PublishDeviceData(BLEdata, false);
+ } else {
+ PublishDeviceData(BLEdata);
+ }
+ } else {
+ Log.trace(F("Filtered MAC device" CR));
+ }
+ }
+ }
+}
+
+
+
+
+void BLEscan() {
+
+ while (uxQueueMessagesWaiting(BLEQueue)) {
+ yield();
+ }
+ disableCore0WDT();
+ Log.notice(F("Scan begin" CR));
+ BLEScan* pBLEScan = BLEDevice::getScan();
+ MyAdvertisedDeviceCallbacks myCallbacks;
+ pBLEScan->setAdvertisedDeviceCallbacks(&myCallbacks);
+ pBLEScan->setActiveScan(ActiveBLEScan);
+ pBLEScan->setInterval(BLEScanInterval);
+ pBLEScan->setWindow(BLEScanWindow);
+ BLEScanResults foundDevices = pBLEScan->start(Scan_duration / 1000, false);
+ scanCount++;
+ Log.notice(F("Found %d devices, scan number %d end" CR), foundDevices.getCount(), scanCount);
+ enableCore0WDT();
+ Log.trace(F("Process BLE stack free: %u" CR), uxTaskGetStackHighWaterMark(xProcBLETaskHandle));
+}
+
+
+
+
+void BLEconnect() {
+ if (!ProcessLock) {
+ Log.notice(F("BLE Connect begin" CR));
+ do {
+ for (vector::iterator it = devices.begin(); it != devices.end(); ++it) {
+ BLEdevice* p = *it;
+ if (p->connect) {
+ Log.trace(F("Model to connect found: %s" CR), p->macAdr);
+ NimBLEAddress addr((const char*)p->macAdr, p->macType);
+ if (p->sensorModel_id == BLEconectable::id::LYWSD03MMC ||
+ p->sensorModel_id == BLEconectable::id::MHO_C401) {
+ LYWSD03MMC_connect BLEclient(addr);
+ BLEclient.processActions(BLEactions);
+ BLEclient.publishData();
+ } else if (p->sensorModel_id == BLEconectable::id::DT24_BLE) {
+ DT24_connect BLEclient(addr);
+ BLEclient.processActions(BLEactions);
+ BLEclient.publishData();
+ } else if (p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC) {
+ HHCCJCY01HHCC_connect BLEclient(addr);
+ BLEclient.processActions(BLEactions);
+ BLEclient.publishData();
+ } else if (p->sensorModel_id == BLEconectable::id::XMWSDJ04MMC) {
+ XMWSDJ04MMC_connect BLEclient(addr);
+ BLEclient.processActions(BLEactions);
+ BLEclient.publishData();
+ } else if (p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::SBS1) {
+ SBS1_connect BLEclient(addr);
+ BLEclient.processActions(BLEactions);
+ } else {
+ GENERIC_connect BLEclient(addr);
+ if (BLEclient.processActions(BLEactions)) {
+
+
+ p->connect = false;
+ }
+ }
+ if (BLEactions.size() > 0) {
+ std::vector swap;
+ for (auto& it : BLEactions) {
+ if (!it.complete && --it.ttl) {
+ swap.push_back(it);
+ } else if (memcmp(it.addr, p->macAdr, sizeof(it.addr)) == 0) {
+ if (p->sensorModel_id != BLEconectable::id::DT24_BLE &&
+ p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC &&
+ p->sensorModel_id != BLEconectable::id::LYWSD03MMC &&
+ p->sensorModel_id != BLEconectable::id::MHO_C401 &&
+ p->sensorModel_id != BLEconectable::id::XMWSDJ04MMC) {
+
+ p->connect = false;
+ }
+ }
+ }
+ std::swap(BLEactions, swap);
+ }
+ }
+ }
+ } while (BLEactions.size() > 0);
+ Log.notice(F("BLE Connect end" CR));
+ }
+}
+
+void stopProcessing() {
+ Log.notice(F("Stop BLE processing" CR));
+ ProcessLock = true;
+ delay(Scan_duration < 2000 ? Scan_duration : 2000);
+}
+
+void startProcessing() {
+ Log.notice(F("Start BLE processing" CR));
+ ProcessLock = false;
+ vTaskResume(xCoreTaskHandle);
+}
+
+void coreTask(void* pvParameters) {
+ while (true) {
+ if (!ProcessLock) {
+ int n = 0;
+ while (client.state() != 0 && n <= InitialMQTTConnectionTimeout && !ProcessLock) {
+ n++;
+ delay(1000);
+ }
+ if (client.state() != 0) {
+ Log.warning(F("MQTT client disconnected no BLE scan" CR));
+ } else if (!ProcessLock) {
+ if (xSemaphoreTake(semaphoreBLEOperation, pdMS_TO_TICKS(30000)) == pdTRUE) {
+ BLEscan();
+
+ if ((!(scanCount % BTConfig.BLEscanBeforeConnect) || scanCount == 1) && BTConfig.bleConnect)
+ BLEconnect();
+ dumpDevices();
+ xSemaphoreGive(semaphoreBLEOperation);
+ } else {
+ Log.error(F("Failed to start scan - BLE busy" CR));
+ }
+ }
+ if (lowpowermode) {
+ lowPowerESP32();
+ int scan = atomic_exchange_explicit(&forceBTScan, 0, ::memory_order_seq_cst);
+ if (scan == 1) BTforceScan();
+ } else {
+ for (int interval = BTConfig.BLEinterval, waitms; interval > 0; interval -= waitms) {
+ int scan = atomic_exchange_explicit(&forceBTScan, 0, ::memory_order_seq_cst);
+ if (scan == 1) BTforceScan();
+ delay(waitms = interval > 100 ? 100 : interval);
+ }
+ }
+ } else {
+ Log.trace(F("BLE core task canceled by processLock" CR));
+ vTaskSuspend(xCoreTaskHandle);
+ }
+ }
+}
+
+void lowPowerESP32() {
+ Log.trace(F("Going to deep sleep for: %l s" CR), (BTConfig.BLEinterval / 1000));
+ deepSleep(BTConfig.BLEinterval * 1000);
+}
+
+void deepSleep(uint64_t time_in_us) {
+# if defined(ZboardM5STACK) || defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5TOUGH)
+ sleepScreen();
+ esp_sleep_enable_ext0_wakeup((gpio_num_t)SLEEP_BUTTON, LOW);
+# endif
+
+ Log.trace(F("Deactivating ESP32 components" CR));
+ BLEDevice::deinit(true);
+ esp_bt_mem_release(ESP_BT_MODE_BTDM);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ adc_power_off();
+#pragma GCC diagnostic pop
+ esp_wifi_stop();
+ esp_deep_sleep(time_in_us);
+}
+
+void changelowpowermode(int newLowPowerMode) {
+ Log.notice(F("Changing LOW POWER mode to: %d" CR), newLowPowerMode);
+# if defined(ZboardM5STACK) || defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5TOUGH)
+ if (lowpowermode == 2) {
+# ifdef ZboardM5STACK
+ M5.Lcd.wakeup();
+# endif
+# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5TOUGH)
+ M5.Axp.SetLDO2(true);
+ M5.Lcd.begin();
+# endif
+ }
+ char lpm[2];
+ sprintf(lpm, "%d", newLowPowerMode);
+ M5Print("Changing LOW POWER mode to:", lpm, "");
+# endif
+ lowpowermode = newLowPowerMode;
+ preferences.begin(Gateway_Short_Name, false);
+ preferences.putUInt("lowpowermode", lowpowermode);
+ preferences.end();
+}
+
+void setupBT() {
+ BTConfig_init();
+ BTConfig_load();
+ Log.notice(F("BLE scans interval: %d" CR), BTConfig.BLEinterval);
+ Log.notice(F("BLE scans number before connect: %d" CR), BTConfig.BLEscanBeforeConnect);
+ Log.notice(F("Publishing only BLE sensors: %T" CR), BTConfig.pubOnlySensors);
+ Log.notice(F("minrssi: %d" CR), -abs(BTConfig.minRssi));
+ Log.notice(F("Low Power Mode: %d" CR), lowpowermode);
+
+ atomic_init(&forceBTScan, 0);
+ atomic_init(&jsonBTBufferQueueNext, 0);
+ atomic_init(&jsonBTBufferQueueLast, 0);
+
+ semaphoreCreateOrUpdateDevice = xSemaphoreCreateBinary();
+ xSemaphoreGive(semaphoreCreateOrUpdateDevice);
+
+ semaphoreBLEOperation = xSemaphoreCreateBinary();
+ xSemaphoreGive(semaphoreBLEOperation);
+
+ BLEQueue = xQueueCreate(32, sizeof(NimBLEAdvertisedDevice*));
+
+ BLEDevice::setScanDuplicateCacheSize(BLEScanDuplicateCacheSize);
+ BLEDevice::init("");
+
+ xTaskCreatePinnedToCore(
+ procBLETask,
+ "procBLETask",
+ 5120,
+ NULL,
+ 2,
+ &xProcBLETaskHandle,
+ 1);
+
+
+ xTaskCreatePinnedToCore(
+ coreTask,
+ "coreTask",
+ 10000,
+ NULL,
+ 1,
+ &xCoreTaskHandle,
+ taskCore);
+ Log.trace(F("ZgatewayBT multicore ESP32 setup done " CR));
+}
+
+bool BTtoMQTT() {
+ BLEscan();
+ return true;
+}
+
+void RemoveJsonPropertyIf(JsonObject& obj, const char* key, bool condition) {
+ if (condition) {
+ Log.trace(F("Removing %s" CR), key);
+ obj.remove(key);
+ }
+}
+
+boolean valid_service_data(const char* data, int size) {
+ for (int i = 0; i < size; ++i) {
+ if (data[i] != 48)
+ return true;
+ }
+ return false;
+}
+
+# ifdef ZmqttDiscovery
+
+void launchBTDiscovery() {
+ if (newDevices == 0)
+ return;
+ if (xSemaphoreTake(semaphoreCreateOrUpdateDevice, pdMS_TO_TICKS(1000)) == pdFALSE) {
+ Log.error(F("Semaphore NOT taken" CR));
+ return;
+ }
+ newDevices = 0;
+ vector localDevices = devices;
+ xSemaphoreGive(semaphoreCreateOrUpdateDevice);
+ for (vector::iterator it = localDevices.begin(); it != localDevices.end(); ++it) {
+ BLEdevice* p = *it;
+ Log.trace(F("Device mac %s" CR), p->macAdr);
+
+ if (!isDiscovered(p) &&
+ p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::IBEACON &&
+ p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::MS_CDP &&
+ p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::GAEN) {
+ String macWOdots = String(p->macAdr);
+ macWOdots.replace(":", "");
+ if (!BTConfig.extDecoderEnable &&
+ p->sensorModel_id > TheengsDecoder::BLE_ID_NUM::UNKNOWN_MODEL &&
+ p->sensorModel_id < TheengsDecoder::BLE_ID_NUM::BLE_ID_MAX &&
+ p->sensorModel_id != TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC) {
+ Log.trace(F("Looking for Model_id: %d" CR), p->sensorModel_id);
+ std::string properties = decoder.getTheengProperties(p->sensorModel_id);
+ Log.trace(F("properties: %s" CR), properties.c_str());
+ std::string brand = decoder.getTheengAttribute(p->sensorModel_id, "brand");
+ std::string model = decoder.getTheengAttribute(p->sensorModel_id, "model");
+ std::string model_id = decoder.getTheengAttribute(p->sensorModel_id, "model_id");
+ if (!properties.empty()) {
+ StaticJsonDocument jsonBuffer;
+ deserializeJson(jsonBuffer, properties);
+ for (JsonPair prop : jsonBuffer["properties"].as()) {
+ Log.trace("Key: %s", prop.key().c_str());
+ Log.trace("Unit: %s", prop.value()["unit"].as());
+ Log.trace("Name: %s", prop.value()["name"].as());
+ String discovery_topic = String(subjectBTtoMQTT) + "/" + macWOdots;
+ String entity_name = String(model_id.c_str()) + "-" + String(prop.key().c_str());
+ String unique_id = macWOdots + "-" + String(prop.key().c_str());
+# if OpenHABDiscovery
+ String value_template = "{{ value_json." + String(prop.key().c_str()) + "}}";
+# else
+ String value_template = "{{ value_json." + String(prop.key().c_str()) + " | is_defined }}";
+# endif
+ if (p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::SBS1 && !strcmp(prop.key().c_str(), "state")) {
+ String payload_on = "{\"SBS1\":\"on\",\"mac\":\"" + String(p->macAdr) + "\"}";
+ String payload_off = "{\"SBS1\":\"off\",\"mac\":\"" + String(p->macAdr) + "\"}";
+ createDiscovery("switch",
+ discovery_topic.c_str(), entity_name.c_str(), unique_id.c_str(),
+ will_Topic, "switch", value_template.c_str(),
+ payload_on.c_str(), payload_off.c_str(), "", 0,
+ Gateway_AnnouncementMsg, will_Message, false, subjectMQTTtoBTset,
+ model.c_str(), brand.c_str(), model_id.c_str(), macWOdots.c_str(), false,
+ stateClassNone, "off", "on");
+ } else {
+ createDiscovery("sensor",
+ discovery_topic.c_str(), entity_name.c_str(), unique_id.c_str(),
+ will_Topic, prop.value()["name"], value_template.c_str(),
+ "", "", prop.value()["unit"],
+ 0, "", "", false, "",
+ model.c_str(), brand.c_str(), model_id.c_str(), macWOdots.c_str(), false,
+ stateClassMeasurement);
+ }
+ }
+ }
+ } else {
+ if (p->sensorModel_id > BLEconectable::id::MIN &&
+ p->sensorModel_id < BLEconectable::id::MAX ||
+ p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC) {
+
+ if (p->sensorModel_id == BLEconectable::id::DT24_BLE) {
+ DT24Discovery(macWOdots.c_str(), "DT24-BLE");
+ }
+ if (p->sensorModel_id == BLEconectable::id::LYWSD03MMC) {
+ LYWSD03MMCDiscovery(macWOdots.c_str(), "LYWSD03MMC");
+ }
+ if (p->sensorModel_id == BLEconectable::id::MHO_C401) {
+ MHO_C401Discovery(macWOdots.c_str(), "MHO-C401");
+ }
+ if (p->sensorModel_id == BLEconectable::id::XMWSDJ04MMC) {
+ XMWSDJ04MMCDiscovery(macWOdots.c_str(), "XMWSDJ04MMC");
+ }
+ if (p->sensorModel_id == TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC) {
+ HHCCJCY01HHCCDiscovery(macWOdots.c_str(), "HHCCJCY01HHCC");
+ }
+ } else {
+ Log.trace(F("Device UNKNOWN_MODEL %s" CR), p->macAdr);
+ }
+ p->isDisc = true;
+ }
+ } else {
+ Log.trace(F("Device already discovered or that doesn't require discovery %s" CR), p->macAdr);
+ }
+ }
+}
+# endif
+
+void PublishDeviceData(JsonObject& BLEdata, bool processBLEData) {
+ if (abs((int)BLEdata["rssi"] | 0) < abs(BTConfig.minRssi)) {
+ if (processBLEData) process_bledata(BLEdata);
+ if (!BTConfig.pubOnlySensors || BLEdata.containsKey("model") || BLEdata.containsKey("distance")) {
+ RemoveJsonPropertyIf(BLEdata, "servicedatauuid", !BTConfig.pubServiceDataUUID && BLEdata.containsKey("model"));
+ RemoveJsonPropertyIf(BLEdata, "servicedata", !BTConfig.pubKnownServiceData && BLEdata.containsKey("model"));
+ RemoveJsonPropertyIf(BLEdata, "manufacturerdata", !BTConfig.pubKnownManufData && BLEdata.containsKey("model"));
+ pubBT(BLEdata);
+ } else {
+ RemoveJsonPropertyIf(BLEdata, "servicedata", !BTConfig.pubUnknownServiceData);
+ RemoveJsonPropertyIf(BLEdata, "manufacturerdata", !BTConfig.pubUnknownManufData && BLEdata.containsKey("model"));
+ }
+ } else if (BLEdata.containsKey("distance")) {
+ pubBT(BLEdata);
+ } else {
+ Log.trace(F("Low rssi, device filtered" CR));
+ }
+}
+
+void process_bledata(JsonObject& BLEdata) {
+ const char* mac = BLEdata["id"].as();
+ int model_id = BTConfig.extDecoderEnable ? -1 : decoder.decodeBLEJson(BLEdata);
+ int mac_type = BLEdata["mac_type"].as();
+ if (model_id >= 0) {
+ Log.trace(F("Decoder found device: %s" CR), BLEdata["model_id"].as());
+ if (model_id == TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC) {
+ createOrUpdateDevice(mac, device_flags_connect, model_id, mac_type);
+ } else {
+ createOrUpdateDevice(mac, device_flags_init, model_id, mac_type);
+ }
+ } else {
+ if (BLEdata.containsKey("name")) {
+ std::string name = BLEdata["name"];
+ if (name.compare("LYWSD03MMC") == 0)
+ model_id = BLEconectable::id::LYWSD03MMC;
+ else if (name.compare("DT24-BLE") == 0)
+ model_id = BLEconectable::id::DT24_BLE;
+ else if (name.compare("MHO-C401") == 0)
+ model_id = BLEconectable::id::MHO_C401;
+ else if (name.compare("XMWSDJ04MMC") == 0)
+ model_id = BLEconectable::id::XMWSDJ04MMC;
+
+ if (model_id > 0) {
+ Log.trace(F("Connectable device found: %s" CR), name.c_str());
+ createOrUpdateDevice(mac, device_flags_connect, model_id, mac_type);
+ }
+ } else if (BTConfig.extDecoderEnable && model_id < 0 && BLEdata.containsKey("servicedata")) {
+ const char* service_data = (const char*)(BLEdata["servicedata"] | "");
+ if (strstr(service_data, "209800") != NULL) {
+ model_id == TheengsDecoder::BLE_ID_NUM::HHCCJCY01HHCC;
+ Log.trace(F("Connectable device found: HHCCJCY01HHCC" CR));
+ createOrUpdateDevice(mac, device_flags_connect, model_id, mac_type);
+ }
+ }
+ }
+ if (!BTConfig.extDecoderEnable && model_id < 0) {
+ Log.trace(F("No device found " CR));
+ }
+}
+
+void hass_presence(JsonObject& HomePresence) {
+ int BLErssi = HomePresence["rssi"];
+ Log.trace(F("BLErssi %d" CR), BLErssi);
+ int txPower = HomePresence["txpower"] | 0;
+ if (txPower >= 0)
+ txPower = -59;
+ Log.trace(F("TxPower: %d" CR), txPower);
+ double ratio = BLErssi * 1.0 / txPower;
+ double distance;
+ if (ratio < 1.0) {
+ distance = pow(ratio, 10);
+ } else {
+ distance = (0.89976) * pow(ratio, 7.7095) + 0.111;
+ }
+ HomePresence["distance"] = distance;
+ Log.trace(F("Ble distance %D" CR), distance);
+}
+
+void BTforceScan() {
+ if (!ProcessLock) {
+ BTtoMQTT();
+ Log.trace(F("Scan done" CR));
+ if (BTConfig.bleConnect)
+ BLEconnect();
+ } else {
+ Log.trace(F("Cannot launch scan due to other process running" CR));
+ }
+}
+
+void immediateBTAction(void* pvParameters) {
+ if (BLEactions.size()) {
+
+ ProcessLock = true;
+ NimBLEScan* pScan = NimBLEDevice::getScan();
+ if (pScan->isScanning()) {
+ pScan->stop();
+ }
+
+ if (xSemaphoreTake(semaphoreBLEOperation, pdMS_TO_TICKS(5000)) == pdTRUE) {
+
+ std::vector dev_swap;
+ dev_swap.push_back(getDeviceByMac(BLEactions.back().addr));
+ std::swap(devices, dev_swap);
+
+ std::vector act_swap;
+ act_swap.push_back(BLEactions.back());
+ BLEactions.pop_back();
+ std::swap(BLEactions, act_swap);
+
+
+ ProcessLock = false;
+ BLEconnect();
+
+ std::swap(devices, dev_swap);
+ std::swap(BLEactions, act_swap);
+
+
+ if ((!(scanCount % BTConfig.BLEscanBeforeConnect) || scanCount == 1) && BTConfig.bleConnect)
+ BLEconnect();
+ xSemaphoreGive(semaphoreBLEOperation);
+ } else {
+ Log.error(F("BLE busy - command not sent" CR));
+ JsonObject result = getBTJsonObject();
+ result["id"] = BLEactions.back().addr;
+ result["success"] = false;
+ pubBT(result);
+ BLEactions.pop_back();
+ ProcessLock = false;
+ }
+ }
+ vTaskDelete(NULL);
+}
+
+void startBTActionTask() {
+ TaskHandle_t th;
+ xTaskCreatePinnedToCore(
+ immediateBTAction,
+ "imActTask",
+ 5120,
+ NULL,
+ 3,
+ &th,
+ 1);
+}
+
+void MQTTtoBTAction(JsonObject& BTdata) {
+ BLEAction action;
+ memset(&action, 0, sizeof(BLEAction));
+ if (BTdata.containsKey("SBS1")) {
+ strcpy(action.addr, (const char*)BTdata["mac"]);
+ action.write = true;
+ action.value = BTdata["SBS1"].as();
+ action.ttl = 1;
+ createOrUpdateDevice(action.addr, device_flags_connect,
+ TheengsDecoder::BLE_ID_NUM::SBS1, 1);
+ BLEactions.push_back(action);
+ startBTActionTask();
+ return;
+ }
+
+ action.ttl = BTdata.containsKey("ttl") ? (uint8_t)BTdata["ttl"] : 1;
+ action.addr_type = BTdata.containsKey("mac_type") ? BTdata["mac_type"].as() : 0;
+ action.value_type = BLE_VAL_STRING;
+ if (BTdata.containsKey("value_type")) {
+ String vt = BTdata["value_type"];
+ vt.toUpperCase();
+ if (vt == "HEX")
+ action.value_type = BLE_VAL_HEX;
+ else if (vt == "INT")
+ action.value_type = BLE_VAL_INT;
+ else if (vt == "FLOAT")
+ action.value_type = BLE_VAL_FLOAT;
+ else if (vt != "STRING") {
+ Log.error(F("BLE value type invalid %s" CR), vt.c_str());
+ return;
+ }
+ }
+
+ Log.trace(F("BLE ACTION TTL = %u" CR), action.ttl);
+ action.complete = false;
+ if (BTdata.containsKey("ble_write_address") &&
+ BTdata.containsKey("ble_write_service") &&
+ BTdata.containsKey("ble_write_char") &&
+ BTdata.containsKey("ble_write_value")) {
+ strcpy(action.addr, (const char*)BTdata["ble_write_address"]);
+ action.service = NimBLEUUID((const char*)BTdata["ble_write_service"]);
+ action.characteristic = NimBLEUUID((const char*)BTdata["ble_write_char"]);
+ action.value = std::string((const char*)BTdata["ble_write_value"]);
+ action.write = true;
+ Log.trace(F("BLE ACTION Write" CR));
+ } else if (BTdata.containsKey("ble_read_address") &&
+ BTdata.containsKey("ble_read_service") &&
+ BTdata.containsKey("ble_read_char")) {
+ strcpy(action.addr, (const char*)BTdata["ble_read_address"]);
+ action.service = NimBLEUUID((const char*)BTdata["ble_read_service"]);
+ action.characteristic = NimBLEUUID((const char*)BTdata["ble_read_char"]);
+ action.write = false;
+ Log.trace(F("BLE ACTION Read" CR));
+ } else {
+ return;
+ }
+
+ createOrUpdateDevice(action.addr, device_flags_connect,
+ TheengsDecoder::BLE_ID_NUM::UNKNOWN_MODEL,
+ action.addr_type);
+
+ BLEactions.push_back(action);
+ if (BTdata.containsKey("immediate") && BTdata["immediate"].as()) {
+ startBTActionTask();
+ }
+}
+
+void MQTTtoBT(char* topicOri, JsonObject& BTdata) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoBTset)) {
+ Log.trace(F("MQTTtoBT json set" CR));
+
+
+ bool WorBupdated;
+ WorBupdated = updateWorB(BTdata, true);
+ WorBupdated |= updateWorB(BTdata, false);
+
+ if (WorBupdated) {
+ if (xSemaphoreTake(semaphoreCreateOrUpdateDevice, pdMS_TO_TICKS(1000)) == pdTRUE) {
+ dumpDevices();
+ xSemaphoreGive(semaphoreCreateOrUpdateDevice);
+ }
+ }
+
+
+ if (BTdata.containsKey("interval") && BTdata["interval"] == 0) {
+ Log.notice(F("BLE forced scan" CR));
+ atomic_store_explicit(&forceBTScan, 1, ::memory_order_seq_cst);
+ }
+
+
+
+
+
+
+
+ if (BTdata.containsKey("init") && BTdata["init"].as()) {
+
+ BTConfig_init();
+ } else if (BTdata.containsKey("load") && BTdata["load"].as()) {
+
+ BTConfig_load();
+ }
+
+
+ BTConfig_fromJson(BTdata);
+
+ if (BTdata.containsKey("lowpowermode")) {
+ changelowpowermode((int)BTdata["lowpowermode"]);
+ }
+
+ MQTTtoBTAction(BTdata);
+ }
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayGFSunInverter.ino"
+# 26 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayGFSunInverter.ino"
+#include "User_config.h"
+
+#ifdef ZgatewayGFSunInverter
+
+GfSun2000 GF = GfSun2000();
+
+void GFSunInverterDataHandler(GfSun2000Data data) {
+ StaticJsonDocument<2 * JSON_MSG_BUFFER> jsonBuffer;
+ JsonObject jdata = jsonBuffer.to();
+
+ jdata["device_id"] = (char*)data.deviceID;
+ Log.trace(F("Device ID : %s\n" CR), data.deviceID);
+ jdata["ac_voltage"] = data.ACVoltage;
+ Log.trace(F("AC Voltage : %.1f\tV\n" CR), data.ACVoltage);
+ jdata["dc_voltage"] = data.DCVoltage;
+ Log.trace(F("DC Voltage : %.1f\tV\n" CR), data.DCVoltage);
+ jdata["power"] = data.averagePower;
+ Log.trace(F("Output Power : %.1f\tW (5min avg)\n" CR), data.averagePower);
+ jdata["c_energy"] = data.customEnergyCounter;
+ Log.trace(F("Custom Energy : %.1f\tkW/h (can be reseted)\n" CR), data.customEnergyCounter);
+ jdata["t_energy"] = data.totalEnergyCounter;
+ Log.trace(F("Total Energy : %.1f\tkW/h\n" CR), data.totalEnergyCounter);
+
+# ifdef GFSUNINVERTER_DEVEL
+ StaticJsonDocument jsonBuffer2;
+ JsonObject jregister = jsonBuffer2.to();
+ char buffer[4];
+ std::map::iterator itr;
+ for (itr = data.modbusRegistry.begin(); itr != data.modbusRegistry.end(); ++itr) {
+ Log.notice("%d: %d\n", itr->first, itr->second);
+ sprintf(buffer, "%d", itr->first);
+ jregister[buffer] = itr->second;
+ }
+ jdata["register"] = jregister;
+# endif
+ pub(subjectRFtoMQTT, jdata);
+}
+
+void GFSunInverterErrorHandler(int errorId, char* errorMessage) {
+ char buffer[50];
+ sprintf(buffer, "Error response: %02X - %s\n", errorId, errorMessage);
+ Log.error(buffer);
+ StaticJsonDocument jsonBuffer;
+ JsonObject jdata = jsonBuffer.to();
+ jdata["status"] = "error";
+ jdata["msg"] = errorMessage;
+ jdata["id"] = errorId;
+ pub(subjectRFtoMQTT, jdata);
+}
+
+void setupGFSunInverter() {
+ GF.setup(Serial2);
+ GF.setDataHandler(GFSunInverterDataHandler);
+ GF.setErrorHandler(GFSunInverterErrorHandler);
+ Log.trace(F("ZgatewayGFSunInverter setup done " CR));
+}
+
+void ZgatewayGFSunInverterMQTT() {
+ GF.readData();
+ delay(GFSUNINVERTER_DELAY);
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayIR.ino"
+# 28 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayIR.ino"
+#include "User_config.h"
+
+#ifdef ZgatewayIR
+
+# if defined(ESP8266) || defined(ESP32)
+# include
+# include
+# include
+# include
+# ifdef DumpMode
+IRrecv irrecv(IR_RECEIVER_GPIO, 1024, 15U, true);
+# else
+IRrecv irrecv(IR_RECEIVER_GPIO);
+# endif
+IRsend irsend(IR_EMITTER_GPIO, IR_EMITTER_INVERTED);
+# else
+# include
+IRrecv irrecv(IR_RECEIVER_GPIO);
+IRsend irsend;
+# endif
+
+
+# ifndef NEC_BITS
+#define NEC_BITS 32U
+# endif
+# ifndef SAMSUNG_BITS
+#define SAMSUNG_BITS 32U
+# endif
+# ifndef SHARP_BITS
+#define SHARP_ADDRESS_BITS 5U
+#define SHARP_COMMAND_BITS 8U
+#define SHARP_BITS (SHARP_ADDRESS_BITS + SHARP_COMMAND_BITS + 2)
+# endif
+# ifndef RC5_BITS
+#define RC5_RAW_BITS 14U
+#define RC5_BITS RC5_RAW_BITS - 2U
+# endif
+# ifndef DISH_BITS
+#define DISH_BITS 16U
+# endif
+# ifndef SONY_20_BITS
+#define SONY_20_BITS 20
+# endif
+# ifndef SONY_12_BITS
+#define SONY_12_BITS 12U
+# endif
+# ifndef LG_BITS
+#define LG_BITS 28U
+# endif
+# ifndef WHYNTER_BITS
+#define WHYNTER_BITS 32U
+# endif
+
+
+uint64_t getUInt64fromHex(char const* str) {
+ uint64_t result = 0;
+ uint16_t offset = 0;
+
+ if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) offset = 2;
+ for (; isxdigit((unsigned char)str[offset]); offset++) {
+ char c = str[offset];
+ result *= 16;
+ if (isdigit(c))
+ result += c - '0';
+ else if (isupper(c))
+ result += c - 'A' + 10;
+ else
+ result += c - 'a' + 10;
+ }
+ return result;
+}
+
+void setupIR() {
+
+# if defined(ESP8266) || defined(ESP32)
+ irsend.begin();
+# endif
+
+ irrecv.enableIRIn();
+
+ Log.notice(F("IR_EMITTER_GPIO: %d " CR), IR_EMITTER_GPIO);
+ Log.notice(F("IR_RECEIVER_GPIO: %d " CR), IR_RECEIVER_GPIO);
+ Log.trace(F("ZgatewayIR setup done " CR));
+}
+
+void IRtoMQTT() {
+ decode_results results;
+
+ if (irrecv.decode(&results)) {
+ Log.trace(F("Creating IR buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject IRdata = jsonBuffer.to();
+
+ Log.trace(F("Rcv. IR" CR));
+# ifdef ESP32
+ Log.trace(F("IR Task running on core :%d" CR), xPortGetCoreID());
+# endif
+ IRdata["value"] = (SIGNAL_SIZE_UL_ULL)(results.value);
+ IRdata["protocol"] = (int)(results.decode_type);
+ IRdata["bits"] = (int)(results.bits);
+# if defined(ESP8266) || defined(ESP32)
+ String hex = resultToHexidecimal(&results);
+ IRdata["hex"] = (const char*)hex.c_str();
+ String protocol = typeToString(results.decode_type, false);
+ IRdata["protocol_name"] = (const char*)protocol.c_str();
+# endif
+ String rawCode = "";
+
+ for (uint16_t i = 1; i < results.rawlen; i++) {
+# if defined(ESP8266) || defined(ESP32)
+ if (i % 100 == 0)
+ yield();
+ rawCode = rawCode + (results.rawbuf[i] * RAWTICK);
+# else
+ rawCode = rawCode + (results.rawbuf[i] * USECPERTICK);
+# endif
+ if (i < results.rawlen - 1)
+ rawCode = rawCode + ",";
+ }
+ IRdata["raw"] = rawCode;
+
+# ifdef RawDirectForward
+# if defined(ESP8266) || defined(ESP32)
+ uint16_t rawsend[results.rawlen];
+ for (uint16_t i = 1; i < results.rawlen; i++) {
+ if (i % 100 == 0)
+ yield();
+# else
+ unsigned int rawsend[results.rawlen];
+ for (int i = 1; i < results.rawlen; i++) {
+# endif
+ rawsend[i] = results.rawbuf[i];
+ }
+ irsend.sendRaw(rawsend, results.rawlen, RawFrequency);
+ Log.trace(F("raw redirected" CR));
+# endif
+ irrecv.resume();
+ SIGNAL_SIZE_UL_ULL MQTTvalue = IRdata["value"].as();
+
+ if ((pubIRunknownPrtcl == false && IRdata["protocol"].as() == -1)) {
+ Log.notice(F("--no pub unknwn prt--" CR));
+ } else if (!isAduplicateSignal(MQTTvalue) && MQTTvalue != 0) {
+ Log.trace(F("Adv data IRtoMQTT" CR));
+ pub(subjectIRtoMQTT, IRdata);
+ Log.trace(F("Store val: %D" CR), MQTTvalue);
+ storeSignalValue(MQTTvalue);
+ if (repeatIRwMQTT) {
+ Log.trace(F("Pub. IR for rpt" CR));
+ pubMQTT(subjectForwardMQTTtoIR, MQTTvalue);
+ }
+ }
+ }
+}
+
+bool sendIdentifiedProtocol(const char* protocol_name, SIGNAL_SIZE_UL_ULL data, const char* hex, unsigned int valueBITS, uint16_t valueRPT);
+
+# if jsonReceiving
+void MQTTtoIR(char* topicOri, JsonObject& IRdata) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoIR)) {
+ Log.trace(F("MQTTtoIR json" CR));
+ uint64_t data = IRdata["value"];
+ const char* raw = IRdata["raw"];
+ const char* hex = IRdata["hex"];
+ if (hex) {
+ Log.trace(F("hex: %s" CR), hex);
+ data = getUInt64fromHex(hex);
+ }
+ if (data != 0 || raw) {
+ Log.trace(F("MQTTtoIR value || raw detected" CR));
+ bool signalSent = false;
+ const char* protocol_name = IRdata["protocol_name"];
+ unsigned int valueBITS = IRdata["bits"] | 0;
+ uint16_t valueRPT = IRdata["repeat"] | repeatIRwNumber;
+
+ if (raw) {
+ Log.trace(F("Raw: %s" CR), raw);
+ unsigned int s = strlen(raw);
+
+ int count = 0;
+ for (int i = 0; i < s; i++) {
+ if (raw[i] == ',') {
+ count++;
+ }
+ }
+# ifdef IR_GC
+ if (strcmp(protocol_name, "GC") == 0) {
+ Log.trace(F("GC" CR));
+
+ uint16_t GC[count + 1];
+ String value = "";
+ int j = 0;
+ for (int i = 0; i < s; i++) {
+ if (raw[i] != ',') {
+ value = value + String(raw[i]);
+ }
+ if ((raw[i] == ',') || (i == s - 1)) {
+ GC[j] = value.toInt();
+ value = "";
+ j++;
+ }
+ }
+ irsend.sendGC(GC, j);
+ signalSent = true;
+ }
+# endif
+# ifdef IR_RAW
+ if (strcmp(protocol_name, "Raw") == 0) {
+ Log.trace(F("Raw" CR));
+
+# if defined(ESP8266) || defined(ESP32)
+ uint16_t Raw[count + 1];
+# else
+ unsigned int Raw[count + 1];
+# endif
+ String value = "";
+ int j = 0;
+ for (int i = 0; i < s; i++) {
+ if (raw[i] != ',') {
+ value = value + String(raw[i]);
+ }
+ if ((raw[i] == ',') || (i == s - 1)) {
+ Raw[j] = value.toInt();
+ value = "";
+ j++;
+ }
+ }
+ irsend.sendRaw(Raw, j, RawFrequency);
+ signalSent = true;
+ }
+# endif
+ } else if (protocol_name && (strcmp(protocol_name, "NEC") != 0)) {
+ Log.trace(F("Using Identified Protocol: %s bits: %d repeat: %d" CR), protocol_name, valueBITS, valueRPT);
+ signalSent = sendIdentifiedProtocol(protocol_name, data, hex, valueBITS, valueRPT);
+ } else {
+ Log.trace(F("Using NEC protocol" CR));
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = NEC_BITS;
+# if defined(ESP8266) || defined(ESP32)
+ irsend.sendNEC(data, valueBITS, valueRPT);
+# else
+ for (int i = 0; i <= valueRPT; i++)
+ irsend.sendNEC(data, valueBITS);
+# endif
+ signalSent = true;
+ }
+ if (signalSent) {
+ Log.notice(F("MQTTtoIR OK" CR));
+ pub(subjectGTWIRtoMQTT, IRdata);
+ }
+ irrecv.enableIRIn();
+ } else {
+ Log.error(F("MQTTtoIR failed json read" CR));
+ }
+ }
+}
+# endif
+
+bool sendIdentifiedProtocol(const char* protocol_name, SIGNAL_SIZE_UL_ULL data, const char* hex, unsigned int valueBITS, uint16_t valueRPT) {
+ uint8_t dataarray[valueBITS];
+ if (hex) {
+ const char* ptr = NULL;
+ (strstr(hex, "0x") != NULL) ? ptr = hex += 2 : ptr = hex;
+ for (int i = 0; i < sizeof dataarray / sizeof *dataarray; i++) {
+ sscanf(ptr, "%2hhx", &dataarray[i]);
+ ptr += 2;
+ }
+ for (int i = 0; i < valueBITS; i++) {
+ Log.trace(F("%x"), dataarray[i]);
+ }
+ }
+# ifdef IR_WHYNTER
+ if (strcmp(protocol_name, "WHYNTER") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = WHYNTER_BITS;
+# if defined(ESP8266) || defined(ESP32)
+ irsend.sendWhynter(data, valueBITS, valueRPT);
+# else
+ for (int i = 0; i <= valueRPT; i++)
+ irsend.sendWhynter(data, valueBITS);
+# endif
+ return true;
+ }
+# endif
+# ifdef IR_LG
+ if (strcmp(protocol_name, "LG") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = LG_BITS;
+# if defined(ESP8266) || defined(ESP32)
+ irsend.sendLG(data, valueBITS, valueRPT);
+# else
+ for (int i = 0; i <= valueRPT; i++)
+ irsend.sendLG(data, valueBITS);
+# endif
+ return true;
+ }
+# endif
+# ifdef IR_SONY
+ if (strcmp(protocol_name, "SONY") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+# if defined(ESP8266) || defined(ESP32)
+ if (valueBITS == 0)
+ valueBITS = SONY_20_BITS;
+ irsend.sendSony(data, valueBITS, valueRPT);
+# else
+ if (valueBITS == 0)
+ valueBITS = SONY_12_BITS;
+ for (int i = 0; i <= valueRPT; i++)
+ irsend.sendSony(data, valueBITS);
+# endif
+ return true;
+ }
+# endif
+# ifdef IR_DISH
+ if (strcmp(protocol_name, "DISH") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = DISH_BITS;
+# if defined(ESP8266) || defined(ESP32)
+ irsend.sendDISH(data, valueBITS, valueRPT);
+# else
+ for (int i = 0; i <= valueRPT; i++)
+ irsend.sendDISH(data, valueBITS);
+# endif
+ return true;
+ }
+# endif
+# ifdef IR_RC5
+ if (strcmp(protocol_name, "RC5") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = RC5_BITS;
+# if defined(ESP8266) || defined(ESP32)
+ irsend.sendRC5(data, valueBITS, valueRPT);
+# else
+ for (int i = 0; i <= valueRPT; i++)
+ irsend.sendRC5(data, valueBITS);
+# endif
+ return true;
+ }
+# endif
+# ifdef IR_RC6
+ if (strcmp(protocol_name, "RC6") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = RC6_MODE0_BITS;
+# if defined(ESP8266) || defined(ESP32)
+ irsend.sendRC6(data, valueBITS, valueRPT);
+# else
+ for (int i = 0; i <= valueRPT; i++)
+ irsend.sendRC6(data, valueBITS);
+# endif
+ return true;
+ }
+# endif
+# ifdef IR_SHARP
+ if (strcmp(protocol_name, "SHARP") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = SHARP_BITS;
+# if defined(ESP8266) || defined(ESP32)
+ irsend.sendSharpRaw(data, valueBITS, valueRPT);
+# else
+ for (int i = 0; i <= valueRPT; i++)
+ irsend.sendSharpRaw(data, valueBITS);
+# endif
+ return true;
+ }
+# endif
+# ifdef IR_SAMSUNG
+ if (strcmp(protocol_name, "SAMSUNG") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = SAMSUNG_BITS;
+# if defined(ESP8266) || defined(ESP32)
+ irsend.sendSAMSUNG(data, valueBITS, valueRPT);
+# else
+ for (int i = 0; i <= valueRPT; i++)
+ irsend.sendSAMSUNG(data, valueBITS);
+# endif
+ return true;
+ }
+# endif
+# ifdef IR_JVC
+ if (strcmp(protocol_name, "JVC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = JVC_BITS;
+# if defined(ESP8266) || defined(ESP32)
+ irsend.sendJVC(data, valueBITS, valueRPT);
+# else
+ for (int i = 0; i <= valueRPT; i++)
+ irsend.sendJVC(data, valueBITS);
+# endif
+ return true;
+ }
+# endif
+# ifdef IR_PANASONIC
+ if (strcmp(protocol_name, "PANASONIC") == 0) {
+# if defined(ESP8266) || defined(ESP32)
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = PANASONIC_BITS;
+ irsend.sendPanasonic(PanasonicAddress, data, valueBITS, valueRPT);
+# else
+ for (int i = 0; i <= valueRPT; i++)
+ irsend.sendPanasonic(PanasonicAddress, data);
+# endif
+ return true;
+ }
+# endif
+
+# if defined(ESP8266) || defined(ESP32)
+# ifdef IR_COOLIX
+ if (strcmp(protocol_name, "COOLIX") == 0) {
+ Log.trace(F("Sending %s:" CR), protocol_name);
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kCoolixBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kCoolixDefaultRepeat);
+ irsend.sendCOOLIX(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_RCMM
+ if (strcmp(protocol_name, "RCMM") == 0) {
+ Log.trace(F("Sending %s:" CR), protocol_name);
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kRCMMBits;
+ irsend.sendRCMM(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_DENON
+ if (strcmp(protocol_name, "DENON") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = DENON_BITS;
+ irsend.sendDenon(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_GICABLE
+ if (strcmp(protocol_name, "GICABLE") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kGicableBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kGicableMinRepeat);
+ irsend.sendGICable(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_SHERWOOD
+ if (strcmp(protocol_name, "SHERWOOD") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kSherwoodBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kSherwoodMinRepeat);
+ irsend.sendSherwood(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MITSUBISHI
+ if (strcmp(protocol_name, "MITSUBISHI") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kMitsubishiBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMitsubishiMinRepeat);
+ irsend.sendMitsubishi(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_NIKAI
+ if (strcmp(protocol_name, "NIKAI") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kNikaiBits;
+ irsend.sendNikai(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MIDEA
+ if (strcmp(protocol_name, "MIDEA") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kMideaBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMideaMinRepeat);
+ irsend.sendMidea(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MAGIQUEST
+ if (strcmp(protocol_name, "MAGIQUEST") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kMagiquestBits;
+ irsend.sendMagiQuest(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_LASERTAG
+ if (strcmp(protocol_name, "LASERTAG") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kLasertagBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kLasertagMinRepeat);
+ irsend.sendLasertag(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_CARRIER_AC
+ if (strcmp(protocol_name, "CARRIER_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kCarrierAcBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kCarrierAcMinRepeat);
+ irsend.sendCarrierAC(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MITSUBISHI2
+ if (strcmp(protocol_name, "MITSUBISHI2") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kMitsubishiBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMitsubishiMinRepeat);
+ irsend.sendMitsubishi2(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_AIWA_RC_T501
+ if (strcmp(protocol_name, "AIWA_RC_T501") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kAiwaRcT501Bits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kAiwaRcT501MinRepeats);
+ irsend.sendAiwaRCT501(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_DAIKIN
+ if (strcmp(protocol_name, "DAIKIN") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kDaikinStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kDaikinDefaultRepeat);
+ irsend.sendDaikin(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_KELVINATOR
+ if (strcmp(protocol_name, "KELVINATOR") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kKelvinatorStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kKelvinatorDefaultRepeat);
+ irsend.sendKelvinator(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MITSUBISHI_AC
+ if (strcmp(protocol_name, "MITSUBISHI_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kMitsubishiACStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMitsubishiACMinRepeat);
+ irsend.sendMitsubishiAC(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_SANYO
+ if (strcmp(protocol_name, "SANYOLC7461") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kSanyoLC7461Bits;
+ irsend.sendSanyoLC7461(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_GREE
+ if (strcmp(protocol_name, "GREE") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kGreeStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kGreeDefaultRepeat);
+ irsend.sendGree(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_ARGO
+ if (strcmp(protocol_name, "ARGO") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kArgoStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kArgoDefaultRepeat);
+ irsend.sendArgo(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_TROTEC
+ if (strcmp(protocol_name, "TROTEC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kTrotecStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kTrotecDefaultRepeat);
+ irsend.sendTrotec(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_TOSHIBA_AC
+ if (strcmp(protocol_name, "TOSHIBA_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kToshibaACBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kToshibaACMinRepeat);
+ irsend.sendToshibaAC(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_FUJITSU_AC
+ if (strcmp(protocol_name, "FUJITSU_AC") == 0) {
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kFujitsuAcMinRepeat);
+ irsend.sendFujitsuAC(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_HAIER_AC
+ if (strcmp(protocol_name, "HAIER_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kHaierACStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kHaierAcDefaultRepeat);
+ irsend.sendHaierAC(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_HITACHI_AC
+ if (strcmp(protocol_name, "HITACHI_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kHitachiAcStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kHitachiAcDefaultRepeat);
+ irsend.sendHitachiAC(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_HITACHI_AC1
+ if (strcmp(protocol_name, "HITACHI_AC1") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kHitachiAc1StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kHitachiAcDefaultRepeat);
+ irsend.sendHitachiAC1(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_HITACHI_AC2
+ if (strcmp(protocol_name, "HITACHI_AC2") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kHitachiAc2StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kHitachiAcDefaultRepeat);
+ irsend.sendHitachiAC2(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_HAIER_AC_YRW02
+ if (strcmp(protocol_name, "HAIER_AC_YRW02") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kHaierACYRW02StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kHaierAcYrw02DefaultRepeat);
+ irsend.sendHaierACYRW02(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_WHIRLPOOL_AC
+ if (strcmp(protocol_name, "WHIRLPOOL_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kWhirlpoolAcStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kWhirlpoolAcDefaultRepeat);
+ irsend.sendWhirlpoolAC(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_SAMSUNG_AC
+ if (strcmp(protocol_name, "SAMSUNG_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kSamsungAcStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kSamsungAcDefaultRepeat);
+ irsend.sendSamsungAC(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_LUTRON
+ if (strcmp(protocol_name, "LUTRON") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kLutronBits;
+ irsend.sendLutron(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_ELECTRA_AC
+ if (strcmp(protocol_name, "ELECTRA_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kElectraAcStateLength;
+ irsend.sendElectraAC(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_PANASONIC_AC
+ if (strcmp(protocol_name, "PANASONIC_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kPanasonicAcStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kPanasonicAcDefaultRepeat);
+ irsend.sendPanasonicAC(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_PIONEER
+ if (strcmp(protocol_name, "PIONEER") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kPioneerBits;
+ irsend.sendPioneer(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_LG2
+ if (strcmp(protocol_name, "LG2") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kLgBits;
+ irsend.sendLG2(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MWM
+ if (strcmp(protocol_name, "MWM") == 0) {
+ irsend.sendMWM(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_DAIKIN2
+ if (strcmp(protocol_name, "DAIKIN2") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kDaikin2StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kDaikin2DefaultRepeat);
+ irsend.sendDaikin2(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_VESTEL_AC
+ if (strcmp(protocol_name, "VESTEL_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kVestelAcBits;
+ irsend.sendVestelAc(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_SAMSUNG36
+ if (strcmp(protocol_name, "SAMSUNG36") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kSamsung36Bits;
+ irsend.sendSamsung36(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_TCL112AC
+ if (strcmp(protocol_name, "TCL112AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kTcl112AcStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kTcl112AcDefaultRepeat);
+ irsend.sendTcl112Ac(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_TECO
+ if (strcmp(protocol_name, "TECO") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kTecoBits;
+ irsend.sendTeco(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_LEGOPF
+ if (strcmp(protocol_name, "LEGOPF") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kLegoPfBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kLegoPfMinRepeat);
+ irsend.sendLegoPf(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MITSUBISHIHEAVY88
+ if (strcmp(protocol_name, "MITSUBISHIHEAVY88") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kMitsubishiHeavy88StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMitsubishiHeavy88MinRepeat);
+ irsend.sendMitsubishiHeavy88(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MITSUBISHIHEAVY152
+ if (strcmp(protocol_name, "MITSUBISHIHEAVY152") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kMitsubishiHeavy152StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMitsubishiHeavy152MinRepeat);
+ irsend.sendMitsubishiHeavy152(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_DAIKIN216
+ if (strcmp(protocol_name, "DAIKIN216") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kDaikin216StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kDaikin216DefaultRepeat);
+ irsend.sendDaikin216(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_SHARP_AC
+ if (strcmp(protocol_name, "SHARP_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kSharpAcStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kSharpAcDefaultRepeat);
+ irsend.sendSharpAc(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_GOODWEATHER
+ if (strcmp(protocol_name, "GOODWEATHER_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kGoodweatherBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kGoodweatherMinRepeat);
+ irsend.sendGoodweather(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_INAX
+ if (strcmp(protocol_name, "INAX") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kInaxBits;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kInaxMinRepeat);
+ irsend.sendInax(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_DAIKIN160
+ if (strcmp(protocol_name, "DAIKIN160") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kDaikin160StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kDaikin160DefaultRepeat);
+ irsend.sendDaikin160(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_NEOCLIMA
+ if (strcmp(protocol_name, "NEOCLIMA") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kNeoclimaStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kNeoclimaMinRepeat);
+ irsend.sendNeoclima(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_DAIKIN176
+ if (strcmp(protocol_name, "DAIKIN176") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kDaikin176StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kDaikin176DefaultRepeat);
+ irsend.sendDaikin176(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_DAIKIN128
+ if (strcmp(protocol_name, "DAIKIN128") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kDaikin128StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kDaikin128DefaultRepeat);
+ irsend.sendDaikin128(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_AMCOR
+ if (strcmp(protocol_name, "AMCOR") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kAmcorStateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kAmcorDefaultRepeat);
+ irsend.sendAmcor(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_DAIKIN152
+ if (strcmp(protocol_name, "DAIKIN152") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kDaikin152StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kDaikin152DefaultRepeat);
+ irsend.sendDaikin152(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MITSUBISHI136
+ if (strcmp(protocol_name, "MITSUBISHI136") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kMitsubishi136StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMitsubishi136MinRepeat);
+ irsend.sendMitsubishi136(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MITSUBISHI112
+ if (strcmp(protocol_name, "MITSUBISHI112") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kMitsubishi112StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMitsubishi112MinRepeat);
+ irsend.sendMitsubishi112(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_HITACHI_AC424
+ if (strcmp(protocol_name, "HITACHI_AC424") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kHitachiAc424StateLength;
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kHitachiAcDefaultRepeat);
+ irsend.sendHitachiAc424(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_SONY_38K
+ if (strcmp(protocol_name, "SONY_38K") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, (uint16_t)(kSonyMinRepeat + 1));
+ if (valueBITS == 0)
+ valueBITS = kSony20Bits;
+ irsend.sendSony38(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_EPSON
+ if (strcmp(protocol_name, "EPSON") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kEpsonMinRepeat);
+ if (valueBITS == 0)
+ valueBITS = kEpsonBits;
+ irsend.sendEpson(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_SYMPHONY
+ if (strcmp(protocol_name, "SYMPHONY") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kSymphonyDefaultRepeat);
+ if (valueBITS == 0)
+ valueBITS = kSymphonyBits;
+ irsend.sendSymphony(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_HITACHI_AC3
+ if (strcmp(protocol_name, "HITACHI_AC3") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kHitachiAcDefaultRepeat);
+ if (valueBITS == 0)
+ Log.error(F("For this protocol you should have a BIT number as there is no default one defined" CR));
+ irsend.sendHitachiAc3(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_DAIKIN64
+ if (strcmp(protocol_name, "DAIKIN64") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kDaikin64DefaultRepeat);
+ if (valueBITS == 0)
+ valueBITS = kDaikin64Bits;
+ irsend.sendDaikin64(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_AIRWELL
+ if (strcmp(protocol_name, "AIRWELL") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kAirwellMinRepeats);
+ if (valueBITS == 0)
+ valueBITS = kAirwellBits;
+ irsend.sendAirwell(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_DELONGHI_AC
+ if (strcmp(protocol_name, "DELONGHI_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kDelonghiAcDefaultRepeat);
+ if (valueBITS == 0)
+ valueBITS = kDelonghiAcBits;
+ irsend.sendDelonghiAc(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_DOSHISHA
+ if (strcmp(protocol_name, "DOSHISHA") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kDoshishaBits;
+ irsend.sendDoshisha(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_CARRIER_AC40
+ if (strcmp(protocol_name, "CARRIER_AC40") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kCarrierAc40MinRepeat);
+ if (valueBITS == 0)
+ valueBITS = kCarrierAc40Bits;
+ irsend.sendCarrierAC40(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_CARRIER_AC64
+ if (strcmp(protocol_name, "CARRIER_AC64") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kCarrierAc64MinRepeat);
+ if (valueBITS == 0)
+ valueBITS = kCarrierAc64Bits;
+ irsend.sendCarrierAC64(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_HITACHI_AC344
+ if (strcmp(protocol_name, "HITACHI_AC344") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kHitachiAc344StateLength;
+ irsend.sendHitachiAc344(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_CORONA_AC
+ if (strcmp(protocol_name, "CORONA_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kCoronaAcStateLength;
+ irsend.sendCoronaAc(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MIDEA24
+ if (strcmp(protocol_name, "MIDEA24") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMidea24MinRepeat);
+ if (valueBITS == 0)
+ valueBITS = kMidea24Bits;
+ irsend.sendMidea24(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_ZEPEAL
+ if (strcmp(protocol_name, "ZEPEAL") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kZepealMinRepeat);
+ if (valueBITS == 0)
+ valueBITS = kZepealBits;
+ irsend.sendZepeal(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_SANYO_AC
+ if (strcmp(protocol_name, "SANYO_AC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kSanyoAcStateLength;
+ irsend.sendSanyoAc(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_VOLTAS
+ if (strcmp(protocol_name, "VOLTAS") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kVoltasStateLength;
+ irsend.sendVoltas(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_METZ
+ if (strcmp(protocol_name, "METZ") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMetzMinRepeat);
+ if (valueBITS == 0)
+ valueBITS = kMetzBits;
+ irsend.sendMetz(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_TRANSCOLD
+ if (strcmp(protocol_name, "TRANSCOLD") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kTranscoldDefaultRepeat);
+ if (valueBITS == 0)
+ valueBITS = kTranscoldBits;
+ irsend.sendTranscold(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_TECHNIBEL_AC
+ if (strcmp(protocol_name, "TECHNIBELAC") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kTechnibelAcDefaultRepeat);
+ if (valueBITS == 0)
+ valueBITS = kTechnibelAcBits;
+ irsend.sendTechnibelAc(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MIRAGE
+ if (strcmp(protocol_name, "MIRAGE") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMirageMinRepeat);
+ if (valueBITS == 0)
+ valueBITS = kMirageStateLength;
+ irsend.sendMirage(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_ELITESCREENS
+ if (strcmp(protocol_name, "ELITESCREENS") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kEliteScreensDefaultRepeat);
+ if (valueBITS == 0)
+ valueBITS = kEliteScreensBits;
+ irsend.sendElitescreens(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_PANASONIC_AC32
+ if (strcmp(protocol_name, "PANASONIC_AC32") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kPioneerBits;
+ irsend.sendPanasonicAC32(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_MILESTAG2
+ if (strcmp(protocol_name, "MILESTAG2") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueRPT == repeatIRwNumber)
+ valueRPT = std::max(valueRPT, kMilesMinRepeat);
+ if (valueBITS == 0)
+ valueBITS = kMilesTag2ShotBits;
+ irsend.sendMilestag2(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_ECOCLIM
+ if (strcmp(protocol_name, "ECOCLIM") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kEcoclimBits;
+ irsend.sendEcoclim(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_XMP
+ if (strcmp(protocol_name, "XMP") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kXmpBits;
+ irsend.sendXmp(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_KELON168
+ if (strcmp(protocol_name, "KELON168") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kKelon168Bits;
+ irsend.sendKelon168(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_TEKNOPOINT
+ if (strcmp(protocol_name, "TEKNOPOINT") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kTeknopointBits;
+ irsend.sendTeknopoint(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_HAIER_AC176
+ if (strcmp(protocol_name, "HAIER_AC176") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kHaierAC176Bits;
+ irsend.sendHaierAC176(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_BOSE
+ if (strcmp(protocol_name, "BOSE") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kBoseBits;
+ irsend.sendBose(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_SANYO_AC88
+ if (strcmp(protocol_name, "SANYO_AC88") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kSanyoAc88Bits;
+ irsend.sendSanyoAc88(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_TROTEC_3550
+ if (strcmp(protocol_name, "TROTEC_3550") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kTrotecBits;
+ irsend.sendTrotec3550(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_ARRIS
+ if (strcmp(protocol_name, "ARRIS") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kArrisBits;
+ irsend.sendArris(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_RHOSS
+ if (strcmp(protocol_name, "RHOSS") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kRhossBits;
+ irsend.sendRhoss(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_AIRTON
+ if (strcmp(protocol_name, "AIRTON") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kAirtonBits;
+ irsend.sendAirton(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_COOLIX48
+ if (strcmp(protocol_name, "COOLIX48") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kCoolix48Bits;
+ irsend.sendCoolix48(data, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_HITACHI_AC264
+ if (strcmp(protocol_name, "HITACHI_AC264") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kHitachiAc264Bits;
+ irsend.sendHitachiAc264(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+# ifdef IR_HITACHI_AC296
+ if (strcmp(protocol_name, "HITACHI_AC296") == 0) {
+ Log.notice(F("Sending IR signal with %s" CR), protocol_name);
+ if (valueBITS == 0)
+ valueBITS = kHitachiAc296Bits;
+ irsend.sendHitachiAc296(dataarray, valueBITS, valueRPT);
+ return true;
+ }
+# endif
+ Log.warning(F("Unknown IR protocol" CR));
+ return false;
+# endif
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayLORA.ino"
+# 28 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayLORA.ino"
+#include "User_config.h"
+
+#ifdef ZgatewayLORA
+
+# include
+# include
+# include
+
+void setupLORA() {
+ SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_SS);
+ LoRa.setPins(LORA_SS, LORA_RST, LORA_DI0);
+
+ if (!LoRa.begin(LORA_BAND)) {
+ Log.error(F("ZgatewayLORA setup failed!" CR));
+ while (1)
+ ;
+ }
+ LoRa.receive();
+ Log.notice(F("LORA_SCK: %d" CR), LORA_SCK);
+ Log.notice(F("LORA_MISO: %d" CR), LORA_MISO);
+ Log.notice(F("LORA_MOSI: %d" CR), LORA_MOSI);
+ Log.notice(F("LORA_SS: %d" CR), LORA_SS);
+ Log.notice(F("LORA_RST: %d" CR), LORA_RST);
+ Log.notice(F("LORA_DI0: %d" CR), LORA_DI0);
+ LoRa.setTxPower(LORA_TX_POWER);
+ LoRa.setSpreadingFactor(LORA_SPREADING_FACTOR);
+ LoRa.setSignalBandwidth(LORA_SIGNAL_BANDWIDTH);
+ LoRa.setCodingRate4(LORA_CODING_RATE);
+ LoRa.setPreambleLength(LORA_PREAMBLE_LENGTH);
+ LoRa.setSyncWord(LORA_SYNC_WORD);
+ Log.trace(F("ZgatewayLORA setup done" CR));
+}
+
+void LORAtoMQTT() {
+ int packetSize = LoRa.parsePacket();
+ if (packetSize) {
+ StaticJsonDocument jsonBuffer;
+ JsonObject LORAdata = jsonBuffer.to();
+ Log.trace(F("Rcv. LORA" CR));
+# ifdef ESP32
+ String taskMessage = "LORA Task running on core ";
+ taskMessage = taskMessage + xPortGetCoreID();
+
+# endif
+ String packet;
+ packet = "";
+ for (int i = 0; i < packetSize; i++) {
+ packet += (char)LoRa.read();
+ }
+ LORAdata["rssi"] = (int)LoRa.packetRssi();
+ LORAdata["snr"] = (float)LoRa.packetSnr();
+ LORAdata["pferror"] = (float)LoRa.packetFrequencyError();
+ LORAdata["packetSize"] = (int)packetSize;
+ LORAdata["message"] = (char*)packet.c_str();
+ pub(subjectLORAtoMQTT, LORAdata);
+ if (repeatLORAwMQTT) {
+ Log.trace(F("Pub LORA for rpt" CR));
+ pub(subjectMQTTtoLORA, LORAdata);
+ }
+ }
+}
+
+# if jsonReceiving
+void MQTTtoLORA(char* topicOri, JsonObject& LORAdata) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoLORA)) {
+ Log.trace(F("MQTTtoLORA json" CR));
+ const char* message = LORAdata["message"];
+ int txPower = LORAdata["txpower"] | LORA_TX_POWER;
+ int spreadingFactor = LORAdata["spreadingfactor"] | LORA_SPREADING_FACTOR;
+ long int frequency = LORAdata["frequency "] | LORA_BAND;
+ long int signalBandwidth = LORAdata["signalbandwidth"] | LORA_SIGNAL_BANDWIDTH;
+ int codingRateDenominator = LORAdata["codingrate"] | LORA_CODING_RATE;
+ int preambleLength = LORAdata["preamblelength"] | LORA_PREAMBLE_LENGTH;
+ byte syncWord = LORAdata["syncword"] | LORA_SYNC_WORD;
+ bool Crc = LORAdata["enablecrc"] | DEFAULT_CRC;
+ if (message) {
+ LoRa.setTxPower(txPower);
+ LoRa.setFrequency(frequency);
+ LoRa.setSpreadingFactor(spreadingFactor);
+ LoRa.setSignalBandwidth(signalBandwidth);
+ LoRa.setCodingRate4(codingRateDenominator);
+ LoRa.setPreambleLength(preambleLength);
+ LoRa.setSyncWord(syncWord);
+ if (Crc)
+ LoRa.enableCrc();
+ LoRa.beginPacket();
+ LoRa.print(message);
+ LoRa.endPacket();
+ Log.trace(F("MQTTtoLORA OK" CR));
+ pub(subjectGTWLORAtoMQTT, LORAdata);
+ } else {
+ Log.error(F("MQTTtoLORA Fail json" CR));
+ }
+ }
+}
+# endif
+# if simpleReceiving
+void MQTTtoLORA(char* topicOri, char* LORAdata) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoLORA)) {
+ LoRa.beginPacket();
+ LoRa.print(LORAdata);
+ LoRa.endPacket();
+ Log.notice(F("MQTTtoLORA OK" CR));
+ pub(subjectGTWLORAtoMQTT, LORAdata);
+ }
+}
+# endif
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayPilight.ino"
+# 28 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayPilight.ino"
+#include "User_config.h"
+
+#ifdef ZgatewayPilight
+
+# ifdef ZradioCC1101
+# include
+# endif
+
+# include
+ESPiLight rf(RF_EMITTER_GPIO);
+
+void pilightCallback(const String& protocol, const String& message, int status,
+ size_t repeats, const String& deviceID) {
+ if (status == VALID) {
+ Log.trace(F("Creating RF PiLight buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject RFPiLightdata = jsonBuffer.to();
+ StaticJsonDocument jsonBuffer2;
+ JsonObject msg = jsonBuffer2.to();
+ auto error = deserializeJson(jsonBuffer2, message);
+ if (error) {
+ Log.error(F("deserializeJson() failed: %s" CR), error.c_str());
+ return;
+ }
+ RFPiLightdata["message"] = msg;
+ RFPiLightdata["protocol"] = (const char*)protocol.c_str();
+ RFPiLightdata["length"] = (const char*)deviceID.c_str();
+
+ const char* device_id = deviceID.c_str();
+ if (!strlen(device_id)) {
+
+
+ char* choices[] = {"key", "unit", "device_id", "systemcode", "unitcode", "programcode"};
+
+ for (uint8_t i = 0; i < 6; i++) {
+ if (msg[choices[i]]) {
+ device_id = (const char*)msg[choices[i]];
+ break;
+ }
+ }
+ }
+
+ RFPiLightdata["value"] = device_id;
+ RFPiLightdata["repeats"] = (int)repeats;
+ RFPiLightdata["status"] = (int)status;
+ pub(subjectPilighttoMQTT, RFPiLightdata);
+ if (repeatPilightwMQTT) {
+ Log.trace(F("Pub Pilight for rpt" CR));
+ pub(subjectMQTTtoPilight, RFPiLightdata);
+ }
+ }
+}
+
+void setupPilight() {
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.Init();
+ ELECHOUSE_cc1101.setMHZ(CC1101_FREQUENCY);
+ ELECHOUSE_cc1101.SetRx(CC1101_FREQUENCY);
+# endif
+ rf.setCallback(pilightCallback);
+ rf.initReceiver(RF_RECEIVER_GPIO);
+ pinMode(RF_EMITTER_GPIO, OUTPUT);
+ Log.notice(F("RF_EMITTER_GPIO: %d " CR), RF_EMITTER_GPIO);
+ Log.notice(F("RF_RECEIVER_GPIO: %d " CR), RF_RECEIVER_GPIO);
+ Log.trace(F("ZgatewayPilight command topic: %s%s%s" CR), mqtt_topic, gateway_name, subjectMQTTtoPilight);
+ Log.trace(F("ZgatewayPilight setup done " CR));
+}
+
+void savePilightConfig() {
+ Log.trace(F("saving Pilight config" CR));
+ DynamicJsonDocument json(4096);
+ deserializeJson(json, rf.enabledProtocols());
+
+ File configFile = SPIFFS.open("/pilight.json", "w");
+ if (!configFile) {
+ Log.error(F("failed to open config file for writing" CR));
+ }
+
+ serializeJsonPretty(json, Serial);
+ serializeJson(json, configFile);
+ configFile.close();
+}
+
+void loadPilightConfig() {
+ Log.trace(F("reading Pilight config file" CR));
+ File configFile = SPIFFS.open("/pilight.json", "r");
+ if (configFile) {
+ Log.trace(F("opened Pilight config file" CR));
+ DynamicJsonDocument json(configFile.size() * 4);
+ auto error = deserializeJson(json, configFile);
+ if (error) {
+ Log.error(F("deserialize config failed: %s, buffer capacity: %u" CR), error.c_str(), json.capacity());
+ }
+ serializeJson(json, Serial);
+ if (!json.isNull()) {
+ String rflimit;
+ serializeJson(json, rflimit);
+ rf.limitProtocols(rflimit);
+ } else {
+ Log.warning(F("failed to load json config" CR));
+ }
+ configFile.close();
+ }
+}
+
+void PilighttoMQTT() {
+ rf.loop();
+}
+
+void MQTTtoPilight(char* topicOri, JsonObject& Pilightdata) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoPilightProtocol)) {
+ bool success = false;
+ if (Pilightdata.containsKey("reset")) {
+ rf.limitProtocols(rf.availableProtocols());
+ savePilightConfig();
+ success = true;
+ }
+ if (Pilightdata.containsKey("limit")) {
+ String output;
+ serializeJson(Pilightdata["limit"], output);
+ rf.limitProtocols(output);
+ savePilightConfig();
+ success = true;
+ }
+ if (Pilightdata.containsKey("enabled")) {
+ Log.notice(F("PiLight protocols enabled: %s" CR), rf.enabledProtocols().c_str());
+ success = true;
+ }
+ if (Pilightdata.containsKey("available")) {
+ Log.notice(F("PiLight protocols available: %s" CR), rf.availableProtocols().c_str());
+ success = true;
+ }
+
+ if (success) {
+ pub(subjectGTWPilighttoMQTT, Pilightdata);
+ } else {
+ pub(subjectGTWPilighttoMQTT, "{\"Status\": \"Error\"}");
+ Log.error(F("MQTTtoPilightProtocol Fail json" CR));
+ }
+ } else if (cmpToMainTopic(topicOri, subjectMQTTtoPilight)) {
+ const char* message = Pilightdata["message"];
+ const char* protocol = Pilightdata["protocol"];
+ const char* raw = Pilightdata["raw"];
+ float tempMhz = Pilightdata["mhz"];
+ bool success = false;
+ if (raw) {
+ uint16_t codes[MAXPULSESTREAMLENGTH];
+ int repeats = rf.stringToRepeats(raw);
+ if (repeats < 0) {
+ switch (repeats) {
+ case ESPiLight::ERROR_INVALID_PULSETRAIN_MSG_R:
+ Log.trace(F("'r' not found in string, or has no data" CR));
+ break;
+ case ESPiLight::ERROR_INVALID_PULSETRAIN_MSG_END:
+ Log.trace(F("';' or '@' not found in data string" CR));
+ break;
+ }
+ repeats = 10;
+ }
+ int msgLength = rf.stringToPulseTrain(raw, codes, MAXPULSESTREAMLENGTH);
+ if (msgLength > 0) {
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.SetTx(CC1101_FREQUENCY);
+ rf.disableReceiver();
+# endif
+ rf.sendPulseTrain(codes, msgLength, repeats);
+ Log.notice(F("MQTTtoPilight raw ok" CR));
+ success = true;
+ } else {
+ Log.trace(F("MQTTtoPilight raw KO" CR));
+ switch (msgLength) {
+ case ESPiLight::ERROR_INVALID_PULSETRAIN_MSG_C:
+ Log.trace(F("'c' not found in string, or has no data" CR));
+ break;
+ case ESPiLight::ERROR_INVALID_PULSETRAIN_MSG_P:
+ Log.trace(F("'p' not found in string, or has no data" CR));
+ break;
+ case ESPiLight::ERROR_INVALID_PULSETRAIN_MSG_END:
+ Log.trace(F("';' or '@' not found in data string" CR));
+ break;
+ case ESPiLight::ERROR_INVALID_PULSETRAIN_MSG_TYPE:
+ Log.trace(F("pulse type not defined" CR));
+ break;
+ }
+ Log.error(F("Invalid JSON: raw data malformed" CR));
+ }
+ }
+ if (message && protocol) {
+ Log.trace(F("MQTTtoPilight msg & protocol ok" CR));
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.SetTx(CC1101_FREQUENCY);
+ rf.disableReceiver();
+# endif
+ int msgLength = rf.send(protocol, message);
+ if (msgLength > 0) {
+ Log.trace(F("Adv data MQTTtoPilight push state via PilighttoMQTT" CR));
+ pub(subjectGTWPilighttoMQTT, Pilightdata);
+ success = true;
+ } else {
+ switch (msgLength) {
+ case ESPiLight::ERROR_UNAVAILABLE_PROTOCOL:
+ Log.error(F("protocol is not available" CR));
+ break;
+ case ESPiLight::ERROR_INVALID_PILIGHT_MSG:
+ Log.error(F("message is invalid" CR));
+ break;
+ case ESPiLight::ERROR_INVALID_JSON:
+ Log.error(F("message is not a proper json object" CR));
+ break;
+ case ESPiLight::ERROR_NO_OUTPUT_PIN:
+ Log.error(F("no transmitter pin" CR));
+ break;
+ default:
+ Log.error(F("Invalid JSON: can't read message/protocol" CR));
+ }
+ }
+ }
+ if (Pilightdata.containsKey("active")) {
+ Log.trace(F("PiLight active:" CR));
+ activeReceiver = ACTIVE_PILIGHT;
+ success = true;
+ }
+# ifdef ZradioCC1101
+ if (Pilightdata.containsKey("mhz") && validFrequency(tempMhz)) {
+ receiveMhz = tempMhz;
+ Log.notice(F("PiLight Receive mhz: %F" CR), receiveMhz);
+ success = true;
+ }
+# endif
+ if (success) {
+ pub(subjectGTWPilighttoMQTT, Pilightdata);
+ } else {
+ pub(subjectGTWPilighttoMQTT, "{\"Status\": \"Error\"}");
+ Log.error(F("MQTTtoPilight Fail json" CR));
+ }
+ enableActiveReceiver(false);
+ }
+}
+
+extern void disablePilightReceive() {
+ Log.trace(F("disablePilightReceive" CR));
+ rf.initReceiver(-1);
+ rf.disableReceiver();
+};
+
+extern void enablePilightReceive() {
+# ifdef ZradioCC1101
+ Log.notice(F("Switching to Pilight Receiver: %F" CR), receiveMhz);
+# else
+ Log.notice(F("Switching to Pilight Receiver" CR));
+# endif
+# ifdef ZgatewayRF
+ disableRFReceive();
+# endif
+# ifdef ZgatewayRF2
+ disableRF2Receive();
+# endif
+# ifdef ZgatewayRTL_433
+ disableRTLreceive();
+# endif
+
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.SetRx(receiveMhz);
+# endif
+ rf.setCallback(pilightCallback);
+ rf.initReceiver(RF_RECEIVER_GPIO);
+ pinMode(RF_EMITTER_GPIO, OUTPUT);
+ rf.enableReceiver();
+ loadPilightConfig();
+};
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayRF.ino"
+# 28 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayRF.ino"
+#include "User_config.h"
+
+#ifdef ZgatewayRF
+
+# ifdef ZradioCC1101
+# include
+# endif
+
+# include
+
+RCSwitch mySwitch = RCSwitch();
+
+
+static const char* bin2tristate(const char* bin) {
+ static char returnValue[50];
+ int pos = 0;
+ int pos2 = 0;
+ while (bin[pos] != '\0' && bin[pos + 1] != '\0') {
+ if (bin[pos] == '0' && bin[pos + 1] == '0') {
+ returnValue[pos2] = '0';
+ } else if (bin[pos] == '1' && bin[pos + 1] == '1') {
+ returnValue[pos2] = '1';
+ } else if (bin[pos] == '0' && bin[pos + 1] == '1') {
+ returnValue[pos2] = 'F';
+ } else {
+ return "-";
+ }
+ pos = pos + 2;
+ pos2++;
+ }
+ returnValue[pos2] = '\0';
+ return returnValue;
+}
+
+static char* dec2binWzerofill(unsigned long Dec, unsigned int bitLength) {
+ static char bin[64];
+ unsigned int i = 0;
+
+ while (Dec > 0) {
+ bin[32 + i++] = ((Dec & 1) > 0) ? '1' : '0';
+ Dec = Dec >> 1;
+ }
+
+ for (unsigned int j = 0; j < bitLength; j++) {
+ if (j >= bitLength - i) {
+ bin[j] = bin[31 + i - (j - (bitLength - i))];
+ } else {
+ bin[j] = '0';
+ }
+ }
+ bin[bitLength] = '\0';
+
+ return bin;
+}
+
+# if defined(ZmqttDiscovery) && !defined(RF_DISABLE_TRANSMIT) && defined(RFmqttDiscovery)
+
+void RFtoMQTTdiscovery(SIGNAL_SIZE_UL_ULL MQTTvalue) {
+
+ char val[11];
+ sprintf(val, "%lu", MQTTvalue);
+ Log.trace(F("RF Entity Discovered, create HA Discovery CFG" CR));
+ char* switchRF[2] = {val, "RF"};
+ Log.trace(F("CreateDiscoverySwitch: %s" CR), switchRF[1]);
+# if valueAsATopic
+ String discovery_topic = String(subjectRFtoMQTT) + "/" + String(switchRF[0]);
+# else
+ String discovery_topic = String(subjectRFtoMQTT);
+# endif
+
+ String theUniqueId = getUniqueId("-" + String(switchRF[0]), "-" + String(switchRF[1]));
+
+ announceDeviceTrigger(
+ false,
+ (char*)discovery_topic.c_str(),
+ "", "",
+ (char*)theUniqueId.c_str(),
+ "", "", "", "");
+}
+# endif
+
+void setupRF() {
+
+ Log.notice(F("RF_EMITTER_GPIO: %d " CR), RF_EMITTER_GPIO);
+ Log.notice(F("RF_RECEIVER_GPIO: %d " CR), RF_RECEIVER_GPIO);
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.Init();
+ ELECHOUSE_cc1101.SetRx(receiveMhz);
+# endif
+# ifdef RF_DISABLE_TRANSMIT
+ mySwitch.disableTransmit();
+# else
+ mySwitch.enableTransmit(RF_EMITTER_GPIO);
+# endif
+ mySwitch.setRepeatTransmit(RF_EMITTER_REPEAT);
+ mySwitch.enableReceive(RF_RECEIVER_GPIO);
+ Log.trace(F("ZgatewayRF command topic: %s%s%s" CR), mqtt_topic, gateway_name, subjectMQTTtoRF);
+ Log.trace(F("ZgatewayRF setup done" CR));
+}
+
+void RFtoMQTT() {
+ if (mySwitch.available()) {
+ StaticJsonDocument jsonBuffer;
+ JsonObject RFdata = jsonBuffer.to();
+ Log.trace(F("Rcv. RF" CR));
+# ifdef ESP32
+ Log.trace(F("RF Task running on core :%d" CR), xPortGetCoreID());
+# endif
+ SIGNAL_SIZE_UL_ULL MQTTvalue = mySwitch.getReceivedValue();
+ int length = mySwitch.getReceivedBitlength();
+ const char* binary = dec2binWzerofill(MQTTvalue, length);
+
+ RFdata["value"] = (SIGNAL_SIZE_UL_ULL)MQTTvalue;
+ RFdata["protocol"] = (int)mySwitch.getReceivedProtocol();
+ RFdata["length"] = (int)mySwitch.getReceivedBitlength();
+ RFdata["delay"] = (int)mySwitch.getReceivedDelay();
+ RFdata["tre_state"] = bin2tristate(binary);
+ RFdata["binary"] = binary;
+
+ unsigned int* raw = mySwitch.getReceivedRawdata();
+ String rawDump = "";
+ for (unsigned int i = 0; i <= length * 2; i++) {
+ rawDump = rawDump + String(raw[i]) + ",";
+ }
+ RFdata["raw"] = rawDump.c_str();
+
+# ifdef ZradioCC1101
+ RFdata["mhz"] = receiveMhz;
+# endif
+ mySwitch.resetAvailable();
+
+ if (!isAduplicateSignal(MQTTvalue) && MQTTvalue != 0) {
+# if defined(ZmqttDiscovery) && !defined(RF_DISABLE_TRANSMIT) && defined(RFmqttDiscovery)
+ if (disc)
+ RFtoMQTTdiscovery(MQTTvalue);
+# endif
+ pub(subjectRFtoMQTT, RFdata);
+
+ Log.trace(F("Store val: %u" CR), (unsigned long)MQTTvalue);
+ storeSignalValue(MQTTvalue);
+ if (repeatRFwMQTT) {
+ Log.trace(F("Pub RF for rpt" CR));
+ pub(subjectMQTTtoRF, RFdata);
+ }
+ }
+ }
+}
+
+# if simpleReceiving
+void MQTTtoRF(char* topicOri, char* datacallback) {
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.SetTx(receiveMhz);
+# endif
+ mySwitch.disableReceive();
+ mySwitch.enableTransmit(RF_EMITTER_GPIO);
+ SIGNAL_SIZE_UL_ULL data = STRTO_UL_ULL(datacallback, NULL, 10);
+
+
+
+ String topic = topicOri;
+ int valuePRT = 0;
+ int valuePLSL = 0;
+ int valueBITS = 0;
+ int pos = topic.lastIndexOf(RFprotocolKey);
+ if (pos != -1) {
+ pos = pos + +strlen(RFprotocolKey);
+ valuePRT = (topic.substring(pos, pos + 1)).toInt();
+ }
+
+ int pos2 = topic.lastIndexOf(RFpulselengthKey);
+ if (pos2 != -1) {
+ pos2 = pos2 + strlen(RFpulselengthKey);
+ valuePLSL = (topic.substring(pos2, pos2 + 3)).toInt();
+ }
+ int pos3 = topic.lastIndexOf(RFbitsKey);
+ if (pos3 != -1) {
+ pos3 = pos3 + strlen(RFbitsKey);
+ valueBITS = (topic.substring(pos3, pos3 + 2)).toInt();
+ }
+
+ if ((cmpToMainTopic(topicOri, subjectMQTTtoRF)) && (valuePRT == 0) && (valuePLSL == 0) && (valueBITS == 0)) {
+ Log.trace(F("MQTTtoRF dflt" CR));
+ mySwitch.setProtocol(1, 350);
+ mySwitch.send(data, 24);
+
+ pub(subjectGTWRFtoMQTT, datacallback);
+ } else if ((valuePRT != 0) || (valuePLSL != 0) || (valueBITS != 0)) {
+ Log.trace(F("MQTTtoRF usr par." CR));
+ if (valuePRT == 0)
+ valuePRT = 1;
+ if (valuePLSL == 0)
+ valuePLSL = 350;
+ if (valueBITS == 0)
+ valueBITS = 24;
+ Log.notice(F("RF Protocol:%d" CR), valuePRT);
+ Log.notice(F("RF Pulse Lgth: %d" CR), valuePLSL);
+ Log.notice(F("Bits nb: %d" CR), valueBITS);
+ mySwitch.setProtocol(valuePRT, valuePLSL);
+ mySwitch.send(data, valueBITS);
+
+ pub(subjectGTWRFtoMQTT, datacallback);
+ }
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.SetRx(receiveMhz);
+ mySwitch.disableTransmit();
+ mySwitch.enableReceive(RF_RECEIVER_GPIO);
+# endif
+}
+# endif
+
+# if jsonReceiving
+void MQTTtoRF(char* topicOri, JsonObject& RFdata) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoRF)) {
+ Log.trace(F("MQTTtoRF json" CR));
+ SIGNAL_SIZE_UL_ULL data = RFdata["value"];
+ if (data != 0) {
+ int valuePRT = RFdata["protocol"] | 1;
+ int valuePLSL = RFdata["delay"] | 350;
+ int valueBITS = RFdata["length"] | 24;
+ int valueRPT = RFdata["repeat"] | RF_EMITTER_REPEAT;
+ Log.notice(F("RF Protocol:%d" CR), valuePRT);
+ Log.notice(F("RF Pulse Lgth: %d" CR), valuePLSL);
+ Log.notice(F("Bits nb: %d" CR), valueBITS);
+# ifdef ZradioCC1101
+ float trMhz = RFdata["mhz"] | CC1101_FREQUENCY;
+ if (validFrequency((int)trMhz)) {
+ ELECHOUSE_cc1101.SetTx(trMhz);
+ Log.notice(F("Transmit mhz: %F" CR), trMhz);
+ }
+# endif
+ mySwitch.disableReceive();
+ mySwitch.enableTransmit(RF_EMITTER_GPIO);
+ mySwitch.setRepeatTransmit(valueRPT);
+ mySwitch.setProtocol(valuePRT, valuePLSL);
+ mySwitch.send(data, valueBITS);
+ Log.notice(F("MQTTtoRF OK" CR));
+ pub(subjectGTWRFtoMQTT, RFdata);
+ mySwitch.setRepeatTransmit(RF_EMITTER_REPEAT);
+ } else {
+ bool success = false;
+ if (RFdata.containsKey("active")) {
+ Log.trace(F("RF active:" CR));
+ activeReceiver = ACTIVE_RF;
+ success = true;
+ }
+# ifdef ZradioCC1101
+ float tempMhz = RFdata["mhz"];
+ if (RFdata.containsKey("mhz") && validFrequency(tempMhz)) {
+ receiveMhz = tempMhz;
+ Log.notice(F("Receive mhz: %F" CR), receiveMhz);
+ success = true;
+ }
+# endif
+ if (success) {
+ pub(subjectGTWRFtoMQTT, RFdata);
+ } else {
+# ifndef ARDUINO_AVR_UNO
+ pub(subjectGTWRFtoMQTT, "{\"Status\": \"Error\"}");
+# endif
+ Log.error(F("MQTTtoRF Fail json" CR));
+ }
+ }
+ enableActiveReceiver(false);
+ }
+}
+# endif
+
+int receiveInterupt = -1;
+
+void disableRFReceive() {
+ Log.trace(F("disableRFReceive %d" CR), receiveInterupt);
+ if (receiveInterupt != -1) {
+ receiveInterupt = -1;
+ mySwitch.disableReceive();
+ }
+}
+
+void enableRFReceive() {
+# ifdef ZradioCC1101
+ Log.notice(F("Switching to RF Receiver: %F" CR), receiveMhz);
+# else
+ Log.notice(F("Switching to RF Receiver" CR));
+# endif
+# ifndef ARDUINO_AVR_UNO
+# ifdef ZgatewayPilight
+ disablePilightReceive();
+# endif
+# ifdef ZgatewayRTL_433
+ disableRTLreceive();
+# endif
+# endif
+# ifdef ZgatewayRF2
+ disableRF2Receive();
+# endif
+
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.SetRx(receiveMhz);
+# endif
+ mySwitch.disableTransmit();
+ receiveInterupt = RF_RECEIVER_GPIO;
+ mySwitch.enableReceive(RF_RECEIVER_GPIO);
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayRF2.ino"
+# 36 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayRF2.ino"
+#include "User_config.h"
+
+#ifdef ZgatewayRF2
+
+# ifdef ZradioCC1101
+# include
+# endif
+
+# include
+# include
+
+struct RF2rxd {
+ unsigned int period;
+ unsigned long address;
+ unsigned long groupBit;
+ unsigned long unit;
+ unsigned long switchType;
+ bool hasNewData;
+};
+
+RF2rxd rf2rd;
+
+void setupRF2() {
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.Init();
+ ELECHOUSE_cc1101.setMHZ(receiveMhz);
+ ELECHOUSE_cc1101.SetRx(receiveMhz);
+# endif
+ NewRemoteReceiver::init(RF_RECEIVER_GPIO, 2, rf2Callback);
+ Log.notice(F("RF_EMITTER_GPIO: %d " CR), RF_EMITTER_GPIO);
+ Log.notice(F("RF_RECEIVER_GPIO: %d " CR), RF_RECEIVER_GPIO);
+ Log.trace(F("ZgatewayRF2 command topic: %s%s%s" CR), mqtt_topic, gateway_name, subjectMQTTtoRF2);
+ Log.trace(F("ZgatewayRF2 setup done " CR));
+ pinMode(RF_EMITTER_GPIO, OUTPUT);
+ digitalWrite(RF_EMITTER_GPIO, LOW);
+}
+
+# ifdef ZmqttDiscovery
+
+void RF2toMQTTdiscovery(JsonObject& data) {
+ Log.trace(F("switchRF2Discovery" CR));
+ String payloadonstr;
+ String payloadoffstr;
+
+ int org_switchtype = data["switchType"];
+ data["switchType"] = 1;
+ serializeJson(data, payloadonstr);
+ data["switchType"] = 0;
+ serializeJson(data, payloadoffstr);
+ data["switchType"] = org_switchtype;
+
+ String switchname;
+ switchname = "RF2_" + String((int)data["unit"]) + "_" +
+ String((int)data["groupbit"]) + "_" +
+ String((unsigned long)data["address"]);
+
+ char* switchRF[8] = {"switch",
+ (char*)switchname.c_str(),
+ "",
+ "",
+ "",
+ (char*)payloadonstr.c_str(),
+ (char*)payloadoffstr.c_str(),
+ ""};
+
+
+
+ Log.trace(F("CreateDiscoverySwitch: %s" CR), switchRF[1]);
+
+
+
+
+
+
+ createDiscovery(switchRF[0], "", switchRF[1],
+ (char*)getUniqueId(switchRF[1], "").c_str(), will_Topic,
+ switchRF[3], switchRF[4], switchRF[5], switchRF[6],
+ switchRF[7], 0, "", "", true, subjectMQTTtoRF2,
+ "", "", "", "", false,
+ stateClassNone);
+}
+# endif
+
+void RF2toMQTT() {
+ if (rf2rd.hasNewData) {
+ Log.trace(F("Creating RF2 buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject RF2data = jsonBuffer.to();
+
+ rf2rd.hasNewData = false;
+
+ Log.trace(F("Rcv. RF2" CR));
+ RF2data["unit"] = (int)rf2rd.unit;
+ RF2data["groupBit"] = (int)rf2rd.groupBit;
+ RF2data["period"] = (int)rf2rd.period;
+ RF2data["address"] = (unsigned long)rf2rd.address;
+ RF2data["switchType"] = (int)rf2rd.switchType;
+# ifdef ZmqttDiscovery
+ if (disc)
+ RF2toMQTTdiscovery(RF2data);
+# endif
+
+ pub(subjectRF2toMQTT, RF2data);
+ }
+}
+
+void rf2Callback(unsigned int period, unsigned long address, unsigned long groupBit, unsigned long unit, unsigned long switchType) {
+ rf2rd.period = period;
+ rf2rd.address = address;
+ rf2rd.groupBit = groupBit;
+ rf2rd.unit = unit;
+ rf2rd.switchType = switchType;
+ rf2rd.hasNewData = true;
+}
+
+# if simpleReceiving
+void MQTTtoRF2(char* topicOri, char* datacallback) {
+# ifdef ZradioCC1101
+ NewRemoteReceiver::disable();
+ ELECHOUSE_cc1101.SetTx(CC1101_FREQUENCY);
+# endif
+
+
+
+ String topic = topicOri;
+ bool boolSWITCHTYPE;
+ boolSWITCHTYPE = to_bool(datacallback);
+ bool isDimCommand = false;
+
+ long valueCODE = 0;
+ int valueUNIT = -1;
+ int valuePERIOD = 0;
+ int valueGROUP = 0;
+ int valueDIM = -1;
+
+ int pos = topic.lastIndexOf(RF2codeKey);
+ if (pos != -1) {
+ pos = pos + +strlen(RF2codeKey);
+ valueCODE = (topic.substring(pos, pos + 8)).toInt();
+ Log.notice(F("RF2 code: %l" CR), valueCODE);
+ }
+ int pos2 = topic.lastIndexOf(RF2periodKey);
+ if (pos2 != -1) {
+ pos2 = pos2 + strlen(RF2periodKey);
+ valuePERIOD = (topic.substring(pos2, pos2 + 3)).toInt();
+ Log.notice(F("RF2 Period: %d" CR), valuePERIOD);
+ }
+ int pos3 = topic.lastIndexOf(RF2unitKey);
+ if (pos3 != -1) {
+ pos3 = pos3 + strlen(RF2unitKey);
+ valueUNIT = (topic.substring(pos3, topic.indexOf("/", pos3))).toInt();
+ Log.notice(F("Unit: %d" CR), valueUNIT);
+ }
+ int pos4 = topic.lastIndexOf(RF2groupKey);
+ if (pos4 != -1) {
+ pos4 = pos4 + strlen(RF2groupKey);
+ valueGROUP = (topic.substring(pos4, pos4 + 1)).toInt();
+ Log.notice(F("RF2 Group: %d" CR), valueGROUP);
+ }
+ int pos5 = topic.lastIndexOf(RF2dimKey);
+ if (pos5 != -1) {
+ isDimCommand = true;
+ valueDIM = atoi(datacallback);
+ Log.notice(F("RF2 Dim: %d" CR), valueDIM);
+ }
+
+ if ((topic == subjectMQTTtoRF2) || (valueCODE != 0) || (valueUNIT != -1) || (valuePERIOD != 0)) {
+ Log.trace(F("MQTTtoRF2" CR));
+ if (valueCODE == 0)
+ valueCODE = 8233378;
+ if (valueUNIT == -1)
+ valueUNIT = 0;
+ if (valuePERIOD == 0)
+ valuePERIOD = 272;
+ NewRemoteReceiver::disable();
+ Log.trace(F("Creating transmitter" CR));
+ NewRemoteTransmitter transmitter(valueCODE, RF_EMITTER_GPIO, valuePERIOD, RF2_EMITTER_REPEAT);
+ Log.trace(F("Sending data" CR));
+ if (valueGROUP) {
+ if (isDimCommand) {
+ transmitter.sendGroupDim(valueDIM);
+ } else {
+ transmitter.sendGroup(boolSWITCHTYPE);
+ }
+ } else {
+ if (isDimCommand) {
+ transmitter.sendDim(valueUNIT, valueDIM);
+ } else {
+ transmitter.sendUnit(valueUNIT, boolSWITCHTYPE);
+ }
+ }
+ Log.trace(F("Data sent" CR));
+ NewRemoteReceiver::enable();
+
+
+ String MQTTAddress;
+ String MQTTperiod;
+ String MQTTunit;
+ String MQTTgroupBit;
+ String MQTTswitchType;
+ String MQTTdimLevel;
+
+ MQTTAddress = String(valueCODE);
+ MQTTperiod = String(valuePERIOD);
+ MQTTunit = String(valueUNIT);
+ MQTTgroupBit = String(rf2rd.groupBit);
+ MQTTswitchType = String(boolSWITCHTYPE);
+ MQTTdimLevel = String(valueDIM);
+ String MQTTRF2string;
+ Log.trace(F("Adv data MQTTtoRF2 push state via RF2toMQTT" CR));
+ if (isDimCommand) {
+ MQTTRF2string = subjectRF2toMQTT + String("/") + RF2codeKey + MQTTAddress + String("/") + RF2unitKey + MQTTunit + String("/") + RF2groupKey + MQTTgroupBit + String("/") + RF2dimKey + String("/") + RF2periodKey + MQTTperiod;
+ pub((char*)MQTTRF2string.c_str(), (char*)MQTTdimLevel.c_str());
+ } else {
+ MQTTRF2string = subjectRF2toMQTT + String("/") + RF2codeKey + MQTTAddress + String("/") + RF2unitKey + MQTTunit + String("/") + RF2groupKey + MQTTgroupBit + String("/") + RF2periodKey + MQTTperiod;
+ pub((char*)MQTTRF2string.c_str(), (char*)MQTTswitchType.c_str());
+ }
+ }
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.SetRx(receiveMhz);
+ NewRemoteReceiver::enable();
+# endif
+}
+# endif
+
+# if jsonReceiving
+void MQTTtoRF2(char* topicOri, JsonObject& RF2data) {
+
+ if (cmpToMainTopic(topicOri, subjectMQTTtoRF2)) {
+ Log.trace(F("MQTTtoRF2 json" CR));
+ int boolSWITCHTYPE = RF2data["switchType"] | 99;
+ bool success = false;
+ if (boolSWITCHTYPE != 99) {
+# ifdef ZradioCC1101
+ NewRemoteReceiver::disable();
+ ELECHOUSE_cc1101.SetTx(CC1101_FREQUENCY);
+# endif
+ Log.trace(F("MQTTtoRF2 switch type ok" CR));
+ bool isDimCommand = boolSWITCHTYPE == 2;
+ unsigned long valueCODE = RF2data["address"];
+ int valueUNIT = RF2data["unit"] | -1;
+ int valuePERIOD = RF2data["period"];
+ int valueGROUP = RF2data["group"];
+ int valueDIM = RF2data["dim"] | -1;
+ if ((valueCODE != 0) || (valueUNIT != -1) || (valuePERIOD != 0)) {
+ Log.trace(F("MQTTtoRF2" CR));
+ if (valueCODE == 0)
+ valueCODE = 8233378;
+ if (valueUNIT == -1)
+ valueUNIT = 0;
+ if (valuePERIOD == 0)
+ valuePERIOD = 272;
+ NewRemoteReceiver::disable();
+ NewRemoteTransmitter transmitter(valueCODE, RF_EMITTER_GPIO, valuePERIOD, RF2_EMITTER_REPEAT);
+ Log.trace(F("Sending" CR));
+ if (valueGROUP) {
+ if (isDimCommand) {
+ transmitter.sendGroupDim(valueDIM);
+ } else {
+ transmitter.sendGroup(boolSWITCHTYPE);
+ }
+ } else {
+ if (isDimCommand) {
+ transmitter.sendDim(valueUNIT, valueDIM);
+ } else {
+ transmitter.sendUnit(valueUNIT, boolSWITCHTYPE);
+ }
+ }
+ Log.notice(F("MQTTtoRF2 OK" CR));
+ NewRemoteReceiver::enable();
+
+ success = true;
+ }
+ }
+ if (RF2data.containsKey("active")) {
+ Log.trace(F("RF2 active:" CR));
+ activeReceiver = ACTIVE_RF2;
+ success = true;
+ }
+# ifdef ZradioCC1101
+ float tempMhz = RF2data["mhz"];
+ if (RF2data.containsKey("mhz") && validFrequency(tempMhz)) {
+ receiveMhz = tempMhz;
+ Log.notice(F("Receive mhz: %F" CR), receiveMhz);
+ success = true;
+ }
+# endif
+ if (success) {
+ pub(subjectGTWRF2toMQTT, RF2data);
+ } else {
+# ifndef ARDUINO_AVR_UNO
+ pub(subjectGTWRF2toMQTT, "{\"Status\": \"Error\"}");
+# endif
+ Log.error(F("MQTTtoRF2 failed json read" CR));
+ }
+ enableActiveReceiver(false);
+ }
+}
+# endif
+
+void disableRF2Receive() {
+ Log.trace(F("disableRF2Receive" CR));
+ NewRemoteReceiver::deinit();
+ NewRemoteReceiver::init(-1, 2, rf2Callback);
+ NewRemoteReceiver::deinit();
+}
+
+void enableRF2Receive() {
+# ifdef ZradioCC1101
+ Log.notice(F("Switching to RF2 Receiver: %F" CR), receiveMhz);
+# else
+ Log.notice(F("Switching to RF2 Receiver" CR));
+# endif
+# ifdef ZgatewayPilight
+ disablePilightReceive();
+# endif
+# ifdef ZgatewayRTL_433
+ disableRTLreceive();
+# endif
+# ifdef ZgatewayRF
+ disableRFReceive();
+# endif
+
+ NewRemoteReceiver::init(RF_RECEIVER_GPIO, 2, rf2Callback);
+# ifdef ZradioCC1101
+ ELECHOUSE_cc1101.SetRx(receiveMhz);
+# endif
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayRFM69.ino"
+# 31 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayRFM69.ino"
+#include "User_config.h"
+
+#ifdef ZgatewayRFM69
+
+# include
+# include
+
+char RadioConfig[128];
+
+
+
+struct _GLOBAL_CONFIG {
+ uint32_t checksum;
+ char rfmapname[32];
+ char encryptkey[16 + 1];
+ uint8_t networkid;
+ uint8_t nodeid;
+ uint8_t powerlevel;
+ uint8_t rfmfrequency;
+};
+
+#define GC_POWER_LEVEL (pGC->powerlevel & 0x1F)
+#define GC_IS_RFM69HCW ((pGC->powerlevel & 0x80) != 0)
+#define SELECTED_FREQ(f) ((pGC->rfmfrequency == f) ? "selected" : "")
+
+struct _GLOBAL_CONFIG* pGC;
+
+
+uint32_t gc_checksum() {
+ uint8_t* p = (uint8_t*)pGC;
+ uint32_t checksum = 0;
+ p += sizeof(pGC->checksum);
+ for (size_t i = 0; i < (sizeof(*pGC) - 4); i++) {
+ checksum += *p++;
+ }
+ return checksum;
+}
+
+# if defined(ESP8266) || defined(ESP32)
+void eeprom_setup() {
+ EEPROM.begin(4096);
+ pGC = (struct _GLOBAL_CONFIG*)EEPROM.getDataPtr();
+
+ if (gc_checksum() != pGC->checksum) {
+ Log.trace(F("Factory reset" CR));
+ memset(pGC, 0, sizeof(*pGC));
+ strcpy_P(pGC->encryptkey, ENCRYPTKEY);
+ strcpy_P(pGC->rfmapname, RFM69AP_NAME);
+ pGC->networkid = NETWORKID;
+ pGC->nodeid = NODEID;
+ pGC->powerlevel = ((IS_RFM69HCW) ? 0x80 : 0x00) | POWER_LEVEL;
+ pGC->rfmfrequency = FREQUENCY;
+ pGC->checksum = gc_checksum();
+ EEPROM.commit();
+ }
+}
+# endif
+
+RFM69 radio;
+
+void setupRFM69(void) {
+# if defined(ESP8266) || defined(ESP32)
+ eeprom_setup();
+# endif
+ int freq;
+ static const char PROGMEM JSONtemplate[] =
+ R"({"msgType":"config","freq":%d,"rfm69hcw":%d,"netid":%d,"power":%d})";
+ char payload[128];
+
+ radio = RFM69(RFM69_CS, RFM69_IRQ, GC_IS_RFM69HCW, RFM69_IRQN);
+
+
+ if (!radio.initialize(pGC->rfmfrequency, pGC->nodeid, pGC->networkid)) {
+ Log.error(F("ZgatewayRFM69 initialization failed" CR));
+ }
+
+ if (GC_IS_RFM69HCW) {
+ radio.setHighPower();
+ }
+ radio.setPowerLevel(GC_POWER_LEVEL);
+
+ if (pGC->encryptkey[0] != '\0')
+ radio.encrypt(pGC->encryptkey);
+
+ switch (pGC->rfmfrequency) {
+ case RF69_433MHZ:
+ freq = 433;
+ break;
+ case RF69_868MHZ:
+ freq = 868;
+ break;
+ case RF69_915MHZ:
+ freq = 915;
+ break;
+ case RF69_315MHZ:
+ freq = 315;
+ break;
+ default:
+ freq = -1;
+ break;
+ }
+ Log.notice(F("ZgatewayRFM69 Listening and transmitting at: %d" CR), freq);
+
+ size_t len = snprintf_P(RadioConfig, sizeof(RadioConfig), JSONtemplate,
+ freq, GC_IS_RFM69HCW, pGC->networkid, GC_POWER_LEVEL);
+ if (len >= sizeof(RadioConfig)) {
+ Log.trace(F("\n\n*** RFM69 config truncated ***\n" CR));
+ }
+}
+
+bool RFM69toMQTT(void) {
+
+ if (radio.receiveDone()) {
+ Log.trace(F("Creating RFM69 buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject RFM69data = jsonBuffer.to();
+ uint8_t data[RF69_MAX_DATA_LEN + 1];
+ uint8_t SENDERID = radio.SENDERID;
+ uint8_t DATALEN = radio.DATALEN;
+ uint16_t RSSI = radio.RSSI;
+
+
+ memcpy(data, (void*)radio.DATA, DATALEN);
+ data[DATALEN] = '\0';
+
+
+
+ if (radio.ACKRequested()) {
+ radio.sendACK();
+ }
+
+
+ Log.trace(F("Data received: %s" CR), (const char*)data);
+
+ char buff[sizeof(subjectRFM69toMQTT) + 4];
+ sprintf(buff, "%s/%d", subjectRFM69toMQTT, SENDERID);
+ RFM69data["data"] = (char*)data;
+ RFM69data["rssi"] = (int)radio.RSSI;
+ RFM69data["senderid"] = (int)radio.SENDERID;
+ pub(buff, RFM69data);
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+# if simpleReceiving
+void MQTTtoRFM69(char* topicOri, char* datacallback) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoRFM69)) {
+ Log.trace(F("MQTTtoRFM69 data analysis" CR));
+ char data[RF69_MAX_DATA_LEN + 1];
+ memcpy(data, (void*)datacallback, RF69_MAX_DATA_LEN);
+ data[RF69_MAX_DATA_LEN] = '\0';
+
+
+ String topic = topicOri;
+ int valueRCV = defaultRFM69ReceiverId;
+ int pos = topic.lastIndexOf(RFM69receiverKey);
+ if (pos != -1) {
+ pos = pos + +strlen(RFM69receiverKey);
+ valueRCV = (topic.substring(pos, pos + 3)).toInt();
+ Log.notice(F("RFM69 receiver ID: %d" CR), valueRCV);
+ }
+ if (radio.sendWithRetry(valueRCV, data, strlen(data), 10)) {
+ Log.notice(F(" OK " CR));
+
+ char buff[sizeof(subjectGTWRFM69toMQTT) + 4];
+ sprintf(buff, "%s/%d", subjectGTWRFM69toMQTT, radio.SENDERID);
+ pub(buff, data);
+ } else {
+ Log.error(F("RFM69 sending failed" CR));
+ }
+ }
+}
+# endif
+# if jsonReceiving
+void MQTTtoRFM69(char* topicOri, JsonObject& RFM69data) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoRFM69)) {
+ const char* data = RFM69data["data"];
+ Log.trace(F("MQTTtoRFM69 json data analysis" CR));
+ if (data) {
+ Log.trace(F("MQTTtoRFM69 data ok" CR));
+ int valueRCV = RFM69data["receiverid"] | defaultRFM69ReceiverId;
+ Log.notice(F("RFM69 receiver ID: %d" CR), valueRCV);
+ if (radio.sendWithRetry(valueRCV, data, strlen(data), 10)) {
+ Log.notice(F(" OK " CR));
+
+ pub(subjectGTWRFM69toMQTT, RFM69data);
+ } else {
+ Log.error(F("MQTTtoRFM69 sending failed" CR));
+ }
+ } else {
+ Log.error(F("MQTTtoRFM69 failed json read" CR));
+ }
+ }
+}
+# endif
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayRS232.ino"
+# 28 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayRS232.ino"
+#include "User_config.h"
+
+#ifdef ZgatewayRS232
+
+# include
+
+SoftwareSerial RS232Serial(RS232_RX_GPIO, RS232_TX_GPIO);
+
+void setupRS232() {
+
+ pinMode(RS232_RX_GPIO, INPUT);
+ pinMode(RS232_TX_GPIO, OUTPUT);
+
+ RS232Serial.begin(RS232Baud);
+
+ Log.notice(F("RS232_RX_GPIO: %d " CR), RS232_RX_GPIO);
+ Log.notice(F("RS232_TX_GPIO: %d " CR), RS232_TX_GPIO);
+ Log.notice(F("RS232Baud: %d " CR), RS232Baud);
+ Log.trace(F("ZgatewayRS232 setup done " CR));
+}
+
+void RS232toMQTT() {
+
+ if (RS232Serial.available()) {
+ Log.trace(F("RS232toMQTT" CR));
+ static char RS232data[MAX_INPUT];
+ static unsigned int input_pos = 0;
+ static char inChar;
+ do {
+ if (RS232Serial.available()) {
+ inChar = RS232Serial.read();
+ RS232data[input_pos] = inChar;
+ input_pos++;
+ Log.trace(F("Received %c" CR), inChar);
+ }
+ } while (inChar != RS232InPost && input_pos < MAX_INPUT);
+ RS232data[input_pos] = 0;
+ input_pos = 0;
+ Log.trace(F("Publish %s" CR), RS232data);
+ char* output = RS232data + sizeof(RS232Pre) - 1;
+ pub(subjectRS232toMQTT, output);
+ }
+}
+
+void MQTTtoRS232(char* topicOri, JsonObject& RS232data) {
+ Log.trace(F("json" CR));
+ if (cmpToMainTopic(topicOri, subjectMQTTtoRS232)) {
+ Log.trace(F("MQTTtoRS232 json" CR));
+ const char* data = RS232data["value"];
+ const char* prefix = RS232data["prefix"] | RS232Pre;
+ const char* postfix = RS232data["postfix"] | RS232Post;
+ Log.trace(F("Value set: %s" CR), data);
+ Log.trace(F("Prefix set: %s" CR), prefix);
+ Log.trace(F("Postfix set: %s" CR), postfix);
+ RS232Serial.print(prefix);
+ RS232Serial.print(data);
+ RS232Serial.print(postfix);
+ }
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayRTL_433.ino"
+# 29 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayRTL_433.ino"
+#include "User_config.h"
+#ifdef ZgatewayRTL_433
+# ifndef ZradioCC1101
+# error "CC1101 is the only supported receiver module for RTL_433 and needs to be enabled."
+# endif
+
+# include
+
+char messageBuffer[JSON_MSG_BUFFER];
+
+rtl_433_ESP rtl_433(-1);
+
+# include
+
+void rtl_433_Callback(char* message) {
+ DynamicJsonDocument jsonBuffer2(JSON_MSG_BUFFER);
+ JsonObject RFrtl_433_ESPdata = jsonBuffer2.to();
+ auto error = deserializeJson(jsonBuffer2, message);
+ if (error) {
+ Log.error(F("deserializeJson() failed: %s" CR), error.c_str());
+ return;
+ }
+
+ String topic = String(subjectRTL_433toMQTT);
+# if valueAsATopic
+ String model = RFrtl_433_ESPdata["model"];
+ String id = RFrtl_433_ESPdata["id"];
+ if (model != 0) {
+ topic = topic + "/" + model;
+ if (id != 0) {
+ topic = topic + "/" + id;
+ }
+ }
+# endif
+
+ pub((char*)topic.c_str(), RFrtl_433_ESPdata);
+# ifdef MEMORY_DEBUG
+ Log.trace(F("Post rtl_433_Callback: %d" CR), ESP.getFreeHeap());
+# endif
+}
+
+void setupRTL_433() {
+ rtl_433.initReceiver(RF_RECEIVER_GPIO, receiveMhz);
+ rtl_433.setCallback(rtl_433_Callback, messageBuffer, JSON_MSG_BUFFER);
+ Log.trace(F("ZgatewayRTL_433 command topic: %s%s%s" CR), mqtt_topic, gateway_name, subjectMQTTtoRTL_433);
+ Log.notice(F("ZgatewayRTL_433 setup done " CR));
+}
+
+void RTL_433Loop() {
+ rtl_433.loop();
+}
+
+extern void MQTTtoRTL_433(char* topicOri, JsonObject& RTLdata) {
+ if (cmpToMainTopic(topicOri, subjectMQTTtoRTL_433)) {
+ float tempMhz = RTLdata["mhz"];
+ bool success = false;
+ if (RTLdata.containsKey("mhz") && validFrequency(tempMhz)) {
+ receiveMhz = tempMhz;
+ Log.notice(F("RTL_433 Receive mhz: %F" CR), receiveMhz);
+ success = true;
+ }
+ if (RTLdata.containsKey("active")) {
+ Log.trace(F("RTL_433 active:" CR));
+ activeReceiver = ACTIVE_RTL;
+ success = true;
+ }
+ if (RTLdata.containsKey("rssi")) {
+ int minimumRssi = RTLdata["rssi"] | 0;
+ Log.notice(F("RTL_433 minimum RSSI: %d" CR), minimumRssi);
+ rtl_433.setMinimumRSSI(minimumRssi);
+ success = true;
+ }
+ if (RTLdata.containsKey("debug")) {
+ int debug = RTLdata["debug"] | -1;
+ Log.notice(F("RTL_433 set debug: %d" CR), debug);
+ rtl_433.setDebug(debug);
+ rtl_433.initReceiver(RF_RECEIVER_GPIO, receiveMhz);
+ success = true;
+ }
+ if (RTLdata.containsKey("status")) {
+ Log.notice(F("RTL_433 get status:" CR));
+ rtl_433.getStatus(1);
+ success = true;
+ }
+ if (success) {
+ pub(subjectRTL_433toMQTT, RTLdata);
+ } else {
+ pub(subjectRTL_433toMQTT, "{\"Status\": \"Error\"}");
+ Log.error(F("MQTTtoRTL_433 Fail json" CR));
+ }
+ enableActiveReceiver(false);
+ }
+}
+
+extern void enableRTLreceive() {
+ Log.notice(F("Switching to RTL_433 Receiver: %FMhz" CR), receiveMhz);
+# ifdef ZgatewayRF
+ disableRFReceive();
+# endif
+# ifdef ZgatewayRF2
+ disableRF2Receive();
+# endif
+# ifdef ZgatewayPilight
+ disablePilightReceive();
+# endif
+ ELECHOUSE_cc1101.SetRx(receiveMhz);
+ rtl_433.enableReceiver(RF_RECEIVER_GPIO);
+ pinMode(RF_EMITTER_GPIO, OUTPUT);
+}
+
+extern void disableRTLreceive() {
+ Log.trace(F("disableRTLreceive" CR));
+ rtl_433.enableReceiver(-1);
+ rtl_433.disableReceiver();
+}
+
+extern int getRTLMinimumRSSI() {
+ return rtl_433.minimumRssi;
+}
+
+extern int getRTLCurrentRSSI() {
+ return rtl_433.currentRssi;
+}
+
+extern int getRTLMessageCount() {
+ return rtl_433.messageCount;
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewaySRFB.ino"
+# 31 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewaySRFB.ino"
+#include "User_config.h"
+
+#ifdef ZgatewaySRFB
+
+unsigned char _uartbuf[RF_MESSAGE_SIZE + 3] = {0};
+unsigned char _uartpos = 0;
+
+void setupSRFB() {
+ Log.trace(F("ZgatewaySRFB setup done " CR));
+ Log.trace(F("Serial Baud: %l" CR), SERIAL_BAUD);
+}
+
+void _rfbSend(byte* message) {
+ Serial.println();
+ Serial.write(RF_CODE_START);
+ Serial.write(RF_CODE_RFOUT);
+ for (unsigned char j = 0; j < RF_MESSAGE_SIZE; j++) {
+ Serial.write(message[j]);
+ }
+ Serial.write(RF_CODE_STOP);
+ Serial.flush();
+ Serial.println();
+}
+
+void _rfbSend(byte* message, int times) {
+ char buffer[RF_MESSAGE_SIZE];
+ _rfbToChar(message, buffer);
+ Log.notice(F("[RFBRIDGE] Sending MESSAGE" CR));
+
+ for (int i = 0; i < times; i++) {
+ if (i > 0) {
+ unsigned long start = millis();
+ while (millis() - start < RF_SEND_DELAY)
+ delay(1);
+ }
+ _rfbSend(message);
+ }
+}
+
+bool SRFBtoMQTT() {
+ static bool receiving = false;
+
+ while (Serial.available()) {
+ yield();
+ byte c = Serial.read();
+
+ if (receiving) {
+ if (c == RF_CODE_STOP) {
+ _rfbDecode();
+ receiving = false;
+ } else {
+ _uartbuf[_uartpos++] = c;
+ }
+ } else if (c == RF_CODE_START) {
+ _uartpos = 0;
+ receiving = true;
+ }
+ }
+ return receiving;
+}
+
+void _rfbDecode() {
+ static unsigned long last = 0;
+ if (millis() - last < RF_RECEIVE_DELAY)
+ return;
+ last = millis();
+
+ byte action = _uartbuf[0];
+ char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
+
+ if (action == RF_CODE_RFIN) {
+ _rfbToChar(&_uartbuf[1], buffer);
+
+ Log.trace(F("Creating SRFB buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject SRFBdata = jsonBuffer.to();
+ SRFBdata["raw"] = String(buffer).substring(0, 18);
+
+ int val_Tsyn = (int)(int)value_from_hex_data(buffer, 0, 4, false, false);
+ SRFBdata["delay"] = (int)val_Tsyn;
+
+ int val_Tlow = (int)value_from_hex_data(buffer, 4, 4, false, false);
+ SRFBdata["val_Tlow"] = (int)val_Tlow;
+
+ int val_Thigh = (int)value_from_hex_data(buffer, 8, 4, false, false);
+ SRFBdata["val_Thigh"] = (int)val_Thigh;
+
+ unsigned long MQTTvalue = (unsigned long)value_from_hex_data(buffer, 12, 8, false, false);
+ SRFBdata["value"] = (unsigned long)MQTTvalue;
+
+ if (!isAduplicateSignal(MQTTvalue) && MQTTvalue != 0) {
+ Log.trace(F("Adv data SRFBtoMQTT" CR));
+ pub(subjectSRFBtoMQTT, SRFBdata);
+ Log.trace(F("Store val: %lu" CR), MQTTvalue);
+ storeSignalValue(MQTTvalue);
+ if (repeatSRFBwMQTT) {
+ Log.trace(F("Publish SRFB for rpt" CR));
+ pub(subjectMQTTtoSRFB, SRFBdata);
+ }
+ }
+ _rfbAck();
+ }
+}
+
+void _rfbAck() {
+ Log.trace(F("[RFBRIDGE] Sending ACK\n" CR));
+ Serial.println();
+ Serial.write(RF_CODE_START);
+ Serial.write(RF_CODE_ACK);
+ Serial.write(RF_CODE_STOP);
+ Serial.flush();
+ Serial.println();
+}
+
+
+
+
+bool _rfbToArray(const char* in, byte* out) {
+ if (strlen(in) != RF_MESSAGE_SIZE * 2)
+ return false;
+ char tmp[3] = {0};
+ for (unsigned char p = 0; p < RF_MESSAGE_SIZE; p++) {
+ memcpy(tmp, &in[p * 2], 2);
+ out[p] = strtol(tmp, NULL, 16);
+ }
+ return true;
+}
+
+
+
+
+bool _rfbToChar(byte* in, char* out) {
+ for (unsigned char p = 0; p < RF_MESSAGE_SIZE; p++) {
+ sprintf_P(&out[p * 2], PSTR("%02X" CR), in[p]);
+ }
+ return true;
+}
+
+# if simpleReceiving
+void MQTTtoSRFB(char* topicOri, char* datacallback) {
+
+ String topic = topicOri;
+ int valueRPT = 0;
+
+ if (topic == subjectMQTTtoSRFB) {
+ int valueMiniPLSL = 0;
+ int valueMaxiPLSL = 0;
+ int valueSYNC = 0;
+
+ int pos = topic.lastIndexOf(SRFBRptKey);
+ if (pos != -1) {
+ pos = pos + +strlen(SRFBRptKey);
+ valueRPT = (topic.substring(pos, pos + 1)).toInt();
+ Log.notice(F("SRFB Repeat: %d" CR), valueRPT);
+ }
+
+ int pos2 = topic.lastIndexOf(SRFBminipulselengthKey);
+ if (pos2 != -1) {
+ pos2 = pos2 + strlen(SRFBminipulselengthKey);
+ valueMiniPLSL = (topic.substring(pos2, pos2 + 3)).toInt();
+ Log.notice(F("RF Mini Pulse Lgth: %d" CR), valueMiniPLSL);
+ }
+
+ int pos3 = topic.lastIndexOf(SRFBmaxipulselengthKey);
+ if (pos3 != -1) {
+ pos3 = pos3 + strlen(SRFBmaxipulselengthKey);
+ valueMaxiPLSL = (topic.substring(pos3, pos3 + 2)).toInt();
+ Log.notice(F("RF Maxi Pulse Lgth: %d" CR), valueMaxiPLSL);
+ }
+
+ int pos4 = topic.lastIndexOf(SRFBsyncKey);
+ if (pos4 != -1) {
+ pos4 = pos4 + strlen(SRFBsyncKey);
+ valueSYNC = (topic.substring(pos4, pos4 + 2)).toInt();
+ Log.notice(F("RF sync: %d" CR), valueSYNC);
+ }
+
+ Log.trace(F("MQTTtoSRFB prts" CR));
+ if (valueRPT == 0)
+ valueRPT = 1;
+ if (valueMiniPLSL == 0)
+ valueMiniPLSL = 320;
+ if (valueMaxiPLSL == 0)
+ valueMaxiPLSL = 900;
+ if (valueSYNC == 0)
+ valueSYNC = 9500;
+
+ byte hex_valueMiniPLSL[2];
+ hex_valueMiniPLSL[0] = (int)((valueMiniPLSL >> 8) & 0xFF);
+ hex_valueMiniPLSL[1] = (int)(valueMiniPLSL & 0xFF);
+
+ byte hex_valueMaxiPLSL[2];
+ hex_valueMaxiPLSL[0] = (int)((valueMaxiPLSL >> 8) & 0xFF);
+ hex_valueMaxiPLSL[1] = (int)(valueMaxiPLSL & 0xFF);
+
+ byte hex_valueSYNC[2];
+ hex_valueSYNC[0] = (int)((valueSYNC >> 8) & 0xFF);
+ hex_valueSYNC[1] = (int)(valueSYNC & 0xFF);
+
+ unsigned long data = strtoul(datacallback, NULL, 10);
+ byte hex_data[3];
+ hex_data[0] = (unsigned long)((data >> 16) & 0xFF);
+ hex_data[1] = (unsigned long)((data >> 8) & 0xFF);
+ hex_data[2] = (unsigned long)(data & 0xFF);
+
+ byte message_b[RF_MESSAGE_SIZE];
+
+ memcpy(message_b, hex_valueSYNC, 2);
+ memcpy(message_b + 2, hex_valueMiniPLSL, 2);
+ memcpy(message_b + 4, hex_valueMaxiPLSL, 2);
+ memcpy(message_b + 6, hex_data, 3);
+
+ _rfbSend(message_b, valueRPT);
+
+ pub(subjectGTWSRFBtoMQTT, datacallback);
+ }
+ if (topic == subjectMQTTtoSRFBRaw) {
+ int pos = topic.lastIndexOf(SRFBRptKey);
+ if (pos != -1) {
+ pos = pos + +strlen(SRFBRptKey);
+ valueRPT = (topic.substring(pos, pos + 1)).toInt();
+ Log.notice(F("SRFB Repeat: %d" CR), valueRPT);
+ }
+ if (valueRPT == 0)
+ valueRPT = 1;
+
+ byte message_b[RF_MESSAGE_SIZE];
+ _rfbToArray(datacallback, message_b);
+ _rfbSend(message_b, valueRPT);
+
+ pub(subjectGTWSRFBtoMQTT, datacallback);
+ }
+}
+# endif
+# if jsonReceiving
+void MQTTtoSRFB(char* topicOri, JsonObject& SRFBdata) {
+
+ const char* raw = SRFBdata["raw"];
+ int valueRPT = SRFBdata["repeat"] | 1;
+ if (cmpToMainTopic(topicOri, subjectMQTTtoSRFB)) {
+ Log.trace(F("MQTTtoSRFB json" CR));
+ if (raw) {
+ Log.trace(F("MQTTtoSRFB raw ok" CR));
+ byte message_b[RF_MESSAGE_SIZE];
+ _rfbToArray(raw, message_b);
+ _rfbSend(message_b, valueRPT);
+ } else {
+ unsigned long data = SRFBdata["value"];
+ if (data != 0) {
+ Log.notice(F("MQTTtoSRFB data ok" CR));
+ int valueMiniPLSL = SRFBdata["val_Tlow"];
+ int valueMaxiPLSL = SRFBdata["val_Thigh"];
+ int valueSYNC = SRFBdata["delay"];
+
+ if (valueRPT == 0)
+ valueRPT = 1;
+ if (valueMiniPLSL == 0)
+ valueMiniPLSL = 320;
+ if (valueMaxiPLSL == 0)
+ valueMaxiPLSL = 900;
+ if (valueSYNC == 0)
+ valueSYNC = 9500;
+
+ Log.notice(F("SRFB Repeat: %d" CR), valueRPT);
+ Log.notice(F("RF Mini Pulse Lgth: %d" CR), valueMiniPLSL);
+ Log.notice(F("RF Maxi Pulse Lgth: %d" CR), valueMaxiPLSL);
+ Log.notice(F("RF sync: %d" CR), valueSYNC);
+
+ byte hex_valueMiniPLSL[2];
+ hex_valueMiniPLSL[0] = (int)((valueMiniPLSL >> 8) & 0xFF);
+ hex_valueMiniPLSL[1] = (int)(valueMiniPLSL & 0xFF);
+
+ byte hex_valueMaxiPLSL[2];
+ hex_valueMaxiPLSL[0] = (int)((valueMaxiPLSL >> 8) & 0xFF);
+ hex_valueMaxiPLSL[1] = (int)(valueMaxiPLSL & 0xFF);
+
+ byte hex_valueSYNC[2];
+ hex_valueSYNC[0] = (int)((valueSYNC >> 8) & 0xFF);
+ hex_valueSYNC[1] = (int)(valueSYNC & 0xFF);
+
+ byte hex_data[3];
+ hex_data[0] = (unsigned long)((data >> 16) & 0xFF);
+ hex_data[1] = (unsigned long)((data >> 8) & 0xFF);
+ hex_data[2] = (unsigned long)(data & 0xFF);
+
+ byte message_b[RF_MESSAGE_SIZE];
+
+ memcpy(message_b, hex_valueSYNC, 2);
+ memcpy(message_b + 2, hex_valueMiniPLSL, 2);
+ memcpy(message_b + 4, hex_valueMaxiPLSL, 2);
+ memcpy(message_b + 6, hex_data, 3);
+
+ Log.notice(F("MQTTtoSRFB OK" CR));
+ _rfbSend(message_b, valueRPT);
+
+ pub(subjectGTWSRFBtoMQTT, SRFBdata);
+ } else {
+ Log.error(F("MQTTtoSRFB error decoding value" CR));
+ }
+ }
+ }
+}
+# endif
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayWeatherStation.ino"
+# 26 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZgatewayWeatherStation.ino"
+#include "User_config.h"
+
+#ifdef ZgatewayWeatherStation
+# include
+WeatherStationDataRx wsdr(RF_WS_RECEIVER_GPIO, true);
+
+void PairedDeviceAdded(byte newID) {
+# if defined(ESP8266) || defined(ESP32)
+ Serial.printf("ZgatewayWeatherStation: New device paired %d\r\n", newID);
+# else
+ Serial.print("ZgatewayWeatherStation: New device paired ");
+ Serial.println(newID, DEC);
+# endif
+ StaticJsonDocument jsonBuffer;
+ JsonObject RFdata = jsonBuffer.to();
+ RFdata["sensor"] = newID;
+ RFdata["action"] = "paired";
+ pub(subjectRFtoMQTT, RFdata);
+ wsdr.pair(NULL, PairedDeviceAdded);
+}
+
+void setupWeatherStation() {
+ Log.notice(F("RF_WS_RECEIVER_GPIO %d" CR), RF_WS_RECEIVER_GPIO);
+ wsdr.begin();
+ wsdr.pair(NULL, PairedDeviceAdded);
+ Log.trace(F("ZgatewayWeatherStation setup done " CR));
+}
+
+void sendWindSpeedData(byte id, float wind_speed, byte battery_status) {
+ unsigned long MQTTvalue = 10000 + round(wind_speed);
+ if (!isAduplicateSignal(MQTTvalue)) {
+ StaticJsonDocument jsonBuffer;
+ JsonObject RFdata = jsonBuffer.to();
+ RFdata["sensor"] = id;
+ RFdata["wind_speed"] = wind_speed;
+ RFdata["battery"] = bitRead(battery_status, 0) == 0 ? "OK" : "Low";
+ pub(subjectRFtoMQTT, RFdata);
+ Log.trace(F("Store wind speed val: %lu" CR), MQTTvalue);
+ storeSignalValue(MQTTvalue);
+ }
+}
+
+void sendRainData(byte id, float rain_volume, byte battery_status) {
+ unsigned long MQTTvalue = 11000 + round(rain_volume * 10.0);
+ if (!isAduplicateSignal(MQTTvalue)) {
+ StaticJsonDocument jsonBuffer;
+ JsonObject RFdata = jsonBuffer.to();
+ RFdata["sensor"] = id;
+ RFdata["rain_volume"] = rain_volume;
+ RFdata["battery"] = bitRead(battery_status, 1) == 0 ? "OK" : "Low";
+ pub(subjectRFtoMQTT, RFdata);
+ Log.trace(F("Store rain_volume: %lu" CR), MQTTvalue);
+ storeSignalValue(MQTTvalue);
+ }
+}
+
+void sendWindData(byte id, int wind_direction, float wind_gust, byte battery_status) {
+ unsigned long MQTTvalue = 20000 + round(wind_gust * 10.0) + wind_direction;
+ if (!isAduplicateSignal(MQTTvalue)) {
+ StaticJsonDocument jsonBuffer;
+ JsonObject RFdata = jsonBuffer.to();
+ RFdata["sensor"] = id;
+ RFdata["wind_direction"] = wind_direction;
+ RFdata["wind_gust"] = wind_gust;
+ RFdata["battery"] = bitRead(battery_status, 0) == 0 ? "OK" : "Low";
+ pub(subjectRFtoMQTT, RFdata);
+ Log.trace(F("Store wind data val: %lu" CR), MQTTvalue);
+ storeSignalValue(MQTTvalue);
+ }
+}
+
+void sendTemperatureData(byte id, float temperature, int humidity, byte battery_status) {
+ unsigned long MQTTvalue = 40000 + abs(round(temperature * 100.0)) + humidity;
+ if (!isAduplicateSignal(MQTTvalue)) {
+ StaticJsonDocument jsonBuffer;
+ JsonObject RFdata = jsonBuffer.to();
+ RFdata["sensor"] = id;
+ RFdata["tempc"] = temperature;
+ RFdata["tempf"] = wsdr.convertCtoF(temperature);
+ RFdata["humidity"] = humidity;
+ RFdata["battery"] = bitRead(battery_status, 0) == 0 ? "OK" : "Low";
+ pub(subjectRFtoMQTT, RFdata);
+ Log.trace(F("Store temp val: %lu" CR), MQTTvalue);
+ storeSignalValue(MQTTvalue);
+ }
+}
+
+void ZgatewayWeatherStationtoMQTT() {
+ char newData = wsdr.readData();
+ switch (newData) {
+ case 'T':
+ Log.trace(F("Temperature" CR));
+ sendTemperatureData(wsdr.sensorID(), wsdr.readTemperature(), wsdr.readHumidity(), wsdr.batteryStatus());
+ break;
+
+ case 'S':
+ Log.trace(F("Wind speed" CR));
+ sendWindSpeedData(wsdr.sensorID(), wsdr.readWindSpeed(), wsdr.batteryStatus());
+ break;
+
+ case 'G':
+ Log.trace(F("Wind direction" CR));
+ sendWindData(wsdr.sensorID(), wsdr.readWindDirection(), wsdr.readWindGust(), wsdr.batteryStatus());
+ break;
+
+ case 'R':
+ Log.trace(F("Rain volume" CR));
+ sendRainData(wsdr.sensorID(), wsdr.readRainVolume(), wsdr.batteryStatus());
+ break;
+
+ default:
+ break;
+ }
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZmqttDiscovery.ino"
+# 26 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZmqttDiscovery.ino"
+#include "User_config.h"
+
+#ifdef ZmqttDiscovery
+
+String getMacAddress() {
+ uint8_t baseMac[6];
+ char baseMacChr[13] = {0};
+# if defined(ESP8266)
+ WiFi.macAddress(baseMac);
+ sprintf(baseMacChr, "%02X%02X%02X%02X%02X%02X", baseMac[0], baseMac[1], baseMac[2], baseMac[3], baseMac[4], baseMac[5]);
+# elif defined(ESP32)
+ esp_read_mac(baseMac, ESP_MAC_WIFI_STA);
+ sprintf(baseMacChr, "%02X%02X%02X%02X%02X%02X", baseMac[0], baseMac[1], baseMac[2], baseMac[3], baseMac[4], baseMac[5]);
+# else
+ sprintf(baseMacChr, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+# endif
+ return String(baseMacChr);
+}
+
+String getUniqueId(String name, String sufix) {
+ String uniqueId = (String)getMacAddress() + "-" + name + sufix;
+ return String(uniqueId);
+}
+
+# ifdef ZgatewayBT
+# 69 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZmqttDiscovery.ino"
+void createDiscoveryFromList(const char* mac,
+ const char* sensorList[][9],
+ int sensorCount,
+ const char* device_name,
+ const char* device_manufacturer,
+ const char* device_model) {
+ for (int i = 0; i < sensorCount; i++) {
+ String discovery_topic = String(subjectBTtoMQTT) + "/" + String(mac);
+ String unique_id = String(mac) + "-" + sensorList[i][1];
+
+ createDiscovery(sensorList[i][0],
+ discovery_topic.c_str(), sensorList[i][1], unique_id.c_str(),
+ will_Topic, sensorList[i][3], sensorList[i][4],
+ sensorList[i][5], sensorList[i][6], sensorList[i][7],
+ 0, "", "", false, "",
+ device_name, device_manufacturer, device_model, mac, false,
+ sensorList[i][8]
+ );
+ }
+}
+# endif
+# 103 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZmqttDiscovery.ino"
+void announceDeviceTrigger(bool use_gateway_info, char* topic, char* type, char* subtype, char* unique_id, char* device_name, char* device_manufacturer, char* device_model, char* device_mac) {
+
+ StaticJsonDocument jsonBuffer;
+ JsonObject sensor = jsonBuffer.to();
+
+
+ sensor["automation_type"] = "trigger";
+
+
+ if (type[0] != 0) {
+ sensor["type"] = type;
+ } else {
+ sensor["type"] = "button_short_press";
+ }
+
+
+ if (subtype[0] != 0) {
+ sensor["subtype"] = subtype;
+ } else {
+ sensor["subtype"] = "turn_on";
+ }
+
+
+ if (topic[0]) {
+ char state_topic[mqtt_topic_max_size];
+
+ strcpy(state_topic, mqtt_topic);
+ strcat(state_topic, gateway_name);
+
+ strcat(state_topic, topic);
+ sensor["topic"] = state_topic;
+ }
+
+
+ StaticJsonDocument jsonDeviceBuffer;
+ JsonObject device = jsonDeviceBuffer.to();
+ JsonArray identifiers = device.createNestedArray("identifiers");
+
+ if (use_gateway_info) {
+ char JSONmessageBuffer[JSON_MSG_BUFFER];
+ serializeJson(modules, JSONmessageBuffer, sizeof(JSONmessageBuffer));
+
+ device["name"] = gateway_name;
+ device["model"] = JSONmessageBuffer;
+ device["manufacturer"] = DEVICEMANUFACTURER;
+ device["sw_version"] = OMG_VERSION;
+ identifiers.add(getMacAddress());
+
+ } else {
+ char deviceid[13];
+ memcpy(deviceid, &unique_id[0], 12);
+ deviceid[12] = '\0';
+
+ identifiers.add(deviceid);
+
+
+ if (device_mac[0] != 0) {
+ JsonArray connections = device.createNestedArray("connections");
+ JsonArray connection_mac = connections.createNestedArray();
+ connection_mac.add("mac");
+ connection_mac.add(device_mac);
+ }
+
+
+ if (device_manufacturer[0]) {
+ device["manufacturer"] = device_manufacturer;
+ }
+
+
+ if (device_name[0]) {
+ device["name"] = device_name;
+ }
+
+
+ if (device_model[0]) {
+ device["model"] = device_model;
+ }
+
+ device["via_device"] = gateway_name;
+ }
+ sensor["device"] = device;
+
+
+ String topic_to_publish = String(discovery_Topic) + "/device_automation/" + String(unique_id) + "/config";
+ Log.trace(F("Announce Device Trigger %s" CR), topic_to_publish.c_str());
+ pub_custom_topic((char*)topic_to_publish.c_str(), sensor, true);
+}
+# 217 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZmqttDiscovery.ino"
+void createDiscovery(const char* sensor_type,
+ const char* st_topic, const char* s_name, const char* unique_id,
+ const char* availability_topic, const char* device_class, const char* value_template,
+ const char* payload_on, const char* payload_off, const char* unit_of_meas,
+ int off_delay,
+ const char* payload_available, const char* payload_not_avalaible, bool gateway_entity, const char* cmd_topic,
+ const char* device_name, const char* device_manufacturer, const char* device_model, const char* device_mac, bool retainCmd,
+ const char* state_class, const char* state_off, const char* state_on) {
+ StaticJsonDocument jsonBuffer;
+ JsonObject sensor = jsonBuffer.to();
+
+
+
+
+
+
+
+ if (st_topic[0]) {
+ char state_topic[mqtt_topic_max_size];
+
+
+ if (gateway_entity) {
+ strcpy(state_topic, mqtt_topic);
+ strcat(state_topic, gateway_name);
+ } else {
+ strcpy(state_topic, "+/+");
+ }
+ strcat(state_topic, st_topic);
+ sensor["stat_t"] = state_topic;
+ }
+
+ if (device_class[0]) {
+
+ int num_classes = sizeof(availableHASSClasses) / sizeof(availableHASSClasses[0]);
+ for (int i = 0; i < num_classes; i++) {
+ if (strcmp(availableHASSClasses[i], device_class) == 0) {
+ sensor["dev_cla"] = device_class;
+ }
+ }
+ }
+
+ if (unit_of_meas[0]) {
+
+ int num_units = sizeof(availableHASSUnits) / sizeof(availableHASSUnits[0]);
+ for (int i = 0; i < num_units; i++) {
+ if (strcmp(availableHASSUnits[i], unit_of_meas) == 0) {
+ sensor["unit_of_meas"] = unit_of_meas;
+ }
+ }
+ }
+ sensor["name"] = s_name;
+ sensor["uniq_id"] = unique_id;
+ if (retainCmd)
+ sensor["retain"] = retainCmd;
+ if (value_template[0])
+ sensor["val_tpl"] = value_template;
+ if (payload_on[0])
+ sensor["pl_on"] = payload_on;
+ if (payload_off[0])
+ sensor["pl_off"] = payload_off;
+ if (off_delay != 0)
+ sensor["off_delay"] = off_delay;
+ if (payload_available[0])
+ sensor["pl_avail"] = payload_available;
+ if (payload_not_avalaible[0])
+ sensor["pl_not_avail"] = payload_not_avalaible;
+ if (state_class[0])
+ sensor["state_class"] = state_class;
+ if (state_on != nullptr)
+ sensor["stat_on"] = state_on;
+ if (state_off != nullptr)
+ sensor["stat_off"] = state_off;
+
+ if (cmd_topic[0]) {
+ char command_topic[mqtt_topic_max_size];
+ strcpy(command_topic, mqtt_topic);
+ strcat(command_topic, gateway_name);
+ strcat(command_topic, cmd_topic);
+ sensor["cmd_t"] = command_topic;
+ }
+
+ StaticJsonDocument jsonDeviceBuffer;
+ JsonObject device = jsonDeviceBuffer.to();
+ JsonArray identifiers = device.createNestedArray("identifiers");
+
+ if (gateway_entity) {
+
+ String model = "";
+ serializeJson(modules, model);
+ device["name"] = String(gateway_name);
+ device["model"] = model;
+ device["manufacturer"] = DEVICEMANUFACTURER;
+ device["sw_version"] = OMG_VERSION;
+ identifiers.add(String(getMacAddress()));
+ } else {
+
+
+ char deviceid[13];
+ memcpy(deviceid, &unique_id[0], 12);
+ deviceid[12] = '\0';
+ identifiers.add(deviceid);
+
+
+ if (device_mac[0] != 0) {
+ JsonArray connections = device.createNestedArray("connections");
+ JsonArray connection_mac = connections.createNestedArray();
+ connection_mac.add("mac");
+ connection_mac.add(device_mac);
+ }
+
+ if (device_manufacturer[0]) {
+ device["manufacturer"] = device_manufacturer;
+ }
+
+ if (device_model[0]) {
+ device["model"] = device_model;
+ }
+
+ if (device_name[0]) {
+
+ device["name"] = device_name + String("-") + String(device_mac + 6);
+ }
+
+ device["via_device"] = String(gateway_name);
+ }
+
+ sensor["device"] = device;
+
+ String topic = String(discovery_Topic) + "/" + String(sensor_type) + "/" + String(unique_id) + "/config";
+ Log.trace(F("Announce Device %s on %s" CR), String(sensor_type).c_str(), topic.c_str());
+ pub_custom_topic((char*)topic.c_str(), sensor, true);
+}
+
+void pubMqttDiscovery() {
+ Log.trace(F("omgStatusDiscovery" CR));
+ createDiscovery("binary_sensor",
+ will_Topic, "SYS: Connectivity", (char*)getUniqueId("connectivity", "").c_str(),
+ will_Topic, "connectivity", "",
+ Gateway_AnnouncementMsg, will_Message, "",
+ 0,
+ Gateway_AnnouncementMsg, will_Message, true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "SYS: Uptime", (char*)getUniqueId("uptime", "").c_str(),
+ "", "", "{{ value_json.uptime }}",
+ "", "", "s",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+
+# if defined(ESP8266) || defined(ESP32)
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "SYS: Free memory", (char*)getUniqueId("freemem", "").c_str(),
+ "", "", "{{ value_json.freemem }}",
+ "", "", "B",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "SYS: IP", (char*)getUniqueId("ip", "").c_str(),
+ "", "", "{{ value_json.ip }}",
+ "", "", "",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# ifndef ESP32_ETHERNET
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "SYS: Rssi", (char*)getUniqueId("rssi", "").c_str(),
+ "", "", "{{ value_json.rssi }}",
+ "", "", "dB",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+# endif
+# ifdef ESP32
+# ifdef ZgatewayBT
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "SYS: Low Power Mode", (char*)getUniqueId("lowpowermode", "").c_str(),
+ "", "", "{{ value_json.lowpowermode }}",
+ "", "", "",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+# if defined(ZboardM5STICKC) || defined(ZboardM5STICKCP) || defined(ZboardM5TOUGH)
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "SYS: Bat voltage", (char*)getUniqueId("m5batvoltage", "").c_str(),
+ "", "", "{{ value_json.m5batvoltage }}",
+ "", "", "V",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "SYS: Bat current", (char*)getUniqueId("m5batcurrent", "").c_str(),
+ "", "", "{{ value_json.m5batcurrent }}",
+ "", "", "A",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "SYS: Vin voltage", (char*)getUniqueId("m5vinvoltage", "").c_str(),
+ "", "", "{{ value_json.m5vinvoltage }}",
+ "", "", "V",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "SYS: Vin current", (char*)getUniqueId("m5vincurrent", "").c_str(),
+ "", "", "{{ value_json.m5vincurrent }}",
+ "", "", "A",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+# ifdef ZboardM5STACK
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "SYS: Batt level", (char*)getUniqueId("m5battlevel", "").c_str(),
+ "", "", "{{ value_json.m5battlevel }}",
+ "", "", "%",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("binary_sensor",
+ subjectSYStoMQTT, "SYS: Is Charging", (char*)getUniqueId("m5ischarging", "").c_str(),
+ "", "{{ value_json.m5ischarging }}", "",
+ "", "", "%",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("binary_sensor",
+ subjectSYStoMQTT, "SYS: Is Charge Full", (char*)getUniqueId("m5ischargefull", "").c_str(),
+ "", "{{ value_json.m5ischargefull }}", "",
+ "", "", "%",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+# endif
+ createDiscovery("switch",
+ will_Topic, "SYS: Restart gateway", (char*)getUniqueId("restart", "").c_str(),
+ will_Topic, "", "",
+ "{\"cmd\":\"restart\"}", "", "",
+ 0,
+ Gateway_AnnouncementMsg, will_Message, true, subjectMQTTtoSYSset,
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("switch",
+ will_Topic, "SYS: Erase credentials", (char*)getUniqueId("erase", "").c_str(),
+ will_Topic, "", "",
+ "{\"cmd\":\"erase\"}", "", "",
+ 0,
+ Gateway_AnnouncementMsg, will_Message, true, subjectMQTTtoSYSset,
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("switch",
+ "", "SYS: Auto discovery", (char*)getUniqueId("discovery", "").c_str(),
+ will_Topic, "", "",
+ "{\"discovery\":true}", "{\"discovery\":false}", "",
+ 0,
+ Gateway_AnnouncementMsg, will_Message, true, subjectMQTTtoSYSset,
+ "", "", "", "", true,
+ stateClassNone
+ );
+
+# ifdef ZsensorBME280
+#define BMEparametersCount 5
+ Log.trace(F("bme280Discovery" CR));
+ char* BMEsensor[BMEparametersCount][8] = {
+ {"sensor", "temp", "bme", "temperature", jsonTempc, "", "", "°C"},
+ {"sensor", "pa", "bme", "", jsonPa, "", "", "hPa"},
+ {"sensor", "hum", "bme", "humidity", jsonHum, "", "", "%"},
+ {"sensor", "altim", "bme", "", jsonAltim, "", "", "m"},
+ {"sensor", "altift", "bme", "", jsonAltif, "", "", "ft"}
+
+ };
+
+ for (int i = 0; i < BMEparametersCount; i++) {
+
+ createDiscovery(BMEsensor[i][0],
+ BMETOPIC, BMEsensor[i][1], (char*)getUniqueId(BMEsensor[i][1], BMEsensor[i][2]).c_str(),
+ will_Topic, BMEsensor[i][3], BMEsensor[i][4],
+ BMEsensor[i][5], BMEsensor[i][6], BMEsensor[i][7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ }
+# endif
+
+# ifdef ZsensorHTU21
+#define HTUparametersCount 2
+ Log.trace(F("htu21Discovery" CR));
+ char* HTUsensor[HTUparametersCount][8] = {
+ {"sensor", "temp", "htu", "temperature", jsonTempc, "", "", "°C"},
+ {"sensor", "hum", "htu", "humidity", jsonHum, "", "", "%"}
+
+ };
+
+ for (int i = 0; i < HTUparametersCount; i++) {
+
+ createDiscovery(HTUsensor[i][0],
+ HTUTOPIC, HTUsensor[i][1], (char*)getUniqueId(HTUsensor[i][1], HTUsensor[i][2]).c_str(),
+ will_Topic, HTUsensor[i][3], HTUsensor[i][4],
+ HTUsensor[i][5], HTUsensor[i][6], HTUsensor[i][7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ }
+# endif
+
+# ifdef ZsensorAHTx0
+#define AHTparametersCount 2
+ Log.trace(F("AHTx0Discovery" CR));
+ char* AHTsensor[AHTparametersCount][8] = {
+ {"sensor", "temp", "aht", "temperature", jsonTempc, "", "", "°C"},
+ {"sensor", "hum", "aht", "humidity", jsonHum, "", "", "%"}
+
+ };
+
+ for (int i = 0; i < AHTparametersCount; i++) {
+ createDiscovery(AHTsensor[i][0],
+ AHTTOPIC, AHTsensor[i][1], (char*)getUniqueId(AHTsensor[i][1], AHTsensor[i][2]).c_str(),
+ will_Topic, AHTsensor[i][3], AHTsensor[i][4],
+ AHTsensor[i][5], AHTsensor[i][6], AHTsensor[i][7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ }
+# endif
+
+# ifdef ZsensorDHT
+#define DHTparametersCount 2
+ Log.trace(F("DHTDiscovery" CR));
+ char* DHTsensor[DHTparametersCount][8] = {
+ {"sensor", "temp", "dht", "temperature", jsonTempc, "", "", "°C"},
+ {"sensor", "hum", "dht", "humidity", jsonHum, "", "", "%"}
+
+ };
+
+ for (int i = 0; i < DHTparametersCount; i++) {
+
+ createDiscovery(DHTsensor[i][0],
+ DHTTOPIC, DHTsensor[i][1], (char*)getUniqueId(DHTsensor[i][1], DHTsensor[i][2]).c_str(),
+ will_Topic, DHTsensor[i][3], DHTsensor[i][4],
+ DHTsensor[i][5], DHTsensor[i][6], DHTsensor[i][7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ }
+# endif
+
+# ifdef ZsensorADC
+ Log.trace(F("ADCDiscovery" CR));
+ char* ADCsensor[8] = {"sensor", "adc", "", "", jsonAdc, "", "", ""};
+
+
+
+ createDiscovery(ADCsensor[0],
+ ADCTOPIC, ADCsensor[1], (char*)getUniqueId(ADCsensor[1], ADCsensor[2]).c_str(),
+ will_Topic, ADCsensor[3], ADCsensor[4],
+ ADCsensor[5], ADCsensor[6], ADCsensor[7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+
+# ifdef ZsensorBH1750
+#define BH1750parametersCount 3
+ Log.trace(F("BH1750Discovery" CR));
+ char* BH1750sensor[BH1750parametersCount][8] = {
+ {"sensor", "lux", "BH1750", "illuminance", jsonLux, "", "", "lx"},
+ {"sensor", "ftCd", "BH1750", "", jsonFtcd, "", "", ""},
+ {"sensor", "wattsm2", "BH1750", "", jsonWm2, "", "", "wm²"}
+
+ };
+
+ for (int i = 0; i < BH1750parametersCount; i++) {
+
+ createDiscovery(BH1750sensor[i][0],
+ subjectBH1750toMQTT, BH1750sensor[i][1], (char*)getUniqueId(BH1750sensor[i][1], BH1750sensor[i][2]).c_str(),
+ will_Topic, BH1750sensor[i][3], BH1750sensor[i][4],
+ BH1750sensor[i][5], BH1750sensor[i][6], BH1750sensor[i][7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ }
+# endif
+
+# ifdef ZsensorTSL2561
+#define TSL2561parametersCount 3
+ Log.trace(F("TSL2561Discovery" CR));
+ char* TSL2561sensor[TSL2561parametersCount][8] = {
+ {"sensor", "lux", "TSL2561", "illuminance", jsonLux, "", "", "lx"},
+ {"sensor", "ftcd", "TSL2561", "", jsonFtcd, "", "", ""},
+ {"sensor", "wattsm2", "TSL2561", "", jsonWm2, "", "", "wm²"}
+
+ };
+
+ for (int i = 0; i < TSL2561parametersCount; i++) {
+
+ createDiscovery(TSL2561sensor[i][0],
+ subjectTSL12561toMQTT, TSL2561sensor[i][1], (char*)getUniqueId(TSL2561sensor[i][1], TSL2561sensor[i][2]).c_str(),
+ will_Topic, TSL2561sensor[i][3], TSL2561sensor[i][4],
+ TSL2561sensor[i][5], TSL2561sensor[i][6], TSL2561sensor[i][7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ }
+# endif
+
+# ifdef ZsensorHCSR501
+ Log.trace(F("HCSR501Discovery" CR));
+ char* HCSR501sensor[8] = {"binary_sensor", "hcsr501", "", "", jsonPresence, "true", "false", ""};
+
+
+
+ createDiscovery(HCSR501sensor[0],
+ subjectHCSR501toMQTT, HCSR501sensor[1], (char*)getUniqueId(HCSR501sensor[1], HCSR501sensor[2]).c_str(),
+ will_Topic, HCSR501sensor[3], HCSR501sensor[4],
+ HCSR501sensor[5], HCSR501sensor[6], HCSR501sensor[7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+
+# ifdef ZsensorGPIOInput
+ Log.trace(F("GPIOInputDiscovery" CR));
+ char* GPIOInputsensor[8] = {"binary_sensor", "GPIOInput", "", "", jsonGpio, INPUT_GPIO_ON_VALUE, INPUT_GPIO_OFF_VALUE, ""};
+
+
+
+ createDiscovery(GPIOInputsensor[0],
+ subjectGPIOInputtoMQTT, GPIOInputsensor[1], (char*)getUniqueId(GPIOInputsensor[1], GPIOInputsensor[2]).c_str(),
+ will_Topic, GPIOInputsensor[3], GPIOInputsensor[4],
+ GPIOInputsensor[5], GPIOInputsensor[6], GPIOInputsensor[7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+
+# ifdef ZsensorINA226
+#define INA226parametersCount 3
+ Log.trace(F("INA226Discovery" CR));
+ char* INA226sensor[INA226parametersCount][8] = {
+ {"sensor", "volt", "INA226", "", jsonVolt, "", "", "V"},
+ {"sensor", "current", "INA226", "", jsonCurrent, "", "", "A"},
+ {"sensor", "power", "INA226", "", jsonPower, "", "", "W"}
+
+ };
+
+ for (int i = 0; i < INA226parametersCount; i++) {
+
+ createDiscovery(INA226sensor[i][0],
+ subjectINA226toMQTT, INA226sensor[i][1], (char*)getUniqueId(INA226sensor[i][1], INA226sensor[i][2]).c_str(),
+ will_Topic, INA226sensor[i][3], INA226sensor[i][4],
+ INA226sensor[i][5], INA226sensor[i][6], INA226sensor[i][7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ }
+# endif
+
+# ifdef ZsensorDS1820
+
+ pubOneWire_HADiscovery();
+# endif
+
+# ifdef ZactuatorONOFF
+ Log.trace(F("actuatorONOFFDiscovery" CR));
+ char* actuatorONOFF[8] = {"switch", "actuatorONOFF", "", "", "", "{\"cmd\":1}", "{\"cmd\":0}", ""};
+
+
+
+ createDiscovery(actuatorONOFF[0],
+ subjectGTWONOFFtoMQTT, actuatorONOFF[1], (char*)getUniqueId(actuatorONOFF[1], actuatorONOFF[2]).c_str(),
+ will_Topic, actuatorONOFF[3], actuatorONOFF[4],
+ actuatorONOFF[5], actuatorONOFF[6], actuatorONOFF[7],
+ 0, "", "", true, subjectMQTTtoONOFF,
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+
+# ifdef ZgatewayRF
+
+ Log.trace(F("gatewayRFDiscovery" CR));
+ char* gatewayRF[8] = {"sensor", "gatewayRF", "", "", jsonVal, "", "", ""};
+
+
+
+ createDiscovery(gatewayRF[0],
+ subjectRFtoMQTT, gatewayRF[1], (char*)getUniqueId(gatewayRF[1], gatewayRF[2]).c_str(),
+ will_Topic, gatewayRF[3], gatewayRF[4],
+ gatewayRF[5], gatewayRF[6], gatewayRF[7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+
+# endif
+
+# ifdef ZgatewayRF2
+
+ Log.trace(F("gatewayRF2Discovery" CR));
+ char* gatewayRF2[8] = {"sensor", "gatewayRF2", "", "", jsonAddress, "", "", ""};
+
+
+
+ createDiscovery(gatewayRF2[0],
+ subjectRF2toMQTT, gatewayRF2[1], (char*)getUniqueId(gatewayRF2[1], gatewayRF2[2]).c_str(),
+ will_Topic, gatewayRF2[3], gatewayRF2[4],
+ gatewayRF2[5], gatewayRF2[6], gatewayRF2[7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+
+# ifdef ZgatewayRFM69
+
+ Log.trace(F("gatewayRFM69Discovery" CR));
+ char* gatewayRFM69[8] = {"sensor", "gatewayRFM69", "", "", jsonVal, "", "", ""};
+
+
+
+ createDiscovery(gatewayRFM69[0],
+ subjectRFM69toMQTT, gatewayRFM69[1], (char*)getUniqueId(gatewayRFM69[1], gatewayRFM69[2]).c_str(),
+ will_Topic, gatewayRFM69[3], gatewayRFM69[4],
+ gatewayRFM69[5], gatewayRFM69[6], gatewayRFM69[7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+
+# ifdef ZgatewayLORA
+
+ Log.trace(F("gatewayLORADiscovery" CR));
+ char* gatewayLORA[8] = {"sensor", "gatewayLORA", "", "", jsonMsg, "", "", ""};
+
+
+
+ createDiscovery(gatewayLORA[0],
+ subjectLORAtoMQTT, gatewayLORA[1], (char*)getUniqueId(gatewayLORA[1], gatewayLORA[2]).c_str(),
+ will_Topic, gatewayLORA[3], gatewayLORA[4],
+ gatewayLORA[5], gatewayLORA[6], gatewayLORA[7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+
+# ifdef ZgatewaySRFB
+
+ Log.trace(F("gatewaySRFBDiscovery" CR));
+ char* gatewaySRFB[8] = {"sensor", "gatewaySRFB", "", "", jsonVal, "", "", ""};
+
+
+
+ createDiscovery(gatewaySRFB[0],
+ subjectSRFBtoMQTT, gatewaySRFB[1], (char*)getUniqueId(gatewaySRFB[1], gatewaySRFB[2]).c_str(),
+ will_Topic, gatewaySRFB[3], gatewaySRFB[4],
+ gatewaySRFB[5], gatewaySRFB[6], gatewaySRFB[7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+
+# ifdef ZgatewayPilight
+
+ Log.trace(F("gatewayPilightDiscovery" CR));
+ char* gatewayPilight[8] = {"sensor", "gatewayPilight", "", "", jsonMsg, "", "", ""};
+
+
+
+ createDiscovery(gatewayPilight[0],
+ subjectPilighttoMQTT, gatewayPilight[1], (char*)getUniqueId(gatewayPilight[1], gatewayPilight[2]).c_str(),
+ will_Topic, gatewayPilight[3], gatewayPilight[4],
+ gatewayPilight[5], gatewayPilight[6], gatewayPilight[7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+
+# ifdef ZgatewayIR
+
+ Log.trace(F("gatewayIRDiscovery" CR));
+ char* gatewayIR[8] = {"sensor", "gatewayIR", "", "", jsonVal, "", "", ""};
+
+
+
+ createDiscovery(gatewayIR[0],
+ subjectIRtoMQTT, gatewayIR[1], (char*)getUniqueId(gatewayIR[1], gatewayIR[2]).c_str(),
+ will_Topic, gatewayIR[3], gatewayIR[4],
+ gatewayIR[5], gatewayIR[6], gatewayIR[7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+
+# ifdef Zgateway2G
+
+ Log.trace(F("gateway2GDiscovery" CR));
+ char* gateway2G[8] = {"sensor", "gateway2G", "", "", jsonMsg, "", "", ""};
+
+
+
+ createDiscovery(gateway2G[0],
+ subject2GtoMQTT, gateway2G[1], (char*)getUniqueId(gateway2G[1], gateway2G[2]).c_str(),
+ will_Topic, gateway2G[3], gateway2G[4],
+ gateway2G[5], gateway2G[6], gateway2G[7],
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+# endif
+
+# ifdef ZgatewayBT
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "BT: Interval between scans", (char*)getUniqueId("interval", "").c_str(),
+ "", "", "{{ value_json.interval }}",
+ "", "", "ms",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("sensor",
+ subjectSYStoMQTT, "BT: Connnect every X scan(s)", (char*)getUniqueId("scanbcnct", "").c_str(),
+ "", "", "{{ value_json.scanbcnct }}",
+ "", "", "",
+ 0,
+ "", "", true, "",
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("switch",
+ will_Topic, "BT: Force scan", (char*)getUniqueId("force_scan", "").c_str(),
+ will_Topic, "", "",
+ "{\"interval\":0}", "", "",
+ 0,
+ Gateway_AnnouncementMsg, will_Message, true, subjectMQTTtoBTset,
+ "", "", "", "", false,
+ stateClassNone
+ );
+ createDiscovery("switch",
+ "", "BT: Publish only sensors", (char*)getUniqueId("only_sensors", "").c_str(),
+ "", "", "",
+ "{\"onlysensors\":true}", "{\"onlysensors\":false}", "",
+ 0,
+ Gateway_AnnouncementMsg, will_Message, true, subjectMQTTtoBTset,
+ "", "", "", "", true,
+ stateClassNone
+ );
+ createDiscovery("switch",
+ "", "BT: Publish HASS presence", (char*)getUniqueId("hasspresence", "").c_str(),
+ "", "", "",
+ "{\"hasspresence\":true}", "{\"hasspresence\":false}", "",
+ 0,
+ Gateway_AnnouncementMsg, will_Message, true, subjectMQTTtoBTset,
+ "", "", "", "", true,
+ stateClassNone
+ );
+# ifdef ESP32
+ createDiscovery("switch",
+ "", "SYS: Low Power Mode command", (char*)getUniqueId("lowpowermode", "").c_str(),
+ "", "", "",
+ "{\"lowpowermode\":2}", "{\"lowpowermode\":0}", "",
+ 0,
+ "", "", true, subjectMQTTtoBTset,
+ "", "", "", "", true,
+ stateClassNone
+ );
+ createDiscovery("switch",
+ "", "BT: Connect to devices", (char*)getUniqueId("bleconnect", "").c_str(),
+ "", "", "",
+ "{\"bleconnect\":true}", "{\"bleconnect\":false}", "",
+ 0,
+ Gateway_AnnouncementMsg, will_Message, true, subjectMQTTtoBTset,
+ "", "", "", "", true,
+ stateClassNone
+ );
+# endif
+# endif
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorADC.ino"
+# 29 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorADC.ino"
+#include "User_config.h"
+
+#ifdef ZsensorADC
+
+# if defined(ESP8266)
+ADC_MODE(ADC_TOUT);
+# endif
+
+
+unsigned long timeadc = 0;
+
+void setupADC() {
+ Log.notice(F("ADC_GPIO: %d" CR), ADC_GPIO);
+}
+
+void MeasureADC() {
+ if (millis() > (timeadc + TimeBetweenReadingADC)) {
+# if defined(ESP8266)
+ yield();
+# endif
+ timeadc = millis();
+ static int persistedadc;
+ int val = analogRead(ADC_GPIO);
+ if (isnan(val)) {
+ Log.error(F("Failed to read from ADC !" CR));
+ } else {
+ if (val >= persistedadc + ThresholdReadingADC || val <= persistedadc - ThresholdReadingADC) {
+ Log.trace(F("Creating ADC buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject ADCdata = jsonBuffer.to();
+ ADCdata["adc"] = (int)val;
+# if defined(ADC_DIVIDER)
+ float volt = 0;
+# if defined(ESP32)
+
+ volt = val * (3.3 / 4096.0);
+# elif defined(ESP8266)
+
+ volt = val * (3.3 / 1024.0);
+# else
+
+ volt = val * (5.0 / 1024.0);
+# endif
+ volt *= ADC_DIVIDER;
+
+ val = (volt * 100);
+ volt = (float)val / 100.0;
+ ADCdata["volt"] = (float)volt;
+# endif
+ pub(ADCTOPIC, ADCdata);
+ persistedadc = val;
+ }
+ }
+ }
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorAHTx0.ino"
+# 38 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorAHTx0.ino"
+#include "User_config.h"
+
+#ifdef ZsensorAHTx0
+# include
+
+# include "Adafruit_AHTX0.h"
+# include "Wire.h"
+# include "config_AHTx0.h"
+
+
+unsigned long timeAHTx0 = 0;
+
+
+Adafruit_AHTX0 ahtSensor;
+
+void setupZsensorAHTx0() {
+ delay(10);
+ Log.notice(F("AHTx0 Initialized - begin()" CR));
+
+# if defined(ESP32)
+ Wire.begin(AHT_I2C_SDA, AHT_I2C_SCL);
+ if (!ahtSensor.begin(&Wire)) {
+ Log.error(F("Failed to initialize AHTx0 sensor!" CR));
+ }
+# else
+ if (!ahtSensor.begin()) {
+ Log.error(F("Failed to initialize AHTx0 sensor!" CR));
+ }
+# endif
+}
+
+void MeasureAHTTempHum() {
+ if (millis() > (timeAHTx0 + TimeBetweenReadingAHTx0)) {
+ Log.trace(F("Read AHTx0 Sensor" CR));
+
+ timeAHTx0 = millis();
+ static float persisted_aht_tempc;
+ static float persisted_aht_hum;
+
+ sensors_event_t ahtTempC, ahtHum;
+ if (!ahtSensor.getEvent(&ahtHum, &ahtTempC))
+ {
+ Log.error(F("Failed to read from sensor AHTx0!" CR));
+ return;
+ }
+
+
+ if (isnan(ahtTempC.temperature) || isnan(ahtHum.relative_humidity)) {
+ Log.error(F("Failed to read from sensor AHTx0!" CR));
+ } else {
+ Log.notice(F("Creating AHTx0 buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject AHTx0data = jsonBuffer.to();
+
+ if (ahtTempC.temperature != persisted_aht_tempc || AHTx0_always) {
+ float ahtTempF = convertTemp_CtoF(ahtTempC.temperature);
+ AHTx0data["tempc"] = (float)ahtTempC.temperature;
+ AHTx0data["tempf"] = (float)ahtTempF;
+ } else {
+ Log.notice(F("Same Temp. Don't send it" CR));
+ }
+
+
+ if (ahtHum.relative_humidity != persisted_aht_hum || AHTx0_always) {
+ AHTx0data["hum"] = (float)ahtHum.relative_humidity;
+ } else {
+ Log.notice(F("Same Humidity. Don't send it" CR));
+ }
+
+ if (AHTx0data.size() > 0) {
+ pub(AHTTOPIC, AHTx0data);
+ }
+ }
+ persisted_aht_tempc = ahtTempC.temperature;
+ persisted_aht_hum = ahtHum.relative_humidity;
+ }
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorBH1750.ino"
+# 39 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorBH1750.ino"
+#include "User_config.h"
+
+#ifdef ZsensorBH1750
+# include "Wire.h"
+# include "math.h"
+
+void setupZsensorBH1750() {
+ Log.notice(F("Setup BH1750 on adress: %H" CR), BH1750_i2c_addr);
+ Wire.begin();
+ Wire.beginTransmission(BH1750_i2c_addr);
+ Wire.write(0x10);
+ Wire.endTransmission();
+ delay(300);
+}
+
+void MeasureLightIntensity() {
+ if (millis() > (timebh1750 + TimeBetweenReadingBH1750)) {
+ Log.trace(F("Creating BH1750 buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject BH1750data = jsonBuffer.to();
+
+ timebh1750 = millis();
+ unsigned int i = 0;
+ static float persistedll;
+ static float persistedlf;
+ static float persistedlw;
+ unsigned int Lux;
+ float ftcd;
+ float Wattsm2;
+
+
+ Wire.requestFrom(BH1750_i2c_addr, 2);
+ if (Wire.available() != 2) {
+ Log.error(F("Failed to read from LightSensor BH1750!" CR));
+ } else {
+ i = Wire.read();
+ i <<= 8;
+ i |= Wire.read();
+
+
+ Lux = i / 1.2;
+ ftcd = Lux / 10.764;
+ Wattsm2 = Lux / 683.0;
+
+
+ if (Lux != persistedll || bh1750_always) {
+ BH1750data["lux"] = (unsigned int)Lux;
+ } else {
+ Log.trace(F("Same lux don't send it" CR));
+ }
+
+
+ if (ftcd != persistedlf || bh1750_always) {
+ BH1750data["ftcd"] = (unsigned int)ftcd;
+ } else {
+ Log.trace(F("Same ftcd don't send it" CR));
+ }
+
+
+ if (Wattsm2 != persistedlw || bh1750_always) {
+ BH1750data["wattsm2"] = (unsigned int)Wattsm2;
+ } else {
+ Log.trace(F("Same wattsm2 don't send it" CR));
+ }
+ if (BH1750data.size() > 0)
+ pub(subjectBH1750toMQTT, BH1750data);
+ }
+ persistedll = Lux;
+ persistedlf = ftcd;
+ persistedlw = Wattsm2;
+ }
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorBME280.ino"
+# 40 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorBME280.ino"
+#include "User_config.h"
+
+#ifdef ZsensorBME280
+# include
+
+# include "SparkFunBME280.h"
+# include "Wire.h"
+
+
+BME280 mySensor;
+
+void setupZsensorBME280() {
+# if defined(ESP8266) || defined(ESP32)
+
+ Wire.begin(BME280_PIN_SDA, BME280_PIN_SCL);
+# else
+ Wire.begin();
+# endif
+
+ mySensor.settings.commInterface = I2C_MODE;
+ mySensor.settings.I2CAddress = BME280_i2c_addr;
+ Log.notice(F("Setup BME280 on adress: %X" CR), BME280_i2c_addr);
+
+
+
+
+
+
+
+ mySensor.settings.runMode = 3;
+# 81 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorBME280.ino"
+ mySensor.settings.tStandby = 1;
+# 90 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorBME280.ino"
+ mySensor.settings.filter = 4;
+
+
+
+
+
+ mySensor.settings.tempOverSample = 1;
+
+
+
+
+
+ mySensor.settings.pressOverSample = 1;
+
+
+
+
+
+ mySensor.settings.humidOverSample = 1;
+
+
+
+
+
+ mySensor.settings.tempCorrection = BME280Correction;
+
+ delay(10);
+
+ int ret = mySensor.begin();
+ if (ret == 0x60) {
+ Log.notice(F("Bosch BME280 successfully initialized: %X" CR), ret);
+ } else {
+ Log.notice(F("Bosch BME280 failed: %X" CR), ret);
+ }
+}
+
+void MeasureTempHumAndPressure() {
+ if (millis() > (timebme280 + TimeBetweenReadingbme280)) {
+ timebme280 = millis();
+ static float persisted_bme_tempc;
+ static float persisted_bme_tempf;
+ static float persisted_bme_hum;
+ static float persisted_bme_pa;
+ static float persisted_bme_altim;
+ static float persisted_bme_altift;
+
+ float BmeTempC = mySensor.readTempC();
+ float BmeTempF = mySensor.readTempF();
+ float BmeHum = mySensor.readFloatHumidity();
+ float BmePa = mySensor.readFloatPressure();
+ float BmeAltiM = mySensor.readFloatAltitudeMeters();
+ float BmeAltiFt = mySensor.readFloatAltitudeFeet();
+
+
+ if (isnan(BmeTempC) || isnan(BmeTempF) || isnan(BmeHum) || isnan(BmePa) || isnan(BmeAltiM) || isnan(BmeAltiFt)) {
+ Log.error(F("Failed to read from BME280!" CR));
+ } else {
+ Log.trace(F("Creating BME280 buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject BME280data = jsonBuffer.to();
+
+ if (BmeTempC != persisted_bme_tempc || bme280_always) {
+ BME280data["tempc"] = (float)BmeTempC;
+ } else {
+ Log.trace(F("Same Degrees C don't send it" CR));
+ }
+
+
+ if (BmeTempF != persisted_bme_tempf || bme280_always) {
+ BME280data["tempf"] = (float)BmeTempF;
+ } else {
+ Log.trace(F("Same Degrees F don't send it" CR));
+ }
+
+
+ if (BmeHum != persisted_bme_hum || bme280_always) {
+ BME280data["hum"] = (float)BmeHum;
+ } else {
+ Log.trace(F("Same Humidity don't send it" CR));
+ }
+
+
+ if (BmePa != persisted_bme_pa || bme280_always) {
+ BME280data["pa"] = (float)BmePa;
+ } else {
+ Log.trace(F("Same Pressure don't send it" CR));
+ }
+
+
+ if (BmeAltiM != persisted_bme_altim || bme280_always) {
+ Log.trace(F("Sending Altitude Meter to MQTT" CR));
+ BME280data["altim"] = (float)BmeAltiM;
+ } else {
+ Log.trace(F("Same Altitude Meter don't send it" CR));
+ }
+
+
+ if (BmeAltiFt != persisted_bme_altift || bme280_always) {
+ BME280data["altift"] = (float)BmeAltiFt;
+ } else {
+ Log.trace(F("Same Altitude Feet don't send it" CR));
+ }
+ if (BME280data.size() > 0)
+ pub(BMETOPIC, BME280data);
+ }
+
+ persisted_bme_tempc = BmeTempC;
+ persisted_bme_tempf = BmeTempF;
+ persisted_bme_hum = BmeHum;
+ persisted_bme_pa = BmePa;
+ persisted_bme_altim = BmeAltiM;
+ persisted_bme_altift = BmeAltiFt;
+ }
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorDHT.ino"
+# 30 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorDHT.ino"
+#include "User_config.h"
+
+#ifdef ZsensorDHT
+# include
+# include
+
+DHT dht(DHT_RECEIVER_GPIO, DHT_SENSOR_TYPE);
+
+
+unsigned long timedht = 0;
+
+void setupDHT() {
+ Log.notice(F("Reading DHT on pin: %d" CR), DHT_RECEIVER_GPIO);
+}
+
+void MeasureTempAndHum() {
+ if (millis() > (timedht + TimeBetweenReadingDHT)) {
+ timedht = millis();
+ static float persistedh;
+ static float persistedt;
+ float h = dht.readHumidity();
+
+ float t = dht.readTemperature();
+
+ if (isnan(h) || isnan(t)) {
+ Log.error(F("Failed to read from DHT sensor!" CR));
+ } else {
+ Log.trace(F("Creating DHT buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject DHTdata = jsonBuffer.to();
+ if (h != persistedh || dht_always) {
+ DHTdata["hum"] = (float)h;
+ } else {
+ Log.trace(F("Same hum don't send it" CR));
+ }
+ if (t != persistedt || dht_always) {
+ DHTdata["tempc"] = (float)t;
+ DHTdata["tempf"] = dht.convertCtoF(t);
+ } else {
+ Log.trace(F("Same temp don't send it" CR));
+ }
+ if (DHTdata.size() > 0)
+ pub(DHTTOPIC, DHTdata);
+ }
+ persistedh = h;
+ persistedt = t;
+ }
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorDS1820.ino"
+# 26 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorDS1820.ino"
+#include "User_config.h"
+
+#ifdef ZsensorDS1820
+# include
+# include
+
+OneWire owbus(DS1820_OWBUS_GPIO);
+DallasTemperature ds1820(&owbus);
+DeviceAddress ds1820_address, ds1820_devices[OW_MAX_SENSORS];
+
+static uint8_t ds1820_count = 0;
+static uint8_t ds1820_resolution[OW_MAX_SENSORS];
+static String ds1820_type[OW_MAX_SENSORS];
+static String ds1820_addr[OW_MAX_SENSORS];
+
+void setupZsensorDS1820() {
+ Log.trace(F("DS1820: configured pin: %d for 1-wire bus" CR), DS1820_OWBUS_GPIO);
+ ds1820.begin();
+
+
+ uint8_t numDevicesOnBus = ds1820.getDeviceCount();
+
+
+ Log.notice(F("DS1820: Found %d devices" CR), numDevicesOnBus);
+
+
+ for (int deviceIndex = 0; deviceIndex < numDevicesOnBus && ds1820_count < OW_MAX_SENSORS; deviceIndex++) {
+
+ if (ds1820.getAddress(ds1820_address, deviceIndex) && ds1820.validFamily(ds1820_address)) {
+ ds1820_addr[ds1820_count] = String("0x");
+ for (uint8_t i = 0; i < 8; i++) {
+ if (ds1820_address[i] < 0x10) ds1820_addr[ds1820_count] += String("0");
+ ds1820_devices[ds1820_count][i] = ds1820_address[i];
+ ds1820_addr[ds1820_count] += String(ds1820_address[i], HEX);
+ }
+
+
+ if (ds1820_address[0] == 0x10) {
+
+
+
+
+
+ ds1820_type[ds1820_count] = String("DS1820/DS18S20");
+ } else if (ds1820_address[0] == 0x28) {
+
+ ds1820_type[ds1820_count] = String("DS18B20");
+ ds1820.setResolution(ds1820_address, DS1820_RESOLUTION);
+ } else if (ds1820_address[0] == 0x22) {
+ ds1820_type[ds1820_count] = String("DS1822");
+ ds1820.setResolution(ds1820_address, DS1820_RESOLUTION);
+ } else {
+ ds1820_type[ds1820_count] = String("DS1825");
+ ds1820.setResolution(ds1820_address, DS1820_RESOLUTION);
+ }
+ ds1820_resolution[ds1820_count] = ds1820.getResolution(ds1820_address);
+ Log.trace(F("DS1820: Device %d, Type: %s, Address: %s, Resolution: %d" CR),
+ ds1820_count,
+ (char*)ds1820_type[ds1820_count].c_str(),
+ (char*)ds1820_addr[ds1820_count].c_str(),
+ ds1820_resolution[ds1820_count]);
+ ds1820_count++;
+ }
+ }
+
+ if (ds1820.getDS18Count() == 0) {
+ Log.error(F("DS1820: Failed to enumerate sensors on 1-wire bus. Check your GPIO assignment!" CR));
+ }
+
+
+
+ ds1820.setWaitForConversion(false);
+}
+
+void pubOneWire_HADiscovery() {
+
+# ifdef ZmqttDiscovery
+
+ if (disc) {
+ for (int index = 0; index < ds1820_count; index++) {
+ createDiscovery("sensor",
+ (char*)(String(OW_TOPIC) + "/" + ds1820_addr[index]).c_str(),
+ (char*)("DS12B20_" + String(index + 1) + "_c").c_str(),
+ (char*)(ds1820_addr[index] + "_c").c_str(),
+ will_Topic,
+ "temperature",
+ jsonTempc,
+ "", "", "°C",
+ 0, "", "", true, "",
+ "", "", "", "", false,
+ stateClassMeasurement
+ );
+ }
+ }
+# endif
+}
+
+void MeasureDS1820Temp() {
+ static float persisted_temp[OW_MAX_SENSORS];
+ static unsigned long timeDS1820 = 0;
+ static boolean triggeredConversion = false;
+ float current_temp[OW_MAX_SENSORS];
+
+
+
+ if (!triggeredConversion && ((millis() - timeDS1820) > (DS1820_INTERVAL_SEC * 1000UL - DS1820_CONV_TIME))) {
+ Log.trace(F("DS1820: Trigger temperature conversion..." CR));
+ ds1820.requestTemperatures();
+ triggeredConversion = true;
+ } else if (triggeredConversion && ((millis() - timeDS1820) > DS1820_INTERVAL_SEC * 1000UL)) {
+ timeDS1820 = millis();
+ triggeredConversion = false;
+
+ if (ds1820_count < 1) {
+ Log.error(F("DS1820: Failed to identify any temperature sensors on 1-wire bus during setup!" CR));
+ } else {
+ Log.trace(F("DS1820: Reading temperature(s) from %d sensor(s)..." CR), ds1820_count);
+ StaticJsonDocument jsonBuffer;
+ JsonObject DS1820data = jsonBuffer.to();
+
+ for (uint8_t i = 0; i < ds1820_count; i++) {
+ current_temp[i] = round(ds1820.getTempC(ds1820_devices[i]) * 10) / 10.0;
+ if (current_temp[i] == -127) {
+ Log.error(F("DS1820: Device %s currently disconnected!" CR), (char*)ds1820_addr[i].c_str());
+ } else if (DS1820_ALWAYS || current_temp[i] != persisted_temp[i]) {
+ DS1820data["tempf"] = (float)DallasTemperature::toFahrenheit(current_temp[i]);
+ DS1820data["tempc"] = (float)current_temp[i];
+
+ if (DS1820_DETAILS) {
+ DS1820data["type"] = ds1820_type[i];
+ DS1820data["res"] = ds1820_resolution[i] + String("bit" CR);
+ DS1820data["addr"] = ds1820_addr[i];
+ }
+ pub((char*)(String(OW_TOPIC) + "/" + ds1820_addr[i]).c_str(), DS1820data);
+ delay(10);
+ } else {
+ Log.trace(F("DS1820: Temperature for device %s didn't change, don't publish it." CR), (char*)ds1820_addr[i].c_str());
+ }
+ persisted_temp[i] = current_temp[i];
+ }
+ }
+ }
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorGPIOInput.ino"
+# 29 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorGPIOInput.ino"
+#include "User_config.h"
+
+#ifdef ZsensorGPIOInput
+# if defined(TRIGGER_GPIO) && INPUT_GPIO == TRIGGER_GPIO
+unsigned long resetTime = 0;
+# endif
+unsigned long lastDebounceTime = 0;
+int InputState = 3;
+int lastInputState = 3;
+
+void setupGPIOInput() {
+ Log.notice(F("Reading GPIO at pin: %d" CR), INPUT_GPIO);
+ pinMode(INPUT_GPIO, INPUT_PULLUP);
+}
+
+void MeasureGPIOInput() {
+ int reading = digitalRead(INPUT_GPIO);
+
+
+
+
+
+
+ if (reading != lastInputState) {
+
+ lastDebounceTime = millis();
+ }
+
+ if ((millis() - lastDebounceTime) > GPIOInputDebounceDelay) {
+
+
+# if defined(ESP8266) || defined(ESP32)
+ yield();
+# endif
+# if defined(TRIGGER_GPIO) && INPUT_GPIO == TRIGGER_GPIO
+ if (reading == LOW) {
+ if (resetTime == 0) {
+ resetTime = millis();
+ } else if ((millis() - resetTime) > 10000) {
+ Log.trace(F("Button Held" CR));
+ Log.notice(F("Erasing ESP Config, restarting" CR));
+ setup_wifimanager(true);
+ }
+ } else {
+ resetTime = 0;
+ }
+# endif
+
+ if (reading != InputState) {
+ InputState = reading;
+ Log.trace(F("Creating GPIOInput buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject GPIOdata = jsonBuffer.to();
+ if (InputState == HIGH) {
+ GPIOdata["gpio"] = "HIGH";
+ }
+ if (InputState == LOW) {
+ GPIOdata["gpio"] = "LOW";
+ }
+ if (GPIOdata.size() > 0)
+ pub(subjectGPIOInputtoMQTT, GPIOdata);
+
+# ifdef ZactuatorONOFF
+ if (InputState == ACTUATOR_BUTTON_TRIGGER_LEVEL) {
+ ActuatorButtonTrigger();
+ }
+# endif
+ }
+ }
+
+
+ lastInputState = reading;
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorGPIOKeyCode.ino"
+# 26 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorGPIOKeyCode.ino"
+#include "User_config.h"
+
+#ifdef ZsensorGPIOKeyCode
+
+int InputStateGPIOKeyCode = 0x0f;
+int lastInputStateGPIOKeyCode = 0x0f;
+int lastLatchStateGPIOKeyCode = 0;
+
+void setupGPIOKeyCode() {
+ pinMode(GPIOKeyCode_LATCH_GPIO, INPUT_PULLUP);
+ pinMode(GPIOKeyCode_D0_GPIO, INPUT_PULLUP);
+ pinMode(GPIOKeyCode_D1_GPIO, INPUT_PULLUP);
+ pinMode(GPIOKeyCode_D2_GPIO, INPUT_PULLUP);
+
+}
+
+void MeasureGPIOKeyCode() {
+ int latch = digitalRead(GPIOKeyCode_LATCH_GPIO);
+
+
+
+
+
+ {
+
+
+# if defined(ESP8266) || defined(ESP32)
+ yield();
+# endif
+
+ if (latch > 0 && lastLatchStateGPIOKeyCode != latch) {
+ int reading = digitalRead(GPIOKeyCode_D0_GPIO) | (digitalRead(GPIOKeyCode_D1_GPIO) << 1) | (digitalRead(GPIOKeyCode_D2_GPIO) << 2);
+
+
+ char hex[3];
+
+ InputStateGPIOKeyCode = reading;
+ sprintf(hex, "%02x", InputStateGPIOKeyCode);
+ hex[2] = 0;
+ Log.notice(F("GPIOKeyCode %H" CR), hex);
+ pub(subjectGPIOKeyCodetoMQTT, hex);
+ lastLatchStateGPIOKeyCode = latch;
+ }
+
+ if (latch != lastLatchStateGPIOKeyCode) {
+ lastLatchStateGPIOKeyCode = latch;
+ Log.notice(F("GPIOKeyCode latch %d" CR), latch);
+ if (latch == 0)
+ pub(subjectGPIOKeyCodeStatetoMQTT, "done");
+ }
+
+
+ lastInputStateGPIOKeyCode = InputStateGPIOKeyCode;
+ }
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorHCSR04.ino"
+# 30 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorHCSR04.ino"
+#include "User_config.h"
+
+#ifdef ZsensorHCSR04
+
+unsigned long timeHCSR04 = 0;
+
+void setupHCSR04() {
+ Log.notice(F("HCSR04 trigger pin: %d" CR), HCSR04_TRI_GPIO);
+ Log.notice(F("HCSR04 echo pin: %d" CR), HCSR04_ECH_GPIO);
+ pinMode(HCSR04_TRI_GPIO, OUTPUT);
+ pinMode(HCSR04_ECH_GPIO, INPUT);
+}
+
+void MeasureDistance() {
+ if (millis() > (timeHCSR04 + TimeBetweenReadingHCSR04)) {
+ timeHCSR04 = millis();
+ Log.trace(F("Creating HCSR04 buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject HCSR04data = jsonBuffer.to();
+ digitalWrite(HCSR04_TRI_GPIO, LOW);
+ delayMicroseconds(2);
+ digitalWrite(HCSR04_TRI_GPIO, HIGH);
+ delayMicroseconds(10);
+ digitalWrite(HCSR04_TRI_GPIO, LOW);
+ unsigned long duration = pulseIn(HCSR04_ECH_GPIO, HIGH);
+ if (isnan(duration)) {
+ Log.error(F("Failed to read from HC SR04 sensor!" CR));
+ } else {
+ static unsigned int distance = 99999;
+ unsigned int d = duration / 58.2;
+ HCSR04data["distance"] = (int)d;
+ if (d > distance) {
+ HCSR04data["direction"] = "away";
+ Log.trace(F("HC SR04 Distance changed" CR));
+ } else if (d < distance) {
+ HCSR04data["direction"] = "towards";
+ Log.trace(F("HC SR04 Distance changed" CR));
+ } else if (HCSR04_always) {
+ HCSR04data["direction"] = "static";
+ Log.trace(F("HC SR04 Distance hasn't changed" CR));
+ }
+ distance = d;
+ if (HCSR04data.size() > 0)
+ pub(subjectHCSR04, HCSR04data);
+ }
+ }
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorHCSR501.ino"
+# 29 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorHCSR501.ino"
+#include "User_config.h"
+
+#ifdef ZsensorHCSR501
+
+void setupHCSR501() {
+ Log.notice(F("HCSR501 pin: %d" CR), HCSR501_GPIO);
+ pinMode(HCSR501_GPIO, INPUT);
+# ifdef HCSR501_LED_NOTIFY_GPIO
+ pinMode(HCSR501_LED_NOTIFY_GPIO, OUTPUT);
+ digitalWrite(HCSR501_LED_NOTIFY_GPIO, LOW);
+# endif
+}
+
+void MeasureHCSR501() {
+ if (millis() > TimeBeforeStartHCSR501) {
+ StaticJsonDocument jsonBuffer;
+ JsonObject HCSR501data = jsonBuffer.to();
+ static int pirState = LOW;
+ int PresenceValue = digitalRead(HCSR501_GPIO);
+# if defined(ESP8266) || defined(ESP32)
+ yield();
+# endif
+ if (PresenceValue == HIGH) {
+ if (pirState == LOW) {
+
+ HCSR501data["presence"] = "true";
+ pirState = HIGH;
+ }
+ } else {
+ if (pirState == HIGH) {
+
+ HCSR501data["presence"] = "false";
+ pirState = LOW;
+ }
+ }
+# ifdef HCSR501_LED_NOTIFY_GPIO
+ digitalWrite(HCSR501_LED_NOTIFY_GPIO, pirState == HCSR501_LED_ON);
+# endif
+ if (HCSR501data.size() > 0)
+ pub(subjectHCSR501toMQTT, HCSR501data);
+ }
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorHTU21.ino"
+# 38 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorHTU21.ino"
+#include "User_config.h"
+
+#ifdef ZsensorHTU21
+# include
+
+# include "SparkFunHTU21D.h"
+# include "Wire.h"
+# include "config_HTU21.h"
+
+
+unsigned long timehtu21 = 0;
+
+
+HTU21D htuSensor;
+
+void setupZsensorHTU21() {
+ delay(10);
+ Log.notice(F("HTU21 Initialized - begin()" CR));
+
+# if defined(ESP32)
+ Wire.begin(I2C_SDA, I2C_SCL);
+ htuSensor.begin(Wire);
+# else
+ htuSensor.begin();
+# endif
+}
+
+void MeasureTempHum() {
+ if (millis() > (timehtu21 + TimeBetweenReadinghtu21)) {
+ Log.trace(F("Read HTU21 Sensor" CR));
+
+ timehtu21 = millis();
+ static float persisted_htu_tempc;
+ static float persisted_htu_hum;
+
+ float HtuTempC = htuSensor.readTemperature();
+ float HtuHum = htuSensor.readHumidity();
+
+ if (HtuTempC >= 998 || HtuHum >= 998) {
+ Log.error(F("Failed to read from sensor HTU21!" CR));
+ return;
+ }
+
+
+ if (isnan(HtuTempC) || isnan(HtuHum)) {
+ Log.error(F("Failed to read from sensor HTU21!" CR));
+ } else {
+ Log.notice(F("Creating HTU21 buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject HTU21data = jsonBuffer.to();
+
+ if (HtuTempC != persisted_htu_tempc || htu21_always) {
+ float HtuTempF = (HtuTempC * 1.8) + 32;
+ HTU21data["tempc"] = (float)HtuTempC;
+ HTU21data["tempf"] = (float)HtuTempF;
+ } else {
+ Log.notice(F("Same Temp. Don't send it" CR));
+ }
+
+
+ if (HtuHum != persisted_htu_hum || htu21_always) {
+ HTU21data["hum"] = (float)HtuHum;
+ } else {
+ Log.notice(F("Same Humidity. Don't send it" CR));
+ }
+
+ if (HTU21data.size() > 0)
+ pub(HTUTOPIC, HTU21data);
+ }
+ persisted_htu_tempc = HtuTempC;
+ persisted_htu_hum = HtuHum;
+ }
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorINA226.ino"
+# 35 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorINA226.ino"
+#include "User_config.h"
+
+#ifdef ZsensorINA226
+# include
+
+float rShunt = 0.1;
+const int INA226_ADDR = 0x40;
+
+
+unsigned long timeINA226 = 0;
+
+void setupINA226() {
+ Wire.begin();
+
+ writeRegister(0x00, 0x4427);
+}
+
+void MeasureINA226() {
+ if (millis() > (timeINA226 + TimeBetweenReadingINA226)) {
+ timeINA226 = millis();
+ Log.trace(F("Creating INA226 buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject INA226data = jsonBuffer.to();
+
+ Log.trace(F("Retrieving electrical data" CR));
+
+ float volt = readRegister(0x02) * 0.00125;
+
+ int shuntvolt = readRegister(0x01);
+ if (shuntvolt && 0x8000) {
+ shuntvolt = ~shuntvolt;
+ shuntvolt += 1;
+ shuntvolt *= -1;
+ }
+ float current = shuntvolt * 0.0000025 / rShunt;
+ float power = abs(volt * current);
+
+ char volt_c[7];
+ char current_c[7];
+ char power_c[7];
+ dtostrf(volt, 6, 3, volt_c);
+ dtostrf(current, 6, 3, current_c);
+ dtostrf(power, 6, 3, power_c);
+ INA226data["volt"] = (char*)volt_c;
+ INA226data["current"] = (char*)current_c;
+ INA226data["power"] = (char*)power_c;
+ pub(subjectINA226toMQTT, INA226data);
+ }
+}
+
+static void writeRegister(byte reg, word value) {
+ Wire.beginTransmission(INA226_ADDR);
+ Wire.write(reg);
+ Wire.write((value >> 8) & 0xFF);
+ Wire.write(value & 0xFF);
+ Wire.endTransmission();
+}
+
+static word readRegister(byte reg) {
+ word res = 0x0000;
+ Wire.beginTransmission(INA226_ADDR);
+ Wire.write(reg);
+ if (Wire.endTransmission() == 0) {
+ if (Wire.requestFrom(INA226_ADDR, 2) >= 2) {
+ res = Wire.read() * 256;
+ res += Wire.read();
+ }
+ }
+ return res;
+}
+
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorSHTC3.ino"
+#include "User_config.h"
+
+#ifdef ZsensorSHTC3
+# include
+
+SHTC3 mySHTC3;
+
+
+unsigned long timedht = 0;
+void errorDecoder(SHTC3_Status_TypeDef message);
+
+void errorDecoder(SHTC3_Status_TypeDef message)
+{
+ switch (message) {
+ case SHTC3_Status_Nominal:
+ Log.notice("Nominal");
+ break;
+ case SHTC3_Status_Error:
+ Log.error("Error");
+ break;
+ case SHTC3_Status_CRC_Fail:
+ Log.error("CRC Fail");
+ break;
+ default:
+ Log.error("Unknown return code");
+ break;
+ }
+}
+
+void setupSHTC3() {
+ Wire.begin();
+ errorDecoder(mySHTC3.begin());
+}
+
+void MeasureTempAndHum() {
+ if (millis() > (timedht + TimeBetweenReadingSHTC3)) {
+ timedht = millis();
+ static float persistedh;
+ static float persistedt;
+ SHTC3_Status_TypeDef result = mySHTC3.update();
+ if (mySHTC3.lastStatus == SHTC3_Status_Nominal) {
+
+ float t = mySHTC3.toDegC();
+ float h = mySHTC3.toPercent();
+
+ if (isnan(h) || isnan(t)) {
+ Log.error(F("Failed to read from SHTC3 sensor!" CR));
+ } else {
+ Log.trace(F("Creating SHTC3 buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject SHTC3data = jsonBuffer.to();
+ if (h != persistedh || shtc3_always) {
+ SHTC3data["hum"] = (float)h;
+ } else {
+ Log.trace(F("Same hum don't send it" CR));
+ }
+ if (t != persistedt || shtc3_always) {
+ SHTC3data["tempc"] = (float)t;
+ SHTC3data["tempf"] = mySHTC3.toDegF();
+ } else {
+ Log.trace(F("Same temp don't send it" CR));
+ }
+ if (SHTC3data.size() > 0)
+ pub(SHTC3TOPIC, SHTC3data);
+ }
+ persistedh = h;
+ persistedt = t;
+ } else {
+ errorDecoder(mySHTC3.lastStatus);
+ Log.error(F("Failed to read from SHTC3 sensor!" CR));
+ }
+ }
+}
+#endif
+# 1 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorTSL2561.ino"
+# 40 "/Users/sgracey/Code/OpenMQTTGatewayProd/main/ZsensorTSL2561.ino"
+#include "User_config.h"
+
+#ifdef ZsensorTSL2561
+# include
+# include
+
+# include "Wire.h"
+# include "math.h"
+
+Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_FLOAT, 12345);
+
+void displaySensorDetails(void) {
+ sensor_t sensor;
+ tsl.getSensor(&sensor);
+ Log.trace(F("------------------------------------" CR));
+ Log.trace(("Sensor: %s" CR), sensor.name);
+ Log.trace(("Driver Ver: %s" CR), sensor.version);
+ Log.trace(("Unique ID: %s" CR), sensor.sensor_id);
+ Log.trace(("Max Value: %s lux" CR), sensor.max_value);
+ Log.trace(("Min Value: %s lux" CR), sensor.min_value);
+ Log.trace(("Resolution: %s lux" CR), sensor.resolution);
+ Log.trace(F("------------------------------------" CR));
+ delay(500);
+}
+
+void setupZsensorTSL2561() {
+ Log.notice(F("Setup TSL2561 on adress: %H" CR), TSL2561_ADDR_FLOAT);
+ Wire.begin();
+ Wire.beginTransmission(TSL2561_ADDR_FLOAT);
+
+ if (!tsl.begin()) {
+ Log.error(F("No TSL2561 detected" CR));
+ }
+
+
+
+
+ tsl.enableAutoRange(true);
+
+
+
+ tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS);
+
+ Log.trace(F("TSL2561 Initialized. Printing detials now." CR));
+ displaySensorDetails();
+}
+
+void MeasureLightIntensityTSL2561() {
+ if (millis() > (timetsl2561 + TimeBetweenReadingtsl2561)) {
+ static uint32_t persisted_lux;
+ timetsl2561 = millis();
+
+ Log.trace(F("Creating TSL2561 buffer" CR));
+ StaticJsonDocument jsonBuffer;
+ JsonObject TSL2561data = jsonBuffer.to();
+
+ sensors_event_t event;
+ tsl.getEvent(&event);
+ if (event.light)
+
+ {
+ if (persisted_lux != event.light || tsl2561_always) {
+ persisted_lux = event.light;
+
+ TSL2561data["lux"] = (float)event.light;
+ TSL2561data["ftcd"] = (float)(event.light) / 10.764;
+ TSL2561data["wattsm2"] = (float)(event.light) / 683.0;
+
+ pub(subjectTSL12561toMQTT, TSL2561data);
+ } else {
+ Log.trace(F("Same lux value, do not send" CR));
+ }
+ } else {
+ Log.error(F("Failed to read from TSL2561" CR));
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/platformio.ini b/platformio.ini
index 79a9195..ea27f62 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -137,6 +137,7 @@ rtl_433_ESP = https://github.com/NorthernMan54/rtl_433_ESP.git#v0.0.4
emodbus = miq19/eModbus@1.0.0
gfSunInverter = https://github.com/BlackSmith/GFSunInverter.git#v1.0.1
decoder = https://github.com/theengs/decoder.git#v0.9.0
+ssd1306 = thingpulse/ESP8266 and ESP32 OLED driver for SSD1306 displays@^4.3.0
[env]
framework = arduino
@@ -859,15 +860,20 @@ build_flags =
# Reading battery level every minutes should be more than enought
'-DTimeBetweenReadingADC=60000'
-[env:heltec-wifi-lora-32-868]
+[env:heltec-wifi-lora-32-868] ; Heltec ESP32 Board with SSD1306 display
platform = ${com.esp32_platform}
board = heltec_wifi_lora_32
lib_deps =
${com-esp.lib_deps}
${libraries.wifimanager32}
${libraries.lora}
+ ${libraries.ssd1306}
build_flags =
${com-esp.build_flags}
+ ; *** OpenMQTTGateway Modules ***
+ '-DZboardHELTEC="HELTEC"'
+ '-DLOG_TO_LCD=true' ; Enable log to LCD
+ ; '-DLOG_LEVEL_LCD=LOG_LEVEL_TRACE' // default is notice log level
'-DZgatewayLORA="LORA"'
'-DLORA_BAND=868E6'
'-DGateway_Name="OpenMQTTGateway_ESP32_LORA"'
@@ -875,15 +881,20 @@ build_flags =
'-DTimeLedON=0.1'
'-DLED_SEND_RECEIVE_ON=1'
-[env:heltec-wifi-lora-32-915]
+[env:heltec-wifi-lora-32-915] ; Heltec ESP32 Board with SSD1306 display
platform = ${com.esp32_platform}
board = heltec_wifi_lora_32
lib_deps =
${com-esp.lib_deps}
${libraries.wifimanager32}
${libraries.lora}
+ ${libraries.ssd1306}
build_flags =
${com-esp.build_flags}
+ ; *** OpenMQTTGateway Modules ***
+ '-DZboardHELTEC="HELTEC"'
+ '-DLOG_TO_LCD=true' ; Enable log to LCD
+ ; '-DLOG_LEVEL_LCD=LOG_LEVEL_TRACE' // default is notice log level
'-DZgatewayLORA="LORA"'
'-DLORA_BAND=915E6'
'-DGateway_Name="OpenMQTTGateway_ESP32_LORA"'