Browse Source

0.1.1, erste feature-complete alpha

FloKra 6 years ago
parent
commit
d6820d2cb7

+ 14 - 0
Compile settings.txt

@@ -0,0 +1,14 @@
+
+for Sonoff Basic, ESP-01:
+
+Generic ESP8266 Module
+Flash MOde: DIO
+Flash Size: 1M (128k SPIFFS)
+
+
+Sonoff Touch
+
+As the Sonoff Touch is based on the ESP8285 using Flash Mode DOUT you will have to make some changes to the proposed Arduino IDE settings as follows:
+
+    Tools → Board → Generic ESP8285 Module
+    Flash Size: 1M (128K SPIFFS)

+ 40 - 0
Hardware - SW config.txt

@@ -0,0 +1,40 @@
+
+
+
+
+Eigenbau Einzelschalter, ESP-01 Modul
+
+#define RELAIS_COUNT 1
+#define PIN_RELAIS1 2
+#define PIN_RELAIS2 4
+#define PIN_RELAIS3 5
+
+#define BUTTONS_COUNT 1
+#define PIN_BUTTON1 0
+#define PIN_BUTTON2 12
+#define PIN_BUTTON3 13
+
+#define RELAISONSTATE LOW
+#define BUTTONONSTATE LOW
+
+-> Relais an GPIO-2, active LOW (PNP + NPN Transistor)
+-> Taster an GPIO-0, active LOW
+
+
+
+
+
+Sonoff Basic Modul
+
+#define RELAIS_COUNT 1
+#define PIN_RELAIS1 12
+#define PIN_RELAIS2 4
+#define PIN_RELAIS3 5
+
+#define BUTTONS_COUNT 1
+#define PIN_BUTTON1 0
+#define PIN_BUTTON2 2
+#define PIN_BUTTON3 13
+
+#define RELAISONSTATE HIGH
+#define BUTTONONSTATE LOW

+ 99 - 0
WiFiSwitch.txt

@@ -0,0 +1,99 @@
+
+
+
+COMMANDS
+
+Alle commands können entweder über die UART, oder über MQTT am In-Topic kommen. 
+
+toggle x	Relais Nummer x (1-3) umschalten
+on x		Relais Nummer x (1-3) ein
+off x		Relais Nummer x (1-3) aus
+all on		alle Relais ein
+all off		alle Relais aus
+
+loadconf	komplette Konfiguration aus dem SPIFFS laden
+restart		restart des ESP
+save		speichert alle Konfigurationsvariablen auf den SPIFFS (aufgeteilt in derzeit 3 files)
+getconf		gibt die aktuellen Konfigurationsvariablen seriell aus (über MQTT derzeit zwar aufrufbar aber die Daten werden nur seriell ausgegeben)
+delconf		formatiert den SPIFFS nach einem reboot. Konfiguration damit gelöscht (ausgenommen Wifi-Konfiguration, diese ist wo anders gespeichert)
+
+set param value		setzt Konfigurationsvariable param auf Wert value
+
+devName		device name
+httpUser	web-interface username, only applicable if user AND password is set
+httpPass	web-interface password, only applicable if user AND password is set
+mqttHost	MQTT broker hostname 
+mqttPort	MQTT broker port (1-65535)
+mqttUser	MQTT username
+mqttPass	MQTT password
+inTop		MQTT In-topic for commands
+outTop		MQTT Out-topic for status updates (Relais states will be published to "/[RelaisNr]" appended)
+outRet		MQTT Out retain-flag set (0 or 1)
+willTop		MQTT Last-Will topic (This is announced to the broker on connect. The broker then publishes the configured message to this topic when the connection is lost.)
+willQos		MQTT Last-Will QOS
+willRet		MQTT Last-Will retain flag
+willMsg		MQTT Last-Will message payload
+domOutTop	Domoticz Out-topic, will be subscribed to to directly interface with a Domoticz server. It should not be set to "domoticz/out" as there is a high amount of messages 
+			published. Instead set Domoticz-server MQTT Client Gateway interface to "out + /" ("combined") mode, create a Floorplan and add only the devices needed to it. 
+			outgoing messagen are on topic domoticz/out/${floorplan name}/${plan name} - which should be configured here
+
+Button topics: 
+For every button 2 MQTT topics and payloads can be set. One for short press, one for hold, which will be published on every button event. 
+
+btnRet		Button topic retain flag
+top1		button topic 1
+top2		button topic 2
+top3		button topic 3
+topHld1		button topic hold 1
+topHld2		button topic hold 2
+topHld3		button topic hold 3
+pld1		button payload 1
+pld2		button payload 2
+pld3		button payload 3
+pldHld1		button payload hold 1
+pldHld2		button payload hold 2
+pldHld3		button payload hold 3
+domIdx1		Domoticz Idx of relais 1
+domIdx2		Domoticz Idx of relais 2
+domIdx3		Domoticz Idx of relais 3
+allOnOffTop	Topic for all ON/OFF command. To be used for a central on/off switch that controls multiple devices. Should be set to a common, not device specific topic on all devices that should be grouped. 
+debtime		button debounce time in ms (default = 120)
+hldtime		button hold time in ms (default = 750)
+
+GPIO-assignment:
+ioRel1		Relais 1
+ioRel2		Relais 2
+ioRel3		Relais 3
+ioBtn1		Button 1
+ioBtn2		Button 2
+ioBtn3		Button 3
+ioLed1		status LED 1
+ioLed2		status LED 2
+ioLed3		status LED 3
+
+invert logic level (0 = default = active LOW, 1 = active HIGH)
+invRel1		invert Relais 1
+invRel2		invert Relais 2
+invRel3		invert Relais 3
+invBtn1		invert Button 1
+invBtn2		invert Button 2
+invBtn3		invert Button 3
+invLed1		invert LED 1
+invLed2		invert LED 2
+invLed3		invert LED 3
+
+impulse mode for relais - if set to >0, the relais will be switched off after specified time in factors of 0.1 s (10 = 1s, 1 = 100ms ...)
+puls1		impulse Relais 1
+puls2		impulse Relais 2
+puls3		impulse Relais 3
+
+enable hardware
+enaRel1		Relais 1
+enaRel2		Relais 2
+enaRel3		Relais 3
+enaBtn1		Button 1
+enaBtn2		Button 2
+enaBtn3		Button 3
+enaLed1		status LED 1
+enaLed2		status LED 2
+enaLed3		status LED 3

+ 14 - 0
changes an Libs.txt

@@ -0,0 +1,14 @@
+
+
+PubSubClient.h
+
+
+// MQTT_MAX_PACKET_SIZE : Maximum packet size
+#ifndef MQTT_MAX_PACKET_SIZE
+#define MQTT_MAX_PACKET_SIZE 512 // need to fix this here, because this define cannot be overruled within the Arduino sketch...
+#endif
+
+// MQTT_KEEPALIVE : keepAlive interval in Seconds
+#ifndef MQTT_KEEPALIVE
+#define MQTT_KEEPALIVE 30
+#endif

+ 12 - 0
clearESP-Sketch/clearESP/clearESP.ino

@@ -0,0 +1,12 @@
+#include <FS.h>
+void setup() {
+  // put your setup code here, to run once:
+  SPIFFS.format();
+  ESP.eraseConfig();
+  ESP.reset();
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+
+}

+ 35 - 16
src/WiFiSwitch/Buttonhandling.ino

@@ -1,51 +1,70 @@
 bool buttonCurrentState[3] = {HIGH, HIGH, HIGH};
 bool buttonLastState[3] = {HIGH, HIGH, HIGH};
 unsigned long buttonDownMillis[3] = {0, 0, 0};
-unsigned long buttonHoldTime[3] = {0, 0, 0};
+unsigned long buttonCurrentHoldTime[3] = {0, 0, 0};
 bool buttonFired[3] = {false, false, false};
 bool buttonHoldFired[3] = {false, false, false};
 unsigned long lastButtonPress[3] = {0, 0, 0};
 
