mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
1025 lines
31 KiB
Arduino
1025 lines
31 KiB
Arduino
/****************************************************************************************************************************\
|
|
* Arduino project "ESP Easy" © Copyright www.letscontrolit.com
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
* You received a copy of the GNU General Public License along with this program in file 'License.txt'.
|
|
*
|
|
* IDE download : https://www.arduino.cc/en/Main/Software
|
|
* ESP8266 Package : https://github.com/esp8266/Arduino
|
|
*
|
|
* Source Code : https://github.com/ESP8266nu/ESPEasy
|
|
* Support : http://www.letscontrolit.com
|
|
* Discussion : http://www.letscontrolit.com/forum/
|
|
*
|
|
* Additional information about licensing can be found at : http://www.gnu.org/licenses
|
|
\*************************************************************************************************************************/
|
|
|
|
// This file incorporates work covered by the following copyright and permission notice:
|
|
|
|
/****************************************************************************************************************************\
|
|
* Arduino project "Nodo" © Copyright 2010..2015 Paul Tonkes
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
* You received a copy of the GNU General Public License along with this program in file 'License.txt'.
|
|
*
|
|
* Voor toelichting op de licentievoorwaarden zie : http://www.gnu.org/licenses
|
|
* Uitgebreide documentatie is te vinden op : http://www.nodo-domotica.nl
|
|
* Compiler voor deze programmacode te downloaden op : http://arduino.cc
|
|
\*************************************************************************************************************************/
|
|
|
|
// Simple Arduino sketch for ESP module, supporting:
|
|
// =================================================================================
|
|
// Simple switch inputs and direct GPIO output control to drive relais, mosfets, etc
|
|
// Analog input (ESP-7/12 only)
|
|
// Pulse counters
|
|
// Dallas OneWire DS18b20 temperature sensors
|
|
// DHT11/22/12 humidity sensors
|
|
// BMP085 I2C Barometric Pressure sensor
|
|
// PCF8591 4 port Analog to Digital converter (I2C)
|
|
// RFID Wiegand-26 reader
|
|
// MCP23017 I2C IO Expanders
|
|
// BH1750 I2C Luminosity sensor
|
|
// Arduino Pro Mini with IO extender sketch, connected through I2C
|
|
// LCD I2C display 4x20 chars
|
|
// HC-SR04 Ultrasonic distance sensor
|
|
// SI7021 I2C temperature/humidity sensors
|
|
// TSL2561 I2C Luminosity sensor
|
|
// TSOP4838 IR receiver
|
|
// PN532 RFID reader
|
|
// Sharp GP2Y10 dust sensor
|
|
// PCF8574 I2C IO Expanders
|
|
// PCA9685 I2C 16 channel PWM driver
|
|
// OLED I2C display with SSD1306 driver
|
|
// MLX90614 I2C IR temperature sensor
|
|
// ADS1115 I2C ADC
|
|
// INA219 I2C voltage/current sensor
|
|
// BME280 I2C temp/hum/baro sensor
|
|
// MSP5611 I2C temp/baro sensor
|
|
// BMP280 I2C Barometric Pressure sensor
|
|
// SHT1X temperature/humidity sensors
|
|
// Ser2Net server
|
|
|
|
// ********************************************************************************
|
|
// User specific configuration
|
|
// ********************************************************************************
|
|
|
|
// Set default configuration settings if you want (not mandatory)
|
|
// You can always change these during runtime and save to eeprom
|
|
// After loading firmware, issue a 'reset' command to load the defaults.
|
|
|
|
#define DEFAULT_NAME "newdevice" // Enter your device friendly name
|
|
#define DEFAULT_SSID "ssid" // Enter your network SSID
|
|
#define DEFAULT_KEY "wpakey" // Enter your network WPA key
|
|
#define DEFAULT_SERVER "192.168.0.8" // Enter your Domoticz Server IP address
|
|
#define DEFAULT_PORT 8080 // Enter your Domoticz Server port value
|
|
#define DEFAULT_DELAY 60 // Enter your Send delay in seconds
|
|
#define DEFAULT_AP_KEY "configesp" // Enter network WPA key for AP (config) mode
|
|
|
|
#define DEFAULT_USE_STATIC_IP false // true or false enabled or disabled set static IP
|
|
#define DEFAULT_IP "192.168.0.50" // Enter your IP address
|
|
#define DEFAULT_DNS "192.168.0.1" // Enter your DNS
|
|
#define DEFAULT_GW "192.168.0.1" // Enter your gateway
|
|
#define DEFAULT_SUBNET "255.255.255.0" // Enter your subnet
|
|
|
|
#define DEFAULT_MQTT_TEMPLATE false // true or false enabled or disabled set mqqt sub and pub
|
|
#define DEFAULT_MQTT_PUB "sensors/espeasy/%sysname%/%tskname%/%valname%" // Enter your pub
|
|
#define DEFAULT_MQTT_SUB "sensors/espeasy/%sysname%/#" // Enter your sub
|
|
|
|
#define DEFAULT_PROTOCOL 1 // Protocol used for controller communications
|
|
// 1 = Domoticz HTTP
|
|
// 2 = Domoticz MQTT
|
|
// 3 = Nodo Telnet
|
|
// 4 = ThingSpeak
|
|
// 5 = OpenHAB MQTT
|
|
// 6 = PiDome MQTT
|
|
// 7 = EmonCMS
|
|
// 8 = Generic HTTP
|
|
// 9 = FHEM HTTP
|
|
|
|
#define UNIT 0
|
|
|
|
#define FEATURE_TIME true
|
|
#define FEATURE_SSDP true
|
|
|
|
// Enable FEATURE_ADC_VCC to measure supply voltage using the analog pin
|
|
// Please note that the TOUT pin has to be disconnected in this mode
|
|
// Use the "System Info" device to read the VCC value
|
|
#define FEATURE_ADC_VCC false
|
|
|
|
// ********************************************************************************
|
|
// DO NOT CHANGE ANYTHING BELOW THIS LINE
|
|
// ********************************************************************************
|
|
#define ESP_PROJECT_PID 2015050101L
|
|
#define ESP_EASY
|
|
#define VERSION 9
|
|
#define BUILD 148
|
|
#define BUILD_NOTES ""
|
|
#define FEATURE_SPIFFS false
|
|
|
|
#define NODE_TYPE_ID_ESP_EASY_STD 1
|
|
#define NODE_TYPE_ID_ESP_EASYM_STD 17
|
|
#define NODE_TYPE_ID_ESP_EASY32_STD 33
|
|
#define NODE_TYPE_ID_ARDUINO_EASY_STD 65
|
|
#define NODE_TYPE_ID_NANO_EASY_STD 81
|
|
#define NODE_TYPE_ID NODE_TYPE_ID_ESP_EASY_STD
|
|
|
|
#define CPLUGIN_PROTOCOL_ADD 1
|
|
#define CPLUGIN_PROTOCOL_TEMPLATE 2
|
|
#define CPLUGIN_PROTOCOL_SEND 3
|
|
#define CPLUGIN_PROTOCOL_RECV 4
|
|
#define CPLUGIN_GET_DEVICENAME 5
|
|
#define CPLUGIN_WEBFORM_SAVE 6
|
|
#define CPLUGIN_WEBFORM_LOAD 7
|
|
|
|
#define LOG_LEVEL_ERROR 1
|
|
#define LOG_LEVEL_INFO 2
|
|
#define LOG_LEVEL_DEBUG 3
|
|
#define LOG_LEVEL_DEBUG_MORE 4
|
|
|
|
#define CMD_REBOOT 89
|
|
#define CMD_WIFI_DISCONNECT 135
|
|
|
|
#define DEVICES_MAX 64
|
|
#define TASKS_MAX 12
|
|
#define VARS_PER_TASK 4
|
|
#define PLUGIN_MAX 64
|
|
#define PLUGIN_CONFIGVAR_MAX 8
|
|
#define PLUGIN_CONFIGFLOATVAR_MAX 4
|
|
#define PLUGIN_CONFIGLONGVAR_MAX 4
|
|
#define PLUGIN_EXTRACONFIGVAR_MAX 16
|
|
#define CPLUGIN_MAX 16
|
|
#define UNIT_MAX 32 // Only relevant for UDP unicast message 'sweeps' and the nodelist.
|
|
#define RULES_TIMER_MAX 8
|
|
#define SYSTEM_TIMER_MAX 8
|
|
#define SYSTEM_CMD_TIMER_MAX 2
|
|
#define PINSTATE_TABLE_MAX 32
|
|
#define RULES_MAX_SIZE 2048
|
|
#define RULES_MAX_NESTING_LEVEL 3
|
|
|
|
#define PIN_MODE_UNDEFINED 0
|
|
#define PIN_MODE_INPUT 1
|
|
#define PIN_MODE_OUTPUT 2
|
|
#define PIN_MODE_PWM 3
|
|
#define PIN_MODE_SERVO 4
|
|
|
|
#define SEARCH_PIN_STATE true
|
|
#define NO_SEARCH_PIN_STATE false
|
|
|
|
#define DEVICE_TYPE_SINGLE 1 // connected through 1 datapin
|
|
#define DEVICE_TYPE_I2C 2 // connected through I2C
|
|
#define DEVICE_TYPE_ANALOG 3 // tout pin
|
|
#define DEVICE_TYPE_DUAL 4 // connected through 2 datapins
|
|
#define DEVICE_TYPE_DUMMY 99 // Dummy device, has no physical connection
|
|
|
|
#define SENSOR_TYPE_SINGLE 1
|
|
#define SENSOR_TYPE_TEMP_HUM 2
|
|
#define SENSOR_TYPE_TEMP_BARO 3
|
|
#define SENSOR_TYPE_TEMP_HUM_BARO 4
|
|
#define SENSOR_TYPE_DUAL 5
|
|
#define SENSOR_TYPE_TRIPLE 6
|
|
#define SENSOR_TYPE_QUAD 7
|
|
#define SENSOR_TYPE_SWITCH 10
|
|
#define SENSOR_TYPE_DIMMER 11
|
|
#define SENSOR_TYPE_LONG 20
|
|
#define SENSOR_TYPE_WIND 21
|
|
|
|
#define PLUGIN_INIT_ALL 1
|
|
#define PLUGIN_INIT 2
|
|
#define PLUGIN_READ 3
|
|
#define PLUGIN_ONCE_A_SECOND 4
|
|
#define PLUGIN_TEN_PER_SECOND 5
|
|
#define PLUGIN_DEVICE_ADD 6
|
|
#define PLUGIN_EVENTLIST_ADD 7
|
|
#define PLUGIN_WEBFORM_SAVE 8
|
|
#define PLUGIN_WEBFORM_LOAD 9
|
|
#define PLUGIN_WEBFORM_SHOW_VALUES 10
|
|
#define PLUGIN_GET_DEVICENAME 11
|
|
#define PLUGIN_GET_DEVICEVALUENAMES 12
|
|
#define PLUGIN_WRITE 13
|
|
#define PLUGIN_EVENT_OUT 14
|
|
#define PLUGIN_WEBFORM_SHOW_CONFIG 15
|
|
#define PLUGIN_SERIAL_IN 16
|
|
#define PLUGIN_UDP_IN 17
|
|
#define PLUGIN_CLOCK_IN 18
|
|
#define PLUGIN_TIMER_IN 19
|
|
|
|
#define VALUE_SOURCE_SYSTEM 1
|
|
#define VALUE_SOURCE_SERIAL 2
|
|
#define VALUE_SOURCE_HTTP 3
|
|
#define VALUE_SOURCE_MQTT 4
|
|
#define VALUE_SOURCE_UDP 5
|
|
|
|
#define BOOT_CAUSE_MANUAL_REBOOT 0
|
|
#define BOOT_CAUSE_COLD_BOOT 1
|
|
#define BOOT_CAUSE_EXT_WD 10
|
|
|
|
#include "Version.h"
|
|
#include <ESP8266WiFi.h>
|
|
#include <DNSServer.h>
|
|
#include <WiFiUdp.h>
|
|
#include <ESP8266WebServer.h>
|
|
#include <Wire.h>
|
|
#include <SPI.h>
|
|
#include <PubSubClient.h>
|
|
#include <ArduinoJson.h>
|
|
#include <LiquidCrystal_I2C.h>
|
|
#include <Servo.h>
|
|
#if FEATURE_SPIFFS
|
|
#include <FS.h>
|
|
#endif
|
|
#include <ESP8266HTTPUpdateServer.h>
|
|
ESP8266HTTPUpdateServer httpUpdater(true);
|
|
#include <base64.h>
|
|
#if FEATURE_ADC_VCC
|
|
ADC_MODE(ADC_VCC);
|
|
#endif
|
|
#ifndef LWIP_OPEN_SRC
|
|
#define LWIP_OPEN_SRC
|
|
#endif
|
|
#include "lwip/opt.h"
|
|
#include "lwip/udp.h"
|
|
#include "lwip/igmp.h"
|
|
#include "include/UdpContext.h"
|
|
|
|
extern "C" {
|
|
#include "user_interface.h"
|
|
}
|
|
|
|
// Setup DNS, only used if the ESP has no valid WiFi config
|
|
const byte DNS_PORT = 53;
|
|
IPAddress apIP(192, 168, 4, 1);
|
|
DNSServer dnsServer;
|
|
|
|
Servo myservo1;
|
|
Servo myservo2;
|
|
|
|
// MQTT client
|
|
WiFiClient mqtt;
|
|
PubSubClient MQTTclient(mqtt);
|
|
|
|
// WebServer
|
|
ESP8266WebServer WebServer(80);
|
|
|
|
// syslog stuff
|
|
WiFiUDP portUDP;
|
|
|
|
#define FLASH_EEPROM_SIZE 4096
|
|
extern "C" {
|
|
#include "spi_flash.h"
|
|
}
|
|
extern "C" uint32_t _SPIFFS_start;
|
|
extern "C" uint32_t _SPIFFS_end;
|
|
extern "C" uint32_t _SPIFFS_page;
|
|
extern "C" uint32_t _SPIFFS_block;
|
|
|
|
struct SecurityStruct
|
|
{
|
|
char WifiSSID[32];
|
|
char WifiKey[64];
|
|
char WifiAPKey[64];
|
|
char ControllerUser[26];
|
|
char ControllerPassword[64];
|
|
char Password[26];
|
|
} SecuritySettings;
|
|
|
|
struct SettingsStruct
|
|
{
|
|
unsigned long PID;
|
|
int Version;
|
|
byte Unit;
|
|
byte Controller_IP[4];
|
|
unsigned int ControllerPort;
|
|
byte IP_Octet;
|
|
char NTPHost[64];
|
|
unsigned long Delay;
|
|
int8_t Pin_i2c_sda;
|
|
int8_t Pin_i2c_scl;
|
|
byte Syslog_IP[4];
|
|
unsigned int UDPPort;
|
|
byte Protocol;
|
|
byte IP[4];
|
|
byte Gateway[4];
|
|
byte Subnet[4];
|
|
char Name[26];
|
|
byte SyslogLevel;
|
|
byte SerialLogLevel;
|
|
byte WebLogLevel;
|
|
unsigned long BaudRate;
|
|
unsigned long MessageDelay;
|
|
byte TaskDeviceNumber[TASKS_MAX];
|
|
unsigned int TaskDeviceID[TASKS_MAX];
|
|
int8_t TaskDevicePin1[TASKS_MAX];
|
|
int8_t TaskDevicePin2[TASKS_MAX];
|
|
byte TaskDevicePort[TASKS_MAX];
|
|
boolean TaskDevicePin1PullUp[TASKS_MAX];
|
|
int16_t TaskDevicePluginConfig[TASKS_MAX][PLUGIN_CONFIGVAR_MAX];
|
|
boolean TaskDevicePin1Inversed[TASKS_MAX];
|
|
byte deepSleep;
|
|
char MQTTpublish[81];
|
|
char MQTTsubscribe[81];
|
|
boolean CustomCSS;
|
|
float TaskDevicePluginConfigFloat[TASKS_MAX][PLUGIN_CONFIGFLOATVAR_MAX];
|
|
long TaskDevicePluginConfigLong[TASKS_MAX][PLUGIN_CONFIGLONGVAR_MAX];
|
|
boolean TaskDeviceSendData[TASKS_MAX];
|
|
int16_t Build;
|
|
byte DNS[4];
|
|
int8_t TimeZone_OLD;
|
|
char ControllerHostName[64];
|
|
boolean UseNTP;
|
|
boolean DST;
|
|
byte WDI2CAddress;
|
|
boolean TaskDeviceGlobalSync[TASKS_MAX];
|
|
int8_t TaskDevicePin3[TASKS_MAX];
|
|
byte TaskDeviceDataFeed[TASKS_MAX];
|
|
int8_t PinBootStates[17];
|
|
byte UseDNS;
|
|
boolean UseRules;
|
|
int8_t Pin_status_led;
|
|
boolean UseSerial;
|
|
unsigned long TaskDeviceTimer[TASKS_MAX];
|
|
boolean UseSSDP;
|
|
unsigned long WireClockStretchLimit;
|
|
boolean GlobalSync;
|
|
unsigned long ConnectionFailuresThreshold;
|
|
int16_t TimeZone;
|
|
boolean MQTTRetainFlag;
|
|
boolean InitSPI;
|
|
} Settings;
|
|
|
|
struct ExtraTaskSettingsStruct
|
|
{
|
|
byte TaskIndex;
|
|
char TaskDeviceName[41];
|
|
char TaskDeviceFormula[VARS_PER_TASK][41];
|
|
char TaskDeviceValueNames[VARS_PER_TASK][41];
|
|
long TaskDevicePluginConfigLong[PLUGIN_EXTRACONFIGVAR_MAX];
|
|
byte TaskDeviceValueDecimals[VARS_PER_TASK];
|
|
} ExtraTaskSettings;
|
|
|
|
struct EventStruct
|
|
{
|
|
byte Source;
|
|
byte TaskIndex;
|
|
byte BaseVarIndex;
|
|
int idx;
|
|
byte sensorType;
|
|
int Par1;
|
|
int Par2;
|
|
int Par3;
|
|
byte OriginTaskIndex;
|
|
String String1;
|
|
String String2;
|
|
byte *Data;
|
|
};
|
|
|
|
struct LogStruct
|
|
{
|
|
unsigned long timeStamp;
|
|
String Message;
|
|
} Logging[10];
|
|
int logcount = -1;
|
|
|
|
struct DeviceStruct
|
|
{
|
|
byte Number;
|
|
byte Type;
|
|
byte VType;
|
|
byte Ports;
|
|
boolean PullUpOption;
|
|
boolean InverseLogicOption;
|
|
boolean FormulaOption;
|
|
byte ValueCount;
|
|
boolean Custom;
|
|
boolean SendDataOption;
|
|
boolean GlobalSyncOption;
|
|
boolean TimerOption;
|
|
boolean TimerOptional;
|
|
boolean DecimalsOnly;
|
|
} Device[DEVICES_MAX + 1]; // 1 more because first device is empty device
|
|
|
|
struct ProtocolStruct
|
|
{
|
|
byte Number;
|
|
boolean usesMQTT;
|
|
boolean usesAccount;
|
|
boolean usesPassword;
|
|
int defaultPort;
|
|
boolean usesTemplate;
|
|
} Protocol[CPLUGIN_MAX];
|
|
|
|
struct NodeStruct
|
|
{
|
|
byte ip[4];
|
|
byte age;
|
|
uint16_t build;
|
|
char* nodeName;
|
|
byte nodeType;
|
|
} Nodes[UNIT_MAX];
|
|
|
|
struct systemTimerStruct
|
|
{
|
|
unsigned long timer;
|
|
byte plugin;
|
|
byte Par1;
|
|
byte Par2;
|
|
byte Par3;
|
|
} systemTimers[SYSTEM_TIMER_MAX];
|
|
|
|
struct systemCMDTimerStruct
|
|
{
|
|
unsigned long timer;
|
|
String action;
|
|
} systemCMDTimers[SYSTEM_CMD_TIMER_MAX];
|
|
|
|
struct pinStatesStruct
|
|
{
|
|
byte plugin;
|
|
byte index;
|
|
byte mode;
|
|
uint16_t value;
|
|
} pinStates[PINSTATE_TABLE_MAX];
|
|
|
|
int deviceCount = -1;
|
|
int protocolCount = -1;
|
|
|
|
boolean printToWeb = false;
|
|
String printWebString = "";
|
|
boolean printToWebJSON = false;
|
|
|
|
float UserVar[VARS_PER_TASK * TASKS_MAX];
|
|
unsigned long RulesTimer[RULES_TIMER_MAX];
|
|
|
|
unsigned long timerSensor[TASKS_MAX];
|
|
unsigned long timer;
|
|
unsigned long timer100ms;
|
|
unsigned long timer1s;
|
|
unsigned long timerwd;
|
|
unsigned long lastSend;
|
|
unsigned int NC_Count = 0;
|
|
unsigned int C_Count = 0;
|
|
boolean AP_Mode = false;
|
|
byte cmd_within_mainloop = 0;
|
|
unsigned long connectionFailures;
|
|
unsigned long wdcounter = 0;
|
|
|
|
#if FEATURE_ADC_VCC
|
|
float vcc = -1.0;
|
|
#endif
|
|
|
|
boolean WebLoggedIn = false;
|
|
int WebLoggedInTimer = 300;
|
|
|
|
boolean (*Plugin_ptr[PLUGIN_MAX])(byte, struct EventStruct*, String&);
|
|
byte Plugin_id[PLUGIN_MAX];
|
|
|
|
boolean (*CPlugin_ptr[PLUGIN_MAX])(byte, struct EventStruct*, String&);
|
|
byte CPlugin_id[PLUGIN_MAX];
|
|
|
|
String dummyString = "";
|
|
|
|
boolean systemOK = false;
|
|
byte lastBootCause = 0;
|
|
|
|
boolean wifiSetup = false;
|
|
boolean wifiSetupConnect = false;
|
|
|
|
unsigned long start = 0;
|
|
unsigned long elapsed = 0;
|
|
unsigned long loopCounter = 0;
|
|
unsigned long loopCounterLast = 0;
|
|
unsigned long loopCounterMax = 1;
|
|
|
|
unsigned long flashWrites = 0;
|
|
|
|
String eventBuffer = "";
|
|
|
|
// Blynk_get prototype
|
|
boolean Blynk_get(String command,float *data = NULL );
|
|
|
|
/*********************************************************************************************\
|
|
* SETUP
|
|
\*********************************************************************************************/
|
|
void setup()
|
|
{
|
|
Serial.begin(115200);
|
|
|
|
if (SpiffsSectors() == 0)
|
|
{
|
|
Serial.println(F("\nNo SPIFFS area..\nSystem Halted\nPlease reflash with SPIFFS"));
|
|
while (true)
|
|
delay(1);
|
|
}
|
|
|
|
#if FEATURE_SPIFFS
|
|
fileSystemCheck();
|
|
#endif
|
|
|
|
emergencyReset();
|
|
|
|
LoadSettings();
|
|
if (strcasecmp(SecuritySettings.WifiSSID, "ssid") == 0)
|
|
wifiSetup = true;
|
|
|
|
ExtraTaskSettings.TaskIndex = 255; // make sure this is an unused nr to prevent cache load on boot
|
|
|
|
// if different version, eeprom settings structure has changed. Full Reset needed
|
|
// on a fresh ESP module eeprom values are set to 255. Version results into -1 (signed int)
|
|
if (Settings.Version == VERSION && Settings.PID == ESP_PROJECT_PID)
|
|
{
|
|
systemOK = true;
|
|
}
|
|
else
|
|
{
|
|
// Direct Serial is allowed here, since this is only an emergency task.
|
|
Serial.print(F("\nPID:"));
|
|
Serial.println(Settings.PID);
|
|
Serial.print(F("Version:"));
|
|
Serial.println(Settings.Version);
|
|
Serial.println(F("INIT : Incorrect PID or version!"));
|
|
delay(1000);
|
|
ResetFactory();
|
|
}
|
|
|
|
if (systemOK)
|
|
{
|
|
if (Settings.UseSerial)
|
|
Serial.begin(Settings.BaudRate);
|
|
|
|
if (Settings.Build != BUILD)
|
|
BuildFixes();
|
|
|
|
String log = F("\nINIT : Booting Build nr:");
|
|
log += BUILD;
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
|
|
if (Settings.UseSerial && Settings.SerialLogLevel >= LOG_LEVEL_DEBUG_MORE)
|
|
Serial.setDebugOutput(true);
|
|
|
|
WiFi.persistent(false); // Do not use SDK storage of SSID/WPA parameters
|
|
WifiAPconfig();
|
|
WifiConnect(3);
|
|
|
|
hardwareInit();
|
|
PluginInit();
|
|
CPluginInit();
|
|
|
|
WebServerInit();
|
|
|
|
// setup UDP
|
|
if (Settings.UDPPort != 0)
|
|
portUDP.begin(Settings.UDPPort);
|
|
|
|
// Setup MQTT Client
|
|
byte ProtocolIndex = getProtocolIndex(Settings.Protocol);
|
|
if (Protocol[ProtocolIndex].usesMQTT)
|
|
MQTTConnect();
|
|
|
|
sendSysInfoUDP(3);
|
|
|
|
log = F("INIT : Boot OK");
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
|
|
if (Settings.deepSleep)
|
|
{
|
|
log = F("INIT : Deep sleep enabled");
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
}
|
|
|
|
byte bootMode = 0;
|
|
if (readFromRTC(&bootMode))
|
|
{
|
|
if (bootMode == 1)
|
|
log = F("INIT : Reboot from deepsleep");
|
|
else
|
|
log = F("INIT : Normal boot");
|
|
}
|
|
else
|
|
{
|
|
// cold boot situation
|
|
if (lastBootCause == 0) // only set this if not set earlier during boot stage.
|
|
lastBootCause = BOOT_CAUSE_COLD_BOOT;
|
|
log = F("INIT : Cold Boot");
|
|
}
|
|
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
|
|
saveToRTC(0);
|
|
|
|
// Setup timers
|
|
if (bootMode == 0)
|
|
{
|
|
for (byte x = 0; x < TASKS_MAX; x++)
|
|
if (Settings.TaskDeviceTimer[x] !=0)
|
|
timerSensor[x] = millis() + 30000 + (x * Settings.MessageDelay);
|
|
|
|
timer = millis() + 30000; // startup delay 30 sec
|
|
}
|
|
else
|
|
{
|
|
for (byte x = 0; x < TASKS_MAX; x++)
|
|
timerSensor[x] = millis() + 0;
|
|
timer = millis() + 0; // no startup from deepsleep wake up
|
|
}
|
|
|
|
timer100ms = millis() + 100; // timer for periodic actions 10 x per/sec
|
|
timer1s = millis() + 1000; // timer for periodic actions once per/sec
|
|
timerwd = millis() + 30000; // timer for watchdog once per 30 sec
|
|
|
|
#if FEATURE_TIME
|
|
if (Settings.UseNTP)
|
|
initTime();
|
|
#endif
|
|
|
|
#if FEATURE_ADC_VCC
|
|
vcc = ESP.getVcc() / 1000.0;
|
|
#endif
|
|
|
|
// Start DNS, only used if the ESP has no valid WiFi config
|
|
// It will reply with it's own address on all DNS requests
|
|
// (captive portal concept)
|
|
if (wifiSetup)
|
|
dnsServer.start(DNS_PORT, "*", apIP);
|
|
|
|
if (Settings.UseRules)
|
|
{
|
|
String event = F("System#Boot");
|
|
rulesProcessing(event);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Serial.println(F("Entered Rescue mode!"));
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* MAIN LOOP
|
|
\*********************************************************************************************/
|
|
void loop()
|
|
{
|
|
loopCounter++;
|
|
|
|
if (wifiSetupConnect)
|
|
{
|
|
// try to connect for setup wizard
|
|
WifiConnect(1);
|
|
wifiSetupConnect = false;
|
|
}
|
|
|
|
if (Settings.UseSerial)
|
|
if (Serial.available())
|
|
if (!PluginCall(PLUGIN_SERIAL_IN, 0, dummyString))
|
|
serial();
|
|
|
|
if (systemOK)
|
|
{
|
|
if (millis() > timer100ms)
|
|
run10TimesPerSecond();
|
|
|
|
if (millis() > timer1s)
|
|
runOncePerSecond();
|
|
|
|
if (millis() > timerwd)
|
|
runEach30Seconds();
|
|
|
|
backgroundtasks();
|
|
|
|
}
|
|
else
|
|
delay(1);
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* Tasks that run 10 times per second
|
|
\*********************************************************************************************/
|
|
void run10TimesPerSecond()
|
|
{
|
|
start = micros();
|
|
timer100ms = millis() + 100;
|
|
PluginCall(PLUGIN_TEN_PER_SECOND, 0, dummyString);
|
|
checkUDP();
|
|
if (Settings.UseRules && eventBuffer.length() > 0)
|
|
{
|
|
rulesProcessing(eventBuffer);
|
|
eventBuffer = "";
|
|
}
|
|
elapsed = micros() - start;
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* Tasks each second
|
|
\*********************************************************************************************/
|
|
void runOncePerSecond()
|
|
{
|
|
timer1s = millis() + 1000;
|
|
|
|
checkSensors();
|
|
|
|
if (Settings.ConnectionFailuresThreshold)
|
|
if (connectionFailures > Settings.ConnectionFailuresThreshold)
|
|
delayedReboot(60);
|
|
|
|
if (cmd_within_mainloop != 0)
|
|
{
|
|
switch (cmd_within_mainloop)
|
|
{
|
|
case CMD_WIFI_DISCONNECT:
|
|
{
|
|
WifiDisconnect();
|
|
break;
|
|
}
|
|
case CMD_REBOOT:
|
|
{
|
|
ESP.reset();
|
|
break;
|
|
}
|
|
}
|
|
cmd_within_mainloop = 0;
|
|
}
|
|
|
|
#if FEATURE_TIME
|
|
// clock events
|
|
if (Settings.UseNTP)
|
|
checkTime();
|
|
#endif
|
|
unsigned long timer = micros();
|
|
PluginCall(PLUGIN_ONCE_A_SECOND, 0, dummyString);
|
|
|
|
checkSystemTimers();
|
|
|
|
if (Settings.UseRules)
|
|
rulesTimers();
|
|
|
|
timer = micros() - timer;
|
|
|
|
if (SecuritySettings.Password[0] != 0)
|
|
{
|
|
if (WebLoggedIn)
|
|
WebLoggedInTimer++;
|
|
if (WebLoggedInTimer > 300)
|
|
WebLoggedIn = false;
|
|
}
|
|
|
|
// I2C Watchdog feed
|
|
if (Settings.WDI2CAddress != 0)
|
|
{
|
|
Wire.beginTransmission(Settings.WDI2CAddress);
|
|
Wire.write(0xA5);
|
|
Wire.endTransmission();
|
|
}
|
|
|
|
if (Settings.SerialLogLevel == 5)
|
|
{
|
|
Serial.print(F("10 ps:"));
|
|
Serial.print(elapsed);
|
|
Serial.print(F(" uS 1 ps:"));
|
|
Serial.println(timer);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* Tasks each 30 seconds
|
|
\*********************************************************************************************/
|
|
void runEach30Seconds()
|
|
{
|
|
wdcounter++;
|
|
timerwd = millis() + 30000;
|
|
char str[60];
|
|
str[0] = 0;
|
|
sprintf_P(str, PSTR("Uptime %u ConnectFailures %u FreeMem %u"), wdcounter / 2, connectionFailures, FreeMem());
|
|
String log = F("WD : ");
|
|
log += str;
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
sendSysInfoUDP(1);
|
|
refreshNodeList();
|
|
MQTTCheck();
|
|
#if FEATURE_SSDP
|
|
if (Settings.UseSSDP)
|
|
SSDP_update();
|
|
#endif
|
|
#if FEATURE_ADC_VCC
|
|
vcc = ESP.getVcc() / 1000.0;
|
|
#endif
|
|
loopCounterLast = loopCounter;
|
|
loopCounter = 0;
|
|
if (loopCounterLast > loopCounterMax)
|
|
loopCounterMax = loopCounterLast;
|
|
|
|
WifiCheck();
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* Check sensor timers
|
|
\*********************************************************************************************/
|
|
void checkSensors()
|
|
{
|
|
// Check sensors and send data to controller when sensor timer has elapsed
|
|
// If deepsleep, use the single timer
|
|
if (Settings.deepSleep)
|
|
{
|
|
if (millis() > timer)
|
|
{
|
|
timer = millis() + Settings.Delay * 1000;
|
|
SensorSend();
|
|
saveToRTC(1);
|
|
String log = F("Enter deep sleep...");
|
|
addLog(LOG_LEVEL_INFO, log);
|
|
String event = F("System#Sleep");
|
|
rulesProcessing(event);
|
|
ESP.deepSleep(Settings.Delay * 1000000, WAKE_RF_DEFAULT); // Sleep for set delay
|
|
}
|
|
}
|
|
else // use individual timers for tasks
|
|
{
|
|
for (byte x = 0; x < TASKS_MAX; x++)
|
|
{
|
|
if ((Settings.TaskDeviceTimer[x] != 0) && (millis() > timerSensor[x]))
|
|
{
|
|
timerSensor[x] = millis() + Settings.TaskDeviceTimer[x] * 1000;
|
|
if (timerSensor[x] == 0) // small fix if result is 0, else timer will be stopped...
|
|
timerSensor[x] = 1;
|
|
SensorSendTask(x);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* send all sensordata
|
|
\*********************************************************************************************/
|
|
void SensorSend()
|
|
{
|
|
for (byte x = 0; x < TASKS_MAX; x++)
|
|
{
|
|
SensorSendTask(x);
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* send specific sensor task data
|
|
\*********************************************************************************************/
|
|
void SensorSendTask(byte TaskIndex)
|
|
{
|
|
if (Settings.TaskDeviceID[TaskIndex] != 0)
|
|
{
|
|
byte varIndex = TaskIndex * VARS_PER_TASK;
|
|
|
|
boolean success = false;
|
|
byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[TaskIndex]);
|
|
LoadTaskSettings(TaskIndex);
|
|
|
|
struct EventStruct TempEvent;
|
|
TempEvent.TaskIndex = TaskIndex;
|
|
TempEvent.BaseVarIndex = varIndex;
|
|
TempEvent.idx = Settings.TaskDeviceID[TaskIndex];
|
|
TempEvent.sensorType = Device[DeviceIndex].VType;
|
|
|
|
float preValue[VARS_PER_TASK]; // store values before change, in case we need it in the formula
|
|
for (byte varNr = 0; varNr < VARS_PER_TASK; varNr++)
|
|
preValue[varNr] = UserVar[varIndex + varNr];
|
|
|
|
if(Settings.TaskDeviceDataFeed[TaskIndex] == 0) // only read local connected sensorsfeeds
|
|
success = PluginCall(PLUGIN_READ, &TempEvent, dummyString);
|
|
else
|
|
success = true;
|
|
|
|
if (success)
|
|
{
|
|
for (byte varNr = 0; varNr < VARS_PER_TASK; varNr++)
|
|
{
|
|
if (ExtraTaskSettings.TaskDeviceFormula[varNr][0] != 0)
|
|
{
|
|
String spreValue = String(preValue[varNr]);
|
|
String formula = ExtraTaskSettings.TaskDeviceFormula[varNr];
|
|
float value = UserVar[varIndex + varNr];
|
|
float result = 0;
|
|
String svalue = String(value);
|
|
formula.replace("%pvalue%", spreValue);
|
|
formula.replace("%value%", svalue);
|
|
byte error = Calculate(formula.c_str(), &result);
|
|
if (error == 0)
|
|
UserVar[varIndex + varNr] = result;
|
|
}
|
|
}
|
|
sendData(&TempEvent);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* set global system timer
|
|
\*********************************************************************************************/
|
|
boolean setSystemTimer(unsigned long timer, byte plugin, byte Par1, byte Par2, byte Par3)
|
|
{
|
|
// plugin number and par1 form a unique key that can be used to restart a timer
|
|
// first check if a timer is not already running for this request
|
|
boolean reUse = false;
|
|
for (byte x = 0; x < SYSTEM_TIMER_MAX; x++)
|
|
if (systemTimers[x].timer != 0)
|
|
{
|
|
if ((systemTimers[x].plugin == plugin) && (systemTimers[x].Par1 == Par1))
|
|
{
|
|
systemTimers[x].timer = millis() + timer;
|
|
reUse = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!reUse)
|
|
{
|
|
// find a new free timer slot...
|
|
for (byte x = 0; x < SYSTEM_TIMER_MAX; x++)
|
|
if (systemTimers[x].timer == 0)
|
|
{
|
|
systemTimers[x].timer = millis() + timer;
|
|
systemTimers[x].plugin = plugin;
|
|
systemTimers[x].Par1 = Par1;
|
|
systemTimers[x].Par2 = Par2;
|
|
systemTimers[x].Par3 = Par3;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* set global system command timer
|
|
\*********************************************************************************************/
|
|
boolean setSystemCMDTimer(unsigned long timer, String& action)
|
|
{
|
|
for (byte x = 0; x < SYSTEM_CMD_TIMER_MAX; x++)
|
|
if (systemCMDTimers[x].timer == 0)
|
|
{
|
|
systemCMDTimers[x].timer = millis() + timer;
|
|
systemCMDTimers[x].action = action;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* check global system timers
|
|
\*********************************************************************************************/
|
|
boolean checkSystemTimers()
|
|
{
|
|
for (byte x = 0; x < SYSTEM_TIMER_MAX; x++)
|
|
if (systemTimers[x].timer != 0)
|
|
{
|
|
if (timeOut(systemTimers[x].timer))
|
|
{
|
|
struct EventStruct TempEvent;
|
|
TempEvent.Par1 = systemTimers[x].Par1;
|
|
TempEvent.Par2 = systemTimers[x].Par2;
|
|
TempEvent.Par3 = systemTimers[x].Par3;
|
|
for (byte y = 0; y < PLUGIN_MAX; y++)
|
|
if (Plugin_id[y] == systemTimers[x].plugin)
|
|
Plugin_ptr[y](PLUGIN_TIMER_IN, &TempEvent, dummyString);
|
|
systemTimers[x].timer = 0;
|
|
}
|
|
}
|
|
|
|
for (byte x = 0; x < SYSTEM_CMD_TIMER_MAX; x++)
|
|
if (systemCMDTimers[x].timer != 0)
|
|
if (timeOut(systemCMDTimers[x].timer))
|
|
{
|
|
struct EventStruct TempEvent;
|
|
parseCommandString(&TempEvent, systemCMDTimers[x].action);
|
|
if (!PluginCall(PLUGIN_WRITE, &TempEvent, systemCMDTimers[x].action))
|
|
ExecuteCommand(VALUE_SOURCE_SYSTEM, systemCMDTimers[x].action.c_str());
|
|
systemCMDTimers[x].timer = 0;
|
|
systemCMDTimers[x].action = "";
|
|
}
|
|
}
|
|
|
|
|
|
/*********************************************************************************************\
|
|
* run background tasks
|
|
\*********************************************************************************************/
|
|
void backgroundtasks()
|
|
{
|
|
// process DNS, only used if the ESP has no valid WiFi config
|
|
if (wifiSetup)
|
|
dnsServer.processNextRequest();
|
|
|
|
WebServer.handleClient();
|
|
MQTTclient.loop();
|
|
statusLED(false);
|
|
checkUDP();
|
|
yield();
|
|
}
|