// pre compiletime config #define FIRMWARE_NAME "WiFi Switch" #define VERSION "0.1" #define COPYRIGHT " by Flo Kra" // default values, can later be overridden via configuration #define DEVICE_NAME "WiFi-Switch-1" #define RELAIS1_IMPULSE 0 #define RELAIS2_IMPULSE 0 #define RELAIS3_IMPULSE 0 #define MQTT_SERVER "10.1.1.11" #define MQTT_PORT 1883 #define MQTT_TOPIC_IN "Test/Switch/cmd" #define MQTT_TOPIC_OUT "Test/Switch/status" #define BUTTON1_HOLD_TOPIC_OUT "Test/Switch/hold/1" #define BUTTON2_HOLD_TOPIC_OUT "Test/Switch/hold/2" #define BUTTON3_HOLD_TOPIC_OUT "Test/Switch/hold/3" #define BUTTON1_HOLD_PAYLOAD_OUT "TOGGLE" #define BUTTON2_HOLD_PAYLOAD_OUT "TOGGLE" #define BUTTON3_HOLD_PAYLOAD_OUT "TOGGLE" #define DOMOTICZ_IN_TOPIC "domoticz/in" #define DOMOTICZ_OUT_TOPIC "domoticz/out" #define DOMOTICZ_IDX_1 209 #define DOMOTICZ_IDX_2 0 #define DOMOTICZ_IDX_3 0 // pin assignments, total relais/button count and default logic levels // can only be changed at compile time here // only use GPIO 0, 2, 4, 5, 12, 13, 14 !! // GPIO 0 and 2 have fixed 10k pullup and must remain high during boot (otherwise flash mode is entered) // GPIO 2 is also connected to builtin LED (active LOW) on most ESP boards, so donĀ“t care or strip it // GPIO 15 has 10k pull-down, so could only be used as active-high input // GPIO 1 = TX and 3=RX, so could only be used when no UART is needed (using only TX for debug output and RX as GPIO3 is possible) // some GPIOs go high for some time at boot, so using only active-low logic is advisable, particularly for switching outputs #define RELAIS_COUNT 3 #define PIN_RELAIS1 12 #define PIN_RELAIS2 4 #define PIN_RELAIS3 5 #define BUTTONS_COUNT 3 #define PIN_BUTTON1 0 #define PIN_BUTTON2 2 #define PIN_BUTTON3 13 #define RELAISONSTATE LOW #define BUTTONONSTATE LOW #include "PersWiFiManager.h" #include #include #include #include #include #include #include #include #include #ifndef MESSZ #define MESSZ 405 // Max number of characters in JSON message string (4 x DS18x20 sensors) #endif // Max message size calculated by PubSubClient is (MQTT_MAX_PACKET_SIZE < 5 + 2 + strlen(topic) + plength) #if (MQTT_MAX_PACKET_SIZE -TOPSZ -7) < MESSZ // If the max message size is too small, throw an error at compile time // See pubsubclient.c line 359 #error "MQTT_MAX_PACKET_SIZE is too small in libraries/PubSubClient/src/PubSubClient.h, increase it to at least 512" #endif // config variables - do not change here! const char* deviceName = DEVICE_NAME; const char* mqtt_server = MQTT_SERVER; int mqtt_port = MQTT_PORT; const char* mqtt_topic_in = MQTT_TOPIC_IN; const char* mqtt_topic_out = MQTT_TOPIC_OUT; const char* mqtt_topic_out_hold[3] = { BUTTON1_HOLD_TOPIC_OUT, BUTTON2_HOLD_TOPIC_OUT, BUTTON3_HOLD_TOPIC_OUT }; const char* mqtt_payload_out_hold[3] = { BUTTON1_HOLD_PAYLOAD_OUT, BUTTON2_HOLD_PAYLOAD_OUT, BUTTON3_HOLD_PAYLOAD_OUT }; int domoticzIdx[3] = {DOMOTICZ_IDX_1, DOMOTICZ_IDX_2, DOMOTICZ_IDX_3}; // initially set to 0, must be defined in config const char* domoticz_out_topic = DOMOTICZ_OUT_TOPIC; // changeable subscription topic, as domoticz supports different flat/hierarchical out-topics int relais_impulse[3] = {RELAIS1_IMPULSE, RELAIS2_IMPULSE, RELAIS3_IMPULSE}; // global variables long mqttLastReconnectAttempt = 0; int usedRelaisCount = RELAIS_COUNT; bool relais_state[RELAIS_COUNT]; bool relais_setState[RELAIS_COUNT]; byte relais_pins[] = {PIN_RELAIS1 #ifdef PIN_RELAIS2 , PIN_RELAIS2 #endif #ifdef PIN_RELAIS3 , PIN_RELAIS3 #endif }; byte buttons_pins[] = {PIN_BUTTON1 #ifdef PIN_BUTTON2 , PIN_BUTTON2 #endif #ifdef PIN_BUTTON3 , PIN_BUTTON3 #endif }; boolean useDomoticz = false; // will be set to true in setup() if idx-values other than 0 are configured boolean domoticzOutParseData = false; // indicates that domoticz/out json data is buffered, will then be parsed in next loop() run boolean domoticzOutParserBusy = false; // indicates that domoticz/out json data is currently processed - no futher data will be accepted until finished char domoticzOutPayload[450]; // buffer for domoticz/out data boolean updateDomoticz[3] = { true, true, true }; // flag to update domoticz for device 1-3 (preventing infinite loop if triggered by domoticz/out via mqtt) byte lastSwitchSource[3] = {0, 0, 0}; // 0 = button, 1 = serial/mqtt cmd, 2 = web, 3 = domoticz/out unsigned long lastSwitchTime[3] = { 0, 0, 0 }; // this is set to millis() when relais was toggled by button or web. domoticz/out updates for this Idx are then filtered out for [dismissUpdateFromDomoticzTimeout] int dismissUpdateFromDomoticzTimeout = 1500; char cmdPayload[100]; // buffer for commands boolean cmdInQueue = false; // command is queued and will be processed next loop() run WiFiClient espClient; PubSubClient mqttclient(espClient); ESP8266WebServer httpServer(80); DNSServer dnsServer; PersWiFiManager persWM(httpServer, dnsServer); ESP8266HTTPUpdateServer httpUpdater; void setup() { Serial.begin(115200); delay(500); Serial.println(); Serial.print(FIRMWARE_NAME); Serial.print(" v"); Serial.print(VERSION); Serial.println(COPYRIGHT); Serial.println("starting..."); Serial.println("Mounting FS..."); if (!SPIFFS.begin()) { Serial.println("Failed to mount file system"); return; } if (!loadConfig()) { Serial.println("Failed to load config"); } else { Serial.println("Config loaded"); } // set relais pin modes for (int i = 0; i < RELAIS_COUNT; i++) { pinMode(relais_pins[i], OUTPUT); digitalWrite(relais_pins[i], HIGH); } // set button pin modes pinMode(PIN_BUTTON1, INPUT_PULLUP); #ifdef PIN_BUTTON2 pinMode(PIN_BUTTON2, INPUT_PULLUP); #endif #ifdef PIN_BUTTON3 pinMode(PIN_BUTTON3, INPUT_PULLUP); #endif checkUseDomoticz(); delay(500); //optional code handlers to run everytime wifi is connected... persWM.onConnect([]() { Serial.print("wifi connected to "); Serial.print(WiFi.SSID()); Serial.print(", IP: "); Serial.println(WiFi.localIP()); }); //...or AP mode is started persWM.onAp([]() { Serial.print("AP MODE: "); Serial.println(persWM.getApSsid()); }); //sets network name for AP mode persWM.setApCredentials(DEVICE_NAME); //persWM.setApCredentials(DEVICE_NAME, "password"); optional password //make connecting/disconnecting non-blocking persWM.setConnectNonBlock(true); //in non-blocking mode, program will continue past this point without waiting persWM.begin(); //persWM.begin("KS61T5SH", "Gurken651salat"); httpServerInit(); mqttClientInit(); Serial.println("setup complete."); } //void setup void loop() { relais_handleImpulseTimeout(); //everySecond(); persWM.handleWiFi(); //in non-blocking mode, handleWiFi must be called in the main loop dnsServer.processNextRequest(); httpServer.handleClient(); mqttHandleConnection(); checkButtonStates(); evalCmd(); //handleRelaisSwitching(); if ( domoticzOutParseData ) parseDomoticzOut(); if (Serial.available()) serialEvent(); delay(50); // save energy by sleeping } //void loop