This commit is contained in:
mvdbro
2017-01-05 08:44:36 +01:00
commit 3a5eab5ac9
18 changed files with 8411 additions and 0 deletions

813
ArduinoEasy.ino Normal file
View File

@@ -0,0 +1,813 @@
/****************************************************************************************************************************\
* Arduino project "Arduino 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
*
* 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
\*************************************************************************************************************************/
// ********************************************************************************
// 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_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_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_PROTOCOL 1 // Protocol used for controller communications
#define UNIT 0
#define FEATURE_MQTT true
#define FEATURE_MQTT_DOM false // Not tested yet!
// ********************************************************************************
// DO NOT CHANGE ANYTHING BELOW THIS LINE
// ********************************************************************************
// Challenges on Arduino/W5100 ethernet platform:
// Only 4 ethernet sockets:
// 1: UPD traffic server/send
// 2: Webserver
// 3: MQTT client
// 4: Webclient, active when webserver serves an incoming client or outgoing webclient calls.
#define socketdebug false
#define ARDUINO_PROJECT_PID 2016110201L
#define VERSION 2
#define BUILD 147
#define BUILD_NOTES ""
#define NODE_TYPE_ID_ESP_EASY_STD 1
#define NODE_TYPE_ID_ESP_EASY4M_STD 17
#define NODE_TYPE_ID_ESP_EASY32_STD 33
#define NODE_TYPE_ID_ARDUINO_EASY_STD 65
#define NODE_TYPE_ID NODE_TYPE_ID_ARDUINO_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 DEVICES_MAX 8 // ESP Easy 64
#define TASKS_MAX 8 // ESP Easy 12
#define VARS_PER_TASK 4
#define PLUGIN_MAX 8 // ESP Easy 64
#define PLUGIN_CONFIGVAR_MAX 8
#define PLUGIN_CONFIGFLOATVAR_MAX 4
#define PLUGIN_CONFIGLONGVAR_MAX 4
#define PLUGIN_EXTRACONFIGVAR_MAX 16
#define CPLUGIN_MAX 4 // ESP Easy 16
#define UNIT_MAX 32 // Only relevant for UDP unicast message 'sweeps' and the nodelist.
#define RULES_TIMER_MAX 8
#define SYSTEM_TIMER_MAX 2 // ESP Easy 8
#define SYSTEM_CMD_TIMER_MAX 1 // ESP Easy 2
#define PINSTATE_TABLE_MAX 16 // ESP Easy 32
#define RULES_MAX_SIZE 512 // ESP Easy 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 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
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#if FEATURE_MQTT
#include <PubSubClient.h>
#include <ArduinoJson.h>
#endif
void(*Reboot)(void)=0;
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
// WebServer
EthernetServer WebServer(80);
#if FEATURE_MQTT
EthernetClient mqtt;
PubSubClient MQTTclient(mqtt);
#endif
#define EthernetShield_CS_SDCard 4
#define EthernetShield_CS_W5100 10
// syslog stuff
EthernetUDP portUDP;
struct SecurityStruct
{
char ControllerUser[26];
char ControllerPassword[64];
char Password[26];
} SecuritySettings;
struct SettingsStruct
{
unsigned long PID;
int Version;
byte Unit;
int16_t Build;
byte IP[4];
byte Gateway[4];
byte Subnet[4];
byte DNS[4];
byte Controller_IP[4];
unsigned int ControllerPort;
byte IP_Octet;
char NTPHost[64];
unsigned long Delay;
byte Syslog_IP[4];
unsigned int UDPPort;
byte Protocol;
char Name[26];
byte SyslogLevel;
byte SerialLogLevel;
unsigned long BaudRate;
unsigned long MessageDelay;
boolean CustomCSS;
char ControllerHostName[64];
boolean UseNTP;
boolean DST;
byte WDI2CAddress;
int8_t PinBootStates[17];
byte UseDNS;
boolean UseRules;
int8_t Pin_status_led;
boolean UseSerial;
boolean GlobalSync;
unsigned long ConnectionFailuresThreshold;
int16_t TimeZone;
byte SDLogLevel;
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];
float TaskDevicePluginConfigFloat[TASKS_MAX][PLUGIN_CONFIGFLOATVAR_MAX];
long TaskDevicePluginConfigLong[TASKS_MAX][PLUGIN_CONFIGLONGVAR_MAX];
boolean TaskDeviceSendData[TASKS_MAX];
boolean TaskDeviceGlobalSync[TASKS_MAX];
int8_t TaskDevicePin3[TASKS_MAX];
byte TaskDeviceDataFeed[TASKS_MAX];
unsigned long TaskDeviceTimer[TASKS_MAX];
boolean MQTTRetainFlag;
char MQTTpublish[81];
char MQTTsubscribe[81];
} 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 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;
} 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;
byte cmd_within_mainloop = 0;
unsigned long connectionFailures;
unsigned long wdcounter = 0;
boolean WebLoggedIn = false;
int WebLoggedInTimer = 300;
boolean (*Plugin_ptr[PLUGIN_MAX])(byte, struct EventStruct*, String&);
byte Plugin_id[PLUGIN_MAX];
boolean (*CPlugin_ptr[CPLUGIN_MAX])(byte, struct EventStruct*, String&);
byte CPlugin_id[CPLUGIN_MAX];
String dummyString = "";
boolean systemOK = 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 = "";
/*********************************************************************************************\
* SETUP
\*********************************************************************************************/
void setup()
{
Serial.begin(115200);
fileSystemCheck();
emergencyReset();
LoadSettings();
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 == ARDUINO_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);
hardwareInit();
PluginInit();
CPluginInit();
mac[5] = Settings.Unit; // make sure every unit has a unique mac address
if (Settings.IP[0] == 0)
Ethernet.begin(mac);
else
Ethernet.begin(mac, Settings.IP, Settings.DNS, Settings.Gateway, Settings.Subnet);
// setup UDP
if (Settings.UDPPort != 0)
portUDP.begin(Settings.UDPPort);
#if FEATURE_MQTT
// Setup MQTT Client
byte ProtocolIndex = getProtocolIndex(Settings.Protocol);
if (Protocol[ProtocolIndex].usesMQTT)
MQTTConnect();
#endif
sendSysInfoUDP(3);
log = F("INIT : Boot OK");
addLog(LOG_LEVEL_INFO, log);
// Setup timers
byte bootMode = 0;
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 (Settings.UseNTP)
initTime();
if (Settings.UseRules)
{
String event = F("System#Boot");
rulesProcessing(event);
}
}
else
{
Serial.println(F("Entered Rescue mode!"));
}
}
/*********************************************************************************************\
* MAIN LOOP
\*********************************************************************************************/
void loop()
{
loopCounter++;
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_REBOOT:
{
Reboot();
break;
}
}
cmd_within_mainloop = 0;
}
// clock events
if (Settings.UseNTP)
checkTime();
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;
String log = F("WD : Uptime ");
log += wdcounter / 2;
log += F(" ConnectFailures ");
log += connectionFailures;
log += F(" Freemem ");
log += FreeMem();
addLog(LOG_LEVEL_INFO, log);
sendSysInfoUDP(1);
refreshNodeList();
loopCounterLast = loopCounter;
loopCounter = 0;
if (loopCounterLast > loopCounterMax)
loopCounterMax = loopCounterLast;
}
/*********************************************************************************************\
* Check sensor timers
\*********************************************************************************************/
void checkSensors()
{
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()
{
WebServerHandleClient();
#if FEATURE_MQTT
MQTTclient.loop();
#endif
statusLED(false);
checkUDP();
}

281
Command.ino Normal file
View File

@@ -0,0 +1,281 @@
#define INPUT_COMMAND_SIZE 80
void ExecuteCommand(byte source, const char *Line)
{
String status = "";
boolean success = false;
char TmpStr1[80];
TmpStr1[0] = 0;
char Command[80];
Command[0] = 0;
int Par1 = 0;
int Par2 = 0;
int Par3 = 0;
GetArgv(Line, Command, 1);
if (GetArgv(Line, TmpStr1, 2)) Par1 = str2int(TmpStr1);
if (GetArgv(Line, TmpStr1, 3)) Par2 = str2int(TmpStr1);
if (GetArgv(Line, TmpStr1, 4)) Par3 = str2int(TmpStr1);
// ****************************************
// commands for debugging
// ****************************************
if (strcasecmp_P(Command, PSTR("w5100")) == 0)
{
success = true;
ShowSocketStatus();
}
if (strcasecmp_P(Command, PSTR("ntp")) == 0)
{
success = true;
getNtpTime();
}
if (strcasecmp_P(Command, PSTR("sdcard")) == 0)
{
success = true;
SelectSDCard(true);
File root = SD.open("/");
root.rewindDirectory();
printDirectory(root, 0);
root.close();
}
if (strcasecmp_P(Command, PSTR("sysload")) == 0)
{
success = true;
Serial.print(100 - (100 * loopCounterLast / loopCounterMax));
Serial.print(F("% (LC="));
Serial.print(int(loopCounterLast / 30));
Serial.println(F(")"));
}
if (strcasecmp_P(Command, PSTR("meminfo")) == 0)
{
success = true;
Serial.print(F("SecurityStruct : "));
Serial.println(sizeof(SecuritySettings));
Serial.print(F("SettingsStruct : "));
Serial.println(sizeof(Settings));
Serial.print(F("ExtraTaskSettingsStruct: "));
Serial.println(sizeof(ExtraTaskSettings));
}
if (strcasecmp_P(Command, PSTR("TaskClear")) == 0)
{
success = true;
taskClear(Par1 - 1, true);
}
if (strcasecmp_P(Command, PSTR("build")) == 0)
{
success = true;
Settings.Build = Par1;
SaveSettings();
}
// ****************************************
// commands for rules
// ****************************************
if (strcasecmp_P(Command, PSTR("TaskValueSet")) == 0)
{
success = true;
if (GetArgv(Line, TmpStr1, 4))
{
float result = 0;
byte error = Calculate(TmpStr1, &result);
UserVar[(VARS_PER_TASK * (Par1 - 1)) + Par2 - 1] = result;
}
}
if (strcasecmp_P(Command, PSTR("TaskRun")) == 0)
{
success = true;
SensorSendTask(Par1 -1);
}
if (strcasecmp_P(Command, PSTR("TimerSet")) == 0)
{
success = true;
RulesTimer[Par1 - 1] = millis() + (1000 * Par2);
}
if (strcasecmp_P(Command, PSTR("Delay")) == 0)
{
success = true;
delayMillis(Par1);
}
if (strcasecmp_P(Command, PSTR("Rules")) == 0)
{
success = true;
if (Par1 == 1)
Settings.UseRules = true;
else
Settings.UseRules = false;
}
if (strcasecmp_P(Command, PSTR("Event")) == 0)
{
success = true;
String event = Line;
event = event.substring(6);
event.replace("$", "#");
if (Settings.UseRules)
rulesProcessing(event);
}
if (strcasecmp_P(Command, PSTR("SendTo")) == 0)
{
success = true;
String event = Line;
event = event.substring(7);
int index = event.indexOf(',');
if (index > 0)
{
event = event.substring(index+1);
SendUDPCommand(Par1, (char*)event.c_str(), event.length());
}
}
if (strcasecmp_P(Command, PSTR("SendToUDP")) == 0)
{
success = true;
String strLine = Line;
String ip = parseString(strLine,2);
String port = parseString(strLine,3);
int msgpos = getParamStartPos(strLine,4);
String message = strLine.substring(msgpos);
byte ipaddress[4];
str2ip((char*)ip.c_str(), ipaddress);
IPAddress UDP_IP(ipaddress[0], ipaddress[1], ipaddress[2], ipaddress[3]);
portUDP.beginPacket(UDP_IP, port.toInt());
portUDP.write(message.c_str(), message.length());
portUDP.endPacket();
}
if (strcasecmp_P(Command, PSTR("SendToHTTP")) == 0)
{
success = true;
String strLine = Line;
String host = parseString(strLine,2);
String port = parseString(strLine,3);
int pathpos = getParamStartPos(strLine,4);
String path = strLine.substring(pathpos);
EthernetClient client;
if (client.connect(host.c_str(), port.toInt()))
{
client.print(String("GET ") + path + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
unsigned long timer = millis() + 200;
while (!client.available() && millis() < timer)
delay(1);
while (client.available()) {
String line = client.readStringUntil('\n');
if (line.substring(0, 15) == "HTTP/1.1 200 OK")
addLog(LOG_LEVEL_DEBUG, line);
delay(1);
}
client.flush();
client.stop();
}
}
// ****************************************
// configure settings commands
// ****************************************
if (strcasecmp_P(Command, PSTR("Reboot")) == 0)
{
success = true;
Reboot();
}
if (strcasecmp_P(Command, PSTR("Restart")) == 0)
{
success = true;
Reboot();
}
if (strcasecmp_P(Command, PSTR("Reset")) == 0)
{
success = true;
ResetFactory();
}
if (strcasecmp_P(Command, PSTR("Save")) == 0)
{
success = true;
SaveSettings();
}
if (strcasecmp_P(Command, PSTR("Load")) == 0)
{
success = true;
LoadSettings();
}
if (strcasecmp_P(Command, PSTR("Debug")) == 0)
{
success = true;
Settings.SerialLogLevel = Par1;
}
if (strcasecmp_P(Command, PSTR("IP")) == 0)
{
success = true;
if (GetArgv(Line, TmpStr1, 2))
if (!str2ip(TmpStr1, Settings.IP))
Serial.println("?");
}
if (strcasecmp_P(Command, PSTR("Settings")) == 0)
{
success = true;
char str[20];
Serial.println();
Serial.println(F("System Info"));
IPAddress ip = Ethernet.localIP();
sprintf_P(str, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
Serial.print(F(" IP Address : ")); Serial.println(str);
Serial.print(F(" Build : ")); Serial.println((int)BUILD);
Serial.print(F(" Unit : ")); Serial.println((int)Settings.Unit);
Serial.print(F(" Free mem : ")); Serial.println(FreeMem());
}
if (success)
status += F("\nOk");
else
status += F("\nUnknown command!");
SendStatus(source,status);
}
void printDirectory(File dir, int numTabs) {
while(true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
break;
}
for (uint8_t i=0; i<numTabs; i++) {
Serial.print('\t');
}
Serial.print(entry.name());
if (entry.isDirectory()) {
Serial.println("/");
printDirectory(entry, numTabs+1);
} else {
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
}
entry.close();
}
}

169
Controller.ino Normal file
View File

@@ -0,0 +1,169 @@
//********************************************************************************
// Interface for Sending to Controllers
//********************************************************************************
boolean sendData(struct EventStruct *event)
{
LoadTaskSettings(event->TaskIndex);
if (Settings.UseRules)
createRuleEvents(event->TaskIndex);
if (Settings.GlobalSync && Settings.TaskDeviceGlobalSync[event->TaskIndex])
SendUDPTaskData(0, event->TaskIndex, event->TaskIndex);
if (!Settings.TaskDeviceSendData[event->TaskIndex])
return false;
if (Settings.MessageDelay != 0)
{
uint16_t dif = millis() - lastSend;
if (dif < Settings.MessageDelay)
{
uint16_t delayms = Settings.MessageDelay - dif;
char log[30];
sprintf_P(log, PSTR("HTTP : Delay %u ms"), delayms);
addLog(LOG_LEVEL_DEBUG_MORE, log);
unsigned long timer = millis() + delayms;
while (millis() < timer)
backgroundtasks();
}
}
LoadTaskSettings(event->TaskIndex); // could have changed during background tasks.
if (Settings.Protocol)
{
byte ProtocolIndex = getProtocolIndex(Settings.Protocol);
CPlugin_ptr[ProtocolIndex](CPLUGIN_PROTOCOL_SEND, event, dummyString);
}
PluginCall(PLUGIN_EVENT_OUT, event, dummyString);
lastSend = millis();
}
/*********************************************************************************************\
* Send status info to request source
\*********************************************************************************************/
void SendStatus(byte source, String status)
{
switch(source)
{
case VALUE_SOURCE_HTTP:
if (printToWeb)
printWebString += status;
break;
case VALUE_SOURCE_SERIAL:
Serial.println(status);
break;
}
}
#if FEATURE_MQTT
/*********************************************************************************************\
* Handle incoming MQTT messages
\*********************************************************************************************/
// handle MQTT messages
void callback(char* c_topic, byte* b_payload, unsigned int length) {
char log[256];
char c_payload[256];
strncpy(c_payload,(char*)b_payload,length);
c_payload[length] = 0;
statusLED(true);
sprintf_P(log, PSTR("%s%s"), "MQTT : Topic: ", c_topic);
addLog(LOG_LEVEL_DEBUG, log);
sprintf_P(log, PSTR("%s%s"), "MQTT : Payload: ", c_payload);
addLog(LOG_LEVEL_DEBUG, log);
struct EventStruct TempEvent;
TempEvent.String1 = c_topic;
TempEvent.String2 = c_payload;
byte ProtocolIndex = getProtocolIndex(Settings.Protocol);
CPlugin_ptr[ProtocolIndex](CPLUGIN_PROTOCOL_RECV, &TempEvent, dummyString);
}
/*********************************************************************************************\
* Connect to MQTT message broker
\*********************************************************************************************/
void MQTTConnect()
{
IPAddress MQTTBrokerIP(Settings.Controller_IP);
MQTTclient.setServer(MQTTBrokerIP, Settings.ControllerPort);
MQTTclient.setCallback(callback);
// MQTT needs a unique clientname to subscribe to broker
String clientid = "ESPClient";
clientid += Settings.Unit;
String subscribeTo = "";
String LWTTopic = Settings.MQTTsubscribe;
LWTTopic.replace("/#", "/status");
LWTTopic.replace("%sysname%", Settings.Name);
for (byte x = 1; x < 3; x++)
{
String log = "";
boolean MQTTresult = false;
if ((SecuritySettings.ControllerUser[0] != 0) && (SecuritySettings.ControllerPassword[0] != 0))
MQTTresult = MQTTclient.connect(clientid.c_str(), SecuritySettings.ControllerUser, SecuritySettings.ControllerPassword, LWTTopic.c_str(), 0, 0, "Connection Lost");
else
MQTTresult = MQTTclient.connect(clientid.c_str(), LWTTopic.c_str(), 0, 0, "Connection Lost");
if (MQTTresult)
{
log = F("MQTT : Connected to broker");
addLog(LOG_LEVEL_INFO, log);
subscribeTo = Settings.MQTTsubscribe;
subscribeTo.replace("%sysname%", Settings.Name);
MQTTclient.subscribe(subscribeTo.c_str());
log = F("Subscribed to: ");
log += subscribeTo;
addLog(LOG_LEVEL_INFO, log);
break; // end loop if succesfull
}
else
{
log = F("MQTT : Failed to connected to broker");
addLog(LOG_LEVEL_ERROR, log);
}
delay(500);
}
}
/*********************************************************************************************\
* Check connection MQTT message broker
\*********************************************************************************************/
void MQTTCheck()
{
byte ProtocolIndex = getProtocolIndex(Settings.Protocol);
if (Protocol[ProtocolIndex].usesMQTT)
if (!MQTTclient.connected())
{
String log = F("MQTT : Connection lost");
addLog(LOG_LEVEL_ERROR, log);
connectionFailures += 2;
MQTTclient.disconnect();
delay(1000);
MQTTConnect();
}
else if (connectionFailures)
connectionFailures--;
}
/*********************************************************************************************\
* Send status info back to channel where request came from
\*********************************************************************************************/
void MQTTStatus(String& status)
{
String pubname = Settings.MQTTsubscribe;
pubname.replace("/#", "/status");
pubname.replace("%sysname%", Settings.Name);
MQTTclient.publish(pubname.c_str(), status.c_str(),Settings.MQTTRetainFlag);
}
#endif

55
Hardware.ino Normal file
View File

@@ -0,0 +1,55 @@
/********************************************************************************************\
* Initialize specific hardware setings (only global ones, others are set through devices)
\*********************************************************************************************/
void hardwareInit()
{
// set GPIO pins state if not set to default
for (byte x=0; x < 17; x++)
if (Settings.PinBootStates[x] != 0)
switch(Settings.PinBootStates[x])
{
case 1:
pinMode(x,OUTPUT);
digitalWrite(x,LOW);
setPinState(1, x, PIN_MODE_OUTPUT, LOW);
break;
case 2:
pinMode(x,OUTPUT);
digitalWrite(x,HIGH);
setPinState(1, x, PIN_MODE_OUTPUT, HIGH);
break;
case 3:
pinMode(x,INPUT_PULLUP);
setPinState(1, x, PIN_MODE_INPUT, 0);
break;
}
String log = F("INIT : I2C");
addLog(LOG_LEVEL_INFO, log);
Wire.begin();
// I2C Watchdog boot status check
if (Settings.WDI2CAddress != 0)
{
delay(500);
Wire.beginTransmission(Settings.WDI2CAddress);
Wire.write(0x83); // command to set pointer
Wire.write(17); // pointer value to status byte
Wire.endTransmission();
Wire.requestFrom(Settings.WDI2CAddress, (uint8_t)1);
if (Wire.available())
{
byte status = Wire.read();
if (status & 0x1)
{
String log = F("INIT : Reset by WD!");
addLog(LOG_LEVEL_ERROR, log);
//lastBootCause = BOOT_CAUSE_EXT_WD;
}
}
}
}

1995
Misc.ino Normal file

File diff suppressed because it is too large Load Diff

369
Networking.ino Normal file
View File

@@ -0,0 +1,369 @@
/*********************************************************************************************\
Syslog client
\*********************************************************************************************/
void syslog(const char *message)
{
if (Settings.Syslog_IP[0] != 0)
{
IPAddress broadcastIP(Settings.Syslog_IP[0], Settings.Syslog_IP[1], Settings.Syslog_IP[2], Settings.Syslog_IP[3]);
portUDP.beginPacket(broadcastIP, 514);
char str[256];
str[0] = 0;
snprintf_P(str, sizeof(str), PSTR("<7>ESP Unit: %u : %s"), Settings.Unit, message);
portUDP.write(str);
portUDP.endPacket();
}
}
/*********************************************************************************************\
Structs for UDP messaging
\*********************************************************************************************/
struct infoStruct
{
byte header = 255;
byte ID = 3;
byte sourcelUnit;
byte destUnit;
byte sourceTaskIndex;
byte destTaskIndex;
byte deviceNumber;
char taskName[26];
char ValueNames[VARS_PER_TASK][26];
};
struct dataStruct
{
byte header = 255;
byte ID = 5;
byte sourcelUnit;
byte destUnit;
byte sourceTaskIndex;
byte destTaskIndex;
float Values[VARS_PER_TASK];
};
/*********************************************************************************************\
Check UDP messages
\*********************************************************************************************/
void checkUDP()
{
if (Settings.UDPPort == 0)
return;
// UDP events
int packetSize = portUDP.parsePacket();
if (packetSize)
{
statusLED(true);
IPAddress remoteIP = portUDP.remoteIP();
if (portUDP.remotePort() == 123)
{
// unexpected NTP reply, drop for now...
return;
}
byte packetBuffer[128];
int len = portUDP.read(packetBuffer, 128);
if (packetBuffer[0] != 255)
{
packetBuffer[len] = 0;
addLog(LOG_LEVEL_DEBUG, (char*)packetBuffer);
struct EventStruct TempEvent;
String request = (char*)packetBuffer;
parseCommandString(&TempEvent, request);
TempEvent.Source = VALUE_SOURCE_SYSTEM;
if (!PluginCall(PLUGIN_WRITE, &TempEvent, request))
ExecuteCommand(VALUE_SOURCE_SYSTEM, (char*)packetBuffer);
}
else
{
if (packetBuffer[1] > 1 && packetBuffer[1] < 6)
{
String log = (F("UDP : Sensor msg "));
for (byte x = 1; x < 6; x++)
{
log += " ";
log += (int)packetBuffer[x];
}
addLog(LOG_LEVEL_DEBUG_MORE, log);
}
// binary data!
switch (packetBuffer[1])
{
case 1: // sysinfo message
{
byte mac[6];
byte ip[4];
byte unit = packetBuffer[12];
for (byte x = 0; x < 6; x++)
mac[x] = packetBuffer[x + 2];
for (byte x = 0; x < 4; x++)
ip[x] = packetBuffer[x + 8];
if (unit < UNIT_MAX)
{
for (byte x = 0; x < 4; x++)
Nodes[unit].ip[x] = packetBuffer[x + 8];
Nodes[unit].age = 0; // reset 'age counter'
if (len >20) // extended packet size
{
Nodes[unit].build = packetBuffer[13] + 256*packetBuffer[14];
}
}
char macaddress[20];
sprintf_P(macaddress, PSTR("%02x:%02x:%02x:%02x:%02x:%02x"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
char ipaddress[16];
sprintf_P(ipaddress, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
char log[80];
sprintf_P(log, PSTR("UDP : %s,%s,%u"), macaddress, ipaddress, unit);
addLog(LOG_LEVEL_DEBUG_MORE, log);
break;
}
case 2: // sensor info pull request
{
SendUDPTaskInfo(packetBuffer[2], packetBuffer[5], packetBuffer[4]);
break;
}
case 3: // sensor info
{
if (Settings.GlobalSync)
{
struct infoStruct infoReply;
memcpy((byte*)&infoReply, (byte*)&packetBuffer, sizeof(infoStruct));
// to prevent flash wear out (bugs in communication?) we can only write to an empty task
// so it will write only once and has to be cleared manually through webgui
if (Settings.TaskDeviceNumber[infoReply.destTaskIndex] == 0)
{
Settings.TaskDeviceNumber[infoReply.destTaskIndex] = infoReply.deviceNumber;
Settings.TaskDeviceDataFeed[infoReply.destTaskIndex] = 1; // remote feed
Settings.TaskDeviceSendData[infoReply.destTaskIndex] = false;
strcpy(ExtraTaskSettings.TaskDeviceName, infoReply.taskName);
for (byte x = 0; x < VARS_PER_TASK; x++)
strcpy( ExtraTaskSettings.TaskDeviceValueNames[x], infoReply.ValueNames[x]);
SaveTaskSettings(infoReply.destTaskIndex);
SaveSettings();
}
}
break;
}
case 4: // sensor data pull request
{
SendUDPTaskData(packetBuffer[2], packetBuffer[5], packetBuffer[4]);
break;
}
case 5: // sensor data
{
if (Settings.GlobalSync)
{
struct dataStruct dataReply;
memcpy((byte*)&dataReply, (byte*)&packetBuffer, sizeof(dataStruct));
// only if this task has a remote feed, update values
if (Settings.TaskDeviceDataFeed[dataReply.destTaskIndex] != 0)
{
for (byte x = 0; x < VARS_PER_TASK; x++)
{
UserVar[dataReply.destTaskIndex * VARS_PER_TASK + x] = dataReply.Values[x];
}
if (Settings.UseRules)
createRuleEvents(dataReply.destTaskIndex);
}
}
break;
}
default:
{
struct EventStruct TempEvent;
TempEvent.Data = (byte*)packetBuffer;
TempEvent.Par1 = remoteIP[3];
PluginCall(PLUGIN_UDP_IN, &TempEvent, dummyString);
break;
}
}
}
}
}
/*********************************************************************************************\
Send task info using UDP message
\*********************************************************************************************/
void SendUDPTaskInfo(byte destUnit, byte sourceTaskIndex, byte destTaskIndex)
{
struct infoStruct infoReply;
infoReply.sourcelUnit = Settings.Unit;
infoReply.sourceTaskIndex = sourceTaskIndex;
infoReply.destTaskIndex = destTaskIndex;
LoadTaskSettings(infoReply.sourceTaskIndex);
infoReply.deviceNumber = Settings.TaskDeviceNumber[infoReply.sourceTaskIndex];
strcpy(infoReply.taskName, ExtraTaskSettings.TaskDeviceName);
for (byte x = 0; x < VARS_PER_TASK; x++)
strcpy(infoReply.ValueNames[x], ExtraTaskSettings.TaskDeviceValueNames[x]);
byte firstUnit = 1;
byte lastUnit = UNIT_MAX - 1;
if (destUnit != 0)
{
firstUnit = destUnit;
lastUnit = destUnit;
}
for (byte x = firstUnit; x <= lastUnit; x++)
{
infoReply.destUnit = x;
sendUDP(x, (byte*)&infoReply, sizeof(infoStruct));
delay(10);
}
delay(50);
}
/*********************************************************************************************\
Send task data using UDP message
\*********************************************************************************************/
void SendUDPTaskData(byte destUnit, byte sourceTaskIndex, byte destTaskIndex)
{
struct dataStruct dataReply;
dataReply.sourcelUnit = Settings.Unit;
dataReply.sourceTaskIndex = sourceTaskIndex;
dataReply.destTaskIndex = destTaskIndex;
for (byte x = 0; x < VARS_PER_TASK; x++)
dataReply.Values[x] = UserVar[dataReply.sourceTaskIndex * VARS_PER_TASK + x];
byte firstUnit = 1;
byte lastUnit = UNIT_MAX - 1;
if (destUnit != 0)
{
firstUnit = destUnit;
lastUnit = destUnit;
}
for (byte x = firstUnit; x <= lastUnit; x++)
{
dataReply.destUnit = x;
sendUDP(x, (byte*) &dataReply, sizeof(dataStruct));
delay(10);
}
delay(50);
}
/*********************************************************************************************\
Send event using UDP message
\*********************************************************************************************/
void SendUDPCommand(byte destUnit, char* data, byte dataLength)
{
byte firstUnit = 1;
byte lastUnit = UNIT_MAX - 1;
if (destUnit != 0)
{
firstUnit = destUnit;
lastUnit = destUnit;
}
for (byte x = firstUnit; x <= lastUnit; x++)
{
sendUDP(x, (byte*)data, dataLength);
delay(10);
}
delay(50);
}
/*********************************************************************************************\
Send UDP message
\*********************************************************************************************/
void sendUDP(byte unit, byte* data, byte size)
{
if (Nodes[unit].ip[0] == 0)
return;
String log = "UDP : Send UDP message to ";
log += unit;
addLog(LOG_LEVEL_DEBUG_MORE, log);
statusLED(true);
IPAddress remoteNodeIP(Nodes[unit].ip[0], Nodes[unit].ip[1], Nodes[unit].ip[2], Nodes[unit].ip[3]);
portUDP.beginPacket(remoteNodeIP, Settings.UDPPort);
portUDP.write(data, size);
portUDP.endPacket();
}
/*********************************************************************************************\
Refresh aging for remote units, drop if too old...
\*********************************************************************************************/
void refreshNodeList()
{
for (byte counter = 0; counter < UNIT_MAX; counter++)
{
if (Nodes[counter].ip[0] != 0)
{
Nodes[counter].age++; // increment age counter
if (Nodes[counter].age > 10) // if entry to old, clear this node ip from the list.
for (byte x = 0; x < 4; x++)
Nodes[counter].ip[x] = 0;
}
}
}
void sendSysInfoUDP(byte repeats)
{
char log[80];
if (Settings.UDPPort == 0)
return;
// 1 byte 'binary token 255'
// 1 byte id '1'
// 6 byte mac
// 4 byte ip
// 1 byte unit
// 2 byte build
// 25 char name
// 1 byte node type id
// send my info to the world...
strcpy_P(log, PSTR("UDP : Send Sysinfo message"));
addLog(LOG_LEVEL_DEBUG_MORE, log);
for (byte counter = 0; counter < repeats; counter++)
{
byte data[80];
data[0] = 255;
data[1] = 1;
for (byte x = 0; x < 6; x++)
data[x + 2] = mac[x];
IPAddress ip = Ethernet.localIP();
for (byte x = 0; x < 4; x++)
data[x + 8] = ip[x];
data[12] = Settings.Unit;
data[13] = Settings.Build & 0xff;
data[14] = Settings.Build >> 8;
memcpy((byte*)data+15,Settings.Name,25);
data[40] = NODE_TYPE_ID;
statusLED(true);
IPAddress broadcastIP(255, 255, 255, 255);
portUDP.beginPacket(broadcastIP, Settings.UDPPort);
portUDP.write(data, 80);
portUDP.endPacket();
if (counter < (repeats - 1))
delay(500);
}
// store my own info also in the list...
if (Settings.Unit < UNIT_MAX)
{
IPAddress ip = Ethernet.localIP();
for (byte x = 0; x < 4; x++)
Nodes[Settings.Unit].ip[x] = ip[x];
Nodes[Settings.Unit].age = 0;
Nodes[Settings.Unit].build = Settings.Build;
}
}

43
Serial.ino Normal file
View File

@@ -0,0 +1,43 @@
/********************************************************************************************\
* Get data from Serial Interface
\*********************************************************************************************/
#define INPUT_BUFFER_SIZE 128
byte SerialInByte;
int SerialInByteCounter = 0;
char InputBuffer_Serial[INPUT_BUFFER_SIZE + 2];
void serial()
{
while (Serial.available())
{
SerialInByte = Serial.read();
if (SerialInByte == 255) // binary data...
{
Serial.flush();
return;
}
if (isprint(SerialInByte))
{
if (SerialInByteCounter < INPUT_BUFFER_SIZE) // add char to string if it still fits
InputBuffer_Serial[SerialInByteCounter++] = SerialInByte;
}
if (SerialInByte == '\n')
{
InputBuffer_Serial[SerialInByteCounter] = 0; // serial data completed
Serial.write('>');
Serial.println(InputBuffer_Serial);
String action = InputBuffer_Serial;
struct EventStruct TempEvent;
parseCommandString(&TempEvent, action);
TempEvent.Source = VALUE_SOURCE_SERIAL;
if (!PluginCall(PLUGIN_WRITE, &TempEvent, action))
ExecuteCommand(VALUE_SOURCE_SERIAL, InputBuffer_Serial);
SerialInByteCounter = 0;
InputBuffer_Serial[0] = 0; // serial data processed, clear buffer
}
}
}

2085
WebServer.ino Normal file

File diff suppressed because it is too large Load Diff

170
_C001.ino Normal file
View File

@@ -0,0 +1,170 @@
//#######################################################################################################
//########################### Controller Plugin 001: Domoticz HTTP ######################################
//#######################################################################################################
#define CPLUGIN_001
#define CPLUGIN_ID_001 1
#define CPLUGIN_NAME_001 "Domoticz HTTP"
boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
switch (function)
{
case CPLUGIN_PROTOCOL_ADD:
{
Protocol[++protocolCount].Number = CPLUGIN_ID_001;
Protocol[protocolCount].usesMQTT = false;
Protocol[protocolCount].usesAccount = true;
Protocol[protocolCount].usesPassword = true;
Protocol[protocolCount].defaultPort = 8080;
break;
}
case CPLUGIN_GET_DEVICENAME:
{
string = F(CPLUGIN_NAME_001);
break;
}
case CPLUGIN_PROTOCOL_SEND:
{
String authHeader = "";
if ((SecuritySettings.ControllerUser[0] != 0) && (SecuritySettings.ControllerPassword[0] != 0))
{
// todo base64 encoder;
// String auth = SecuritySettings.ControllerUser;
// auth += ":";
// auth += SecuritySettings.ControllerPassword;
// authHeader = "Authorization: Basic " + encoder.encode(auth) + " \r\n";
}
char log[80];
boolean success = false;
char host[20];
sprintf_P(host, PSTR("%u.%u.%u.%u"), Settings.Controller_IP[0], Settings.Controller_IP[1], Settings.Controller_IP[2], Settings.Controller_IP[3]);
sprintf_P(log, PSTR("%s%s using port %u"), "HTTP : connecting to ", host,Settings.ControllerPort);
addLog(LOG_LEVEL_DEBUG, log);
// Use WiFiClient class to create TCP connections
EthernetClient client;
if (!client.connect(host, Settings.ControllerPort))
{
connectionFailures++;
strcpy_P(log, PSTR("HTTP : connection failed"));
addLog(LOG_LEVEL_ERROR, log);
return false;
}
#if socketdebug
ShowSocketStatus();
#endif
statusLED(true);
if (connectionFailures)
connectionFailures--;
// We now create a URI for the request
String url = F("/json.htm?type=command&param=udevice&idx=");
url += event->idx;
switch (event->sensorType)
{
case SENSOR_TYPE_SINGLE: // single value sensor, used for Dallas, BH1750, etc
url += F("&svalue=");
url += toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
break;
case SENSOR_TYPE_LONG: // single LONG value, stored in two floats (rfid tags)
url += F("&svalue=");
url += (unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16);
break;
case SENSOR_TYPE_DUAL: // any sensor that uses two simple values
url += F("&svalue=");
url += toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
url += ";";
url += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
break;
case SENSOR_TYPE_TEMP_HUM: // temp + hum + hum_stat, used for DHT11
url += F("&svalue=");
url += toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
url += ";";
url += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
url += ";0";
break;
case SENSOR_TYPE_TEMP_BARO: // temp + hum + hum_stat + bar + bar_fore, used for BMP085
url += F("&svalue=");
url += toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
url += ";0;0;";
url += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
url += ";0";
break;
case SENSOR_TYPE_TEMP_HUM_BARO: // temp + hum + hum_stat + bar + bar_fore, used for BME280
url += F("&svalue=");
url += toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
url += ";";
url += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
url += ";0;";
url += toString(UserVar[event->BaseVarIndex + 2],ExtraTaskSettings.TaskDeviceValueDecimals[2]);
url += ";0";
break;
case SENSOR_TYPE_SWITCH:
url = F("/json.htm?type=command&param=switchlight&idx=");
url += event->idx;
url += F("&switchcmd=");
if (UserVar[event->BaseVarIndex] == 0)
url += "Off";
else
url += "On";
break;
case SENSOR_TYPE_DIMMER:
url = F("/json.htm?type=command&param=switchlight&idx=");
url += event->idx;
url += F("&switchcmd=");
if (UserVar[event->BaseVarIndex] == 0)
url += "Off";
else
{
url += F("Set%20Level&level=");
url += UserVar[event->BaseVarIndex];
}
break;
}
url.toCharArray(log, 80);
addLog(LOG_LEVEL_DEBUG_MORE, log);
// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" + authHeader +
"Connection: close\r\n\r\n");
unsigned long timer = millis() + 200;
while (!client.available() && millis() < timer)
delay(1);
// Read all the lines of the reply from server and print them to Serial
while (client.available()) {
String line = client.readStringUntil('\n');
line.toCharArray(log, 80);
addLog(LOG_LEVEL_DEBUG_MORE, log);
if (line.substring(0, 15) == "HTTP/1.1 200 OK")
{
strcpy_P(log, PSTR("HTTP : Success"));
addLog(LOG_LEVEL_DEBUG, log);
success = true;
}
delay(1);
}
strcpy_P(log, PSTR("HTTP : closing connection"));
addLog(LOG_LEVEL_DEBUG, log);
client.flush();
client.stop();
break;
}
}
return success;
}

227
_C002.ino Normal file
View File

@@ -0,0 +1,227 @@
//#######################################################################################################
//########################### Controller Plugin 002: Domoticz MQTT ######################################
//#######################################################################################################
#if FEATURE_MQTT_DOM
#define CPLUGIN_002
#define CPLUGIN_ID_002 2
#define CPLUGIN_NAME_002 "Domoticz MQTT"
boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
switch (function)
{
case CPLUGIN_PROTOCOL_ADD:
{
Protocol[++protocolCount].Number = CPLUGIN_ID_002;
Protocol[protocolCount].usesMQTT = true;
Protocol[protocolCount].usesTemplate = true;
Protocol[protocolCount].usesAccount = true;
Protocol[protocolCount].usesPassword = true;
Protocol[protocolCount].defaultPort = 1883;
break;
}
case CPLUGIN_GET_DEVICENAME:
{
string = F(CPLUGIN_NAME_002);
break;
}
case CPLUGIN_PROTOCOL_TEMPLATE:
{
strcpy_P(Settings.MQTTsubscribe, PSTR("domoticz/out"));
strcpy_P(Settings.MQTTpublish, PSTR("domoticz/in"));
break;
}
case CPLUGIN_PROTOCOL_RECV:
{
char json[512];
json[0] = 0;
event->String2.toCharArray(json, 512);
StaticJsonBuffer<512> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(json);
if (root.success())
{
long idx = root["idx"];
float nvalue = root["nvalue"];
long nvaluealt = root["nvalue"];
//const char* name = root["name"]; // Not used
//const char* svalue = root["svalue"]; // Not used
const char* svalue1 = root["svalue1"];
//const char* svalue2 = root["svalue2"]; // Not used
//const char* svalue3 = root["svalue3"]; // Not used
const char* switchtype = root["switchType"]; // Expect "On/Off" or "dimmer"
if (nvalue == 0)
nvalue = nvaluealt;
if ((int)switchtype == 0)
switchtype = "?";
for (byte x = 0; x < TASKS_MAX; x++)
{
if (Settings.TaskDeviceID[x] == idx)
{
if (Settings.TaskDeviceNumber[x] == 1) // temp solution, if input switch, update state
{
String action = F("inputSwitchState,");
action += x;
action += ",";
action += nvalue;
struct EventStruct TempEvent;
parseCommandString(&TempEvent, action);
PluginCall(PLUGIN_WRITE, &TempEvent, action);
}
if (Settings.TaskDeviceNumber[x] == 29) // temp solution, if plugin 029, set gpio
{
String action = "";
int baseVar = x * VARS_PER_TASK;
struct EventStruct TempEvent;
if (strcasecmp_P(switchtype, PSTR("dimmer")) == 0)
{
int pwmValue = UserVar[baseVar];
action = F("pwm,");
action += Settings.TaskDevicePin1[x];
action += ",";
switch ((int)nvalue)
{
case 0:
pwmValue = 0;
break;
case 1:
pwmValue = UserVar[baseVar];
break;
case 2:
pwmValue = 10 * atol(svalue1);
UserVar[baseVar] = pwmValue;
break;
}
action += pwmValue;
}
else
{
UserVar[baseVar] = nvalue;
action = F("gpio,");
action += Settings.TaskDevicePin1[x];
action += ",";
action += nvalue;
}
parseCommandString(&TempEvent, action);
PluginCall(PLUGIN_WRITE, &TempEvent, action);
}
}
}
}
break;
}
case CPLUGIN_PROTOCOL_SEND:
{
StaticJsonBuffer<200> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["idx"] = event->idx;
String values;
char str[80];
switch (event->sensorType)
{
case SENSOR_TYPE_SINGLE: // single value sensor, used for Dallas, BH1750, etc
root["nvalue"] = 0;
values = toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
values.toCharArray(str, 80);
root["svalue"] = str;
break;
case SENSOR_TYPE_LONG: // single LONG value, stored in two floats (rfid tags)
root["nvalue"] = 0;
values = (unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16);
values.toCharArray(str, 80);
root["svalue"] = str;
break;
case SENSOR_TYPE_DUAL: // any sensor that uses two simple values
root["nvalue"] = 0;
values = toString(UserVar[event->BaseVarIndex ],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
values += ";";
values += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
values.toCharArray(str, 80);
root["svalue"] = str;
break;
case SENSOR_TYPE_TEMP_HUM: // temp + hum + hum_stat, used for DHT11
root["nvalue"] = 0;
values = toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
values += ";";
values += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
values += ";0";
values.toCharArray(str, 80);
root["svalue"] = str;
break;
case SENSOR_TYPE_TEMP_BARO: // temp + hum + hum_stat + bar + bar_fore, used for BMP085
root["nvalue"] = 0;
values = toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
values += ";0;0;";
values += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
values += ";0";
values.toCharArray(str, 80);
root["svalue"] = str;
break;
case SENSOR_TYPE_TEMP_HUM_BARO: // temp + hum + hum_stat + bar + bar_fore, used for BME280
root["nvalue"] = 0;
values = toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
values += ";";
values += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
values += ";0;";
values += toString(UserVar[event->BaseVarIndex + 2],ExtraTaskSettings.TaskDeviceValueDecimals[2]);
values += ";0";
values.toCharArray(str, 80);
root["svalue"] = str;
break;
case SENSOR_TYPE_SWITCH:
root["command"] = "switchlight";
if (UserVar[event->BaseVarIndex] == 0)
root["switchcmd"] = "Off";
else
root["switchcmd"] = "On";
break;
case SENSOR_TYPE_DIMMER:
root["command"] = "switchlight";
if (UserVar[event->BaseVarIndex] == 0)
root["switchcmd"] = "Off";
else
root["Set%20Level"] = UserVar[event->BaseVarIndex];
break;
}
char json[256];
root.printTo(json, sizeof(json));
String log = F("MQTT : ");
log += json;
addLog(LOG_LEVEL_DEBUG, json);
String pubname = Settings.MQTTpublish;
pubname.replace("%sysname%", Settings.Name);
pubname.replace("%tskname%", ExtraTaskSettings.TaskDeviceName);
pubname.replace("%id%", String(event->idx));
if (!MQTTclient.publish(pubname.c_str(), json, Settings.MQTTRetainFlag))
{
log = F("MQTT publish failed");
addLog(LOG_LEVEL_DEBUG, json);
MQTTConnect();
connectionFailures++;
}
else if (connectionFailures)
connectionFailures--;
break;
}
}
return success;
}
#endif

110
_C005.ino Normal file
View File

@@ -0,0 +1,110 @@
//#######################################################################################################
//########################### Controller Plugin 005: OpenHAB MQTT #######################################
//#######################################################################################################
#define CPLUGIN_005
#define CPLUGIN_ID_005 5
#define CPLUGIN_NAME_005 "OpenHAB MQTT"
boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
switch (function)
{
case CPLUGIN_PROTOCOL_ADD:
{
Protocol[++protocolCount].Number = CPLUGIN_ID_005;
Protocol[protocolCount].usesMQTT = true;
Protocol[protocolCount].usesTemplate = true;
Protocol[protocolCount].usesAccount = true;
Protocol[protocolCount].usesPassword = true;
Protocol[protocolCount].defaultPort = 1883;
break;
}
case CPLUGIN_GET_DEVICENAME:
{
string = F(CPLUGIN_NAME_005);
break;
}
case CPLUGIN_PROTOCOL_TEMPLATE:
{
strcpy_P(Settings.MQTTsubscribe, PSTR("/%sysname%/#"));
strcpy_P(Settings.MQTTpublish, PSTR("/%sysname%/%tskname%/%valname%"));
break;
}
case CPLUGIN_PROTOCOL_RECV:
{
// Split topic into array
String tmpTopic = event->String1.substring(1);
String topicSplit[10];
int SlashIndex = tmpTopic.indexOf('/');
byte count = 0;
while (SlashIndex > 0 && count < 10 - 1)
{
topicSplit[count] = tmpTopic.substring(0, SlashIndex);
tmpTopic = tmpTopic.substring(SlashIndex + 1);
SlashIndex = tmpTopic.indexOf('/');
count++;
}
topicSplit[count] = tmpTopic;
String cmd = "";
struct EventStruct TempEvent;
if (topicSplit[count] == "cmd")
{
cmd = event->String2;
parseCommandString(&TempEvent, cmd);
TempEvent.Source = VALUE_SOURCE_MQTT;
}
else
{
cmd = topicSplit[count - 1];
TempEvent.Par1 = topicSplit[count].toInt();
TempEvent.Par2 = event->String2.toFloat();
TempEvent.Par3 = 0;
}
// in case of event, store to buffer and return...
String command = parseString(cmd, 1);
if (command == F("event"))
eventBuffer = cmd.substring(6);
else
PluginCall(PLUGIN_WRITE, &TempEvent, cmd);
break;
}
case CPLUGIN_PROTOCOL_SEND:
{
statusLED(true);
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String pubname = Settings.MQTTpublish;
pubname.replace("%sysname%", Settings.Name);
pubname.replace("%tskname%", ExtraTaskSettings.TaskDeviceName);
pubname.replace("%id%", String(event->idx));
String value = "";
byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[event->TaskIndex]);
byte valueCount = getValueCountFromSensorType(event->sensorType);
for (byte x = 0; x < valueCount; x++)
{
String tmppubname = pubname;
tmppubname.replace("%valname%", ExtraTaskSettings.TaskDeviceValueNames[x]);
if (event->sensorType == SENSOR_TYPE_LONG)
value = (unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16);
else
value = toString(UserVar[event->BaseVarIndex + x], ExtraTaskSettings.TaskDeviceValueDecimals[x]);
MQTTclient.publish(tmppubname.c_str(), value.c_str(), Settings.MQTTRetainFlag);
}
break;
}
return success;
}
}

346
_P001_Switch.ino Normal file
View File

@@ -0,0 +1,346 @@
//#######################################################################################################
//#################################### Plugin 001: Input Switch #########################################
//#######################################################################################################
// Adapted from ESP Easy, changes:
// WebServer.arg() -> WebServerarg()
// Changed pin limit from 0-16 to 2-13
#define PLUGIN_001
#define PLUGIN_ID_001 1
#define PLUGIN_NAME_001 "Switch input"
#define PLUGIN_VALUENAME1_001 "Switch"
boolean Plugin_001(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
static byte switchstate[TASKS_MAX];
static byte outputstate[TASKS_MAX];
switch (function)
{
case PLUGIN_DEVICE_ADD:
{
Device[++deviceCount].Number = PLUGIN_ID_001;
Device[deviceCount].Type = DEVICE_TYPE_SINGLE;
Device[deviceCount].VType = SENSOR_TYPE_SWITCH;
Device[deviceCount].Ports = 0;
Device[deviceCount].PullUpOption = true;
Device[deviceCount].InverseLogicOption = true;
Device[deviceCount].FormulaOption = false;
Device[deviceCount].ValueCount = 1;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].TimerOptional = true;
Device[deviceCount].GlobalSyncOption = true;
break;
}
case PLUGIN_GET_DEVICENAME:
{
string = F(PLUGIN_NAME_001);
break;
}
case PLUGIN_GET_DEVICEVALUENAMES:
{
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_001));
break;
}
case PLUGIN_WEBFORM_LOAD:
{
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
String options[2];
options[0] = F("Switch");
options[1] = F("Dimmer");
int optionValues[2];
optionValues[0] = 1;
optionValues[1] = 2;
string += F("<TR><TD>Switch Type:<TD><select name='plugin_001_type'>");
for (byte x = 0; x < 2; x++)
{
string += F("<option value='");
string += optionValues[x];
string += "'";
if (choice == optionValues[x])
string += F(" selected");
string += ">";
string += options[x];
string += F("</option>");
}
string += F("</select>");
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == 2)
{
char tmpString[128];
sprintf_P(tmpString, PSTR("<TR><TD>Dim value:<TD><input type='text' name='plugin_001_dimvalue' value='%u'>"), Settings.TaskDevicePluginConfig[event->TaskIndex][1]);
string += tmpString;
}
choice = Settings.TaskDevicePluginConfig[event->TaskIndex][2];
String buttonOptions[3];
buttonOptions[0] = F("Normal Switch");
buttonOptions[1] = F("Push Button Active Low");
buttonOptions[2] = F("Push Button Active High");
int buttonOptionValues[3];
buttonOptionValues[0] = 0;
buttonOptionValues[1] = 1;
buttonOptionValues[2] = 2;
string += F("<TR><TD>Switch Button Type:<TD><select name='plugin_001_button'>");
for (byte x = 0; x < 3; x++)
{
string += F("<option value='");
string += buttonOptionValues[x];
string += "'";
if (choice == buttonOptionValues[x])
string += F(" selected");
string += ">";
string += buttonOptions[x];
string += F("</option>");
}
string += F("</select>");
string += F("<TR><TD>Send Boot state:<TD>");
if (Settings.TaskDevicePluginConfig[event->TaskIndex][3])
string += F("<input type=checkbox name=plugin_001_boot checked>");
else
string += F("<input type=checkbox name=plugin_001_boot>");
success = true;
break;
}
case PLUGIN_WEBFORM_SAVE:
{
String plugin1 = WebServerarg(F("plugin_001_type"));
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == 2)
{
String plugin2 = WebServerarg(F("plugin_001_dimvalue"));
Settings.TaskDevicePluginConfig[event->TaskIndex][1] = plugin2.toInt();
}
String plugin3 = WebServerarg(F("plugin_001_button"));
Settings.TaskDevicePluginConfig[event->TaskIndex][2] = plugin3.toInt();
String plugin4 = WebServerarg(F("plugin_001_boot"));
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = (plugin4 == "on");
success = true;
break;
}
case PLUGIN_INIT:
{
if (Settings.TaskDevicePin1PullUp[event->TaskIndex])
pinMode(Settings.TaskDevicePin1[event->TaskIndex], INPUT_PULLUP);
else
pinMode(Settings.TaskDevicePin1[event->TaskIndex], INPUT);
setPinState(PLUGIN_ID_001, Settings.TaskDevicePin1[event->TaskIndex], PIN_MODE_INPUT, 0);
switchstate[event->TaskIndex] = digitalRead(Settings.TaskDevicePin1[event->TaskIndex]);
outputstate[event->TaskIndex] = switchstate[event->TaskIndex];
// if boot state must be send, inverse default state
if (Settings.TaskDevicePluginConfig[event->TaskIndex][3])
{
switchstate[event->TaskIndex] = !switchstate[event->TaskIndex];
outputstate[event->TaskIndex] = !outputstate[event->TaskIndex];
}
success = true;
break;
}
case PLUGIN_TEN_PER_SECOND:
{
byte state = digitalRead(Settings.TaskDevicePin1[event->TaskIndex]);
if (state != switchstate[event->TaskIndex])
{
switchstate[event->TaskIndex] = state;
byte currentOutputState = outputstate[event->TaskIndex];
if (Settings.TaskDevicePluginConfig[event->TaskIndex][2] == 0) //normal switch
outputstate[event->TaskIndex] = state;
else
{
if (Settings.TaskDevicePluginConfig[event->TaskIndex][2] == 1) // active low push button
{
if (state == 0)
outputstate[event->TaskIndex] = !outputstate[event->TaskIndex];
}
else // active high push button
{
if (state == 1)
outputstate[event->TaskIndex] = !outputstate[event->TaskIndex];
}
}
// send if output needs to be changed
if (currentOutputState != outputstate[event->TaskIndex])
{
byte sendState = outputstate[event->TaskIndex];
if (Settings.TaskDevicePin1Inversed[event->TaskIndex])
sendState = !outputstate[event->TaskIndex];
UserVar[event->BaseVarIndex] = sendState;
event->sensorType = SENSOR_TYPE_SWITCH;
if ((sendState == 1) && (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == 2))
{
event->sensorType = SENSOR_TYPE_DIMMER;
UserVar[event->BaseVarIndex] = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
}
String log = F("SW : State ");
log += sendState;
addLog(LOG_LEVEL_INFO, log);
sendData(event);
}
}
success = true;
break;
}
case PLUGIN_READ:
{
// We do not actually read the pin state as this is already done 10x/second
// Instead we just send the last known state stored in Uservar
String log = F("SW : State ");
log += UserVar[event->BaseVarIndex];
addLog(LOG_LEVEL_INFO, log);
success = true;
break;
}
case PLUGIN_WRITE:
{
String log = "";
String command = parseString(string, 1);
if (command == F("gpio"))
{
success = true;
if (event->Par1 >= 2 && event->Par1 <= 13)
{
pinMode(event->Par1, OUTPUT);
digitalWrite(event->Par1, event->Par2);
setPinState(PLUGIN_ID_001, event->Par1, PIN_MODE_OUTPUT, event->Par2);
log = String(F("SW : GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(event->Par2);
addLog(LOG_LEVEL_INFO, log);
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_001, event->Par1, log, 0));
}
}
if (command == F("pwm"))
{
success = true;
if (event->Par1 >= 2 && event->Par1 <= 13)
{
pinMode(event->Par1, OUTPUT);
if(event->Par3 != 0)
{
byte prev_mode;
uint16_t prev_value;
getPinState(PLUGIN_ID_001, event->Par1, &prev_mode, &prev_value);
if(prev_mode != PIN_MODE_PWM)
prev_value = 0;
int32_t step_value = ((event->Par2 - prev_value) << 12) / event->Par3;
int32_t curr_value = prev_value << 12;
int16_t new_value;
int i = event->Par3;
while(i--){
curr_value += step_value;
new_value = (uint16_t)(curr_value >> 12);
analogWrite(event->Par1, new_value);
delay(1);
}
}
analogWrite(event->Par1, event->Par2);
setPinState(PLUGIN_ID_001, event->Par1, PIN_MODE_PWM, event->Par2);
log = String(F("SW : GPIO ")) + String(event->Par1) + String(F(" Set PWM to ")) + String(event->Par2);
addLog(LOG_LEVEL_INFO, log);
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_001, event->Par1, log, 0));
}
}
if (command == F("pulse"))
{
success = true;
if (event->Par1 >= 2 && event->Par1 <= 13)
{
pinMode(event->Par1, OUTPUT);
digitalWrite(event->Par1, event->Par2);
delay(event->Par3);
digitalWrite(event->Par1, !event->Par2);
setPinState(PLUGIN_ID_001, event->Par1, PIN_MODE_OUTPUT, event->Par2);
log = String(F("SW : GPIO ")) + String(event->Par1) + String(F(" Pulsed for ")) + String(event->Par3) + String(F(" mS"));
addLog(LOG_LEVEL_INFO, log);
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_001, event->Par1, log, 0));
}
}
if (command == F("longpulse"))
{
success = true;
if (event->Par1 >= 2 && event->Par1 <= 13)
{
pinMode(event->Par1, OUTPUT);
digitalWrite(event->Par1, event->Par2);
setPinState(PLUGIN_ID_001, event->Par1, PIN_MODE_OUTPUT, event->Par2);
setSystemTimer(event->Par3 * 1000, PLUGIN_ID_001, event->Par1, !event->Par2, 0);
log = String(F("SW : GPIO ")) + String(event->Par1) + String(F(" Pulse set for ")) + String(event->Par3) + String(F(" S"));
addLog(LOG_LEVEL_INFO, log);
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_001, event->Par1, log, 0));
}
}
if (command == F("servo"))
{
success = true;
if (event->Par1 >= 0 && event->Par1 <= 2)
switch (event->Par1)
{
case 1:
// todo myservo1.attach(event->Par2);
// todo myservo1.write(event->Par3);
break;
case 2:
// todo myservo2.attach(event->Par2);
// todo myservo2.write(event->Par3);
break;
}
setPinState(PLUGIN_ID_001, event->Par2, PIN_MODE_SERVO, event->Par3);
log = String(F("SW : GPIO ")) + String(event->Par2) + String(F(" Servo set to ")) + String(event->Par3);
addLog(LOG_LEVEL_INFO, log);
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_001, event->Par2, log, 0));
}
if (command == F("status"))
{
if (parseString(string, 2) == F("gpio"))
{
success = true;
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_001, event->Par2, dummyString, 0));
}
}
if (command == F("inputswitchstate"))
{
success = true;
UserVar[event->Par1 * VARS_PER_TASK] = event->Par2;
outputstate[event->Par1] = event->Par2;
}
break;
}
case PLUGIN_TIMER_IN:
{
digitalWrite(event->Par1, event->Par2);
setPinState(PLUGIN_ID_001, event->Par1, PIN_MODE_OUTPUT, event->Par2);
break;
}
}
return success;
}

62
_P002_ADC.ino Normal file
View File

@@ -0,0 +1,62 @@
//#######################################################################################################
//#################################### Plugin 002: Analog ###############################################
//#######################################################################################################
// Adapted from ESP Easy, changes:
// WebServer.arg() -> WebServerarg()
// port selection as we have a lot of analog ports here...
#define PLUGIN_002
#define PLUGIN_ID_002 2
#define PLUGIN_NAME_002 "Analog input"
#define PLUGIN_VALUENAME1_002 "Analog"
boolean Plugin_002(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
switch (function)
{
case PLUGIN_DEVICE_ADD:
{
Device[++deviceCount].Number = PLUGIN_ID_002;
Device[deviceCount].Type = DEVICE_TYPE_ANALOG;
Device[deviceCount].VType = SENSOR_TYPE_SINGLE;
Device[deviceCount].Ports = 1;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].FormulaOption = true;
Device[deviceCount].ValueCount = 1;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = true;
break;
}
case PLUGIN_GET_DEVICENAME:
{
string = F(PLUGIN_NAME_002);
break;
}
case PLUGIN_GET_DEVICEVALUENAMES:
{
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_002));
break;
}
case PLUGIN_READ:
{
int value = analogRead(Settings.TaskDevicePort[event->TaskIndex]);
UserVar[event->BaseVarIndex] = (float)value;
String log = F("ADC : Analog port ");
log += Settings.TaskDevicePort[event->TaskIndex];
log += F(" value: ");
log += value;
addLog(LOG_LEVEL_INFO,log);
success = true;
break;
}
}
return success;
}

260
_P003_Pulse.ino Normal file
View File

@@ -0,0 +1,260 @@
//#######################################################################################################
//#################################### Plugin 003: Pulse ###############################################
//#######################################################################################################
// Adapted from ESP Easy, changes:
// WebServer.arg() -> WebServerarg()
#define PLUGIN_003
#define PLUGIN_ID_003 3
#define PLUGIN_NAME_003 "Pulse Counter"
#define PLUGIN_VALUENAME1_003 "Count"
#define PLUGIN_VALUENAME2_003 "Total"
#define PLUGIN_VALUENAME3_003 "Time"
unsigned long Plugin_003_pulseCounter[TASKS_MAX];
unsigned long Plugin_003_pulseTotalCounter[TASKS_MAX];
unsigned long Plugin_003_pulseTime[TASKS_MAX];
unsigned long Plugin_003_pulseTimePrevious[TASKS_MAX];
boolean Plugin_003(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
switch (function)
{
case PLUGIN_DEVICE_ADD:
{
Device[++deviceCount].Number = PLUGIN_ID_003;
Device[deviceCount].Type = DEVICE_TYPE_SINGLE;
Device[deviceCount].VType = SENSOR_TYPE_SINGLE;
Device[deviceCount].Ports = 0;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].FormulaOption = true;
Device[deviceCount].ValueCount = 3;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = true;
break;
}
case PLUGIN_GET_DEVICENAME:
{
string = F(PLUGIN_NAME_003);
break;
}
case PLUGIN_GET_DEVICEVALUENAMES:
{
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_003));
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_003));
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_003));
break;
}
case PLUGIN_WEBFORM_LOAD:
{
char tmpString[128];
sprintf_P(tmpString, PSTR("<TR><TD>Debounce Time (mSec):<TD><input type='text' name='plugin_003' value='%u'>"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
string += tmpString;
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
String options[3];
options[0] = F("Delta");
options[1] = F("Delta/Total/Time");
options[2] = F("Total");
int optionValues[3];
optionValues[0] = 0;
optionValues[1] = 1;
optionValues[2] = 2;
string += F("<TR><TD>Counter Type:<TD><select name='plugin_003_countertype'>");
for (byte x = 0; x < 3; x++)
{
string += F("<option value='");
string += optionValues[x];
string += "'";
if (choice == optionValues[x])
string += F(" selected");
string += ">";
string += options[x];
string += F("</option>");
}
string += F("</select>");
if (choice !=0)
string += F("<span style=\"color:red\">Total count is not persistent!</span>");
success = true;
break;
}
case PLUGIN_WEBFORM_SAVE:
{
String plugin1 = WebServerarg(F("plugin_003"));
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
String plugin2 = WebServerarg(F("plugin_003_countertype"));
Settings.TaskDevicePluginConfig[event->TaskIndex][1] = plugin2.toInt();
success = true;
break;
}
case PLUGIN_WEBFORM_SHOW_VALUES:
{
string += F("<div class=\"div_l\">");
string += ExtraTaskSettings.TaskDeviceValueNames[0];
string += F(":</div><div class=\"div_r\">");
string += Plugin_003_pulseCounter[event->TaskIndex];
string += F("</div><div class=\"div_br\"></div><div class=\"div_l\">");
string += ExtraTaskSettings.TaskDeviceValueNames[1];
string += F(":</div><div class=\"div_r\">");
string += Plugin_003_pulseTotalCounter[event->TaskIndex];
string += F("</div><div class=\"div_br\"></div><div class=\"div_l\">");
string += ExtraTaskSettings.TaskDeviceValueNames[2];
string += F(":</div><div class=\"div_r\">");
string += Plugin_003_pulseTime[event->TaskIndex];
string += F("</div>");
success = true;
break;
}
case PLUGIN_INIT:
{
String log = F("INIT : Pulse ");
log += Settings.TaskDevicePin1[event->TaskIndex];
addLog(LOG_LEVEL_INFO,log);
pinMode(Settings.TaskDevicePin1[event->TaskIndex], INPUT_PULLUP);
Plugin_003_pulseinit(Settings.TaskDevicePin1[event->TaskIndex], event->TaskIndex);
success = true;
break;
}
case PLUGIN_READ:
{
UserVar[event->BaseVarIndex] = Plugin_003_pulseCounter[event->TaskIndex];
UserVar[event->BaseVarIndex+1] = Plugin_003_pulseTotalCounter[event->TaskIndex];
UserVar[event->BaseVarIndex+2] = Plugin_003_pulseTime[event->TaskIndex];
switch (Settings.TaskDevicePluginConfig[event->TaskIndex][1])
{
case 0:
{
event->sensorType = SENSOR_TYPE_SINGLE;
UserVar[event->BaseVarIndex] = Plugin_003_pulseCounter[event->TaskIndex];
break;
}
case 1:
{
event->sensorType = SENSOR_TYPE_TRIPLE;
UserVar[event->BaseVarIndex] = Plugin_003_pulseCounter[event->TaskIndex];
UserVar[event->BaseVarIndex+1] = Plugin_003_pulseTotalCounter[event->TaskIndex];
UserVar[event->BaseVarIndex+2] = Plugin_003_pulseTime[event->TaskIndex];
break;
}
case 2:
{
event->sensorType = SENSOR_TYPE_SINGLE;
UserVar[event->BaseVarIndex] = Plugin_003_pulseTotalCounter[event->TaskIndex];
break;
}
}
Plugin_003_pulseCounter[event->TaskIndex] = 0;
success = true;
break;
}
}
return success;
}
/*********************************************************************************************\
* Check Pulse Counters (called from irq handler)
\*********************************************************************************************/
void Plugin_003_pulsecheck(byte Index)
{
unsigned long PulseTime=millis() - Plugin_003_pulseTimePrevious[Index];
if(PulseTime > Settings.TaskDevicePluginConfig[Index][0]) // check with debounce time for this task
{
Plugin_003_pulseCounter[Index]++;
Plugin_003_pulseTotalCounter[Index]++;
Plugin_003_pulseTime[Index] = PulseTime;
Plugin_003_pulseTimePrevious[Index]=millis();
}
}
/*********************************************************************************************\
* Pulse Counter IRQ handlers
\*********************************************************************************************/
void Plugin_003_pulse_interrupt1()
{
Plugin_003_pulsecheck(0);
}
void Plugin_003_pulse_interrupt2()
{
Plugin_003_pulsecheck(1);
}
void Plugin_003_pulse_interrupt3()
{
Plugin_003_pulsecheck(2);
}
void Plugin_003_pulse_interrupt4()
{
Plugin_003_pulsecheck(3);
}
void Plugin_003_pulse_interrupt5()
{
Plugin_003_pulsecheck(4);
}
void Plugin_003_pulse_interrupt6()
{
Plugin_003_pulsecheck(5);
}
void Plugin_003_pulse_interrupt7()
{
Plugin_003_pulsecheck(6);
}
void Plugin_003_pulse_interrupt8()
{
Plugin_003_pulsecheck(7);
}
/*********************************************************************************************\
* Init Pulse Counters
\*********************************************************************************************/
void Plugin_003_pulseinit(byte Par1, byte Index)
{
// Init IO pins
String log = F("PULSE: Init");
addLog(LOG_LEVEL_INFO,log);
switch (Index)
{
case 0:
attachInterrupt(digitalPinToInterrupt(Par1), Plugin_003_pulse_interrupt1, FALLING);
break;
case 1:
attachInterrupt(digitalPinToInterrupt(Par1), Plugin_003_pulse_interrupt2, FALLING);
break;
case 2:
attachInterrupt(digitalPinToInterrupt(Par1), Plugin_003_pulse_interrupt3, FALLING);
break;
case 3:
attachInterrupt(digitalPinToInterrupt(Par1), Plugin_003_pulse_interrupt4, FALLING);
break;
case 4:
attachInterrupt(digitalPinToInterrupt(Par1), Plugin_003_pulse_interrupt5, FALLING);
break;
case 5:
attachInterrupt(digitalPinToInterrupt(Par1), Plugin_003_pulse_interrupt6, FALLING);
break;
case 6:
attachInterrupt(digitalPinToInterrupt(Par1), Plugin_003_pulse_interrupt7, FALLING);
break;
case 7:
attachInterrupt(digitalPinToInterrupt(Par1), Plugin_003_pulse_interrupt8, FALLING);
break;
}
}

