Skip to content

Instantly share code, notes, and snippets.

@tspspi

tspspi/LICENSE Secret

Created January 29, 2020 11:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tspspi/fa5912ef18fd88300981f868dfb17bc1 to your computer and use it in GitHub Desktop.
Save tspspi/fa5912ef18fd88300981f868dfb17bc1 to your computer and use it in GitHub Desktop.
Simple Sonoff T4EU1C Arduino Firmware

What is this?

This is a really basic and simple firmware to be used on an Sonoff T4EU1C that provides some basic capabilities like

  • Local control via touch button
  • Manual control that prevents remote control for a given duration
  • Remote control via HTTP webinterface
  • Remote control via MQTT
  • Status update via MQTT
  • Detection of connection loss & automatic reconnection
  • Automatically powering off devices after a given timeout

Since this is one of the most basic solutions it's built using the Arduino framework and includes all configuration inside code instead of flash memory. This also means that one has to build an own image for every device and has to take care not to leak any authentication information along with the code. It also only uses WPA-PSK which might be a bad idea.

More detail can be found at https://www.tspi.at/2020/01/15/sonofft4eu1c.html

Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
/*
Simple alternative ESP8266 or ESP8265 firmware for
T4EU1C switches.
Note:
This firmware contains the following placeholders inside code to be
filled by a shellscript / the Jenkins build file:
- WiFi SSID and Password
{FILLIN_WIFI_SSID}
{FILLIN_WIFI_PWD}
- OTA shared secret and device name
{FILLIN_OTA_NAME}
{FILLIN_OTA_KEY}
- MQTT credentials and server configuration
{FILLIN_MQTT_SERVER}
{FILLIN_MQTT_PORT}
{FILLIN_MQTT_USER}
{FILLIN_MQTT_PWD}
{FILLIN_MQTT_TOPIC}
- Additional title
{FILLIN_TITLE}
An own firmware image has to be built for every device. Also make
sure not to publish your credentials to any SCM system (for example
by filling them in your CI/CD chain).
*/
// Select which control mechanisms should be embedded into the firmware
#define CTRL_HTTP_ENABLE 1
#define CTRL_MQTT_ENABLE 1
/* Credentials and connection information */
const char* ssid = "{FILLIN_WIFI_SSID}";
const char* password = "{FILLIN_WIFI_PWD}";
#define MQTT_SERVER "{FILLIN_MQTT_SERVER}"
#define MQTT_PORT {FILLIN_MQTT_PORT}
#define MQTT_USER "{FILLIN_MQTT_USER}"
#define MQTT_PASSWORD "{FILLIN_MQTT_PWD}"
#ifdef CTRL_HTTP_ENABLE
static const char* htmlPart1 = "<!DOCTYPE HTML><html><head><title>Lichtschalter {FILLIN_TITLE}</title><style type=\"text/css\">h1{display:block;color:white;background-color:darkgreen;font-variant:small-caps;padding:0.5ex 1ex 0.5ex 1ex;margin-bottom:0;}div#m{margin-top:0;border:1px solid grey;padding:0.5ex 1ex 0.5ex 1ex;}</style></head><body><h1>Simpler Lichtschalter {FILLIN_TITLE}</h1><div id=\"m\"><p>Status: <strong>";
static const char* htmlPart2 = "</strong> (<a href=\"/on\">Ein</a>, <a href=\"/off\">Aus</a>)</p></div></body></html>";
static const char* htmlOn = "Ein";
static const char* htmlOff = "Aus";
#endif
/*
One should simply always enable OTA if there is not
a really really good reason not to do so
*/
#define OTA_ENABLE 1
/*
Touch delays for special commands:
1000-5000 ms Enable or disable manual mode
*/
#define TOUCHDURATION_MANUAL__MIN 1000
#define TOUCHDURATION_MANUAL__MAX 5000
/*
If the following option is set the pure connection to
HTTP prevents local control like MQTT control would
do. Note that HTTP normally doesnt provide event
notification to your home automation system so this
might be problematic ...
Note that automatic control can be overriden using
manual mode.
*/
// #define HTTP_PREVENTS_MANUAL 1
/*
LED2 (WiFi LED) configuration:
NONET Blinking period in case no WiFi connection is available (default: 250 ms)
NOMQTT Blinking period in case WiFi is available but MQTT is not connected (default: 1 second)
NOCMD Blinking period after command timeout (default: 5 seconds)
AUTO Period for automatic mode (default: no blinking, 0)
LOCAL Period for manual mode (default: permanent on, ~0)
*/
#ifndef DELAY_BLINK_NONET
#define DELAY_BLINK_NONET 250
#endif
#ifndef DELAY_BLINK_NOMQTT
#define DELAY_BLINK_NOMQTT 1000
#endif
#ifndef DELAY_BLINK_NOCMD
#define DELAY_BLINK_NOCMD 10000
#endif
#ifndef DELAY_BLINK_AUTO
#define DELAY_BLINK_AUTO 0
#endif
#ifndef DELAY_BLINK_LOCAL
#define DELAY_BLINK_LOCAL (~0)
#endif
#ifndef TIMEOUT_MANUAL_MODE
/*
Set default manual mode timeout. This is the time after which a
device switches back from forced manual modhe if nothing else happens.
If a network connection is available it switches back into automatic
mode, else into non-forced manual mode.
If set to 0 this feature is disabled and the device never switches
back to automatic mode on it's own (note that local intervention is
required in this case to leave manual mode).
Default: 4 hours
*/
#define TIMEOUT_MANUAL_MODE (4*3600)
#endif
#ifndef TIMEOUT_AUTO_MODE
/*
This is the keepalive timeout. If no automatic control message is
received during this period the device switches to manual mode
to provide local control in case of system failure.
If set to 0 this feature is disabled (but it's highly recommended
to be used to prevent device disfunctioning in case of backend
error)
Default: 5 minutes
*/
#define TIMEOUT_AUTO_MODE (5*60)
#endif
#ifndef TIMEOUT_POWERON
/*
Power on timeout. If power is enabled for longer than this period
power is automatically disabled (switched off) to prevent to keep
a device powered if forgotten.
If set to 0 this feature is disabled. It's recommended to use this
feature if disabling is not a strict requirement for the given
use-case.
*/
#define TIMEOUT_POWERON (24*3600)
#endif
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <ESP8266WebServer.h>
#include <Adafruit_MQTT.h>
#include <Adafruit_MQTT_Client.h>
#include <wpa2_enterprise.h>
/*
MQTT reconnection interval. This interval is *not* related
to the internal reconnection strategy by the MQTT library. It
is an (seconds) interval after which the MQTT client gets
reset locally
*/
#ifndef MQTT_RECONNECT_INTERVAL
#define MQTT_RECONNECT_INTERVAL 5
#endif
/*
MQTT report interval. This is the interval after which the
switch sends one of his periodic status reports (this might
even be used for monitoring). If undefined periodic reports
do not happen.
*/
#ifndef MQTT_REPORT_INTERVAL
#define MQTT_REPORT_INTERVAL 60
#endif
#ifndef MQTT_PROCESS_PACKETS_TIMEOUT
#define MQTT_PROCESS_PACKETS_TIMEOUT 150
#endif
/*
Sonoff T1:
GPIO Direction Function Level information
00 INPUT Button/Touch sensor Active low
12 OUTPUT Relays and main LED Active high
13 OUTPUT WiFi LED (blue) Active low
2 Gang Version:
5 OUTPUT Channel 2
10 INPUT Button/Touch sensor Active low
2 Gang Version:
4 OUTPUT Channel 3
9 INPUT Button/Touch sensor Active low
*/
#define LEDPORT 13
#define RELAISPORT 12
#define SWITCHPORT 0
// Webserver object (if HTTP control is enabled)
#ifdef CTRL_HTTP_ENABLE
ESP8266WebServer server(80);
#endif
#ifdef CTRL_MQTT_ENABLE
WiFiClient wclient;
// WiFiClientSecure wclient; /* For SSL/TLS */
Adafruit_MQTT_Client mqtt(&wclient, MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASSWORD);
Adafruit_MQTT_Publish mqttTopicOut(&mqtt, "{FILLIN_MQTT_TOPIC}");
Adafruit_MQTT_Subscribe mqttTopicIn_On(&mqtt, "{FILLIN_MQTT_TOPIC}/on");
Adafruit_MQTT_Subscribe mqttTopicIn_Off(&mqtt, "{FILLIN_MQTT_TOPIC}/off");
Adafruit_MQTT_Subscribe mqttTopicIn_Status(&mqtt, "{FILLIN_MQTT_TOPIC}/status");
unsigned long int dwMQTTLastReport;
#endif
/*
State block for local state machines:
Mains state (switch status):
statePower just keeps track of the relais / main LEDs status and can be true or false
Network state:
stateNet keeps track of overall status:
not connected
WiFi connected but no MQTT
MQTT connected (if MQTT is enabled, else WiFi is same as MQTT)
Control state:
unsigned long int stateAutoKeepaliveTimeout
counts down up to the timeout of the last automatic control message. If no
message is received during this time the device switches to manual control
mode automatically
unsigned long int stateManualForceTimeout
counts down till a timeout after which the device automatically switches back
from manual mode to automatic mode if put into FORCED mode.
unsigned long int dwPowerTimeout:
Counts downwards in case of enabled power. This provides an auto-shutdown
feature for devices that have been forgotten to be turned off.
*/
static boolean statePower = false;
static boolean led2State = false;
static unsigned long int dwTimeoutManualMode;
static unsigned long int dwTimeoutAutoKeepalive;
static unsigned long int dwPowerTimeout;
static unsigned long int dwDelayBlink;
static unsigned long int dwWiFiCheckTimeout; /* This period is used to check if WiFi connectivity is available */
static unsigned long int oldMillis; /* Used for delay calculations */
static unsigned long int dwTimeoutTickLast;
static unsigned long int dwMqttLastConnectTry;
enum stateNetwork {
stateNetwork_Disconnected = 0,
stateNetwork_ConnectedWiFi = 1,
stateNetwork_ConnectedMQTT = 2,
};
static enum stateNetwork stateNet;
static boolean isManualMode() {
// If manual mode is forced then it's actiev till timeout
if (dwTimeoutManualMode > 0) {
return true;
}
/*
If manual mode is not forced and MQTT connection is
available (or WiFi in case MQTT is not used) the device
is NOT in manual mode; if it's not connected properly
it's in manual mode
*/
#ifdef CTRL_MQTT_ENABLE
if (stateNet != stateNetwork_ConnectedMQTT) {
return true;
}
#else
#ifdef HTTP_PREVENTS_MANUAL
if (stateNet != stateNetwork_ConnectedWiFi) {
return true;
}
#else
return true;
#endif
#endif
/*
In case our keepalive timeout counted down to zero
we switch into manual mode automatically
*/
if (dwTimeoutAutoKeepalive == 0) {
return true;
}
// We are in automatic mode (connected AND timeout not reached AND not forced)
return false;
}
static unsigned long int getBlinkDelay() {
/*
Blink modes:
DELAY_BLINK_NONET If no network connection is available; (and we accept local commands)
DELAY_BLINK_NOMQTT If network is available but no MQTT connection is active (if compiled); (and we accept local commands)
DELAY_BLINK_NOCMD If command timeout has reached (and we accept local commands)
DELAY_BLINK_AUTO If everything is connected and we are in auto mode
DELAY_BLINK_LOCAL If we are in forced local mode
There are two "special" delays:
0 Disable LED, no blinking at all
~0 Enable LED, no blinking at all
*/
// If manual mode is forced then it's active till timeout
if (dwTimeoutManualMode > 0) {
return DELAY_BLINK_LOCAL;
}
/*
If manual mode is not forced and MQTT connection is
available (or WiFi in case MQTT is not used) the device
is NOT in manual mode; if it's not connected properly
it's in manual mode
*/
if ((stateNet != stateNetwork_ConnectedWiFi) && (stateNet != stateNetwork_ConnectedMQTT)) {
return DELAY_BLINK_NONET;
}
#ifdef CTRL_MQTT_ENABLE
if (stateNet != stateNetwork_ConnectedMQTT) {
return DELAY_BLINK_NOMQTT;
}
#endif
/*
In case our keepalive timeout counted down to zero
we switch into manual mode automatically
*/
if (dwTimeoutAutoKeepalive == 0) {
return DELAY_BLINK_NOCMD;
}
// We are in automatic mode (connected AND timeout not reached AND not forced)
return DELAY_BLINK_AUTO;
}
static void switchOn() {
digitalWrite(RELAISPORT, HIGH);
statePower = true;
#if TIMEOUT_POWERON > 0
dwPowerTimeout = TIMEOUT_POWERON;
#else
dwPowerTimeout = 1; // Any constant != 0 will do
#endif
#ifdef DEBUG
Serial.println("Switching power on");
#endif
#ifdef CTRL_MQTT_ENABLE
mqttRequestStatus(NULL, 0);
#endif
}
static void switchOff() {
digitalWrite(RELAISPORT, LOW);
statePower = false;
dwPowerTimeout = 0;
#ifdef DEBUG
Serial.println("Switching power off");
#endif
#ifdef CTRL_MQTT_ENABLE
mqttRequestStatus(NULL, 0);
#endif
}
#ifdef CTRL_HTTP_ENABLE
static void sendStatusPage(int statusCode) {
String resp;
resp += htmlPart1;
resp += statePower ? htmlOn : htmlOff;
resp += htmlPart2;
/* We set auto-refresh every 15 seconds */
server.sendHeader("Refresh", "15; url=/");
/*
And disable client- and proxy side caching
as well as reverse proxy caching
*/
server.sendHeader("Cache-control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.send(statusCode, "text/html", resp);
}
static void handleOn() {
if (isManualMode() && (dwTimeoutManualMode > 0)) {
sendStatusPage(403);
} else {
switchOn();
sendStatusPage(200);
#if TIMEOUT_AUTO_MODE > 0
dwTimeoutAutoKeepalive = TIMEOUT_AUTO_MODE;
#else
dwTimeoutAutoKeepalive = 1;
#endif
}
}
static void handleOff() {
if (isManualMode() && (dwTimeoutManualMode > 0)) {
sendStatusPage(403);
} else {
switchOff();
sendStatusPage(200);
#if TIMEOUT_AUTO_MODE > 0
dwTimeoutAutoKeepalive = TIMEOUT_AUTO_MODE;
#else
dwTimeoutAutoKeepalive = 1;
#endif
}
}
static void handleRoot() {
sendStatusPage(200);
}
static void handleNotFound() {
server.send(404, "text/plain", "Unknown URI");
}
#endif
static unsigned long int touchDuration = 0;
ICACHE_RAM_ATTR void touchSensor() {
/*
Check when last touch was detected (software debounce)
and in case it's long enough
- Check if is manual control is enabled (i.e. not
disabled OR we have no connection to the network).
In this case invert ledStatus and set output
- In case manual control is disabled transmit
notification about button touch to MQTT broker
but do not modify state ...
*/
if (digitalRead(SWITCHPORT) == LOW) {
touchDuration = millis();
if (isManualMode()) {
// Manual mode: Perform state change (and send message if required)
if (statePower) {
switchOff();
} else {
switchOn();
}
if (dwTimeoutManualMode > 0) {
dwTimeoutManualMode = TIMEOUT_MANUAL_MODE;
}
mqttRequestStatus(NULL, 0);
} else {
// Automatic mode: Simply dispatch messages (ToDo)
String resp;
resp += "{ \"status\" : ";
resp += (statePower) ? "true" : "false";
resp += ", \"manual\" : ";
resp += (isManualMode()) ? "true" : "false";
resp += ", \"eventButton\" : 1 ";
resp += "}";
dwMQTTLastReport = millis();
mqttTopicOut.publish(resp.c_str());
}
} else {
/*
We released the keypress. In case of longer keypresses we want
to trigger some different actions ...
*/
unsigned long int dt;
unsigned long int curMillis = millis();
if(curMillis > touchDuration) { dt = curMillis - touchDuration; } else { dt = (~0)-touchDuration + curMillis; }
#if TOUCHDURATION_MANUAL__MIN != TOUCHDURATION_MANUAL__MAX
if((dt >= TOUCHDURATION_MANUAL__MIN) && (dt < TOUCHDURATION_MANUAL__MAX)) {
// We triggered manual mode switching
if(dwTimeoutManualMode == 0) {
#if TIMEOUT_MANUAL_MODE > 0
dwTimeoutManualMode = TIMEOUT_MANUAL_MODE;
#else
dwTimeoutManualMode = 1;
#endif
// ToDo: Send message that we ENTERED manual mode ...
} else {
dwTimeoutManualMode = 0;
// ToDo: Send message that we LEFT manual mode
}
mqttRequestStatus(NULL, 0);
}
#endif
}
}
#ifdef CTRL_MQTT_ENABLE
static void mqttRequestStatus(char* data, uint16_t len) {
if(data != NULL) {
#if TIMEOUT_AUTO_MODE > 0
dwTimeoutAutoKeepalive = TIMEOUT_AUTO_MODE;
#else
dwTimeoutAutoKeepalive = 1;
#endif
}
String resp;
resp += " { \"status\" : ";
resp += (statePower) ? "true" : "false";
resp += ", \"manual\" : ";
resp += (isManualMode()) ? "true" : "false";
if((data == NULL) && (len == 1)) {
resp += ", \"errormsg\" : \"Manual mode is enabled, request not fulfilled\"";
resp += ", \"error\" : \"manualset\"";
}
resp += "}";
dwMQTTLastReport = millis();
mqttTopicOut.publish(resp.c_str());
}
static void mqttRequestOn(char* data, uint16_t len) {
#if TIMEOUT_AUTO_MODE > 0
dwTimeoutAutoKeepalive = TIMEOUT_AUTO_MODE;
#else
dwTimeoutAutoKeepalive = 1;
#endif
if (isManualMode() && (dwTimeoutManualMode > 0)) {
mqttRequestStatus(NULL, 1);
} else {
switchOn();
}
}
static void mqttRequestOff(char* data, uint16_t len) {
#if TIMEOUT_AUTO_MODE > 0
dwTimeoutAutoKeepalive = TIMEOUT_AUTO_MODE;
#else
dwTimeoutAutoKeepalive = 1;
#endif
if (isManualMode() && (dwTimeoutManualMode > 0)) {
mqttRequestStatus(NULL, 1);
} else {
switchOff();
}
}
#endif
void setup() {
byte mac[6];
/*
Setup I/O pins:
Relaisport Output
LED port Output
Switch port Input
*/
pinMode(RELAISPORT, OUTPUT);
pinMode(LEDPORT, OUTPUT);
pinMode(SWITCHPORT, INPUT_PULLUP);
digitalWrite(LEDPORT, LOW);
digitalWrite(RELAISPORT, LOW);
attachInterrupt(digitalPinToInterrupt(SWITCHPORT), touchSensor, CHANGE);
#ifdef DEBUG
Serial.begin(115200);
#endif
delay(1000);
digitalWrite(LEDPORT, HIGH);
delay(1000);
#ifdef DEBUG
delay(150);
while (!Serial) {
delay(1);
}
#endif
#ifdef DEBUG
Serial.println("Booting");
Serial.setDebugOutput(true);
#endif
WiFi.macAddress(mac);
#ifdef DEBUG
Serial.print("MAC: ");
Serial.print(mac[0], HEX);
Serial.print(":");
Serial.print(mac[1], HEX);
Serial.print(":");
Serial.print(mac[2], HEX);
Serial.print(":");
Serial.print(mac[3], HEX);
Serial.print(":");
Serial.print(mac[4], HEX);
Serial.print(":");
Serial.println(mac[5], HEX);
#endif
// Reset WPA Enterprise settings (ToDo: Configure EAP-TLS)
wifi_station_set_wpa2_enterprise_auth(0);
wifi_station_clear_cert_key();
wifi_station_clear_enterprise_ca_cert();
wifi_station_clear_enterprise_identity();
wifi_station_clear_enterprise_username();
wifi_station_clear_enterprise_password();
wifi_station_clear_enterprise_new_password();
// Try to build up WiFi connection (we do NOT wait for the connection to actually happen)
WiFi.persistent(false); /* Disable WiFi persistence to avoid flash writes */
WiFi.mode(WIFI_OFF);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
#ifdef OTA_ENABLE
// Hostname defaults to esp8266-[ChipID]
ArduinoOTA.setHostname("{FILLIN_OTA_NAME}");
ArduinoOTA.setPassword("{FILLIN_OTA_KEY}");
ArduinoOTA.onStart([]() {
#ifdef DEBUG
Serial.println("Start updating");
#endif
return;
});
ArduinoOTA.onEnd([]() {
#ifdef DEBUG
Serial.println("\nEnd");
#endif
return;
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
#ifdef DEBUG
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
#endif
return;
});
ArduinoOTA.onError([](ota_error_t error) {
#ifdef DEBUG
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
#endif
return;
});
ArduinoOTA.begin();
#endif
#ifdef CTRL_HTTP_ENABLE
// Enable HTTP server if
server.on("/on", handleOn);
server.on("/off", handleOff);
server.on("/", handleRoot);
server.onNotFound(handleNotFound);
server.begin();
#ifdef DEBUG
Serial.println("HTTP Server started");
#endif
#endif
#ifdef CTRL_MQTT_ENABLE
mqttTopicIn_On.setCallback(&mqttRequestOn);
mqttTopicIn_Off.setCallback(&mqttRequestOff);
mqttTopicIn_Status.setCallback(&mqttRequestStatus);
mqtt.subscribe(&mqttTopicIn_On);
mqtt.subscribe(&mqttTopicIn_Off);
mqtt.subscribe(&mqttTopicIn_Status);
dwMqttLastConnectTry = 0;
dwMQTTLastReport = millis() - MQTT_REPORT_INTERVAL;
#endif
statePower = false;
led2State = false;
dwTimeoutManualMode = 0;
#if TIMEOUT_AUTO_MODE > 0
dwTimeoutAutoKeepalive = TIMEOUT_AUTO_MODE;
#else
dwTimeoutAutoKeepalive = 1;
#endif
dwPowerTimeout = 0;
dwDelayBlink = 0;
dwWiFiCheckTimeout = millis();
oldMillis = millis();
dwTimeoutTickLast = oldMillis;
}
void loop() {
unsigned long currentMillis = millis();
unsigned long int dwLastWiFiCheck;
if (currentMillis >= dwWiFiCheckTimeout) {
dwLastWiFiCheck = currentMillis - dwWiFiCheckTimeout;
} else {
dwLastWiFiCheck = (~0) - dwWiFiCheckTimeout + currentMillis;
}
int wifiState = WiFi.status();
if (dwLastWiFiCheck > 5000) {
if (wifiState != WL_CONNECTED) {
stateNet = stateNetwork_Disconnected;
#ifdef DEBUG
Serial.println("WiFi connection not available! Running in manual mode");
#endif
WiFi.mode(WIFI_OFF);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
#ifdef DEBUG
Serial.print("Trying to connect to "); Serial.println(ssid);
#endif
} else {
if (stateNet == stateNetwork_Disconnected) {
#ifdef DEBUG
Serial.println("WiFi connection became available");
#endif
Serial.println(WiFi.localIP());
stateNet = stateNetwork_ConnectedWiFi;
// ToDo: Transmit message (including our state) if we get online ...
#ifndef CTRL_MQTT_ENABLE
#if TIMEOUT_AUTO_MODE > 0
dwTimeoutAutoKeepalive = TIMEOUT_AUTO_MODE;
#else
dwTimeoutAutoKeepalive = 1;
#endif
#else
mqtt.connect(); // Try MQTT connection ...
#endif
}
}
dwWiFiCheckTimeout = currentMillis;
}
// If we are connected to the network handle OTA and HTTP
if (wifiState == WL_CONNECTED) {
#ifdef OTA_ENABLE
yield();
ArduinoOTA.handle();
#endif
#ifdef CTRL_HTTP_ENABLE
yield();
server.handleClient();
#endif
}
#ifdef CTRL_MQTT_ENABLE
yield();
if (wifiState == WL_CONNECTED) {
if (mqtt.connected()) {
if (stateNet == stateNetwork_ConnectedWiFi) {
#if TIMEOUT_AUTO_MODE > 0
dwTimeoutAutoKeepalive = TIMEOUT_AUTO_MODE;
#else
dwTimeoutAutoKeepalive = 1;
#endif
stateNet = stateNetwork_ConnectedMQTT;
}
mqtt.processPackets(MQTT_PROCESS_PACKETS_TIMEOUT);
} else {
stateNet = stateNetwork_ConnectedWiFi;
if (dwMqttLastConnectTry == 0) {
mqtt.disconnect();
dwMqttLastConnectTry = currentMillis;
} else {
unsigned long int dwTimeSinceLastTry;
if (currentMillis > dwMqttLastConnectTry) {
dwTimeSinceLastTry = currentMillis - dwMqttLastConnectTry;
} else {
dwTimeSinceLastTry = (~0) - dwMqttLastConnectTry + currentMillis;
}
if (dwTimeSinceLastTry > (MQTT_RECONNECT_INTERVAL*1000)) {
mqtt.connect();
dwMqttLastConnectTry = 0;
}
}
}
}
yield();
unsigned long int dwDurationSinceLastReport;
if (currentMillis > dwMQTTLastReport) {
dwDurationSinceLastReport = currentMillis - dwMQTTLastReport;
} else {
dwDurationSinceLastReport = (~0) - dwMQTTLastReport + currentMillis;
}
if (dwDurationSinceLastReport > (MQTT_REPORT_INTERVAL*1000)) {
mqttRequestStatus(NULL, 0);
}
#endif
unsigned long int dwTimeoutLastDiff;
if (currentMillis >= dwTimeoutTickLast) {
dwTimeoutLastDiff = currentMillis - dwTimeoutTickLast;
} else {
dwTimeoutLastDiff = (~0) - dwTimeoutTickLast + currentMillis;
}
if (dwTimeoutLastDiff >= 1000) {
dwTimeoutTickLast = currentMillis;
/* Possible timeout caused state transitions */
#if TIMEOUT_MANUAL_MODE > 0
if (isManualMode()) {
if (dwTimeoutManualMode > 0) {
dwTimeoutManualMode = dwTimeoutManualMode - 1;
}
// If this counter reaches 0 we automatically leave manual mode again (ToDo: Transmit message?)
}
#endif
#if TIMEOUT_AUTO_MODE > 0
if (!isManualMode()) {
if (dwTimeoutAutoKeepalive > 0) {
dwTimeoutAutoKeepalive = dwTimeoutAutoKeepalive - 1;
}
if (dwTimeoutAutoKeepalive == 0) {
// ToDo :Transmit a message about this transition
}
// If the dwTimeoutAutoKeepalive counter reaches 0 we automatically switch to manual mode (ToDo: Transmit message?)
}
#endif
#if TIMEOUT_POWERON > 0
if (statePower) {
if (dwPowerTimeout > 0) {
dwPowerTimeout = dwPowerTimeout - 1;
}
if (dwPowerTimeout == 0) {
switchOff();
// ToDo: Transmit timeout message additionally to power of status message sent by switchOff
}
}
#endif
}
/* LED status */
unsigned long milliDiff;
if (currentMillis >= oldMillis) {
milliDiff = currentMillis - oldMillis;
} else {
milliDiff = ((~0) - oldMillis) + currentMillis;
}
unsigned long int blinkDelay = getBlinkDelay();
if (blinkDelay == 0) {
digitalWrite(LEDPORT, HIGH);
led2State = false;
} else if (blinkDelay == (~0)) {
digitalWrite(LEDPORT, LOW);
led2State = true;
} else if (blinkDelay <= milliDiff) {
oldMillis = currentMillis;
digitalWrite(LEDPORT, led2State ? HIGH : LOW);
led2State = !led2State;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment