// pre compiletime config #define SPIFFS_DBG #define SPIFFS_USE_MAGIC #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/Switch1/cmd" #define MQTT_TOPIC_OUT "Test/Switch1/status" #define BUTTON_DEBOUNCE_TIME 120 #define BUTTON_HOLD_TIME 750 #define BUTTON1_TOPIC_OUT "" #define BUTTON2_TOPIC_OUT "" #define BUTTON3_TOPIC_OUT "" #define BUTTON1_PAYLOAD_OUT "" #define BUTTON2_PAYLOAD_OUT "" #define BUTTON3_PAYLOAD_OUT "" #define BUTTON1_HOLD_TOPIC_OUT "" #define BUTTON2_HOLD_TOPIC_OUT "" #define BUTTON3_HOLD_TOPIC_OUT "" #define BUTTON1_HOLD_PAYLOAD_OUT "" #define BUTTON2_HOLD_PAYLOAD_OUT "" #define BUTTON3_HOLD_PAYLOAD_OUT "" #define DOMOTICZ_IN_TOPIC "domoticz/in" #define DOMOTICZ_OUT_TOPIC "domoticz/out" #define DOMOTICZ_IDX_1 0 #define DOMOTICZ_IDX_2 0 #define DOMOTICZ_IDX_3 0 #define CLEARCONF_TOKEN "TUES" // 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 12 //#define PIN_BUTTON3 13 //#define PIN_LED1 0 //#define PIN_LED2 0 //#define PIN_LED3 0 // default logic levels #define RELAISONSTATE LOW #define BUTTONONSTATE LOW #define LEDONSTATE LOW #include "PersWiFiManager.h" #include #include #include #include #include #include #include "PubSubClient.h" #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 = 2; boolean mqtt_willRetain = false; char mqtt_willMsg[31]; char domoticz_out_topic[55]; boolean mqtt_outRetain = false; char mqtt_topic_in[51]; char mqtt_topic_out[51]; boolean mqtt_btnRetain = false; char mqtt_topic_out_1[51]; char mqtt_topic_out_2[51]; char mqtt_topic_out_3[51]; char mqtt_payload_out_1[31]; char mqtt_payload_out_2[31]; char mqtt_payload_out_3[31]; 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]; char mqtt_allOnOffTopic[51]; int domoticzIdx[3] = {DOMOTICZ_IDX_1, DOMOTICZ_IDX_2, DOMOTICZ_IDX_3}; // initially set to 0, must be defined in config int relais_impulse[3] = {RELAIS1_IMPULSE, RELAIS2_IMPULSE, RELAIS3_IMPULSE}; boolean relais_invert[3] = {false, false, false}; boolean button_invert[3] = {false, false, false}; boolean led_invert[3] = {false, false, false}; int debounceTime = BUTTON_DEBOUNCE_TIME; int buttonHoldTime = BUTTON_HOLD_TIME; boolean relais_enabled[3] = {true, false, false}; boolean button_enabled[3] = {true, false, false}; boolean led_enabled[3] = {false, false, false}; byte hldToRel[3] = {0, 0, 0}; // global variables long mqttLastReconnectAttempt = 0; int mqttReconnects = 0; boolean relais_state[RELAIS_COUNT]; byte relais_pins[3]; byte buttons_pins[3]; byte leds_pins[3]; 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; bool saveConfigHwToFlash = false; byte mqttMode = 0; WiFiClient espClient; void mqttCallback(char* topic, byte* payload, unsigned int length); 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..."); // set config default parameters #ifdef PIN_RELAIS1 relais_pins[0] = PIN_RELAIS1; #endif #ifdef PIN_RELAIS2 relais_pins[1] = PIN_RELAIS2; #endif #ifdef PIN_RELAIS3 relais_pins[2] = PIN_RELAIS3; #endif #ifdef PIN_BUTTON1 buttons_pins[0] = PIN_BUTTON1; #endif #ifdef PIN_BUTTON2 buttons_pins[1] = PIN_BUTTON2; #endif #ifdef PIN_BUTTON3 buttons_pins[2] = PIN_BUTTON3; #endif #ifdef PIN_LED1 leds_pins[0] = PIN_LED1; #endif #ifdef PIN_LED2 leds_pins[1] = PIN_LED2; #endif #ifdef PIN_LED3 leds_pins[2] = PIN_LED3; #endif 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_1, BUTTON1_TOPIC_OUT, 51); strlcpy(mqtt_topic_out_2, BUTTON2_TOPIC_OUT, 51); strlcpy(mqtt_topic_out_3, BUTTON3_TOPIC_OUT, 51); strlcpy(mqtt_payload_out_1, BUTTON1_PAYLOAD_OUT, 31); strlcpy(mqtt_payload_out_2, BUTTON2_PAYLOAD_OUT, 31); strlcpy(mqtt_payload_out_3, BUTTON3_PAYLOAD_OUT, 31); 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.."); Serial.println("Mounting FS..."); if (!SPIFFS.begin()) { Serial.println("Failed to mount file system"); return; } //SPIFFS.format(); //Serial.print("Format SPIFFS complete."); 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"); } f.close(); } else { Serial.println("SPIFFS is formatted. Moving along..."); } // // load config from SPIFFS if files exist if (!loadConfigHw()) { Serial.println("Failed to load confhw.json"); } else { Serial.println("confhw.json loaded"); } if (!loadConfig()) { Serial.println("Failed to load conf.json"); } else { Serial.println("conf.json loaded"); } if (!loadConfig2()) { Serial.println("Failed to load conf2.json"); } else { Serial.println("conf2.json loaded"); } mqttPrepareConnection(); // set relais pin modes for (int i = 0; i < 3; i++) { if (relais_enabled[i]) { if (relais_pins[i] == 0 || relais_pins[i] == 2 || relais_pins[i] == 4 || relais_pins[i] == 5 || relais_pins[i] == 12 || relais_pins[i] == 13 || relais_pins[i] == 14 || relais_pins[i] == 15) { pinMode(relais_pins[i], OUTPUT); if (relais_invert[i]) digitalWrite(relais_pins[i], RELAISONSTATE); else digitalWrite(relais_pins[i], !RELAISONSTATE); Serial.print("set GPIO "); Serial.print(relais_pins[i]); Serial.print(" as Output for Relais "); Serial.println(i + 1); } } } // set button pin modes for (int i = 0; i < 3; i++) { if (button_enabled[i]) { if (buttons_pins[i] == 0 || buttons_pins[i] == 2 || buttons_pins[i] == 4 || buttons_pins[i] == 5 || buttons_pins[i] == 12 || buttons_pins[i] == 13 || buttons_pins[i] == 14 || buttons_pins[i] == 15) { if (button_invert[i]) pinMode(buttons_pins[i], INPUT); else pinMode(buttons_pins[i], INPUT_PULLUP); Serial.print("set GPIO "); Serial.print(buttons_pins[i]); Serial.print(" as Input for Button "); Serial.println(i + 1); } } } // set LED pin modes for (int i = 0; i < 3; i++) { if (led_enabled[i]) { if (leds_pins[i] == 0 || leds_pins[i] == 2 || leds_pins[i] == 4 || leds_pins[i] == 5 || leds_pins[i] == 12 || leds_pins[i] == 13 || leds_pins[i] == 14 || leds_pins[i] == 15) { pinMode(leds_pins[i], OUTPUT); if (led_invert[i]) digitalWrite(leds_pins[i], LEDONSTATE); else digitalWrite(leds_pins[i], !LEDONSTATE); Serial.print("set GPIO "); Serial.print(leds_pins[i]); Serial.print(" as Output for status LED "); Serial.println(i + 1); } } } 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(); delay(500); httpServerInit(); mqttClientInit(); Serial.println("setup complete."); delay(1000); } //void setup void loop() { checkMillis(); persWM.handleWiFi(); //in non-blocking mode, handleWiFi must be called in the main loop yield(); dnsServer.processNextRequest(); httpServer.handleClient(); yield(); mqttHandleConnection(); checkButtonStates(); yield(); evalCmd(); if ( domoticzOutParseData ) { parseDomoticzOut(); yield(); } if (Serial.available()) { serialEvent(); yield(); } } //void loop