115
_P033_Dummy.ino Normal file
View File

@@ -0,0 +1,115 @@
//#######################################################################################################
//#################################### Plugin 033: Dummy ################################################
//#######################################################################################################
// Adapted from ESP Easy, changes:
// WebServer.arg() -> WebServerarg()
#define PLUGIN_033
#define PLUGIN_ID_033 33
#define PLUGIN_NAME_033 "Dummy Device"
#define PLUGIN_VALUENAME1_033 "Dummy"
boolean Plugin_033(byte function, struct EventStruct *event, String& string)
{
boolean success = false;
switch (function)
{
case PLUGIN_DEVICE_ADD:
{
Device[++deviceCount].Number = PLUGIN_ID_033;
Device[deviceCount].Type = DEVICE_TYPE_DUMMY;
Device[deviceCount].VType = SENSOR_TYPE_SINGLE;
Device[deviceCount].Ports = 0;
Device[deviceCount].PullUpOption = false;
Device[deviceCount].InverseLogicOption = false;
Device[deviceCount].FormulaOption = false;
Device[deviceCount].DecimalsOnly = true;
Device[deviceCount].ValueCount = 4;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].GlobalSyncOption = true;
break;
}
case PLUGIN_GET_DEVICENAME:
{
string = F(PLUGIN_NAME_033);
break;
}
case PLUGIN_GET_DEVICEVALUENAMES:
{
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_033));
break;
}
case PLUGIN_WEBFORM_LOAD:
{
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
String options[9];
options[0] = F("SENSOR_TYPE_SINGLE");
options[1] = F("SENSOR_TYPE_TEMP_HUM");
options[2] = F("SENSOR_TYPE_TEMP_BARO");
options[3] = F("SENSOR_TYPE_TEMP_HUM_BARO");
options[4] = F("SENSOR_TYPE_DUAL");
options[5] = F("SENSOR_TYPE_TRIPLE");
options[6] = F("SENSOR_TYPE_QUAD");
options[7] = F("SENSOR_TYPE_SWITCH");
options[8] = F("SENSOR_TYPE_DIMMER");
int optionValues[9];
optionValues[0] = SENSOR_TYPE_SINGLE;
optionValues[1] = SENSOR_TYPE_TEMP_HUM;
optionValues[2] = SENSOR_TYPE_TEMP_BARO;
optionValues[3] = SENSOR_TYPE_TEMP_HUM_BARO;
optionValues[4] = SENSOR_TYPE_DUAL;
optionValues[5] = SENSOR_TYPE_TRIPLE;
optionValues[6] = SENSOR_TYPE_QUAD;
optionValues[7] = SENSOR_TYPE_SWITCH;
optionValues[8] = SENSOR_TYPE_DIMMER;
string += F("<TR><TD>Simulate Data Type:<TD><select name='plugin_033_sensortype'>");
for (byte x = 0; x < 9; x++)
{
string += F("<option value='");
string += optionValues[x];
string += "'";
if (choice == optionValues[x])
string += F(" selected");
string += ">";
string += options[x];
string += F("</option>");
}
string += F("</select>");
success = true;
break;
}
case PLUGIN_WEBFORM_SAVE:
{
String plugin1 = WebServerarg(F("plugin_033_sensortype"));
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
success = true;
break;
}
case PLUGIN_READ:
{
event->sensorType =Settings.TaskDevicePluginConfig[event->TaskIndex][0];
for (byte x=0; x<4;x++)
{
String log = F("Dummy: value ");
log += x+1;
log += F(": ");
log += UserVar[event->BaseVarIndex+x];
addLog(LOG_LEVEL_INFO,log);
}
success = true;
break;
}
}
return success;
}