-int debounceTime = 120;
-
-void buttonAction(byte relnr) {
-  lastSwitchSource[relnr] = 0;
-  relaisToggle(relnr);
+void buttonAction(byte btn) {
+  lastSwitchSource[btn] = 0;
+  relaisToggle(btn);
+  switch (btn) {
+    case 0:
+      if (strlen(mqtt_topic_out_1) > 0) mqttclient.publish(mqtt_topic_out_1, mqtt_payload_out_1, mqtt_btnRetain);
+      break;
+    case 1:
+      if (strlen(mqtt_topic_out_2) > 0) mqttclient.publish(mqtt_topic_out_2, mqtt_payload_out_2, mqtt_btnRetain);
+      break;
+    case 2:
+      if (strlen(mqtt_topic_out_3) > 0) mqttclient.publish(mqtt_topic_out_3, mqtt_payload_out_3, mqtt_btnRetain);
+      break;
+  }
 }
 
 void buttonHoldAction(byte btn) {
+  if (hldToRel[btn] > 0) {
+    relaisToggle( hldToRel[ btn - 1 ] );
+  }
   switch (btn) {
     case 0:
-      mqttclient.publish(mqtt_topic_out_hold_1, mqtt_payload_out_hold_1);
+      if (strlen(mqtt_topic_out_hold_1) > 0) mqttclient.publish(mqtt_topic_out_hold_1, mqtt_payload_out_hold_1, mqtt_btnRetain);
       break;
     case 1:
-      mqttclient.publish(mqtt_topic_out_hold_2, mqtt_payload_out_hold_2);
+      if (strlen(mqtt_topic_out_hold_2) > 0) mqttclient.publish(mqtt_topic_out_hold_2, mqtt_payload_out_hold_2, mqtt_btnRetain);
       break;
     case 2:
-      mqttclient.publish(mqtt_topic_out_hold_3, mqtt_payload_out_hold_3);
+      if (strlen(mqtt_topic_out_hold_3) > 0) mqttclient.publish(mqtt_topic_out_hold_3, mqtt_payload_out_hold_3, mqtt_btnRetain);
       break;
   }
+  if (led_enabled[btn]) {
+    // blink LED when hold action is triggered
+    bool ledState = digitalRead(leds_pins[btn]);
+    digitalWrite(leds_pins[btn], !ledState);
+    delay(500);
+    digitalWrite(leds_pins[btn], ledState);
+  }
 }
 
 void checkButtonStates() {
   for (int i = 0; i < BUTTONS_COUNT; i++) {
-    checkButtonState(i);
+    if (button_enabled[i]) checkButtonState(i);
   }
 }
 
 void checkButtonState(byte btn) {
-  buttonHoldTime[btn] = millis() - buttonDownMillis[btn];
+  buttonCurrentHoldTime[btn] = millis() - buttonDownMillis[btn];
   buttonCurrentState[btn] = digitalRead(buttons_pins[btn]);
   if (buttonCurrentState[btn] == LOW && buttonLastState[btn] == HIGH) { // button was unpressed and is millis() pressed
     buttonDownMillis[btn] = millis();
     buttonLastState[btn] = buttonCurrentState[btn];
   }
   else if (buttonCurrentState[btn] == LOW && buttonLastState[btn] == LOW) { // button is held
-    if ( buttonHoldTime[btn] > debounceTime ) {
+    if ( buttonCurrentHoldTime[btn] > debounceTime ) {
       buttonLastState[btn] = buttonCurrentState[btn];
-      if ( buttonHoldTime[btn] > 750 && !buttonHoldFired[btn] ) {
+      if ( buttonCurrentHoldTime[btn] > buttonHoldTime && !buttonHoldFired[btn] ) {
         // button is held longer
-        if ( (millis() - lastButtonPress[btn]) > 700 ) { // mitigate double triggering
+        if ( (millis() - lastButtonPress[btn]) > 500 ) { // avoid double triggering
           lastButtonPress[btn] = millis();
           Serial.print("Button ");
           Serial.print(btn + 1);
@@ -57,10 +76,10 @@ void checkButtonState(byte btn) {
     }
   }
   else if (buttonCurrentState[btn] == HIGH && buttonLastState[btn] == LOW) { // button is released again
-    if (buttonHoldTime[btn] > debounceTime) {          // entprellung
+    if (buttonCurrentHoldTime[btn] > debounceTime) {          // entprellung
       buttonLastState[btn] = buttonCurrentState[btn];
       if (!buttonHoldFired[btn] && !buttonFired[btn]) {
-        if ( (millis() - lastButtonPress[btn]) > 700 ) { // mitigate double triggering
+        if ( (millis() - lastButtonPress[btn]) > 500 ) { // avoid double triggering
           lastButtonPress[btn] = millis();
           Serial.print("Button ");
           Serial.print(btn + 1);

+ 590 - 0
src/WiFiSwitch/PubSubClient.cpp

@@ -0,0 +1,590 @@
+/*
+  PubSubClient.cpp - A simple client for MQTT.
+  Nick O'Leary
+  http://knolleary.net
+*/
+
+#include "PubSubClient.h"
+#include "Arduino.h"
+
+PubSubClient::PubSubClient() {
+    this->_state = MQTT_DISCONNECTED;
+    this->_client = NULL;
+    this->stream = NULL;
+    setCallback(NULL);
+}
+
+PubSubClient::PubSubClient(Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setClient(client);
+    this->stream = NULL;
+}
+
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr, port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr, port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip, port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip, port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+boolean PubSubClient::connect(const char *id) {
+    return connect(id,NULL,NULL,0,0,0,0);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
+    return connect(id,user,pass,0,0,0,0);
+}
+
+boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
+    return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
+    if (!connected()) {
+        int result = 0;
+
+        if (domain != NULL) {
+            result = _client->connect(this->domain, this->port);
+        } else {
+            result = _client->connect(this->ip, this->port);
+        }
+        if (result == 1) {
+            nextMsgId = 1;
+            // Leave room in the buffer for header and variable length field
+            uint16_t length = 5;
+            unsigned int j;
+
+#if MQTT_VERSION == MQTT_VERSION_3_1
+            uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
+#define MQTT_HEADER_VERSION_LENGTH 9
+#elif MQTT_VERSION == MQTT_VERSION_3_1_1
+            uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
+#define MQTT_HEADER_VERSION_LENGTH 7
+#endif
+            for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
+                buffer[length++] = d[j];
+            }
+
+            uint8_t v;
+            if (willTopic) {
+                v = 0x06|(willQos<<3)|(willRetain<<5);
+            } else {
+                v = 0x02;
+            }
+
+            if(user != NULL) {
+                v = v|0x80;
+
+                if(pass != NULL) {
+                    v = v|(0x80>>1);
+                }
+            }
+
+            buffer[length++] = v;
+
+            buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
+            buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
+            length = writeString(id,buffer,length);
+            if (willTopic) {
+                length = writeString(willTopic,buffer,length);
+                length = writeString(willMessage,buffer,length);
+            }
+
+            if(user != NULL) {
+                length = writeString(user,buffer,length);
+                if(pass != NULL) {
+                    length = writeString(pass,buffer,length);
+                }
+            }
+
+            write(MQTTCONNECT,buffer,length-5);
+
+            lastInActivity = lastOutActivity = millis();
+
+            while (!_client->available()) {
+                unsigned long t = millis();
+                if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
+                    _state = MQTT_CONNECTION_TIMEOUT;
+                    _client->stop();
+                    return false;
+                }
+            }
+            uint8_t llen;
+            uint16_t len = readPacket(&llen);
+
+            if (len == 4) {
+                if (buffer[3] == 0) {
+                    lastInActivity = millis();
+                    pingOutstanding = false;
+                    _state = MQTT_CONNECTED;
+                    return true;
+                } else {
+                    _state = buffer[3];
+                }
+            }
+            _client->stop();
+        } else {
+            _state = MQTT_CONNECT_FAILED;
+        }
+        return false;
+    }
+    return true;
+}
+
+// reads a byte into result
+boolean PubSubClient::readByte(uint8_t * result) {
+   uint32_t previousMillis = millis();
+   while(!_client->available()) {
+     uint32_t currentMillis = millis();
+     if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
+       return false;
+     }
+   }
+   *result = _client->read();
+   return true;
+}
+
+// reads a byte into result[*index] and increments index
+boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
+  uint16_t current_index = *index;
+  uint8_t * write_address = &(result[current_index]);
+  if(readByte(write_address)){
+    *index = current_index + 1;
+    return true;
+  }
+  return false;
+}
+
+uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
+    uint16_t len = 0;
+    if(!readByte(buffer, &len)) return 0;
+    bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
+    uint32_t multiplier = 1;
+    uint16_t length = 0;
+    uint8_t digit = 0;
+    uint16_t skip = 0;
+    uint8_t start = 0;
+
+    do {
+        if(!readByte(&digit)) return 0;
+        buffer[len++] = digit;
+        length += (digit & 127) * multiplier;
+        multiplier *= 128;
+    } while ((digit & 128) != 0);
+    *lengthLength = len-1;
+
+    if (isPublish) {
+        // Read in topic length to calculate bytes to skip over for Stream writing
+        if(!readByte(buffer, &len)) return 0;
+        if(!readByte(buffer, &len)) return 0;
+        skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
+        start = 2;
+        if (buffer[0]&MQTTQOS1) {
+            // skip message id
+            skip += 2;
+        }
+    }
+
+    for (uint16_t i = start;i<length;i++) {
+        if(!readByte(&digit)) return 0;
+        if (this->stream) {
+            if (isPublish && len-*lengthLength-2>skip) {
+                this->stream->write(digit);
+            }
+        }
+        if (len < MQTT_MAX_PACKET_SIZE) {
+            buffer[len] = digit;
+        }
+        len++;
+    }
+
+    if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
+        len = 0; // This will cause the packet to be ignored.
+    }
+
+    return len;
+}
+
+boolean PubSubClient::loop() {
+    if (connected()) {
+        unsigned long t = millis();
+        if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
+            if (pingOutstanding) {
+                this->_state = MQTT_CONNECTION_TIMEOUT;
+                _client->stop();
+                return false;
+            } else {
+                buffer[0] = MQTTPINGREQ;
+                buffer[1] = 0;
+                _client->write(buffer,2);
+                lastOutActivity = t;
+                lastInActivity = t;
+                pingOutstanding = true;
+            }
+        }
+        if (_client->available()) {
+            uint8_t llen;
+            uint16_t len = readPacket(&llen);
+            uint16_t msgId = 0;
+            uint8_t *payload;
+            if (len > 0) {
+                lastInActivity = t;
+                uint8_t type = buffer[0]&0xF0;
+                if (type == MQTTPUBLISH) {
+                    if (callback) {
+                        uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2];
+                        char topic[tl+1];
+                        for (uint16_t i=0;i<tl;i++) {
+                            topic[i] = buffer[llen+3+i];
+                        }
+                        topic[tl] = 0;
+                        // msgId only present for QOS>0
+                        if ((buffer[0]&0x06) == MQTTQOS1) {
+                            msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
+                            payload = buffer+llen+3+tl+2;
+                            callback(topic,payload,len-llen-3-tl-2);
+
+                            buffer[0] = MQTTPUBACK;
+                            buffer[1] = 2;
+                            buffer[2] = (msgId >> 8);
+                            buffer[3] = (msgId & 0xFF);
+                            _client->write(buffer,4);
+                            lastOutActivity = t;
+
+                        } else {
+                            payload = buffer+llen+3+tl;
+                            callback(topic,payload,len-llen-3-tl);
+                        }
+                    }
+                } else if (type == MQTTPINGREQ) {
+                    buffer[0] = MQTTPINGRESP;
+                    buffer[1] = 0;
+                    _client->write(buffer,2);
+                } else if (type == MQTTPINGRESP) {
+                    pingOutstanding = false;
+                }
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+boolean PubSubClient::publish(const char* topic, const char* payload) {
+    return publish(topic,(const uint8_t*)payload,strlen(payload),false);
+}
+
+boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
+    return publish(topic,(const uint8_t*)payload,strlen(payload),retained);
+}
+
+boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
+    return publish(topic, payload, plength, false);
+}
+
+boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
+    if (connected()) {
+        if (MQTT_MAX_PACKET_SIZE < 5 + 2+strlen(topic) + plength) {
+            // Too long
+            return false;
+        }
+        // Leave room in the buffer for header and variable length field
+        uint16_t length = 5;
+        length = writeString(topic,buffer,length);
+        uint16_t i;
+        for (i=0;i<plength;i++) {
+            buffer[length++] = payload[i];
+        }
+        uint8_t header = MQTTPUBLISH;
+        if (retained) {
+            header |= 1;
+        }
+        return write(header,buffer,length-5);
+    }
+    return false;
+}
+
+boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
+    uint8_t llen = 0;
+    uint8_t digit;
+    unsigned int rc = 0;
+    uint16_t tlen;
+    unsigned int pos = 0;
+    unsigned int i;
+    uint8_t header;
+    unsigned int len;
+
+    if (!connected()) {
+        return false;
+    }
+
+    tlen = strlen(topic);
+
+    header = MQTTPUBLISH;
+    if (retained) {
+        header |= 1;
+    }
+    buffer[pos++] = header;
+    len = plength + 2 + tlen;
+    do {
+        digit = len % 128;
+        len = len / 128;
+        if (len > 0) {
+            digit |= 0x80;
+        }
+        buffer[pos++] = digit;
+        llen++;
+    } while(len>0);
+
+    pos = writeString(topic,buffer,pos);
+
+    rc += _client->write(buffer,pos);
+
+    for (i=0;i<plength;i++) {
+        rc += _client->write((char)pgm_read_byte_near(payload + i));
+    }
+
+    lastOutActivity = millis();
+
+    return rc == tlen + 4 + plength;
+}
+
+boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
+    uint8_t lenBuf[4];
+    uint8_t llen = 0;
+    uint8_t digit;
+    uint8_t pos = 0;
+    uint16_t rc;
+    uint16_t len = length;
+    do {
+        digit = len % 128;
+        len = len / 128;
+        if (len > 0) {
+            digit |= 0x80;
+        }
+        lenBuf[pos++] = digit;
+        llen++;
+    } while(len>0);
+
+    buf[4-llen] = header;
+    for (int i=0;i<llen;i++) {
+        buf[5-llen+i] = lenBuf[i];
+    }
+
+#ifdef MQTT_MAX_TRANSFER_SIZE
+    uint8_t* writeBuf = buf+(4-llen);
+    uint16_t bytesRemaining = length+1+llen;  //Match the length type
+    uint8_t bytesToWrite;
+    boolean result = true;
+    while((bytesRemaining > 0) && result) {
+        bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
+        rc = _client->write(writeBuf,bytesToWrite);
+        result = (rc == bytesToWrite);
+        bytesRemaining -= rc;
+        writeBuf += rc;
+    }
+    return result;
+#else
+    rc = _client->write(buf+(4-llen),length+1+llen);
+    lastOutActivity = millis();
+    return (rc == 1+llen+length);
+#endif
+}
+
+boolean PubSubClient::subscribe(const char* topic) {
+    return subscribe(topic, 0);
+}
+
+boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
+    if (qos < 0 || qos > 1) {
+        return false;
+    }
+    if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
+        // Too long
+        return false;
+    }
+    if (connected()) {
+        // Leave room in the buffer for header and variable length field
+        uint16_t length = 5;
+        nextMsgId++;
+        if (nextMsgId == 0) {
+            nextMsgId = 1;
+        }
+        buffer[length++] = (nextMsgId >> 8);
+        buffer[length++] = (nextMsgId & 0xFF);
+        length = writeString((char*)topic, buffer,length);
+        buffer[length++] = qos;
+        return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5);
+    }
+    return false;
+}
+
+boolean PubSubClient::unsubscribe(const char* topic) {
+    if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
+        // Too long
+        return false;
+    }
+    if (connected()) {
+        uint16_t length = 5;
+        nextMsgId++;
+        if (nextMsgId == 0) {
+            nextMsgId = 1;
+        }
+        buffer[length++] = (nextMsgId >> 8);
+        buffer[length++] = (nextMsgId & 0xFF);
+        length = writeString(topic, buffer,length);
+        return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5);
+    }
+    return false;
+}
+
+void PubSubClient::disconnect() {
+    buffer[0] = MQTTDISCONNECT;
+    buffer[1] = 0;
+    _client->write(buffer,2);
+    _state = MQTT_DISCONNECTED;
+    _client->stop();
+    lastInActivity = lastOutActivity = millis();
+}
+
+uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
+    const char* idp = string;
+    uint16_t i = 0;
+    pos += 2;
+    while (*idp) {
+        buf[pos++] = *idp++;
+        i++;
+    }
+    buf[pos-i-2] = (i >> 8);
+    buf[pos-i-1] = (i & 0xFF);
+    return pos;
+}
+
+
+boolean PubSubClient::connected() {
+    boolean rc;
+    if (_client == NULL ) {
+        rc = false;
+    } else {
+        rc = (int)_client->connected();
+        if (!rc) {
+            if (this->_state == MQTT_CONNECTED) {
+                this->_state = MQTT_CONNECTION_LOST;
+                _client->flush();
+                _client->stop();
+            }
+        }
+    }
+    return rc;
+}
+
+PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
+    IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
+    return setServer(addr,port);
+}
+
+PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
+    this->ip = ip;
+    this->port = port;
+    this->domain = NULL;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
+    this->domain = domain;
+    this->port = port;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
+    this->callback = callback;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setClient(Client& client){
+    this->_client = &client;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setStream(Stream& stream){
+    this->stream = &stream;
+    return *this;
+}
+
+int PubSubClient::state() {
+    return this->_state;
+}

+ 144 - 0
src/WiFiSwitch/PubSubClient.h

@@ -0,0 +1,144 @@
+/*
+ PubSubClient.h - A simple client for MQTT.
+  Nick O'Leary
+  http://knolleary.net
+*/
+
+#ifndef PubSubClient_h
+#define PubSubClient_h
+
+#include <Arduino.h>
+#include "IPAddress.h"
+#include "Client.h"
+#include "Stream.h"
+
+#define MQTT_VERSION_3_1      3
+#define MQTT_VERSION_3_1_1    4
+
+// MQTT_VERSION : Pick the version
+//#define MQTT_VERSION MQTT_VERSION_3_1
+#ifndef MQTT_VERSION
+#define MQTT_VERSION MQTT_VERSION_3_1_1
+#endif
+
+// MQTT_MAX_PACKET_SIZE : Maximum packet size
+#ifndef MQTT_MAX_PACKET_SIZE
+#define MQTT_MAX_PACKET_SIZE 512 // need to fix this here, because this define cannot be overruled within the Arduino sketch...
+#endif
+
+// MQTT_KEEPALIVE : keepAlive interval in Seconds
+#ifndef MQTT_KEEPALIVE
+#define MQTT_KEEPALIVE 30
+#endif
+
+// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
+#ifndef MQTT_SOCKET_TIMEOUT
+#define MQTT_SOCKET_TIMEOUT 2
+#endif
+
+// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
+//  in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
+//  pass the entire MQTT packet in each write call.
+//#define MQTT_MAX_TRANSFER_SIZE 80
+
+// Possible values for client.state()
+#define MQTT_CONNECTION_TIMEOUT     -4
+#define MQTT_CONNECTION_LOST        -3
+#define MQTT_CONNECT_FAILED         -2
+#define MQTT_DISCONNECTED           -1
+#define MQTT_CONNECTED               0
+#define MQTT_CONNECT_BAD_PROTOCOL    1
+#define MQTT_CONNECT_BAD_CLIENT_ID   2
+#define MQTT_CONNECT_UNAVAILABLE     3
+#define MQTT_CONNECT_BAD_CREDENTIALS 4
+#define MQTT_CONNECT_UNAUTHORIZED    5
+
+#define MQTTCONNECT     1 << 4  // Client request to connect to Server
+#define MQTTCONNACK     2 << 4  // Connect Acknowledgment
+#define MQTTPUBLISH     3 << 4  // Publish message
+#define MQTTPUBACK      4 << 4  // Publish Acknowledgment
+#define MQTTPUBREC      5 << 4  // Publish Received (assured delivery part 1)
+#define MQTTPUBREL      6 << 4  // Publish Release (assured delivery part 2)
+#define MQTTPUBCOMP     7 << 4  // Publish Complete (assured delivery part 3)
+#define MQTTSUBSCRIBE   8 << 4  // Client Subscribe request
+#define MQTTSUBACK      9 << 4  // Subscribe Acknowledgment
+#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
+#define MQTTUNSUBACK    11 << 4 // Unsubscribe Acknowledgment
+#define MQTTPINGREQ     12 << 4 // PING Request
+#define MQTTPINGRESP    13 << 4 // PING Response
+#define MQTTDISCONNECT  14 << 4 // Client is Disconnecting
+#define MQTTReserved    15 << 4 // Reserved
+
+#define MQTTQOS0        (0 << 1)
+#define MQTTQOS1        (1 << 1)
+#define MQTTQOS2        (2 << 1)
+
+#ifdef ESP8266
+#include <functional>
+#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
+#else
+#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
+#endif
+
+class PubSubClient {
+private:
+   Client* _client;
+   uint8_t buffer[MQTT_MAX_PACKET_SIZE];
+   uint16_t nextMsgId;
+   unsigned long lastOutActivity;
+   unsigned long lastInActivity;
+   bool pingOutstanding;
+   MQTT_CALLBACK_SIGNATURE;
+   uint16_t readPacket(uint8_t*);
+   boolean readByte(uint8_t * result);
+   boolean readByte(uint8_t * result, uint16_t * index);
+   boolean write(uint8_t header, uint8_t* buf, uint16_t length);
+   uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
+   IPAddress ip;
+   const char* domain;
+   uint16_t port;
+   Stream* stream;
+   int _state;
+public:
+   PubSubClient();
+   PubSubClient(Client& client);
+   PubSubClient(IPAddress, uint16_t, Client& client);
+   PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
+   PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+   PubSubClient(uint8_t *, uint16_t, Client& client);
+   PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
+   PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+   PubSubClient(const char*, uint16_t, Client& client);
+   PubSubClient(const char*, uint16_t, Client& client, Stream&);
+   PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+
+   PubSubClient& setServer(IPAddress ip, uint16_t port);
+   PubSubClient& setServer(uint8_t * ip, uint16_t port);
+   PubSubClient& setServer(const char * domain, uint16_t port);
+   PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
+   PubSubClient& setClient(Client& client);
+   PubSubClient& setStream(Stream& stream);
+
+   boolean connect(const char* id);
+   boolean connect(const char* id, const char* user, const char* pass);
+   boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
+   boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
+   void disconnect();
+   boolean publish(const char* topic, const char* payload);
+   boolean publish(const char* topic, const char* payload, boolean retained);
+   boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
+   boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
+   boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
+   boolean subscribe(const char* topic);
+   boolean subscribe(const char* topic, uint8_t qos);
+   boolean unsubscribe(const char* topic);
+   boolean loop();
+   boolean connected();
+   int state();
+};
+
+
+#endif

+ 175 - 77
src/WiFiSwitch/WiFiSwitch.ino

@@ -14,20 +14,32 @@
 #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 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"
+
+#define MAIN_SLEEP 10
+
 
 // pin assignments, total relais/button count and default logic levels
 // can only be changed at compile time here
@@ -37,19 +49,24 @@
 // 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 1
-#define PIN_RELAIS1 2
-#define PIN_RELAIS2 4
-#define PIN_RELAIS3 5
+#define RELAIS_COUNT 3
+#define PIN_RELAIS1 12
+//#define PIN_RELAIS2 4
+//#define PIN_RELAIS3 5
 
-#define BUTTONS_COUNT 1
+#define BUTTONS_COUNT 3
 #define PIN_BUTTON1 0
-#define PIN_BUTTON2 12
-#define PIN_BUTTON3 13
+//#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 <ArduinoJson.h>
@@ -58,7 +75,7 @@
 #include <ESP8266WebServer.h>
 #include <ESP8266mDNS.h>
 #include <ESP8266HTTPUpdateServer.h>
-#include <PubSubClient.h>
+#include "PubSubClient.h"
 #include <DNSServer.h>
 #include <FS.h>
 
@@ -81,47 +98,48 @@ int mqtt_port = MQTT_PORT;
 char mqtt_user[31];
 char mqtt_pass[31];
 char mqtt_willTopic[51];
-int mqtt_willQos;
-boolean mqtt_willRetain;
+int mqtt_willQos = 2;
+boolean mqtt_willRetain = false;
 char mqtt_willMsg[31];
-int usedRelaisCount = RELAIS_COUNT;
-int usedButtonsCount = BUTTONS_COUNT;
+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
-char domoticz_out_topic[55];
-
 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};
+int sleep = MAIN_SLEEP;
+byte hldToRel[3] = {0, 0, 0};
 
 // global variables
 long mqttLastReconnectAttempt = 0;
+int mqttReconnects = 0;
+boolean relais_state[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
-                      };
+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
@@ -137,8 +155,13 @@ boolean cmdInQueue = false; // command is queued and will be processed next loop
 
 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);
@@ -159,10 +182,45 @@ void setup() {
   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);
