// 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/outTEST" #define DOMOTICZ_IDX_1 0 #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! char deviceName[31]; char http_user[31]; char http_pass[31]; char mqtt_server[41]; int mqtt_port = MQTT_PORT; char mqtt_user[31]; char mqtt_pass[31]; char mqtt_willTopic[51]; int mqtt_willQos; boolean mqtt_willRetain; char mqtt_willMsg[31]; int usedRelaisCount = RELAIS_COUNT; int usedButtonsCount = BUTTONS_COUNT; char mqtt_topic_in[51]; char mqtt_topic_out[51]; char mqtt_topic_out_hold_1[51]; char mqtt_topic_out_hold_2[51]; char mqtt_topic_out_hold_3[51]; char mqtt_payload_out_hold_1[31]; char mqtt_payload_out_hold_2[31]; char mqtt_payload_out_hold_3[31]; int domoticzIdx[3] = {DOMOTICZ_IDX_1, DOMOTICZ_IDX_2, DOMOTICZ_IDX_3}; // initially set to 0, must be defined in config char domoticz_out_topic[51]; int relais_impulse[3] = {RELAIS1_IMPULSE, RELAIS2_IMPULSE, RELAIS3_IMPULSE}; // global variables long mqttLastReconnectAttempt = 0; 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[101]; // buffer for commands boolean cmdInQueue = false; // command is queued and will be processed next loop() run bool saveConfigToFlash = false; bool saveConfig2ToFlash = false; 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; } // set config default parameters strlcpy(deviceName, DEVICE_NAME, 31); strlcpy(mqtt_server, MQTT_SERVER, 41); strlcpy(mqtt_topic_in, MQTT_TOPIC_IN, 51); strlcpy(mqtt_topic_out, MQTT_TOPIC_OUT, 51); strlcpy(mqtt_topic_out_hold_1, BUTTON1_HOLD_TOPIC_OUT, 51); strlcpy(mqtt_topic_out_hold_2, BUTTON2_HOLD_TOPIC_OUT, 51); strlcpy(mqtt_topic_out_hold_3, BUTTON3_HOLD_TOPIC_OUT, 51); strlcpy(mqtt_payload_out_hold_1, BUTTON1_HOLD_PAYLOAD_OUT, 31); strlcpy(mqtt_payload_out_hold_2, BUTTON2_HOLD_PAYLOAD_OUT, 31); strlcpy(mqtt_payload_out_hold_3, BUTTON3_HOLD_PAYLOAD_OUT, 31); strlcpy(domoticz_out_topic, DOMOTICZ_OUT_TOPIC, 51); // changeable subscription topic, as domoticz supports different flat/hierarchical out-topics Serial.println("default config values loaded.."); SPIFFS.begin(); if (!SPIFFS.exists("/formatComplete.txt")) { Serial.println("Please wait 30 secs for SPIFFS to be formatted"); SPIFFS.format(); Serial.println("Spiffs formatted"); File f = SPIFFS.open("/formatComplete.txt", "w"); if (!f) { Serial.println("file open failed"); } else { f.println("Format Complete"); } } else { Serial.println("SPIFFS is formatted. Moving along..."); } //SPIFFS.format(); // load config from SPIFFS file if exists if (!loadConfig()) { Serial.println("Failed to load config"); } else { Serial.println("Config loaded"); } if (!loadConfig2()) { Serial.println("Failed to load config2"); } else { Serial.println("Config2 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."); delay(1000); } //void setup void loop() { checkSaveConfigTriggered(); 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