142
__CPlugin.ino Normal file
View File

@@ -0,0 +1,142 @@
//********************************************************************************
// Initialize all Controller CPlugins that where defined earlier
// and initialize the function call pointer into the CCPlugin array
//********************************************************************************
void CPluginInit(void)
{
byte x;
// Clear pointer table for all plugins
for (x = 0; x < CPLUGIN_MAX; x++)
{
CPlugin_ptr[x] = 0;
CPlugin_id[x] = 0;
}
x = 0;
#ifdef CPLUGIN_001
CPlugin_id[x] = 1; CPlugin_ptr[x++] = &CPlugin_001;
#endif
#ifdef CPLUGIN_002
CPlugin_id[x] = 2; CPlugin_ptr[x++] = &CPlugin_002;
#endif
#ifdef CPLUGIN_003
CPlugin_id[x] = 3; CPlugin_ptr[x++] = &CPlugin_003;
#endif
#ifdef CPLUGIN_004
CPlugin_id[x] = 4; CPlugin_ptr[x++] = &CPlugin_004;
#endif
#ifdef CPLUGIN_005
CPlugin_id[x] = 5; CPlugin_ptr[x++] = &CPlugin_005;
#endif
#ifdef CPLUGIN_006
CPlugin_id[x] = 6; CPlugin_ptr[x++] = &CPlugin_006;
#endif
#ifdef CPLUGIN_007
CPlugin_id[x] = 7; CPlugin_ptr[x++] = &CPlugin_007;
#endif
#ifdef CPLUGIN_008
CPlugin_id[x] = 8; CPlugin_ptr[x++] = &CPlugin_008;
#endif
#ifdef CPLUGIN_009
CPlugin_id[x] = 9; CPlugin_ptr[x++] = &CPlugin_009;
#endif
#ifdef CPLUGIN_010
CPlugin_id[x] = 10; CPlugin_ptr[x++] = &CPlugin_010;
#endif
#ifdef CPLUGIN_011
CPlugin_id[x] = 11; CPlugin_ptr[x++] = &CPlugin_011;
#endif
#ifdef CPLUGIN_012
CPlugin_id[x] = 12; CPlugin_ptr[x++] = &CPlugin_012;
#endif
#ifdef CPLUGIN_013
CPlugin_id[x] = 13; CPlugin_ptr[x++] = &CPlugin_013;
#endif
#ifdef CPLUGIN_014
CPlugin_id[x] = 14; CPlugin_ptr[x++] = &CPlugin_014;
#endif
#ifdef CPLUGIN_015
CPlugin_id[x] = 15; CPlugin_ptr[x++] = &CPlugin_015;
#endif
#ifdef CPLUGIN_016
CPlugin_id[x] = 16; CPlugin_ptr[x++] = &CPlugin_016;
#endif
#ifdef CPLUGIN_017
CPlugin_id[x] = 17; CPlugin_ptr[x++] = &CPlugin_017;
#endif
#ifdef CPLUGIN_018
CPlugin_id[x] = 18; CPlugin_ptr[x++] = &CPlugin_018;
#endif
#ifdef CPLUGIN_019
CPlugin_id[x] = 19; CPlugin_ptr[x++] = &CPlugin_019;
#endif
#ifdef CPLUGIN_020
CPlugin_id[x] = 20; CPlugin_ptr[x++] = &CPlugin_020;
#endif
#ifdef CPLUGIN_021
CPlugin_id[x] = 21; CPlugin_ptr[x++] = &CPlugin_021;
#endif
#ifdef CPLUGIN_022
CPlugin_id[x] = 22; CPlugin_ptr[x++] = &CPlugin_022;
#endif
#ifdef CPLUGIN_023
CPlugin_id[x] = 23; CPlugin_ptr[x++] = &CPlugin_023;
#endif
#ifdef CPLUGIN_024
CPlugin_id[x] = 24; CPlugin_ptr[x++] = &CPlugin_024;
#endif
#ifdef CPLUGIN_025
CPlugin_id[x] = 25; CPlugin_ptr[x++] = &CPlugin_025;
#endif
CPluginCall(CPLUGIN_PROTOCOL_ADD, 0);
}
byte CPluginCall(byte Function, struct EventStruct *event)
{
int x;
struct EventStruct TempEvent;
if (event == 0)
event=&TempEvent;
switch (Function)
{
// Unconditional calls to all plugins
case CPLUGIN_PROTOCOL_ADD:
for (x = 0; x < CPLUGIN_MAX; x++)
if (CPlugin_id[x] != 0)
CPlugin_ptr[x](Function, event, dummyString);
return true;
break;
}
return false;
}

1156
__Plugin.ino Normal file

File diff suppressed because it is too large Load Diff

13
__ReleaseNotes.ino Normal file
View File

@@ -0,0 +1,13 @@
// R147 08-12-2016
// First alpha version (Proof Of Concept!)
// Based on ESP Easy R147
// Limited on several features:
// SSDP is not implemented
// No SPIFFS but using SD card instead. So it does not run without an SD card fitted!
// Reused existing UDP socket for NTP, due to shortage of W5100 sockets.
// Known issues:
// WD message was incorrect, failures reported as freemem, freemem is missing, workaround is using string object...
// when timed reboot, system does not reboot ???