@@ -182,7 +240,8 @@ void setup() {
 
 
   //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();
@@ -200,39 +259,71 @@ void setup() {
   }
 
 
+  //  // load config from SPIFFS if files exist
+  if (!loadConfigHw()) {
+    Serial.println("Failed to load confhw.json");
+  } else {
+    Serial.println("confhw.json loaded");
+  }
 
-
-
-
-
-  //  // load config from SPIFFS file if exists
   if (!loadConfig()) {
-    Serial.println("Failed to load config");
+    Serial.println("Failed to load conf.json");
   } else {
-    Serial.println("Config loaded");
+    Serial.println("conf.json loaded");
   }
 
   if (!loadConfig2()) {
-    Serial.println("Failed to load config2");
+    Serial.println("Failed to load conf2.json");
   } else {
-    Serial.println("Config2 loaded");
+    Serial.println("conf2.json loaded");
   }
 
+  mqttPrepareConnection();
+
 
   // set relais pin modes
-  for (int i = 0; i < RELAIS_COUNT; i++) {
-    pinMode(relais_pins[i], OUTPUT);
-    digitalWrite(relais_pins[i], !RELAISONSTATE);
+  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
-  pinMode(PIN_BUTTON1, INPUT_PULLUP);
-#ifdef PIN_BUTTON2
-  pinMode(PIN_BUTTON2, INPUT_PULLUP);
-#endif
-#ifdef PIN_BUTTON3
-  pinMode(PIN_BUTTON3, INPUT_PULLUP);
-#endif
+  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();
 
@@ -260,10 +351,9 @@ void setup() {
 
   //in non-blocking mode, program will continue past this point without waiting
   persWM.begin();
-  //persWM.begin("KS61T5SH", "Gurken651salat");
+  delay(500);
 
   httpServerInit();
-
   mqttClientInit();
 
   Serial.println("setup complete.");
@@ -273,30 +363,38 @@ void setup() {
 
 
 void loop() {
-
-  checkSaveConfigTriggered();
-
-  relais_handleImpulseTimeout();
-
-  //everySecond();
-
+  checkMillis();
+  yield();
+  
   persWM.handleWiFi(); //in non-blocking mode, handleWiFi must be called in the main loop
+  yield();
 
   dnsServer.processNextRequest();
+  yield();
+  
   httpServer.handleClient();
+  
+  yield();
 
   mqttHandleConnection();
+  yield();
 
   checkButtonStates();
+  yield();
 
   evalCmd();
+  yield();
 
-  //handleRelaisSwitching();
-
-  if ( domoticzOutParseData ) parseDomoticzOut();
+  if ( domoticzOutParseData ) {
+    parseDomoticzOut();
+    yield();
+  }
 
-  if (Serial.available()) serialEvent();
+  if (Serial.available()) {
+    serialEvent();
+    yield();
+  }
 
-  delay(50); // save energy by sleeping
+  delay(sleep); // save energy by sleeping, 50ms does not affect button and web
 } //void loop
 

+ 28 - 43
src/WiFiSwitch/commands.ino

@@ -46,7 +46,7 @@ void evalCmd() {
         relaisOn(relnr - 1);
       }
     }
-
+    
     else if (strncmp(cmdPayload, "off ", 4) == 0) {
       char inValue[1];
       inValue[0] = (char)cmdPayload[4];
@@ -55,10 +55,20 @@ void evalCmd() {
         lastSwitchSource[relnr] = 1;
         relaisOff(relnr - 1);
       }
+    }    
+    else if (strncmp(cmdPayload, "all on", 6) == 0) {
+      allRelaisOn();
+    }
+
+    else if (strncmp(cmdPayload, "all off", 7) == 0) {
+      allRelaisOff();
     }
 
     else if (strncmp(cmdPayload, "loadconf", 8) == 0) {
+      loadConfigHw();
       loadConfig();
+      loadConfig2();
+      mqttPrepareConnection();
     }
     
     else if (strncmp(cmdPayload, "set ", 4) == 0) {
@@ -82,11 +92,7 @@ void evalCmd() {
         setconfCmdLen++;
       }
       setconfCmd[setconfCmdLen] = '\0';
-      //Serial.print("setconfCmdLen: ");
-      //Serial.println(setconfCmdLen);
-
-      //      Serial.print("setconf '");
-      //      Serial.print(setconfCmd);
+      yield();
 
       int setconfPayloadLen = 0;
       for (int i = 0; i < len; i++) {
@@ -95,45 +101,9 @@ void evalCmd() {
         setconfPayloadLen++;
       }
       setconfPayload[setconfPayloadLen] = '\0';
-      //Serial.print("setconfPayloadLen: ");
-      //Serial.println(setconfPayloadLen);
-
-      //      Serial.print("' to value '");
-      //      Serial.print(setconfPayload);
-      //      Serial.println("'");
 
       setConfig(setconfCmd, setconfPayload);
 
-
-      //
-      //
-      //
-      //
-      //
-      //      if (strncmp(cmdPayload, "mqtthost ", 13) == 0) {
-      //        char buf[50];
-      //        int len = strlen(cmdPayload) - 13;
-      //        for (int i = 0; i < len; i++) {
-      //          buf[i] = cmdPayload[i + 13];
-      //        }
-      //        buf[len - 1] = '\0';
-      //        Serial.print("MQTT host: '");
-      //        Serial.print(buf);
-      //        Serial.println("' set");
-      //      }
-      //
-      //
-      //      else if (strncmp(cmdPayload, "set mqttport ", 13) == 0) {
-      //        char buf[50];
-      //        int len = strlen(cmdPayload) - 13;
-      //        for (int i = 0; i < len; i++) {
-      //          buf[i] = cmdPayload[i + 13];
-      //        }
-      //        buf[len - 1] = '\0';
-      //        Serial.print("MQTT port: '");
-      //        Serial.print(buf);
-      //        Serial.println("' set");
-      //      }
       //      else if (strncmp(cmdPayload, "set wifissid ", 13) == 0) {
       //        char buf[50];
       //        int len = strlen(cmdPayload) - 13;
@@ -159,18 +129,33 @@ void evalCmd() {
     }
     else if (strncmp(cmdPayload, "restart", 7) == 0) {
       Serial.print("restarting...");
+      delay(100);
       ESP.restart();
     }
     else if (strncmp(cmdPayload, "save", 4) == 0) {
       saveConfig();
+      yield();
+      saveConfig2();
+      yield();
+      saveConfigHw();
+      yield();
       Serial.println("saved config to SPIFFS");
       Serial.println("reloading config to check...");
       loadConfig();
+      yield();
+      loadConfig2();
+      yield();
+      loadConfigHw();
+      yield();
     }
     else if (strncmp(cmdPayload, "getconf", 7) == 0) {
       printConfig();
+      printConfig2();
+      printConfigHw();
+    }
+    else if (strncmp(cmdPayload, "delconf", 7) == 0) {
+      deleteConfig();
     }
-
 
 
     cmdInQueue = false;

+ 550 - 119
src/WiFiSwitch/config.ino

@@ -10,6 +10,7 @@ bool setConfig(char* param, char* value) {
   Serial.print(value);
   Serial.println("'");
 
+  //confdata
   if ( strcmp(param, "devName") == 0 ) {
     strlcpy(deviceName, value, 31);
     //    deviceName[30] = '\0';
@@ -20,12 +21,6 @@ bool setConfig(char* param, char* value) {
   else if ( strcmp(param, "httpPass") == 0 ) {
     strlcpy(http_pass, value, 31);
   }
-  else if ( strcmp(param, "usedRelais") == 0 ) {
-    usedRelaisCount = atoi(value);
-  }
-  else if ( strcmp(param, "usedButtons") == 0 ) {
-    usedButtonsCount = atoi(value);
-  }
   else if ( strcmp(param, "mqttHost") == 0 ) {
     strlcpy(mqtt_server, value, 41);
     //mqtt_server[40] = '\0';
@@ -39,11 +34,22 @@ bool setConfig(char* param, char* value) {
   else if ( strcmp(param, "mqttPass") == 0 ) {
     strlcpy(mqtt_pass, value, 31);
   }
+  else if ( strcmp(param, "inTop") == 0 ) {
+    strlcpy(mqtt_topic_in, value, 51);
+  }
+  else if ( strcmp(param, "outTop") == 0 ) {
+    strlcpy(mqtt_topic_out, value, 51);
+  }
+  else if ( strcmp(param, "outRet") == 0 ) {
+    if (atoi(value) == 1) mqtt_outRetain = true;
+    else mqtt_outRetain = false;
+  }
   else if ( strcmp(param, "willTop") == 0 ) {
     strlcpy(mqtt_willTopic, value, 51);
   }
   else if ( strcmp(param, "willQos") == 0 ) {
-    mqtt_willQos = atoi(value);
+    int tmpval = atoi(value);
+    if (tmpval >= 0 && tmpval <= 2) mqtt_willQos = tmpval;
   }
   else if ( strcmp(param, "willRet") == 0 ) {
     if (atoi(value) == 1) mqtt_willRetain = true;
@@ -52,58 +58,217 @@ bool setConfig(char* param, char* value) {
   else if ( strcmp(param, "willMsg") == 0 ) {
     strlcpy(mqtt_willMsg, value, 31);
   }
-  else if ( strcmp(param, "inTop") == 0 ) {
-    strlcpy(mqtt_topic_in, value, 51);
+  else if ( strcmp(param, "domOutTop") == 0 ) {
+    strlcpy(domoticz_out_topic, value, 51);
   }
-  else if ( strcmp(param, "outTop") == 0 ) {
-    strlcpy(mqtt_topic_out, value, 51);
+  else if ( strcmp(param, "allOnOffTop") == 0 ) {
+    strlcpy(mqtt_allOnOffTopic, value, 51);
+  }
+  else if ( strcmp(param, "sleep") == 0 ) {
+    if (atoi(value) >= 0 && atoi(value) <= 250) {
+      sleep = atoi(value);
+    }
   }
-  else if ( strcmp(param, "outTop_hold1") == 0 ) {
+
+  //confdata2
+  else if ( strcmp(param, "btnRet") == 0 ) {
+    if (atoi(value) == 1) mqtt_btnRetain = true;
+    else mqtt_btnRetain = false;
+  }
+  else if ( strcmp(param, "top1") == 0 ) {
+    strlcpy(mqtt_topic_out_1, value, 51);
+  }
+  else if ( strcmp(param, "top2") == 0 ) {
+    strlcpy(mqtt_topic_out_2, value, 51);
+  }
+  else if ( strcmp(param, "top3") == 0 ) {
+    strlcpy(mqtt_topic_out_3, value, 51);
+  }
+  else if ( strcmp(param, "topHld1") == 0 ) {
     strlcpy(mqtt_topic_out_hold_1, value, 51);
   }
-  else if ( strcmp(param, "outTop_hold2") == 0 ) {
+  else if ( strcmp(param, "topHld2") == 0 ) {
     strlcpy(mqtt_topic_out_hold_2, value, 51);
   }
-  else if ( strcmp(param, "outTop_hold3") == 0 ) {
+  else if ( strcmp(param, "topHld3") == 0 ) {
     strlcpy(mqtt_topic_out_hold_3, value, 51);
   }
-  else if ( strcmp(param, "outPld_hold1") == 0 ) {
-    Serial.print("set outPld_hold1");
+  else if ( strcmp(param, "pld1") == 0 ) {
+    strlcpy(mqtt_payload_out_1, value, 31);
+  }
+  else if ( strcmp(param, "pld2") == 0 ) {
+    strlcpy(mqtt_payload_out_2, value, 31);
+  }
+  else if ( strcmp(param, "pld3") == 0 ) {
+    strlcpy(mqtt_payload_out_3, value, 31);
+  }
+  else if ( strcmp(param, "pldHld1") == 0 ) {
     strlcpy(mqtt_payload_out_hold_1, value, 31);
   }
-  else if ( strcmp(param, "outPld_hold2") == 0 ) {
-    Serial.print("set outPld_hold2");
+  else if ( strcmp(param, "pldHld2") == 0 ) {
     strlcpy(mqtt_payload_out_hold_2, value, 31);
   }
-  else if ( strcmp(param, "outPld_hold3") == 0 ) {
-    Serial.print("set outPld_hold3");
+  else if ( strcmp(param, "pldHld3") == 0 ) {
     strlcpy(mqtt_payload_out_hold_3, value, 31);
   }
-  else if ( strcmp(param, "domoIdx1") == 0 ) {
+  else if ( strcmp(param, "domIdx1") == 0 ) {
     domoticzIdx[0] = atoi(value);
   }
-  else if ( strcmp(param, "domoIdx2") == 0 ) {
+  else if ( strcmp(param, "domIdx2") == 0 ) {
     domoticzIdx[1] = atoi(value);
   }
-  else if ( strcmp(param, "domoIdx3") == 0 ) {
+  else if ( strcmp(param, "domIdx3") == 0 ) {
     domoticzIdx[2] = atoi(value);
   }
-  else if ( strcmp(param, "domoOutTop") == 0 ) {
-    strlcpy(domoticz_out_topic, value, 51);
+  else if ( strcmp(param, "hld1ToRel") == 0 ) {
+    hldToRel[0] = atoi(value);
+  }
+  else if ( strcmp(param, "hld2ToRel") == 0 ) {
+    hldToRel[1] = atoi(value);
+  }
+  else if ( strcmp(param, "hld3ToRel") == 0 ) {
+    hldToRel[2] = atoi(value);
+  }
+
+  // confdatahw
+  else if ( strcmp(param, "debtime") == 0 ) {
+    debounceTime = atoi(value);
+  }
+  else if ( strcmp(param, "hldtime") == 0 ) {
+    buttonHoldTime = atoi(value);
+  }
+  else if ( strcmp(param, "ioRel1") == 0 ) {
+    int io = atoi(value);
+    if ( io == 0 || io == 2 || io == 4 || io == 5 || io == 12 || io == 13 || io == 14 ) relais_pins[0] = io;
+    else Serial.println("ioRel1 - false IO number, not set");
+  }
+  else if ( strcmp(param, "ioRel2") == 0 ) {
+    int io = atoi(value);
+    if ( io == 0 || io == 2 || io == 4 || io == 5 || io == 12 || io == 13 || io == 14 ) relais_pins[1] = io;
+    else Serial.println("ioRel2 - false IO number, not set");
   }
-  else if ( strcmp(param, "impuls1") == 0 ) {
+  else if ( strcmp(param, "ioRel3") == 0 ) {
+    int io = atoi(value);
+    if ( io == 0 || io == 2 || io == 4 || io == 5 || io == 12 || io == 13 || io == 14 ) relais_pins[2] = io;
+    else Serial.println("ioRel3 - false IO number, not set");
+  }
+  else if ( strcmp(param, "ioBtn1") == 0 ) {
+    int io = atoi(value);
+    if ( io == 0 || io == 2 || io == 4 || io == 5 || io == 12 || io == 13 || io == 14 ) buttons_pins[0] = io;
+    else Serial.println("ioBtn1 - false IO number, not set");
+  }
+  else if ( strcmp(param, "ioBtn2") == 0 ) {
+    int io = atoi(value);
+    if ( io == 0 || io == 2 || io == 4 || io == 5 || io == 12 || io == 13 || io == 14 ) buttons_pins[1] = io;
+    else Serial.println("ioBtn2 - false IO number, not set");
+  }
+  else if ( strcmp(param, "ioBtn3") == 0 ) {
+    int io = atoi(value);
+    if ( io == 0 || io == 2 || io == 4 || io == 5 || io == 12 || io == 13 || io == 14 ) buttons_pins[2] = io;
+    else Serial.println("ioBtn3 - false IO number, not set");
+  }
+
+  else if ( strcmp(param, "ioLed1") == 0 ) {
+    int io = atoi(value);
+    if ( io == 0 || io == 2 || io == 4 || io == 5 || io == 12 || io == 13 || io == 14 ) leds_pins[0] = io;
+    else Serial.println("ioLed1 - false IO number, not set");
+  }
+  else if ( strcmp(param, "ioLed2") == 0 ) {
+    int io = atoi(value);
+    if ( io == 0 || io == 2 || io == 4 || io == 5 || io == 12 || io == 13 || io == 14 ) leds_pins[1] = io;
+    else Serial.println("ioLed2 - false IO number, not set");
+  }
+  else if ( strcmp(param, "ioLed3") == 0 ) {
+    int io = atoi(value);
+    if ( io == 0 || io == 2 || io == 4 || io == 5 || io == 12 || io == 13 || io == 14 ) leds_pins[2] = io;
+    else Serial.println("ioLed3 - false IO number, not set");
+  }
+  else if ( strcmp(param, "invRel1") == 0 ) {
+    if (atoi(value) == 1) relais_invert[0] = true;
+    else relais_invert[0] = false;
+  }
+  else if ( strcmp(param, "invRel2") == 0 ) {
+    if (atoi(value) == 1) relais_invert[1] = true;
+    else relais_invert[1] = false;
+  }
+  else if ( strcmp(param, "invRel3") == 0 ) {
+    if (atoi(value) == 1) relais_invert[2] = true;
+    else relais_invert[2] = false;
+  }
+  else if ( strcmp(param, "invBtn1") == 0 ) {
+    if (atoi(value) == 1) button_invert[0] = true;
+    else button_invert[0] = false;
+  }
+  else if ( strcmp(param, "invBtn2") == 0 ) {
+    if (atoi(value) == 1) button_invert[1] = true;
+    else button_invert[1] = false;
+  }
+  else if ( strcmp(param, "invBtn3") == 0 ) {
+    if (atoi(value) == 1) button_invert[2] = true;
+    else button_invert[2] = false;
+  }
+  else if ( strcmp(param, "invLed1") == 0 ) {
+    if (atoi(value) == 1) led_invert[0] = true;
+    else led_invert[0] = false;
+  }
+  else if ( strcmp(param, "invLed2") == 0 ) {
+    if (atoi(value) == 1) led_invert[1] = true;
+    else led_invert[1] = false;
+  }
+  else if ( strcmp(param, "invLed3") == 0 ) {
+    if (atoi(value) == 1) led_invert[2] = true;
+    else led_invert[2] = false;
+  }
+  else if ( strcmp(param, "puls1") == 0 ) {
     relais_impulse[0] = atoi(value);
   }
-  else if ( strcmp(param, "impuls2") == 0 ) {
+  else if ( strcmp(param, "puls2") == 0 ) {
     relais_impulse[1] = atoi(value);
   }
-  else if ( strcmp(param, "impuls3") == 0 ) {
+  else if ( strcmp(param, "puls3") == 0 ) {
     relais_impulse[2] = atoi(value);
   }
+  else if ( strcmp(param, "enaRel1") == 0 ) {
+    if (atoi(value) == 1) relais_enabled[0] = true;
+    else relais_enabled[0] = false;
+  }
+  else if ( strcmp(param, "enaRel2") == 0 ) {
+    if (atoi(value) == 1) relais_enabled[1] = true;
+    else relais_enabled[1] = false;
+  }
+  else if ( strcmp(param, "enaRel3") == 0 ) {
+    if (atoi(value) == 1) relais_enabled[2] = true;
+    else relais_enabled[2] = false;
+  }
+  else if ( strcmp(param, "enaBtn1") == 0 ) {
+    if (atoi(value) == 1) button_enabled[0] = true;
+    else button_enabled[0] = false;
+  }
+  else if ( strcmp(param, "enaBtn2") == 0 ) {
+    if (atoi(value) == 1) button_enabled[1] = true;
+    else button_enabled[1] = false;
+  }
+  else if ( strcmp(param, "enaBtn3") == 0 ) {
+    if (atoi(value) == 1) button_enabled[2] = true;
+    else button_enabled[2] = false;
+  }
+  else if ( strcmp(param, "enaLed1") == 0 ) {
+    if (atoi(value) == 1) led_enabled[0] = true;
+    else led_enabled[0] = false;
+  }
+  else if ( strcmp(param, "enaLed2") == 0 ) {
+    if (atoi(value) == 1) led_enabled[1] = true;
+    else led_enabled[1] = false;
+  }
+  else if ( strcmp(param, "enaLed3") == 0 ) {
+    if (atoi(value) == 1) led_enabled[2] = true;
+    else led_enabled[2] = false;
+  }
 }
 
 void printConfig() {
   // prints current config vars to serial
+  Serial.println("\nconfdata:");
   Serial.print("devName: ");
   Serial.println(deviceName);
   Serial.print("httpUser: ");
@@ -122,6 +287,8 @@ void printConfig() {
   Serial.println(mqtt_topic_in);
   Serial.print("outTop: ");
   Serial.println(mqtt_topic_out);
+  Serial.print("outRet: ");
+  Serial.println(mqtt_outRetain);
   Serial.print("willTop: ");
   Serial.println(mqtt_willTopic);
   Serial.print("willQos: ");
@@ -130,65 +297,149 @@ void printConfig() {
   Serial.println(mqtt_willRetain);
   Serial.print("willMsg: ");
   Serial.println(mqtt_willMsg);
-  Serial.print("domoOutTop: ");
+  Serial.print("domOutTop: ");
   Serial.println(domoticz_out_topic);
-
-  Serial.print("usedRelais: ");
-  Serial.println(usedRelaisCount);
-  Serial.print("usedButtons: ");
-  Serial.println(usedButtonsCount);
-  Serial.print("outTop_hold1: ");
+  Serial.print("allOnOffTop: ");
+  Serial.println(mqtt_allOnOffTopic);
+  Serial.print("sleep: ");
+  Serial.println(sleep);
+  Serial.println();
+}
+void printConfig2() {
+  Serial.println("\nconfdata2:");
+  Serial.print("btnRet: ");
+  Serial.println(mqtt_btnRetain);
+  Serial.print("top1: ");
+  Serial.println(mqtt_topic_out_1);
+  Serial.print("top2: ");
+  Serial.println(mqtt_topic_out_2);
+  Serial.print("top3: ");
+  Serial.println(mqtt_topic_out_3);
+  Serial.print("pld1: ");
+  Serial.println(mqtt_payload_out_1);
+  Serial.print("pld2: ");
+  Serial.println(mqtt_payload_out_2);
+  Serial.print("pld3: ");
+  Serial.println(mqtt_payload_out_3);
+  Serial.print("topHld1: ");
   Serial.println(mqtt_topic_out_hold_1);
-  Serial.print("outTop_hold2: ");
+  Serial.print("topHld2: ");
   Serial.println(mqtt_topic_out_hold_2);
-  Serial.print("outTop_hold3: ");
+  Serial.print("topHld3: ");
   Serial.println(mqtt_topic_out_hold_3);
-  Serial.print("outPld_hold1: ");
+  Serial.print("pldHld1: ");
   Serial.println(mqtt_payload_out_hold_1);
-  Serial.print("outPld_hold2: ");
+  Serial.print("pldHld2: ");
   Serial.println(mqtt_payload_out_hold_2);
-  Serial.print("outPld_hold3: ");
+  Serial.print("pldHld3: ");
   Serial.println(mqtt_payload_out_hold_3);
-  Serial.print("domoIdx1: ");
+  Serial.print("domIdx1: ");
   Serial.println(domoticzIdx[0]);
-  Serial.print("domoIdx2: ");
+  Serial.print("domIdx2: ");
   Serial.println(domoticzIdx[1]);
-  Serial.print("domoIdx3: ");
+  Serial.print("domIdx3: ");
   Serial.println(domoticzIdx[2]);
-  Serial.print("impuls1: ");
+  Serial.print("hld1ToRel: ");
+  Serial.println(hldToRel[0]);
+  Serial.print("hld2ToRel: ");
+  Serial.println(hldToRel[1]);
+  Serial.print("hld3ToRel: ");
+  Serial.println(hldToRel[2]);
+  Serial.println();
+}
+void printConfigHw() {
+  Serial.println("\nconfdatahw:");
+  Serial.print("debtime: ");
+  Serial.println(debounceTime);
+  Serial.print("holdtime: ");
+  Serial.println(buttonHoldTime);
+  Serial.print("enaRel1: ");
+  Serial.println(relais_enabled[0]);
+  Serial.print("enaRel2: ");
+  Serial.println(relais_enabled[1]);
+  Serial.print("enaRel3: ");
+  Serial.println(relais_enabled[2]);
+  Serial.print("enaBtn1: ");
+  Serial.println(button_enabled[0]);
+  Serial.print("enaBtn2: ");
+  Serial.println(button_enabled[1]);
+  Serial.print("enaBtn3: ");
+  Serial.println(button_enabled[2]);
+  Serial.print("enaLed1: ");
+  Serial.println(led_enabled[0]);
+  Serial.print("enaLed2: ");
+  Serial.println(led_enabled[1]);
+  Serial.print("enaLed3: ");
+  Serial.println(led_enabled[2]);
+  Serial.print("ioRel1: ");
+  Serial.println(relais_pins[0]);
+  Serial.print("ioRel2: ");
+  Serial.println(relais_pins[1]);
+  Serial.print("ioRel3: ");
+  Serial.println(relais_pins[2]);
+  Serial.print("ioBtn1: ");
+  Serial.println(buttons_pins[0]);
+  Serial.print("ioBtn2: ");
+  Serial.println(buttons_pins[1]);
+  Serial.print("ioBtn3: ");
+  Serial.println(buttons_pins[2]);
+  Serial.print("ioLed1: ");
+  Serial.println(leds_pins[0]);
+  Serial.print("ioLed2: ");
+  Serial.println(leds_pins[1]);
+  Serial.print("ioLed3: ");
+  Serial.println(leds_pins[2]);
+  Serial.print("invRel1: ");
+  Serial.println(relais_invert[0]);
+  Serial.print("invRel2: ");
+  Serial.println(relais_invert[1]);
+  Serial.print("invRel3: ");
+  Serial.println(relais_invert[2]);
+  Serial.print("invBtn1: ");
+  Serial.println(button_invert[0]);
+  Serial.print("invBtn2: ");
+  Serial.println(button_invert[1]);
+  Serial.print("invBtn3: ");
+  Serial.println(button_invert[2]);
+  Serial.print("invLed1: ");
+  Serial.println(led_invert[0]);
+  Serial.print("invLed2: ");
+  Serial.println(led_invert[1]);
+  Serial.print("invLed3: ");
+  Serial.println(led_invert[2]);
+  Serial.print("puls1: ");
   Serial.println(relais_impulse[0]);
-  Serial.print("impuls2: ");
+  Serial.print("puls2: ");
   Serial.println(relais_impulse[1]);
-  Serial.print("impuls3: ");
+  Serial.print("puls3: ");
   Serial.println(relais_impulse[2]);
-
   Serial.println();
 }
 
 bool loadConfig() { // loadConfig 1
-  if (SPIFFS.exists("/config.json")) {
-    File configFile = SPIFFS.open("/config.json", "r");
+  if (SPIFFS.exists("/conf.json")) {
+    File configFile = SPIFFS.open("/conf.json", "r");
     if (!configFile) {
-      Serial.println("ERR: Failed to open file /config.json");
+      Serial.println("ERR: Failed to open file /conf.json");
       return false;
     }
     else {
-      Serial.println("file /config.json opened");
+      Serial.println("file /conf.json opened");
       size_t size = configFile.size();
 
       Serial.print("file size: ");
       Serial.println(size);
 
-//      Serial.println("file content:");
-//
-//      while (configFile.available()) {
-//        //Lets read line by line from the file
-//        String line = configFile.readStringUntil('\n');
-//        Serial.println(line);
-//      }
-//      Serial.println();
+      //      Serial.println("file content:");
+      //
+      //      while (configFile.available()) {
+      //        //Lets read line by line from the file
+      //        String line = configFile.readStringUntil('\n');
+      //        Serial.println(line);
+      //      }
+      //      Serial.println();
 
-      if (size > 900) {
+      if (size > 1000) {
         Serial.println("Config file size is too large");
         return false;
       }
@@ -203,22 +454,14 @@ bool loadConfig() { // loadConfig 1
       // use configFile.readString instead.
       configFile.readBytes(buf.get(), size);
 
-      Serial.println("create json buffer");
-
-      StaticJsonBuffer<800> jsonBuffer;
-
-      Serial.println("parse json data");
-
+      StaticJsonBuffer<1050> jsonBuffer;
       JsonObject& json = jsonBuffer.parseObject(buf.get());
 
-      Serial.println("do her kumm ma a no");
-
       if (!json.success()) {
         Serial.println("Failed to parse config file");
         return false;
       }
 
-      Serial.println("und da ?");
       strlcpy(deviceName, json["devName"] | "", 31);
       strlcpy(http_user, json["httpUser"] | "", 31);
       strlcpy(http_pass, json["httpPass"] | "", 31);
@@ -228,12 +471,21 @@ bool loadConfig() { // loadConfig 1
       strlcpy(mqtt_pass, json["mqttPass"] | "", 31);
       strlcpy(mqtt_topic_in, json["inTop"] | "", 51);
       strlcpy(mqtt_topic_out, json["outTop"] | "", 51);
+
+      if (atoi(json["outRet"] | "") == 1) mqtt_outRetain = true;
+      else mqtt_outRetain = false;
+
       strlcpy(mqtt_willTopic, json["willTop"] | "", 51);
       mqtt_willQos = atoi(json["willQos"] | "0");
+
       if (atoi(json["willRet"] | "") == 1) mqtt_willRetain = true;
       else mqtt_willRetain = false;
+
       strlcpy(mqtt_willMsg, json["willMsg"] | "", 31);
-      strlcpy(domoticz_out_topic, json["domoOutTop"] | "", 51);
+      strlcpy(domoticz_out_topic, json["domOutTop"] | "", 51);
+      strlcpy(mqtt_allOnOffTopic, json["allOnOffTop"] | "", 51);
+
+      sleep = atoi(json["sleep"] | "");
 
       Serial.println("Loaded config values:");
       printConfig();
@@ -250,36 +502,98 @@ bool loadConfig() { // loadConfig 1
 } // loadConfig 1
 
 bool loadConfig2() {
-  if (SPIFFS.exists("/config2.json")) {
-    File configFile = SPIFFS.open("/config2.json", "r");
+  if (SPIFFS.exists("/conf2.json")) {
+    File configFile = SPIFFS.open("/conf2.json", "r");
     if (!configFile) {
-      Serial.println("ERR: Failed to open file /config2.json");
+      Serial.println("ERR: Failed to open file /conf2.json");
       return false;
     }
     else {
-      Serial.println("file /config2.json opened");
+      Serial.println("file /conf2.json opened");
       size_t size = configFile.size();
       Serial.print("file size: ");
       Serial.println(size);
-      if (size > 800) {
+      if (size > 1000) {
         Serial.println("Config file size is too large");
         return false;
       }
 
       // Allocate a buffer to store contents of the file.
+      std::unique_ptr<char[]> buf(new char[size]);
 
+      // We don't use String here because ArduinoJson library requires the input
+      // buffer to be mutable. If you don't use ArduinoJson, you may as well
+      // use configFile.readString instead.
+      configFile.readBytes(buf.get(), size);
 
+      StaticJsonBuffer<1050> jsonBuffer;
+      JsonObject& json = jsonBuffer.parseObject(buf.get());
 
-      std::unique_ptr<char[]> buf(new char[size]);
+      if (!json.success()) {
+        Serial.println("Failed to parse config file");
+        return false;
+      }
 
+      if (atoi(json["btnRet"] | "") == 1) mqtt_btnRetain = true;
+      else mqtt_btnRetain = false;
+
+      strlcpy(mqtt_topic_out_1, json["top1"] | "", 51);
+      strlcpy(mqtt_topic_out_2, json["top2"] | "", 51);
+      strlcpy(mqtt_topic_out_3, json["top3"] | "", 51);
+      strlcpy(mqtt_topic_out_hold_1, json["topHld1"] | "", 51);
+      strlcpy(mqtt_topic_out_hold_2, json["topHld2"] | "", 51);
+      strlcpy(mqtt_topic_out_hold_3, json["topHld3"] | "", 51);
+      strlcpy(mqtt_payload_out_1, json["pld1"] | "", 31);
+      strlcpy(mqtt_payload_out_2, json["pld2"] | "", 31);
+      strlcpy(mqtt_payload_out_3, json["pld3"] | "", 31);
+      strlcpy(mqtt_payload_out_hold_1, json["pldHld1"] | "", 31);
+      strlcpy(mqtt_payload_out_hold_2, json["pldHld2"] | "", 31);
+      strlcpy(mqtt_payload_out_hold_3, json["pldHld3"] | "", 31);
+      domoticzIdx[0] = atoi(json["domIdx1"] | "");
+      domoticzIdx[1] = atoi(json["domIdx2"] | "");
+      domoticzIdx[2] = atoi(json["domIdx3"] | "");
+
+      hldToRel[0] = atoi(json["hld1ToRel"] | "");
+      hldToRel[1] = atoi(json["hld2ToRel"] | "");
+      hldToRel[2] = atoi(json["hld3ToRel"] | "");
 
+      Serial.println("Loaded config values:");
+      printConfig2();
+      return true;
+    }
+  }
+  else {
+    Serial.println("file /conf2.json file does not exist");
+    return false;
+  }
+} //loadConfig2
+
+bool loadConfigHw() {
+  if (SPIFFS.exists("/confhw.json")) {
+    File configFile = SPIFFS.open("/confhw.json", "r");
+    if (!configFile) {
+      Serial.println("ERR: Failed to open file /confhw.json");
+      return false;
+    }
+    else {
+      Serial.println("file /confhw.json opened");
+      size_t size = configFile.size();
+      Serial.print("file size: ");
+      Serial.println(size);
+      if (size > 1000) {
+        Serial.println("Config file size is too large");
+        return false;
+      }
+
+      // Allocate a buffer to store contents of the file.
+      std::unique_ptr<char[]> buf(new char[size]);
 
       // We don't use String here because ArduinoJson library requires the input
       // buffer to be mutable. If you don't use ArduinoJson, you may as well
       // use configFile.readString instead.
       configFile.readBytes(buf.get(), size);
 
-      StaticJsonBuffer<810> jsonBuffer;
+      StaticJsonBuffer<1050> jsonBuffer;
       JsonObject& json = jsonBuffer.parseObject(buf.get());
 
       if (!json.success()) {
@@ -287,35 +601,78 @@ bool loadConfig2() {
         return false;
       }
 
-      usedRelaisCount = atoi(json["usedRelais"] | "");
-      usedButtonsCount = atoi(json["usedButtons"] | "");
-      strlcpy(mqtt_topic_out_hold_1, json["outTop_hold1"] | "", 51);
-      strlcpy(mqtt_topic_out_hold_2, json["outTop_hold2"] | "", 51);
-      strlcpy(mqtt_topic_out_hold_3, json["outTop_hold3"] | "", 51);
-      strlcpy(mqtt_payload_out_hold_1, json["outPld_hold1"] | "", 31);
-      strlcpy(mqtt_payload_out_hold_2, json["outPld_hold2"] | "", 31);
-      strlcpy(mqtt_payload_out_hold_3, json["outPld_hold3"] | "", 31);
-      domoticzIdx[0] = atoi(json["domoIdx1"] | "");
-      domoticzIdx[1] = atoi(json["domoIdx2"] | "");
-      domoticzIdx[2] = atoi(json["domoIdx3"] | "");
-      relais_impulse[0] = atoi(json["impuls1"] | "");
-      relais_impulse[1] = atoi(json["impuls2"] | "");
-      relais_impulse[2] = atoi(json["impuls3"] | "");
+      debounceTime = atoi(json["debtime"] | "");
+      buttonHoldTime = atoi(json["hldtime"] | "");
+      relais_pins[0] = atoi(json["ioRel1"] | "");
+      relais_pins[1] = atoi(json["ioRel2"] | "");
+      relais_pins[2] = atoi(json["ioRel3"] | "");
+      buttons_pins[0] = atoi(json["ioBtn1"] | "");
+      buttons_pins[1] = atoi(json["ioBtn2"] | "");
+      buttons_pins[2] = atoi(json["ioBtn3"] | "");
+      leds_pins[0] = atoi(json["ioLed1"] | "");
+      leds_pins[1] = atoi(json["ioLed2"] | "");
+      leds_pins[2] = atoi(json["ioLed3"] | "");
+
+      if (atoi(json["enaRel1"] | "") == 1) relais_enabled[0] = true;
+      else relais_enabled[0] = false;
+      if (atoi(json["enaRel2"] | "") == 1) relais_enabled[1] = true;
+      else relais_enabled[1] = false;
+      if (atoi(json["enaRel3"] | "") == 1) relais_enabled[2] = true;
+      else relais_enabled[2] = false;
+
+      if (atoi(json["enaBtn1"] | "") == 1) button_enabled[0] = true;
+      else button_enabled[0] = false;
+      if (atoi(json["enaBtn2"] | "") == 1) button_enabled[1] = true;
+      else button_enabled[1] = false;
+      if (atoi(json["enaBtn3"] | "") == 1) button_enabled[2] = true;
+      else button_enabled[2] = false;
+
+      if (atoi(json["enaLed1"] | "") == 1) led_enabled[0] = true;
+      else led_enabled[0] = false;
+      if (atoi(json["enaLed2"] | "") == 1) led_enabled[1] = true;
+      else led_enabled[1] = false;
+      if (atoi(json["enaLed3"] | "") == 1) led_enabled[2] = true;
+      else led_enabled[2] = false;
+
+      if (atoi(json["invRel1"] | "") == 1) relais_invert[0] = true;
+      else relais_invert[0] = false;
+      if (atoi(json["invRel2"] | "") == 1) relais_invert[1] = true;
+      else relais_invert[1] = false;
+      if (atoi(json["invRel3"] | "") == 1) relais_invert[2] = true;
+      else relais_invert[2] = false;
+
+      if (atoi(json["invBtn1"] | "") == 1) button_invert[0] = true;
+      else button_invert[0] = false;
+      if (atoi(json["invBtn2"] | "") == 1) button_invert[1] = true;
+      else button_invert[1] = false;
+      if (atoi(json["invBtn3"] | "") == 1) button_invert[2] = true;
+      else button_invert[2] = false;
+
+      if (atoi(json["invLed1"] | "") == 1) led_invert[0] = true;
+      else led_invert[0] = false;
+      if (atoi(json["invLed2"] | "") == 1) led_invert[1] = true;
+      else led_invert[1] = false;
+      if (atoi(json["invLed3"] | "") == 1) led_invert[2] = true;
+      else led_invert[2] = false;
+
+      relais_impulse[0] = atoi(json["puls1"] | "");
+      relais_impulse[1] = atoi(json["puls2"] | "");
+      relais_impulse[2] = atoi(json["puls3"] | "");
 
       Serial.println("Loaded config values:");
-      printConfig();
+      printConfigHw();
       return true;
     }
   }
   else {
-    Serial.println("file /config2.json file does not exist");
+    Serial.println("file /confhw.json file does not exist");
     return false;
   }
-}
+}//loadConfigHw
 
 
 bool saveConfig() { // safeConfig
-  StaticJsonBuffer<810> jsonBuffer;
+  StaticJsonBuffer<1050> jsonBuffer;
   JsonObject& json = jsonBuffer.createObject();
 
   json["devName"] = deviceName;
@@ -325,17 +682,22 @@ bool saveConfig() { // safeConfig
   json["mqttPort"] = mqtt_port;
   json["mqttUser"] = mqtt_user;
   json["mqttPass"] = mqtt_pass;
+  json["inTop"] = mqtt_topic_in;
+  json["outTop"] = mqtt_topic_out;
+  json["outRet"] = mqtt_outRetain;
   json["willTop"] = mqtt_willTopic;
   json["willQos"] = mqtt_willQos;
   json["willRet"] = mqtt_willRetain;
   json["willMsg"] = mqtt_willMsg;
-  json["inTop"] = mqtt_topic_in;
-  json["outTop"] = mqtt_topic_out;
-  json["domoOutTop"] = domoticz_out_topic;
+  json["domOutTop"] = domoticz_out_topic;
+  json["allOnOffTop"] = mqtt_allOnOffTopic;
+  json["sleep"] = sleep;
 
-  File configFile = SPIFFS.open("/config.json", "w");
+  yield();
+
+  File configFile = SPIFFS.open("/conf.json", "w");
   if (!configFile) {
-    Serial.println("Failed to open config file for writing");
+    Serial.println("Failed to open conf file for writing");
     return false;
   }
 
@@ -345,27 +707,34 @@ bool saveConfig() { // safeConfig
 
 
 bool saveConfig2() { // safeConfig2
-  StaticJsonBuffer<810> jsonBuffer;
+  StaticJsonBuffer<1050> jsonBuffer;
   JsonObject& json = jsonBuffer.createObject();
 
-  json["usedRelais"] = usedRelaisCount;
-  json["usedButtons"] = usedButtonsCount;
-  json["outTop_hold1"] = mqtt_topic_out_hold_1;
-  json["outTop_hold2"] = mqtt_topic_out_hold_2;
-  json["outTop_hold3"] = mqtt_topic_out_hold_3;
-  json["outPld_hold1"] = mqtt_payload_out_hold_1;
-  json["outPld_hold2"] = mqtt_payload_out_hold_2;
-  json["outPld_hold3"] = mqtt_payload_out_hold_3;
-  json["domoIdx1"] = domoticzIdx[0];
-  json["domoIdx2"] = domoticzIdx[1];
-  json["domoIdx3"] = domoticzIdx[2];
-  json["impuls1"] = relais_impulse[0];
-  json["impuls2"] = relais_impulse[1];
-  json["impuls3"] = relais_impulse[2];
-
-  File configFile = SPIFFS.open("/config2.json", "w");
+  json["btnRet"] = mqtt_btnRetain;
+  json["top1"] = mqtt_topic_out_1;
+  json["top2"] = mqtt_topic_out_2;
+  json["top3"] = mqtt_topic_out_3;
+  json["pld1"] = mqtt_payload_out_1;
+  json["pld2"] = mqtt_payload_out_2;
+  json["pld3"] = mqtt_payload_out_3;
+  json["topHld1"] = mqtt_topic_out_hold_1;
+  json["topHld2"] = mqtt_topic_out_hold_2;
+  json["topHld3"] = mqtt_topic_out_hold_3;
+  json["pldHld1"] = mqtt_payload_out_hold_1;
+  json["pldHld2"] = mqtt_payload_out_hold_2;
+  json["pldHld3"] = mqtt_payload_out_hold_3;
+  json["domIdx1"] = domoticzIdx[0];
+  json["domIdx2"] = domoticzIdx[1];
+  json["domIdx3"] = domoticzIdx[2];
+  json["hld1ToRel"] = hldToRel[0];
+  json["hld2ToRel"] = hldToRel[1];
+  json["hld3ToRel"] = hldToRel[2];
+
+  yield();
+
+  File configFile = SPIFFS.open("/conf2.json", "w");
   if (!configFile) {
-    Serial.println("Failed to open config2 file for writing");
+    Serial.println("Failed to open conf2 file for writing");
     return false;
   }
 
@@ -373,6 +742,54 @@ bool saveConfig2() { // safeConfig2
   return true;
 } // safeConfig2
 
+bool saveConfigHw() { // safeConfigHw
+  StaticJsonBuffer<1050> jsonBuffer;
+  JsonObject& json = jsonBuffer.createObject();
+
+  json["debtime"] = debounceTime;
+  json["hldtime"] = buttonHoldTime;
+  json["enaRel1"] = relais_enabled[0];
+  json["enaRel2"] = relais_enabled[1];
+  json["enaRel3"] = relais_enabled[2];
+  json["enaBtn1"] = button_enabled[0];
+  json["enaBtn2"] = button_enabled[1];
+  json["enaBtn3"] = button_enabled[2];
+  json["enaLed1"] = led_enabled[0];
+  json["enaLed2"] = led_enabled[1];
+  json["enaLed3"] = led_enabled[2];
+  json["ioRel1"] = relais_pins[0];
+  json["ioRel2"] = relais_pins[1];
+  json["ioRel3"] = relais_pins[2];
+  json["ioBtn1"] = buttons_pins[0];
+  json["ioBtn2"] = buttons_pins[1];
+  json["ioBtn3"] = buttons_pins[2];
+  json["ioLed1"] = leds_pins[0];
+  json["ioLed2"] = leds_pins[1];
+  json["ioLed3"] = leds_pins[2];
+  json["invRel1"] = relais_invert[0];
+  json["invRel2"] = relais_invert[1];
+  json["invRel3"] = relais_invert[2];
+  json["invBtn1"] = button_invert[0];
+  json["invBtn2"] = button_invert[1];
+  json["invBtn3"] = button_invert[2];
+  json["invLed1"] = led_invert[0];
+  json["invLed2"] = led_invert[1];
+  json["invLed3"] = led_invert[2];
+  json["puls1"] = relais_impulse[0];
+  json["puls2"] = relais_impulse[1];
+  json["puls3"] = relais_impulse[2];
+  yield();
+
+  File configFile = SPIFFS.open("/confhw.json", "w");
+  if (!configFile) {
+    Serial.println("Failed to open confhw file for writing");
+    return false;
+  }
+
+  json.printTo(configFile);
+  return true;
+} // safeConfigHw
+
 void checkSaveConfigTriggered() {
   if (saveConfigToFlash) {
     saveConfigToFlash = false;
@@ -382,4 +799,18 @@ void checkSaveConfigTriggered() {
     saveConfig2ToFlash = false;
     saveConfig2();
   }
-}
+  if (saveConfigHwToFlash) {
+    saveConfigHwToFlash = false;
+    saveConfigHw();
+  }
+}
+
+void deleteConfig() {
+  Serial.println("deleting configuration");
+  if (SPIFFS.remove("/formatComplete.txt")) {
+    Serial.println("config will be deleted after reboot");
+    delay(100);
+    ESP.restart();
+  }
+}
+

+ 102 - 78
src/WiFiSwitch/data/conf.htm

@@ -1,84 +1,108 @@
-<h3>Configuration</h3>
-
-<form id='reloadForm' onsubmit='return transmit(this)'>
-<input type='submit' value='reload current values'/>
-</form>
-  
-<form id='form1' onsubmit='return transmit(this)'>Device Name: <input type='text' name='devName' id='devName'/><input type='submit' value='Update'/></form>
-<form id='form2' onsubmit='return transmit(this)'>MQTT Server: <input type='text' name='mqttHost' id='mqttHost'/><input type='submit' value='Update'/></form>
-<form id='form3' onsubmit='return transmit(this)'>MQTT Port: <input type='number' name='mqttPort' id='mqttPort'/><input type='submit' value='Update'/></form>
-<form id='form4' onsubmit='return transmit(this)'>MQTT User: <input type='text' name='mqttUser' id='mqttUser'/><input type='submit' value='Update'/></form>
-<form id='form5' onsubmit='return transmit(this)'>MQTT Password: <input type='text' name='mqttPass' id='mqttPass'/><input type='submit' value='Update'/></form>
-<form id='form6' onsubmit='return transmit(this)'>In Topic: <input type='text' name='inTop' id='inTop'/><input type='submit'  value='Update'/></form>
-<form id='form7' onsubmit='return transmit(this)'>Out Topic: <input type='text' name='outTop' id='outTop'/><input type='submit' value='Update'/></form>
-<form id='form8' onsubmit='return transmit(this)'>Out Topic Hold 1: <input type='text' name='outTop_hold1' id='outTop_hold1'/><input type='submit' value='Update'/></form>
-<form id='form9' onsubmit='return transmit(this)'>Out Topic Hold 2: <input type='text' name='outTop_hold2' id='outTop_hold2'/><input type='submit' value='Update'/></form>
-<form id='form10' onsubmit='return transmit(this)'>Out Topic Hold 3: <input type='text' name='outTop_hold3' id='outTop_hold3'/><input type='submit' value='Update'/></form>
-<form id='form11' onsubmit='return transmit(this)'>Out Payload Hold 1: <input type='text' name='outPld_hold1' id='outPld_hold1'/><input type='submit' value='Update'/></form>
-<form id='form12' onsubmit='return transmit(this)'>Out Payload Hold 2: <input type='text' name='outPld_hold2' id='outPld_hold2'/><input type='submit' value='Update'/></form>
-<form id='form13' onsubmit='return transmit(this)'>Out Payload Hold 3: <input type='text' name='outPld_hold3' id='outPld_hold3'/><input type='submit' value='Update'/></form>
-<form id='form14' onsubmit='return transmit(this)'>Domoticz Idx 1: <input type='number' name='domoIdx1' id='domoIdx1'/><input type='submit' value='Update'/></form>
-<form id='form15' onsubmit='return transmit(this)'>Domoticz Idx 2: <input type='number' name='domoIdx2' id='domoIdx2'/><input type='submit' value='Update'/></form>
-<form id='form16' onsubmit='return transmit(this)'>Domoticz Idx 3: <input type='number' name='domoIdx3' id='domoIdx3'/><input type='submit' value='Update'/></form>
-<form id='form17' onsubmit='return transmit(this)'>Domoticz Out Topic: <input type='text' name='domoOutTop' id='domoOutTop'/><input type='submit'  value='Update'/></form>
-<form id='form18' onsubmit='return transmit(this)'>Relais Impulse 1: <input type='number' name='impuls1' id='impuls1'/><input type='submit' value='Update'/></form>
-<form id='form19' onsubmit='return transmit(this)'>Relais Impulse 2: <input type='number' name='impuls2' id='impuls2'/><input type='submit' value='Update'/></form>
-<form id='form20' onsubmit='return transmit(this)'>Relais Impulse 3: <input type='number' name='impuls3' id='impuls3'/><input type='submit' value='Update'/></form>
-
+<html><head><body>
+<h3>Base configuration</h3>
+<a href='/'>Home</a><br><br>
+<input type='button' value='reload' onclick='return transmit()'/><br><br>
+<form id='form1' onsubmit='return transmit(this)'>
+Device Name: <input type='text' name='devName' id='devName'/><br><br>
+HTTP User *: <input type='text' name='httpUser' id='httpUser'/><br>
+HTTP Password *: <input type='text' name='httpPass' id='httpPass'/><br><br>
+MQTT Server *: <input type='text' name='mqttHost' id='mqttHost'/><br>
+MQTT Port *: <input type='number' name='mqttPort' id='mqttPort'/><br>
+MQTT User *: <input type='text' name='mqttUser' id='mqttUser'/><br>
+MQTT Password *: <input type='text' name='mqttPass' id='mqttPass'/><br><br>
+In Topic *: <input type='text' name='inTop' id='inTop'/><br>
+Out Topic: <input type='text' name='outTop' id='outTop'/><br>
+Out Retain *: <input type='checkbox' name='outRet' id='outRet'/><br><br>
+LastWill Topic *: <input type='text' name='willTop' id='willTop'/><br>
+LastWill Qos *: <select name='willQos' id='willQos'><option>0</option><option>1</option><option>2</option></select><br>
+LastWill Retain *: <input type='checkbox' name='willRet' id='willRet'/><br>
+LastWill Message *: <input type='text' name='willMsg' id='willMsg'/><br><br>
+Domoticz Out Topic *: <input type='text' name='domOutTop' id='domOutTop'/><br><br>
+All ON/OFF Topic *: <input type='text' name='allOnOffTop' id='allOnOffTop'/><br><br>
+Sleep: <input type='number' name='sleep' id='sleep'/><br>
 <br>
-<form id='saveForm' onsubmit='return transmit(this)'>
-<input type='hidden' name='save' value='1'>
-<input type='submit' value='Save changes to Flash'/>
+<input type='submit' value='Save'/>
 </form>
-<form id='rebootForm' onsubmit='return transmit(this)'>
-<input type='hidden' name='reboot' value='1'>
-<input type='submit' value='Reboot'/>
+<form id='restartForm' onsubmit='return res()'>
+<input type='hidden' name='restart' value='1'>
+<input type='submit' value='Restart'/>
 </form>
 
-
 <script>
-	function g(i) { return document.getElementById(i) };
-	var xhttp, updateTime;
-
-	function transmit(f) {
-		if (!xhttp) { //prevent simultaneous requests
-			//g('status').innerHTML = 'updating...';
-			xhttp = new XMLHttpRequest();
-			xhttp.open('POST', 'confdata');
-			xhttp.send(f ? (new FormData(f)) : '');
-			xhttp.onreadystatechange = function () {
-				if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
-					var data = JSON.parse(xhttp.responseText);
-					g('devName').value = data.devName
-					g('mqttHost').value = data.mqttHost;
-					g('mqttPort').value = data.mqttPort;
-					g('mqttUser').value = data.mqttUser;
-					g('mqttPass').value = data.mqttPass;
-					g('inTop').value = data.inTop;
-					g('outTop').value = data.outTop;
-					g('outTop_hold1').value = data.outTop_hold1;
-					g('outTop_hold2').value = data.outTop_hold2;
-					g('outTop_hold3').value = data.outTop_hold3;
-					g('outPld_hold1').value = data.outPld_hold1;
-					g('outPld_hold2').value = data.outPld_hold2;
-					g('outPld_hold3').value = data.outPld_hold3;
-					g('domoIdx1').value = data.domoIdx1;
-					g('domoIdx2').value = data.domoIdx2;
-					g('domoIdx3').value = data.domoIdx3;
-					g('domoOutTop').value = data.domoOutTop;
-					g('impuls1').value = data.impuls1;
-					g('impuls2').value = data.impuls2;
-					g('impuls3').value = data.impuls3;
-					
-					xhttp = null;
-					//g('status').innerHTML = '';
-					updateTime = 0;
-				}
-			}
-		}
-		return false; //prevent form redirect
-	}
-	transmit();
-	//setInterval(function () { g('ut').innerHTML = ++updateTime; }, 1000);
-	//setInterval(transmit, 5000); //autoupdate display every 5s
+ function g(i) { return document.getElementById(i) };
+  var xhttp, reqTime, reqFin, rxhttp;
+  
+  function res() {
+    rxhttp = new XMLHttpRequest();
+ rxhttp.timeout = 1000;
+  rxhttp.open('POST', 'restart');
+  rxhttp.send('');
+  rxhttp = null;
+  return false;
+  }
+  function setCheckbox(ele, dat) {
+    if(dat == 1) {
+      ele.checked = true;
+      ele.style.visibility = 'visible';
+    }
+    else {
+      ele.checked = false;
+      ele.style.visibility = 'visible';
+    }
+  }
+  function updateCheckboxValue(ele) {
+    if (ele.checked) ele.value ='1';
+    else {
+      ele.value = '0';
+      ele.checked = true;
+      ele.style.visibility = 'hidden';
+    }
+  }
+  
+  function transmit(f) {
+    if (!xhttp) {   
+    reqTime = 0;
+    reqFin = false;
+    updateCheckboxValue(g('outRet'));
+    updateCheckboxValue(g('willRet'));    
+    xhttp = new XMLHttpRequest();
+    xhttp.timeout = 2000;
+    xhttp.open('POST', 'confdata');
+    xhttp.send(f ? (new FormData(f)) : '');
+    xhttp.onreadystatechange = function () {
+      if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
+        var data = JSON.parse(xhttp.responseText);
+        g('httpUser').value = data.httpUser;
+        g('httpPass').value = data.httpPass;
+        g('devName').value = data.devName
+        g('mqttHost').value = data.mqttHost;
+        g('mqttPort').value = data.mqttPort;
+        g('mqttUser').value = data.mqttUser;
+        g('mqttPass').value = data.mqttPass;
+        g('inTop').value = data.inTop;
+        g('outTop').value = data.outTop;
+        g('willTop').value = data.willTop;
+        g('willQos').value = data.willQos;
+        setCheckbox(g('outRet'), data.outRet);
+        setCheckbox(g('willRet'), data.willRet);
+        g('willMsg').value = data.willMsg;
+        g('domOutTop').value = data.domOutTop;
+        g('allOnOffTop').value = data.allOnOffTop;
+        g('sleep').value = data.sleep;
+        xhttp = null;
+        reqFin = true;
+       }
+       else {
+         if(!reqFin && reqTime > 10) {
+           xhttp = null;
+           reqFin = true;
+         }
+       }
+     }
+   }
+    return false;
+  }
+  transmit();
+  setInterval(function () { ++reqTime; }, 1000);
 </script>
+</body></html>

+ 0 - 17
src/WiFiSwitch/data/conf.json

@@ -1,17 +0,0 @@
-{
-  "devName":"1234567890123456789012345678900",
-  "usedRelais":3,
-  "usedButtons":3,
-  "httpHost":"1234567890123456789012345678900",
-  "httpPass":"1234567890123456789012345678900",
-  "mqttHost":"12345678901234567890123456789012345678900",
-  "mqttPort":12345,
-  "mqttUser":"1234567890123456789012345678900",
-  "mqttPass":"1234567890123456789012345678900",
-  "inTop":"123456789012345678901234567890123456789012345678900",
-  "outTop":"123456789012345678901234567890123456789012345678900",
-  "willTop":"123456789012345678901234567890123456789012345678900",
-  "willQos":2,
-  "willRet":false,
-  "willMsg":"1234567890123456789012345678900",
-}

+ 114 - 0
src/WiFiSwitch/data/conf2.htm

@@ -0,0 +1,114 @@
+<html><head><body>
+<h3>Extended configuration</h3>
+<a href='/'>Home</a><br><br>
+<input type='button' value='reload' onclick='return transmit()'/><br>
+<form id='form1' onsubmit='return transmit(this)'>
+
+<h4>Domoticz</h4>
+Domoticz Idx 1: <input type='number' name='domIdx1' id='domIdx1'/><br>
+Domoticz Idx 2: <input type='number' name='domIdx2' id='domIdx2'/><br>
+Domoticz Idx 3: <input type='number' name='domIdx3' id='domIdx3'/><br>
+
+<h4>Button MQTT-Out</h4>
+Retain *: <input type='checkbox' name='btnRet' id='btnRet'/><br><br>
+1:<br>
+Topic: <input type='text' name='top1' id='top1'/> Payload: <input type='text' name='pld1' id='pld1'/><br>
+Hold:<br>
+Control relais: <select name='hld1ToRel' id='hld1ToRel'><option value='0'>-</option><option>1</option><option>2</option><option>3</option></select><br>
+Topic: <input type='text' name='topHld1' id='topHld1'/> Payload: <input type='text' name='pldHld1' id='pldHld1'/><br><br>
+2:<br>
+Topic: <input type='text' name='top2' id='top2'/> Payload: <input type='text' name='pld2' id='pld2'/><br>
+Hold:<br>
+Control relais: <select name='hld2ToRel' id='hld2ToRel'><option value='0'>-</option><option>1</option><option>2</option><option>3</option></select><br>
+Topic: <input type='text' name='topHld2' id='topHld2'/> Payload: <input type='text' name='pldHld2' id='pldHld2'/><br><br>
+3:<br>
+Topic: <input type='text' name='top3' id='top3'/> Payload: <input type='text' name='pld3' id='pld3'/><br>
+Hold:<br>
+Control relais: <select name='hld3ToRel' id='hld3ToRel'><option value='0'>-</option><option>1</option><option>2</option><option>3</option></select><br>
+Topic: <input type='text' name='topHld3' id='topHld3'/> Payload: <input type='text' name='pldHld3' id='pldHld3'/><br>
+
+<br>
+<input type='submit' value='Save'/>
+</form>
+<form id='rebootForm' onsubmit='return res()'>
+<input type='submit' value='Restart'/>
+</form>
+
+<script>
+ function g(i) { return document.getElementById(i) };
+  var xhttp, reqTime, reqFin, rxhttp;
+  
+  function res() {
+    rxhttp = new XMLHttpRequest();
+ rxhttp.timeout = 1000;
+  rxhttp.open('POST', 'restart');
+  rxhttp.send('');
+  rxhttp = null;
+  return false;
+  }
+  function setCheckbox(ele, dat) {
+    if(dat == 1) {
+      ele.checked = true;
+      ele.style.visibility = 'visible';
+    }
+    else {
+      ele.checked = false;
+      ele.style.visibility = 'visible';
+    }
+  }
+  function updateCheckboxValue(ele) {
+    if (ele.checked) ele.value ='1';
+    else {
+      ele.value = '0';
+      ele.checked = true;
+      ele.style.visibility = 'hidden';
+    }
+  }
+
+  function transmit(f) {
+    if (!xhttp) {
+      updateCheckboxValue(g('btnRet'));
+      xhttp = new XMLHttpRequest();
+      xhttp.timeout = 2000;
+      xhttp.open('POST', 'confdata2');
+      xhttp.send(f ? (new FormData(f)) : '');
+      xhttp.onreadystatechange = function () {
+        if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
+          var data = JSON.parse(xhttp.responseText);
+          setCheckbox(g('btnRet'), data.btnRet);
+          g('top1').value = data.top1;
+          g('top2').value = data.top2;
+          g('top3').value = data.top3;
+          g('topHld1').value = data.topHld1;
+          g('topHld2').value = data.topHld2;
+          g('topHld3').value = data.topHld3;
+          g('pld1').value = data.pld1;
+          g('pld2').value = data.pld2;
+          g('pld3').value = data.pld3;
+          g('pldHld1').value = data.pldHld1;
+          g('pldHld2').value = data.pldHld2;
+          g('pldHld3').value = data.pldHld3;
+     g('hld1ToRel').value = data.hld1ToRel;
+      g('hld2ToRel').value = data.hld2ToRel;
+      g('hld3ToRel').value = data.hld3ToRel;
+          g('domIdx1').value = data.domIdx1;
+          g('domIdx2').value = data.domIdx2;
+          g('domIdx3').value = data.domIdx3;
+      
+          xhttp = null;
+          reqFin = false;
+        }
+      else {
+          if(!reqFin && reqTime > 10) {
+            xhttp = null;
+            reqFin = true;
+          }
+       }
+    }
+    }
+    return false;
+  }
+  transmit();
+  setInterval(function () { ++reqTime; }, 1000);
+</script>
+</body></html>

+ 0 - 14
src/WiFiSwitch/data/conf2.json

@@ -1,14 +0,0 @@
-{
-  "outTop_hold1":"123456789012345678901234567890123456789012345678900",
-  "outTop_hold2":"123456789012345678901234567890123456789012345678900",
-  "outTop_hold3":"123456789012345678901234567890123456789012345678900",
-  "outPld_hold1":"1234567890123456789012345678900",
-  "outPld_hold2":"1234567890123456789012345678900",
-  "outPld_hold3":"1234567890123456789012345678900",
-  "domoIdx1":12345,
-  "domoIdx2":12345,
-  "domoIdx3":12345,
-  "impuls1":12345,
-  "impuls2":12345,
-  "impuls3":12345
-}

+ 0 - 22
src/WiFiSwitch/data/confdata

@@ -1,22 +0,0 @@
-{
-  "devName":"Testname",
-  "mqttHost":"10.1.1.11",
-  "mqttPort":1883,
-  "mqttUser":"",
-  "mqttPass":"",
-  "inTop":"Test/Switch/cmd",
-  "outTop":"Test/Switch/status",
-  "outTop_hold1":"Test/Switch/hold",
-  "outTop_hold2":"Test/Switch/hold",
-  "outTop_hold3":"Test/Switch/hold",
-  "outPld_hold1":"TOGGLE",
-  "outPld_hold2":"TOGGLE",
-  "outPld_hold3":"TOGGLE",
-  "domoIdx1":209,
-  "domoIdx2":1,
-  "domoIdx3":0,
-  "domoOutTop":"domoticz/out",
-  "impuls1":0,
-  "impuls2":0,
-  "impuls3":0
-}

+ 1 - 0
src/WiFiSwitch/data/confdata.json

@@ -0,0 +1 @@
+{"devName":"T5-AZ-Stehlampe","httpUser":"","httpPass":"","mqttHost":"10.1.1.11","mqttPort":1883,"mqttUser":"","mqttPass":"","inTop":"Top5/Licht/AZ/Stl/cmd","outTop":"Top5/Licht/AZ/Stl/status","outRet":0,"willTop":"","willQos":2,"willRet":0,"willMsg":"","domOutTop":"domoticz/out/Top 5/Arbeitszimmer","allOnOffTop":"Top5/Licht/all","sleep":10}

+ 1 - 0
src/WiFiSwitch/data/confdata2.json

@@ -0,0 +1 @@
+{"btnRet":0,"top1":"Top5/Licht/AZ/Stl/Btn1","top2":"","top3":"","pld1":"TOGGLE","pld2":"TOGGLE","pld3":"TOGGLE","topHld1":"","topHld2":"","topHld3":"","pldHld1":"TOGGLE","pldHld2":"TOGGLE","pldHld3":"TOGGLE","domIdx1":141,"domIdx2":0,"domIdx3":0,"hld1ToRel":0,"hld2ToRel":0,"hld3ToRel":0}

+ 1 - 0
src/WiFiSwitch/data/confdatahw.json

@@ -0,0 +1 @@
+{"debtime":120,"hldtime":750,"enaRel1":1,"enaRel2":0,"enaRel3":0,"enaBtn1":1,"enaBtn2":0,"enaBtn3":0,"enaLed1":1,"enaLed2":0,"enaLed3":0,"ioRel1":12,"ioRel2":0,"ioRel3":0,"ioBtn1":0,"ioBtn2":0,"ioBtn3":0,"ioLed1":13,"ioLed2":0,"ioLed3":0,"invRel1":1,"invRel2":0,"invRel3":0,"invBtn1":0,"invBtn2":0,"invBtn3":0,"invLed1":0,"invLed2":0,"invLed3":0,"puls1":0,"puls2":0,"puls3":0}

+ 169 - 0
src/WiFiSwitch/data/confhw.htm

@@ -0,0 +1,169 @@
+<html><head><body>
+<h3>Hardware configuration</h3>
+<a href='/'>Home</a><br><br>
+<input type='button' value='reload' onclick='return transmit()'/><br>
+<form id='form1' onsubmit='return transmit(this)'>
+<br>
+Avoid duplicate GPIO assignments!<br>
+GPIO 0 and 2 have fixed 10k pullup and must remain high during boot (otherwise flash mode is entered).<br>
+GPIO 2 is also connected to builtin LED (active LOW) on most ESP boards, so don't care or strip it.<br>
+GPIO 15 has 10k pull-down, so could only be used as active-high in/output.<br>
+Some GPIOs go high for some time at boot, so using only active-low logic is advisable, particularly for switching outputs<br>
+Invert = active HIGH, default LOW<br>
+Impulses are 0 by default (on until off), otherwise will auto-turn-off after specified time in factors of 100ms.<br>
+
+<h4>Relais</h4>
+1 - Enable: <input type='checkbox' name='enaRel1' id='enaRel1'/><br>
+GPIO: <select name='ioRel1' id='ioRel1'><option>0</option><option>2</option><option>4</option><option>5</option><option>12</option><option>13</option><option>14</option><option>15</option></select><br>
+Impulse: <input type='number' name='puls1' id='puls1'/><br>
+invert: <input type='checkbox' name='invRel1' id='invRel1'/><br><br>
+2 - Enable: <input type='checkbox' name='enaRel2' id='enaRel2'/><br>
+GPIO: <select name='ioRel2' id='ioRel2'><option>0</option><option>2</option><option>4</option><option>5</option><option>12</option><option>13</option><option>14</option><option>15</option></select><br>
+Impulse: <input type='number' name='puls2' id='puls2'/><br>
+invert: <input type='checkbox' name='invRel2' id='invRel2'/><br><br>
+3 - Enable: <input type='checkbox' name='enaRel3' id='enaRel3'/><br>
+GPIO: <select name='ioRel3' id='ioRel3'><option>0</option><option>2</option><option>4</option><option>5</option><option>12</option><option>13</option><option>14</option><option>15</option></select><br>
+Impulse: <input type='number' name='puls3' id='puls3'/><br>
+invert: <input type='checkbox' name='invRel3' id='invRel3'/><br>
+
+<h4>Buttons</h4>
+Debounce time: <input type='number' name='debtime' id='debtime'/> in ms. default: 120<br>
+Hold time: <input type='number' name='hldtime' id='hldtime'/> in ms. default: 750<br><br>
+1 - Enable: <input type='checkbox' name='enaBtn1' id='enaBtn1'/><br>
+GPIO: <select name='ioBtn1' id='ioBtn1'><option>0</option><option>2</option><option>4</option><option>5</option><option>12</option><option>13</option><option>14</option><option>15</option></select><br>
+invert: <input type='checkbox' name='invBtn1' id='invBtn1'/><br><br>
+2 - Enable: <input type='checkbox' name='enaBtn2' id='enaBtn2'/><br>
+GPIO: <select name='ioBtn2' id='ioBtn2'><option>0</option><option>2</option><option>4</option><option>5</option><option>12</option><option>13</option><option>14</option><option>15</option></select><br>
+invert: <input type='checkbox' name='invBtn2' id='invBtn2'/><br><br>
+3 - Enable: <input type='checkbox' name='enaBtn3' id='enaBtn3'/><br>
+GPIO: <select name='ioBtn3' id='ioBtn3'><option>0</option><option>2</option><option>4</option><option>5</option><option>12</option><option>13</option><option>14</option><option>15</option></select><br>
+invert: <input type='checkbox' name='invBtn3' id='invBtn3'/><br>
+
+<h4>Status LEDs</h4>
+1 - Enable: <input type='checkbox' name='enaLed1' id='enaLed1'/><br>
+GPIO: <select name='ioLed1' id='ioLed1'><option>0</option><option>2</option><option>4</option><option>5</option><option>12</option><option>13</option><option>14</option><option>15</option></select><br>
+invert: <input type='checkbox' name='invLed1' id='invLed1'/><br><br>
+2 - Enable: <input type='checkbox' name='enaLed2' id='enaLed2'/><br>
+GPIO: <select name='ioLed2' id='ioLed2'><option>0</option><option>2</option><option>4</option><option>5</option><option>12</option><option>13</option><option>14</option><option>15</option></select><br>
+invert: <input type='checkbox' name='invLed2' id='invLed2'/><br><br>
+3 - Enable: <input type='checkbox' name='enaLed3' id='enaLed3'/><br>
+GPIO: <select name='ioLed3' id='ioLed3'><option>0</option><option>2</option><option>4</option><option>5</option><option>12</option><option>13</option><option>14</option><option>15</option></select><br>
+invert: <input type='checkbox' name='invLed3' id='invLed3'/><br><br>
+
+<br>
+<input type='submit' value='Save'/>
+</form>
+<form id='rebootForm' onsubmit='return res()'>
+<input type='submit' value='Restart'/>
+</form>
+
+<script>
+ function g(i) { return document.getElementById(i) };
+  var xhttp, reqTime, reqFin, rxhttp;
+  
+  function res() {
+    rxhttp = new XMLHttpRequest();
+ rxhttp.timeout = 1000;
+  rxhttp.open('POST', 'restart');
+  rxhttp.send('');
+  rxhttp = null;
+  return false;
+  }
+  
+  function setCb(ele, dat) {
+    if(dat == 1) {
+      ele.checked = true;
+      ele.style.visibility = 'visible';
+    }
+    else {
+      ele.checked = false;
+      ele.style.visibility = 'visible';
+    }
+  }
+  
+  function updCbVal(ele) {
+    if (ele.checked) ele.value ='1';
+    else {
+      ele.value = '0';
+      ele.checked = true;
+      ele.style.visibility = 'hidden';
+    }
+  }
+  
+
+  function transmit(f) {
+    if (!xhttp) {
+   updCbVal(g('enaRel1'));
+      updCbVal(g('enaRel2'));
+      updCbVal(g('enaRel3'));
+      updCbVal(g('enaBtn1'));
+      updCbVal(g('enaBtn2'));
+      updCbVal(g('enaBtn3'));
+      updCbVal(g('invRel1'));
+      updCbVal(g('invRel2'));
+      updCbVal(g('invRel3'));
+      updCbVal(g('invBtn1'));
+      updCbVal(g('invBtn2'));
+      updCbVal(g('invBtn3'));
+   updCbVal(g('enaLed1'));
+      updCbVal(g('enaLed2'));
+      updCbVal(g('enaLed3'));
+    updCbVal(g('invLed1'));
+      updCbVal(g('invLed2'));
+      updCbVal(g('invLed3'));
+      xhttp = new XMLHttpRequest();
+      xhttp.timeout = 2000;
+      xhttp.open('POST', 'confdatahw');
+      xhttp.send(f ? (new FormData(f)) : '');
+      xhttp.onreadystatechange = function () {
+        if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
+          var data = JSON.parse(xhttp.responseText);
+          g('debtime').value = data.debtime;
+          g('hldtime').value = data.hldtime;
+          g('ioRel1').value = data.ioRel1;
+          g('ioRel2').value = data.ioRel2;
+          g('ioRel3').value = data.ioRel3;
+          g('puls1').value = data.puls1;
+          g('puls2').value = data.puls2;
+          g('puls3').value = data.puls3;
+          g('ioBtn1').value = data.ioBtn1;
+          g('ioBtn2').value = data.ioBtn2;
+          g('ioBtn3').value = data.ioBtn3;
+      g('ioLed1').value = data.ioLed1;
+          g('ioLed2').value = data.ioLed2;
+          g('ioLed3').value = data.ioLed3;
+          setCb(g('enaRel1'), data.enaRel1);
+          setCb(g('enaRel2'), data.enaRel2);
+          setCb(g('enaRel3'), data.enaRel3);
+          setCb(g('enaBtn1'), data.enaBtn1);
+          setCb(g('enaBtn2'), data.enaBtn2);
+          setCb(g('enaBtn3'), data.enaBtn3);
+          setCb(g('invRel1'), data.invRel1);
+          setCb(g('invRel2'), data.invRel2);
+          setCb(g('invRel3'), data.invRel3);
+          setCb(g('invBtn1'), data.invBtn1);
+          setCb(g('invBtn2'), data.invBtn2);
+          setCb(g('invBtn3'), data.invBtn3);
+      setCb(g('enaLed1'), data.enaLed1);
+          setCb(g('enaLed2'), data.enaLed2);
+          setCb(g('enaLed3'), data.enaLed3);
+      setCb(g('invLed1'), data.invLed1);
+          setCb(g('invLed2'), data.invLed2);
+          setCb(g('invLed3'), data.invLed3);
+          xhttp = null;
+      reqFin = false;
+        }
+      else {
+          if(!reqFin && reqTime > 10) {
+            xhttp = null;
+            reqFin = true;
+            }
+      }
+    }
+    }
+    return false;
+  }
+  transmit();
+  setInterval(function () { ++reqTime; }, 1000);
+</script>
+</body></html>

+ 92 - 54
src/WiFiSwitch/data/index.htm

@@ -1,54 +1,92 @@
-<h3>Current Data</h3>
-Temp:
-<span id="temp"></span>
-<br/> set Temp:
-<span id="setTemp"></span>
-<br/> current WiFi SSID:
-<span id="ssid"></span>
-
-<h6>Last updated
-	<span id="ut"></span> seconds ago.
-	<span id="status"></span>
-</h6>
-
-<h3>Update Data</h3>
-<form id="yform" onsubmit="return transmit(this)">
-	set temp:
-	<input type="text" name="setTemp" />
-	<input type="submit" />
-</form>
-
-<a href="wifi.htm">WiFi settings</a><br/>
-<a href="/update">Firmware Update</a>
-
-<script>
-	function g(i) { return document.getElementById(i) };
-	var xhttp, updateTime;
-
-	function transmit(f) {
-		if (!xhttp) { //prevent simultaneous requests
-			g("status").innerHTML = "updating...";
-			xhttp = new XMLHttpRequest();
-			xhttp.open("POST", "/api");
-			xhttp.send(f ? (new FormData(f)) : "");
-			xhttp.onreadystatechange = function () {
-				if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
-					var data = JSON.parse(xhttp.responseText);
-					g("x").innerHTML = data.x;
-					g("y").innerHTML = data.y;
-					g("z").innerHTML = data.z;
-					g("ssid").innerHTML = data.ssid;
-					g("temp").innerHTML = data.temp;
-					g("setTemp").innerHTML = data.setTemp;
-					xhttp = null;
-					g("status").innerHTML = "";
-					updateTime = 0;
-				}
-			}
-		}
-		return false; //prevent form redirect
-	}
-	transmit();
-	setInterval(function () { g("ut").innerHTML = ++updateTime; }, 1000);
-	setInterval(transmit, 5000); //autoupdate display every 5s
-</script>
+<html><body>
+  <h1><span id='devname'></span></h1>
+  <h3>WiFi Switch</h3>  
+  <form id='BtnFrm1'><input type='hidden' name='Btn1' value='1'></form>
+  <form id='BtnFrm2'><input type='hidden' name='Btn2' value='1'></form>
+  <form id='BtnFrm3'><input type='hidden' name='Btn3' value='1'></form>
+  <form id='AllOnFrm'><input type='hidden' name='allOn' value='1'></form>
+  <form id='AllOffFrm'><input type='hidden' name='allOff' value='1'></form>
+  <input type='button' value='All ON' id='allOn' onclick='return sendAllOn()'/><input type='button' value='All OFF' id='allOff' onclick='return sendAllOff()'/>
+  <div style='font-size:xx-large'>
+  <div id='sw1div' style='display:none'>1: <input type='button' id='tbtn1' onclick='return sendBtn(1)'/><br></div>
+  <div id='sw2div' style='display:none'>2: <input type='button' id='tbtn2' onclick='return sendBtn(2)'/><br></div>
+  <div id='sw3div' style='display:none'>3: <input type='button' id='tbtn3' onclick='return sendBtn(3)'/><br></div>
+  </div>
+  <br>  
+  <br>
+  WiFi connected to <i><span id='ssid'></span></i>.<br>
+  <h6>Last update 
+  <span id='ut'></span> seconds ago. 
+  <span id='status'></span>
+  </h6>
+  <br>
+  <a href='/wifi.htm'>WiFi settings</a><br>
+  <a href='/confhw'>Hardware configuration</a><br>
+  <a href='/conf'>Base configuration</a><br>
+  <a href='/conf2'>Extended configuration</a><br>
+  <a href='/update'>Firmware update</a><br>
+  <a href='/restart'>Restart</a>
+  <script>
+  function g(i) { return document.getElementById(i) };
+  var xhttp, updateTime, reqTime, reqFin;
+  var textA = 'OFF';
+  var textE = 'ON';
+  
+  function sendBtn(btn) {
+  var frmn='BtnFrm'+btn;
+  var form = g(frmn);
+  return transmit(form);
+  }
+  
+  function sendAllOn() {
+  return transmit(g('AllOnFrm'));
+  }
+  function sendAllOff() {
+  return transmit(g('AllOffFrm'));
+  }
+  
+  function transmit(f) {
+    if (!xhttp) { 
+      g('status').innerHTML = 'loading...';
+      reqTime = 0;
+      reqFin = false;
+      xhttp = new XMLHttpRequest();
+      xhttp.timeout = 2000;
+      xhttp.open('POST', 'api');
+      xhttp.send(f ? (new FormData(f)) : '');
+      xhttp.onreadystatechange = function () {
+        if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
+          var data = JSON.parse(xhttp.responseText);
+          g('ssid').innerHTML = data.ssid;
+          if(data.devname != undefined) g('devname').innerHTML = data.devname;
+          if(data.swState1 != undefined) g('sw1div').style.display='inline';
+          else g('sw1div').style.display='none';
+     if(data.swState2 != undefined) g('sw2div').style.display='inline';
+          else g('sw2div').style.display='none';
+          if(data.swState3 != undefined) g('sw3div').style.display='inline';
+          else g('sw3div').style.display='none';
+          if(data.swState1 == '1') g('tbtn1').value = textE;
+          else g('tbtn1').value = textA;
+          if(data.swState2 == '1') g('tbtn2').value = textE;
+          else g('tbtn2').value = textA;
+          if(data.swState3 == '1') g('tbtn3').value = textE;
+          else g('tbtn3').value = textA;
+          xhttp = null;
+          updateTime = 0;
+          reqFin = true;
+          }
+        else {
+          if(!reqFin && reqTime > 10) {
+            xhttp = null;
+            reqFin = true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+  transmit();
+  setInterval(function () { g('ut').innerHTML = ++updateTime; ++reqTime; }, 1000);
+  setInterval(transmit, 5000);
+</script>
+</body></html>

+ 0 - 108
src/WiFiSwitch/data/wifi.htm

@@ -1,108 +0,0 @@
-<!DOCTYPE html>
-<html>
-
-<head>
-	<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
-	<title>ESP WiFi</title>
-	<script>
-		function g(i){return document.getElementById(i);};
-		function p(t,l){if(confirm(t)) window.location=l;};
-		function E(s){return document.createElement(s)};
-		var S="setAttribute",A="appendChild",H="innerHTML",X,wl;
-		function scan(){
-			if(X) return;
-			X=new XMLHttpRequest(),wl=document.getElementById('wl');
-			wl[H]="Scanning...";
-			X.onreadystatechange=function(){
-				if (this.readyState==4&&this.status==200) {
-					X=0;
-					wl[H]="";
-					this.responseText.split("\n").forEach(function (e) {
-						let t=e.split(","), s=t.slice(2).join(',');
-						var d=E('div'),i=E('a'),c=E('a');
-						i[S]('class','s'); c[S]('class','q');
-						i.onclick=function(){g('s').value=s;g('p').focus();};
-						i[A](document.createTextNode(s));
-						c[H]=t[0]+"%"+(parseInt(t[1])?"\uD83D\uDD12":"\u26A0");
-						wl[A](i); wl[A](c);
-						wl[A](document.createElement('br'));
-					});
-				}
-			};
-			X.open("GET","wifi/list",true);
-			X.send();
-		};
-	</script>
-	<style>
-		input {
-			padding:5px;
-			font-size:1em;
-			width:95%;
-			filter:invert(100%);
-		}
-
-		body {
-			text-align:center;
-			font-family:verdana;
-			background-color:black;
-			color:white;
-		}
-
-		a {
-			color:#1fa3ec;
-		}
-
-		button {
-			border:0;
-			border-radius:0.3em;
-			background-color:#1fa3ec;
-			color:#fff;
-			line-height:2.4em;
-			font-size:1.2em;
-			width:100%;
-			display:block;
-		}
-
-		.q {
-			float:right;
-		}
-
-		.s {
-			display:inline-block;
-			width:14em;
-			overflow:hidden;
-			text-overflow:ellipsis;
-			white-space:nowrap;
-		}
-		#wl{
-			line-height:1.5em;
-		}
-	</style>
-</head>
-
-<body>
-	<div style='text-align:left;display:inline-block;width:320px;padding:5px'>
-		<button onclick="scan()">&#x21bb; Scan</button>
-		<p id='wl'></p>
-		<form method='post' action='/wifi/connect'>
-			<input id='s' name='n' length=32 placeholder='SSID'>
-			<br>
-			<input id='p' name='p' length=64 type='password' placeholder='password'>
-			<br>
-			<br>
-			<button type='submit'>Connect</button>
-		</form>
-		<br>
-		<br>
-		<button onclick="p('Start WPS?','/wifi/wps')">WPS Setup</button>
-		<br>
-		<button onclick="p('Start AP mode?','/wifi/ap')">AP Mode</button>
-		<br>
-		<button onclick="p('Reboot device?','/wifi/rst')">Reboot</button>
-		<br>
-		<a href="javascript:history.back()">Back</a> | 
-		<a href="/">Home</a>
-	</div>
-</body>
-
-</html>

+ 6 - 1
src/WiFiSwitch/domoticz.ino

@@ -22,11 +22,13 @@ void parseDomoticzOut() {
   int16_t nvalue;
   int16_t found = 0;
 
-  StaticJsonBuffer<450> jsonBuf;
+  StaticJsonBuffer<500> jsonBuf;
   JsonObject& domoticz = jsonBuf.parseObject(domoticzOutPayload);
   idx = domoticz["idx"];
   nvalue = domoticz["nvalue"];
 
+  yield();
+
   for (int i = 0; i < sizeof(domoticzIdx); i++) {
     if ( idx == domoticzIdx[i] && idx != 0 ) {
       if ( dismissUpdateFromDomoticzTimeout == 0 || (lastSwitchSource[i] == 3 || (lastSwitchSource[i] < 3 && (millis() - lastSwitchTime[i]) > dismissUpdateFromDomoticzTimeout)) ) {
@@ -38,6 +40,9 @@ void parseDomoticzOut() {
         Serial.print(idx);
         Serial.print(", nvalue=");
         Serial.println(nvalue);
+        
+        yield();
+        
         if (nvalue == 1) {
           updateDomoticz[i] = false; // prevent loop - when update came from domoticz, do not update domoticz state
           lastSwitchSource[i] = 3;

+ 0 - 64
src/WiFiSwitch/html/index - Copy.htm

@@ -1,64 +0,0 @@
-<html><body>
-  <h1>WiFi Thermostat</h1>
-  
-  <form id='minusBtn' onsubmit='return transmit(this)'>
-	<input type='hidden' name='minusBtn' value='1'>
-	<input type='submit' value='-'/>
-  </form>
-  <span id='setTemp'>22.5</span><br>
-  <form id='plusBtn' onsubmit='return transmit(this)'>
-    <input type='hidden' name='plusBtn' value='1'>
-	<input type='submit' value='+'/>
-  </form>
-  
-  Betriebsmodus: <span id='mode'></span><br>
-  <br>
-  Aktuell: <span id='temp'></span> °C&nbsp;&nbsp;&nbsp;<span id='hum'></span> %<br>  
-  <br>
-  WiFi verbunden mit: <span id='ssid'></span><br>
-  <h6>Letztes Update vor
-  <span id='ut'></span> Sekunden.
-  <span id='status'></span>
-  </h6>
-  <br><a href='/wifi.htm'>WiFi-Einstellungen</a><br>
-  <a href='/update'>Firmware Update</a>
-  <script>
-  function g(i) { return document.getElementById(i) };
-  var xhttp, updateTime;
-  function transmit(f) {
-    if (!xhttp) { 
-      g('status').innerHTML = 'lade...';
-      xhttp = new XMLHttpRequest();
-      xhttp.open('POST', '/api');
-      xhttp.send(f ? (new FormData(f)) : '');
-      xhttp.onreadystatechange = function () {
-        if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
-          var data = JSON.parse(xhttp.responseText);
-          g('temp').innerHTML = data.temp;
-		  g('hum').innerHTML = data.hum;
-          g('setTemp').innerHTML = data.setTemp;          
-          g('ssid').innerHTML = data.ssid;
-		  
-		  if(data.mode == '0') {
-		    g('mode').innerHTML = 'AUS';
-		  }
-		  else if(data.mode == '1') {
-			g('mode').innerHTML = 'Normalbetrieb';
-		  }
-		  else if(data.mode == '2') {
-			g('mode').innerHTML = 'Nachtabsenkung';
-		  }
-
-          xhttp = null;
-          g('status').innerHTML = '';
-          updateTime = 0;
-        }
-      }
-    }
-    return false;
-  }
-  transmit();
-  setInterval(function () { g('ut').innerHTML = ++updateTime; }, 1000);
-  setInterval(transmit, 5000);
-</script>
-  </body></html>

+ 0 - 72
src/WiFiSwitch/html/index.htm

@@ -1,72 +0,0 @@
-<html><body>
-  <h1>WiFi Thermostat</h1>
-  
-  <!--<form id='tempbtns'>
-  <input type='text' id='tempbtn' name='tempbtn' value='xx'>
-  </form>-->
-  <span id='setTemp'>22.5</span> &#8451;
-	<input type='button' onclick='return sendBtn("min")' value='-'/>
-	<input type='button' onclick='return sendBtn("pls")' value='+'/>
-  
-  <br><br>
-  Betriebsmodus: <span id='mode'></span><br>
-  <br>
-  Aktuell: <span id='temp'></span> &#8451;&nbsp;&nbsp;&nbsp;<span id='hum'></span> %<br>  
-  <br>
-  WiFi verbunden mit: <span id='ssid'></span><br>
-  <h6>Letztes Update vor
-  <span id='ut'></span> Sekunden.
-  <span id='status'></span>
-  </h6>
-  <br><a href='/wifi.htm'>WiFi-Einstellungen</a><br>
-  <a href='/update'>Firmware Update</a>
-  <script>
-  function g(i) { return document.getElementById(i) };
-  var xhttp, updateTime;
-  
-  function sendBtn(btn) {
-  //var form = document.getElementById('tempbtns');
-  //var act=document.getElementById('tempbtn')
-  //act.value = btn;
-  var fd = new FormData();
-  fd.append('tempbtn', btn);
-  return transmit(fd);
-  }
-  
-  function transmit(f) {
-    if (!xhttp) { 
-      g('status').innerHTML = 'lade...';
-      xhttp = new XMLHttpRequest();
-      xhttp.open('POST', 'api.php');
-      xhttp.send(f ? (new FormData(f)) : '');
-      xhttp.onreadystatechange = function () {
-        if (xhttp.readyState === XMLHttpRequest.DONE && xhttp.status === 200) {
-          var data = JSON.parse(xhttp.responseText);
-          g('temp').innerHTML = data.temp;
-		  g('hum').innerHTML = data.hum;
-          g('setTemp').innerHTML = data.setTemp;          
-          g('ssid').innerHTML = data.ssid;
-		  
-		  if(data.mode == '0') {
-		    g('mode').innerHTML = 'AUS';
-		  }
-		  else if(data.mode == '1') {
-			g('mode').innerHTML = 'Normalbetrieb';
-		  }
-		  else if(data.mode == '2') {
-			g('mode').innerHTML = 'Nachtabsenkung';
-		  }
-
-          xhttp = null;
-          g('status').innerHTML = '';
-          updateTime = 0;
-        }
-      }
-    }
-    return false;
-  }
-  transmit();
-  setInterval(function () { g('ut').innerHTML = ++updateTime; }, 1000);
-  setInterval(transmit, 5000);
-</script>
-  </body></html>

File diff suppressed because it is too large
+ 665 - 288
src/WiFiSwitch/httpServer.ino


+ 39 - 4
src/WiFiSwitch/misc.ino

@@ -1,10 +1,45 @@
 unsigned long lastRun = 0;
+int count100ms = 0;
+int countSeconds = 0;
 
-void everySecond() {
-  if( (millis() - lastRun) > 1000 ) {
+void checkMillis() {
+  if ( (millis() - lastRun) > 100 ) {
     lastRun = millis();
-    // this code is run once per second
-    checkUseDomoticz();
+    every100ms();
+  }
+}
+
+void every100ms() {
+  if (count100ms < 10) count100ms++;
+  else {
+    count100ms = 0;
+    everySecond();
+  }
+
+  checkSaveConfigTriggered();
+  relais_handleImpulseTimeout();
+
+}
+
+void everySecond() {
+  if (countSeconds < 60) countSeconds++;
+  else {
+    countSeconds = 0;
+    everyMinute();
   }
+
+  checkUseDomoticz();
+}
+
+void everyMinute() {
+  publishStatus();
+}
+
+
+void publishStatus() {
+  char outMsg[60];
+  long upTime = millis() / 1000;
+  sprintf(outMsg, "connected, reconnects: %d, uptime: %d, free heap: %d", mqttReconnects - 1, upTime, ESP.getFreeHeap());
+  mqttclient.publish(mqtt_topic_out, outMsg, mqtt_outRetain);
 }
 

+ 69 - 25
src/WiFiSwitch/mqtt.ino

@@ -11,6 +11,7 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
   if (strcmp(topic, mqtt_topic_in) == 0) { //if topic = mqtt_topic_in
     Serial.print("received MQTT ");
     Serial.println(mqtt_topic_in);
+
     for (int i = 0; i < length; i++) {
       cmdPayload[i] = (char)payload[i];
     }
@@ -23,8 +24,29 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
 
   }//if topic = mqtt_topic_in
 
-  //if ( useDomoticz ) {
-  if (strcmp(topic, domoticz_out_topic) == 0) { //if topic = DOMOTICZ_OUT_TOPIC
+  if (strcmp(topic, mqtt_allOnOffTopic) == 0) { //if topic = mqtt_allOnOffTopic
+    Serial.print("received MQTT ");
+    Serial.println(mqtt_allOnOffTopic);
+
+    char tmpPayload[length + 1];
+
+    for (int i = 0; i < length; i++) {
+      tmpPayload[i] = (char)payload[i];
+    }
+    tmpPayload[length] = '\0';
+
+    Serial.print("tmpPayload:");
+    Serial.println(tmpPayload);
+
+    if (strcmp(tmpPayload, "ON") == 0 || strcmp(tmpPayload, "On") == 0 || strcmp(tmpPayload, "on") == 0) {
+      allRelaisOn();
+    }
+    else if (strcmp(tmpPayload, "OFF") == 0 || strcmp(tmpPayload, "Off") == 0 || strcmp(tmpPayload, "off") == 0) {
+      allRelaisOff();
+    }
+  }//if topic = mqtt_allOnOffTopic
+
+  if (strcmp(topic, domoticz_out_topic) == 0) { //if topic = domoticz_out_topic
     Serial.print("received MQTT ");
     Serial.println(domoticz_out_topic);
     if ( !domoticzOutParserBusy ) {
@@ -34,66 +56,88 @@ void mqttCallback(char* topic, byte* payload, unsigned int length) {
       domoticzOutPayload[length + 1] = '\0';
       domoticzOutParseData = true; // parse domoticz data in the next loop()
     }
-  }
-  //}
-
-
+  }//if topic = domoticz_out_topic
 }//mqttCallback
 
-int mqttReconnects=0;
-
-boolean mqttReconnect() {
-  // Create a random MQTT client ID
-  String mqttClientId = "ESP8266Client-";
-  mqttClientId += String(random(0xffff), HEX);
-  boolean connRes;
+void mqttPrepareConnection() {
   Serial.print("MQTT connection with ");
   if (strlen(mqtt_user) > 0 && strlen(mqtt_willTopic) == 0) {
     // user and password, no Last Will
-    // boolean connect(const char* id, const char* user, const char* pass);
-    connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_user, mqtt_pass);
     Serial.println("user and password, no Last Will");
+    mqttMode = 2;
   }
   else if (strlen(mqtt_user) > 0 && strlen(mqtt_willTopic) > 0) {
     // user, password and Last Will
-    // boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
-    connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_user, mqtt_pass, mqtt_willTopic, mqtt_willQos, mqtt_willRetain, mqtt_willMsg);
     Serial.println("user, password and Last Will");
+    mqttMode = 3;
   }
   else if (strlen(mqtt_user) == 0 && strlen(mqtt_willTopic) > 0) {
     // Last Will but no user and password
-    // boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
-    connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_willTopic, mqtt_willQos, mqtt_willRetain, mqtt_willMsg);
     Serial.println("Last Will but no user and password");
+    mqttMode = 4;
   }
   else {
     // no user, password and no Last Will
     Serial.println("no user, password and no Last Will");
-    connRes = mqttclient.connect(mqttClientId.c_str());
+    mqttMode = 1;
   }
+}
 
+boolean mqttReconnect() {
+  // Create a random MQTT client ID
+  String mqttClientId = "ESP8266Client-";
+  mqttClientId += String(random(0xffff), HEX);
+
+  Serial.print("MQTT connection with ");
+
+  boolean connRes;
+
+  switch (mqttMode) {
+    case 1:
+      Serial.println("no user, password and no Last Will");
+      connRes = mqttclient.connect(mqttClientId.c_str());
+      break;
+    case 2:
+      Serial.println("user and password, no Last Will");
+      connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_user, mqtt_pass);
+      break;
+    case 3:
+      Serial.println("user, password and Last Will");
+      connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_user, mqtt_pass, mqtt_willTopic, mqtt_willQos, mqtt_willRetain, mqtt_willMsg);
+      break;
+    case 4:
+      Serial.println("Last Will, no user and password");
+      connRes = mqttclient.connect(mqttClientId.c_str(), mqtt_willTopic, mqtt_willQos, mqtt_willRetain, mqtt_willMsg);
+      break;
+  }
 
   if (connRes) {
     Serial.print("MQTT connected. Reconnects: ");
     Serial.println(mqttReconnects);
-    
+    mqttReconnects++;
+
     // Once connected, publish an announcement...
-    char outMsg[30];
-    sprintf(outMsg, "connected, %d reconnects", mqttReconnects);
-    mqttclient.publish(mqtt_topic_out, outMsg);
+    //    char outMsg[30];
+    //    sprintf(outMsg, "connected, %d reconnects", mqttReconnects);
+    //    mqttclient.publish(mqtt_topic_out, outMsg, mqtt_outRetain);
+    publishStatus();
     //mqttclient.publish(mqtt_topic_out, "connected");
     // ... and resubscribe
     Serial.println("Subscribed to:");
     if (mqttclient.subscribe(mqtt_topic_in)) {
       Serial.println(mqtt_topic_in);
     }
+    if (strlen(mqtt_allOnOffTopic) > 0) {
+      if (mqttclient.subscribe(mqtt_allOnOffTopic)) {
+        Serial.println(mqtt_allOnOffTopic);
+      }
+    }
     if (useDomoticz && strlen(domoticz_out_topic) > 0) {
       if (mqttclient.subscribe(domoticz_out_topic)) {
         Serial.println(domoticz_out_topic);
       }
     }
   }
-  mqttReconnects++;
   return mqttclient.connected();
 } //mqttReconnect
 

+ 27 - 8
src/WiFiSwitch/relais.ino

@@ -1,3 +1,13 @@
+void allRelaisOn() {
+  for ( int i = 0; i < 3; i++) {
+    relaisOn(i);
+  }
+}
+void allRelaisOff() {
+  for ( int i = 0; i < 3; i++) {
+    relaisOff(i);
+  }
+}
 void relaisToggle(byte relaisnr) {
   if (relais_state[relaisnr]) {
     relaisOff(relaisnr);
@@ -7,12 +17,10 @@ void relaisToggle(byte relaisnr) {
   }
 }
 void relaisOn(byte relaisnr) {
-  switchRelais(relaisnr, true);
-  //relais_setState[relaisnr] = true;
+  if (relais_enabled[relaisnr]) switchRelais(relaisnr, true);
 }
 void relaisOff(byte relaisnr) {
-  switchRelais(relaisnr, false);
-  //relais_setState[relaisnr] = false;
+  if (relais_enabled[relaisnr]) switchRelais(relaisnr, false);
 }
 
 //void handleRelaisSwitching() {
@@ -31,14 +39,25 @@ void relaisOff(byte relaisnr) {
 
 void switchRelais(byte relaisnr, bool onoff) {
   lastSwitchTime[relaisnr] = millis();
-  bool toState;
+  bool toState, ledToState;
 
   if ( onoff ) toState = RELAISONSTATE;
   else toState = !RELAISONSTATE;
 
+  if (relais_invert[relaisnr]) toState = !toState;
+
   digitalWrite(relais_pins[relaisnr], toState);
   relais_state[relaisnr] = onoff;
 
+  // status LED if enabled
+  if (led_enabled[relaisnr]) {
+    if ( onoff ) ledToState = LEDONSTATE;
+    else ledToState = !LEDONSTATE;
+    if (led_invert[relaisnr]) ledToState = !ledToState;
+
+    digitalWrite(leds_pins[relaisnr], ledToState);
+  }
+
   Serial.print("Relais ");
   Serial.print(relaisnr + 1);
   Serial.print(" ");
@@ -52,7 +71,7 @@ void switchRelais(byte relaisnr, bool onoff) {
   char tmp_topic_out[50];
   sprintf(tmp_topic_out, "%s/%d", mqtt_topic_out, relaisnr + 1);
 
-  mqttclient.publish(tmp_topic_out, stateName);
+  mqttclient.publish(tmp_topic_out, stateName, mqtt_outRetain);
 
   if (updateDomoticz[relaisnr]) {
     int idx = domoticzIdx[relaisnr];
@@ -75,8 +94,8 @@ void switchRelais(byte relaisnr, bool onoff) {
 
 void relais_handleImpulseTimeout() {
   for (int i = 0; i < RELAIS_COUNT; i++) {
-    if (relais_impulse[i] > 0) {
-      if (relais_state[i] == true && (millis() - lastSwitchTime[i]) > relais_impulse[i]) {
+    if (relais_impulse[i] > 0 && relais_enabled[i]) {
+      if (relais_state[i] == true && (millis() - lastSwitchTime[i]) > ( relais_impulse[i] * 100 ) ) {
         Serial.print("Relais ");
         Serial.print(i + 1);
         Serial.println(" impulse timeout reached");

Some files were not shown because too many files changed in this diff