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"'