mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Compare commits
28
Commits
mega-20190409
...
legacy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f1dd6f6b8 | ||
|
|
8f7fd936b8 | ||
|
|
884205feea | ||
|
|
21bd7d73d1 | ||
|
|
cd60d32966 | ||
|
|
e53a92cd82 | ||
|
|
c618d37f0b | ||
|
|
f450aa4a01 | ||
|
|
32b9f40d02 | ||
|
|
ae082fd2bb | ||
|
|
315adbc42f | ||
|
|
face8a8bfc | ||
|
|
fe1025e470 | ||
|
|
4f6d7d1b82 | ||
|
|
894a01a534 | ||
|
|
a3317bcde1 | ||
|
|
6d9cbb3988 | ||
|
|
aa9cf0a795 | ||
|
|
d8244e1726 | ||
|
|
cabcd33a63 | ||
|
|
3140833abb | ||
|
|
890351b920 | ||
|
|
0868b983d4 | ||
|
|
997e612c97 | ||
|
|
aadb6e0521 | ||
|
|
fcd5c1ec01 | ||
|
|
e9c55e8685 | ||
|
|
72c57aa2e3 |
@@ -0,0 +1 @@
|
||||
Version.h
|
||||
+55
-24
@@ -27,12 +27,12 @@ void ExecuteCommand(byte source, const char *Line)
|
||||
Serial.print(int(loopCounterLast / 30));
|
||||
Serial.println(F(")"));
|
||||
}
|
||||
|
||||
|
||||
if (strcasecmp_P(Command, PSTR("SerialFloat")) == 0)
|
||||
{
|
||||
success = true;
|
||||
pinMode(1,INPUT);
|
||||
pinMode(3,INPUT);
|
||||
pinMode(1, INPUT);
|
||||
pinMode(3, INPUT);
|
||||
delay(60000);
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ void ExecuteCommand(byte source, const char *Line)
|
||||
if (strcasecmp_P(Command, PSTR("TaskRun")) == 0)
|
||||
{
|
||||
success = true;
|
||||
SensorSendTask(Par1 -1);
|
||||
SensorSendTask(Par1 - 1);
|
||||
}
|
||||
|
||||
if (strcasecmp_P(Command, PSTR("TimerSet")) == 0)
|
||||
@@ -152,7 +152,7 @@ void ExecuteCommand(byte source, const char *Line)
|
||||
int index = event.indexOf(',');
|
||||
if (index > 0)
|
||||
{
|
||||
event = event.substring(index+1);
|
||||
event = event.substring(index + 1);
|
||||
SendUDPCommand(Par1, (char*)event.c_str(), event.length());
|
||||
}
|
||||
}
|
||||
@@ -165,19 +165,19 @@ void ExecuteCommand(byte source, const char *Line)
|
||||
int index = event.indexOf(',');
|
||||
if (index > 0)
|
||||
{
|
||||
String topic = event.substring(0,index);
|
||||
String value = event.substring(index+1);
|
||||
MQTTclient.publish(topic.c_str(), value.c_str(),Settings.MQTTRetainFlag);
|
||||
String topic = event.substring(0, index);
|
||||
String value = event.substring(index + 1);
|
||||
MQTTclient.publish(topic.c_str(), value.c_str(), Settings.MQTTRetainFlag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 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);
|
||||
@@ -191,16 +191,16 @@ void ExecuteCommand(byte source, const char *Line)
|
||||
{
|
||||
success = true;
|
||||
String strLine = Line;
|
||||
String host = parseString(strLine,2);
|
||||
String port = parseString(strLine,3);
|
||||
int pathpos = getParamStartPos(strLine,4);
|
||||
String host = parseString(strLine, 2);
|
||||
String port = parseString(strLine, 3);
|
||||
int pathpos = getParamStartPos(strLine, 4);
|
||||
String path = strLine.substring(pathpos);
|
||||
WiFiClient 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");
|
||||
"Host: " + host + "\r\n" +
|
||||
"Connection: close\r\n\r\n");
|
||||
|
||||
unsigned long timer = millis() + 200;
|
||||
while (!client.available() && millis() < timer)
|
||||
@@ -217,6 +217,38 @@ void ExecuteCommand(byte source, const char *Line)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PLUGIN_BUILD_TESTING
|
||||
// ****************************************
|
||||
// special commands for Blynk
|
||||
// ****************************************
|
||||
|
||||
if (strcasecmp(Command, "BlynkGet") == 0)
|
||||
{
|
||||
String event = Line;
|
||||
event = event.substring(9);
|
||||
int index = event.indexOf(',');
|
||||
if (index > 0)
|
||||
{
|
||||
int index = event.lastIndexOf(',');
|
||||
String blynkcommand = event.substring(index+1);
|
||||
float value = 0;
|
||||
if (Blynk_get(blynkcommand, &value))
|
||||
{
|
||||
UserVar[(VARS_PER_TASK * (Par1 - 1)) + Par2 - 1] = value;
|
||||
}
|
||||
else
|
||||
status = F("Error getting data");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Blynk_get(event))
|
||||
{
|
||||
status = F("Error getting data");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
// ****************************************
|
||||
// configure settings commands
|
||||
@@ -238,19 +270,19 @@ void ExecuteCommand(byte source, const char *Line)
|
||||
success = true;
|
||||
WifiScan();
|
||||
}
|
||||
|
||||
|
||||
if (strcasecmp_P(Command, PSTR("WifiConnect")) == 0)
|
||||
{
|
||||
success = true;
|
||||
WifiConnect(1);
|
||||
}
|
||||
|
||||
|
||||
if (strcasecmp_P(Command, PSTR("WifiDisconnect")) == 0)
|
||||
{
|
||||
success = true;
|
||||
WifiDisconnect();
|
||||
}
|
||||
|
||||
|
||||
if (strcasecmp_P(Command, PSTR("Reboot")) == 0)
|
||||
{
|
||||
success = true;
|
||||
@@ -354,12 +386,11 @@ void ExecuteCommand(byte source, const char *Line)
|
||||
}
|
||||
|
||||
yield();
|
||||
|
||||
|
||||
if (success)
|
||||
status += F("\nOk");
|
||||
else
|
||||
else
|
||||
status += F("\nUnknown command!");
|
||||
SendStatus(source,status);
|
||||
SendStatus(source, status);
|
||||
yield();
|
||||
}
|
||||
|
||||
|
||||
+9
-5
@@ -187,6 +187,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
|
||||
@@ -218,6 +219,7 @@
|
||||
#define BOOT_CAUSE_COLD_BOOT 1
|
||||
#define BOOT_CAUSE_EXT_WD 10
|
||||
|
||||
#include "Version.h"
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <DNSServer.h>
|
||||
#include <WiFiUdp.h>
|
||||
@@ -347,7 +349,7 @@ struct SettingsStruct
|
||||
unsigned long ConnectionFailuresThreshold;
|
||||
int16_t TimeZone;
|
||||
boolean MQTTRetainFlag;
|
||||
boolean InitSPI;
|
||||
boolean InitSPI;
|
||||
} Settings;
|
||||
|
||||
struct ExtraTaskSettingsStruct
|
||||
@@ -497,6 +499,9 @@ unsigned long flashWrites = 0;
|
||||
|
||||
String eventBuffer = "";
|
||||
|
||||
// Blynk_get prototype
|
||||
boolean Blynk_get(String command,float *data = NULL );
|
||||
|
||||
/*********************************************************************************************\
|
||||
* SETUP
|
||||
\*********************************************************************************************/
|
||||
@@ -612,7 +617,7 @@ void setup()
|
||||
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
|
||||
@@ -811,7 +816,7 @@ void runEach30Seconds()
|
||||
loopCounterMax = loopCounterLast;
|
||||
|
||||
WifiCheck();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -895,7 +900,7 @@ void SensorSendTask(byte TaskIndex)
|
||||
if (success)
|
||||
{
|
||||
for (byte varNr = 0; varNr < VARS_PER_TASK; varNr++)
|
||||
{
|
||||
{
|
||||
if (ExtraTaskSettings.TaskDeviceFormula[varNr][0] != 0)
|
||||
{
|
||||
String spreValue = String(preValue[varNr]);
|
||||
@@ -1017,4 +1022,3 @@ void backgroundtasks()
|
||||
checkUDP();
|
||||
yield();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ byte getValueCountFromSensorType(byte sensorType)
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM_BARO:
|
||||
case SENSOR_TYPE_TRIPLE:
|
||||
#ifdef PLUGIN_186
|
||||
case SENSOR_TYPE_WIND:
|
||||
#endif
|
||||
valueCount = 3;
|
||||
break;
|
||||
case SENSOR_TYPE_QUAD:
|
||||
@@ -1327,7 +1330,7 @@ String parseTemplate(String &tmpString, byte lineSize)
|
||||
newString.replace("%ip%", strIP);
|
||||
|
||||
newString.replace("%sysload%", String(100 - (100 * loopCounterLast / loopCounterMax)));
|
||||
|
||||
|
||||
// padding spaces
|
||||
while (newString.length() < lineSize)
|
||||
newString += " ";
|
||||
@@ -2244,3 +2247,177 @@ void createRuleEvents(byte TaskIndex)
|
||||
rulesProcessing(eventString);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef PLUGIN_BUILD_TESTING
|
||||
|
||||
#define isdigit(n) (n >= '0' && n <= '9')
|
||||
|
||||
/********************************************************************************************\
|
||||
Generate a tone of specified frequency on pin
|
||||
\*********************************************************************************************/
|
||||
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) {
|
||||
analogWriteFreq(frequency);
|
||||
analogWrite(_pin,100);
|
||||
delay(duration);
|
||||
analogWrite(_pin,0);
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
Play RTTTL string on specified pin
|
||||
\*********************************************************************************************/
|
||||
void play_rtttl(uint8_t _pin, char *p )
|
||||
{
|
||||
#define OCTAVE_OFFSET 0
|
||||
// Absolutely no error checking in here
|
||||
|
||||
int notes[] = { 0,
|
||||
262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
|
||||
523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
|
||||
1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976,
|
||||
2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951
|
||||
};
|
||||
|
||||
|
||||
|
||||
byte default_dur = 4;
|
||||
byte default_oct = 6;
|
||||
int bpm = 63;
|
||||
int num;
|
||||
long wholenote;
|
||||
long duration;
|
||||
byte note;
|
||||
byte scale;
|
||||
|
||||
// format: d=N,o=N,b=NNN:
|
||||
// find the start (skip name, etc)
|
||||
|
||||
while(*p != ':') p++; // ignore name
|
||||
p++; // skip ':'
|
||||
|
||||
// get default duration
|
||||
if(*p == 'd')
|
||||
{
|
||||
p++; p++; // skip "d="
|
||||
num = 0;
|
||||
while(isdigit(*p))
|
||||
{
|
||||
num = (num * 10) + (*p++ - '0');
|
||||
}
|
||||
if(num > 0) default_dur = num;
|
||||
p++; // skip comma
|
||||
}
|
||||
|
||||
// get default octave
|
||||
if(*p == 'o')
|
||||
{
|
||||
p++; p++; // skip "o="
|
||||
num = *p++ - '0';
|
||||
if(num >= 3 && num <=7) default_oct = num;
|
||||
p++; // skip comma
|
||||
}
|
||||
|
||||
// get BPM
|
||||
if(*p == 'b')
|
||||
{
|
||||
p++; p++; // skip "b="
|
||||
num = 0;
|
||||
while(isdigit(*p))
|
||||
{
|
||||
num = (num * 10) + (*p++ - '0');
|
||||
}
|
||||
bpm = num;
|
||||
p++; // skip colon
|
||||
}
|
||||
|
||||
// BPM usually expresses the number of quarter notes per minute
|
||||
wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole note (in milliseconds)
|
||||
|
||||
// now begin note loop
|
||||
while(*p)
|
||||
{
|
||||
// first, get note duration, if available
|
||||
num = 0;
|
||||
while(isdigit(*p))
|
||||
{
|
||||
num = (num * 10) + (*p++ - '0');
|
||||
}
|
||||
|
||||
if (num) duration = wholenote / num;
|
||||
else duration = wholenote / default_dur; // we will need to check if we are a dotted note after
|
||||
|
||||
// now get the note
|
||||
note = 0;
|
||||
|
||||
switch(*p)
|
||||
{
|
||||
case 'c':
|
||||
note = 1;
|
||||
break;
|
||||
case 'd':
|
||||
note = 3;
|
||||
break;
|
||||
case 'e':
|
||||
note = 5;
|
||||
break;
|
||||
case 'f':
|
||||
note = 6;
|
||||
break;
|
||||
case 'g':
|
||||
note = 8;
|
||||
break;
|
||||
case 'a':
|
||||
note = 10;
|
||||
break;
|
||||
case 'b':
|
||||
note = 12;
|
||||
break;
|
||||
case 'p':
|
||||
default:
|
||||
note = 0;
|
||||
}
|
||||
p++;
|
||||
|
||||
// now, get optional '#' sharp
|
||||
if(*p == '#')
|
||||
{
|
||||
note++;
|
||||
p++;
|
||||
}
|
||||
|
||||
// now, get optional '.' dotted note
|
||||
if(*p == '.')
|
||||
{
|
||||
duration += duration/2;
|
||||
p++;
|
||||
}
|
||||
|
||||
// now, get scale
|
||||
if(isdigit(*p))
|
||||
{
|
||||
scale = *p - '0';
|
||||
p++;
|
||||
}
|
||||
else
|
||||
{
|
||||
scale = default_oct;
|
||||
}
|
||||
|
||||
scale += OCTAVE_OFFSET;
|
||||
|
||||
if(*p == ',')
|
||||
p++; // skip comma for next note (or we may be at the end)
|
||||
|
||||
// now play the note
|
||||
if(note)
|
||||
{
|
||||
tone(_pin, notes[(scale - 4) * 12 + note], duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
delay(duration/10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# ESPEasy
|
||||
Easy MultiSensor device based on ESP8266
|
||||
# ESPEasy legacy branch
|
||||
|
||||
This is where development takes place. Beware that latest versions may be unstable.
|
||||
**MASTER**
|
||||
:warning:This is the ESPEasy legacy branch which isn't developed anymore.:warning:
|
||||
|
||||
Stable versions including libraries are currently on SoureForge:
|
||||
Current development branch: https://github.com/letscontrolit/ESPEasy/tree/mega (new features + fixes from v2.0 )
|
||||
|
||||
Next stable version: https://github.com/letscontrolit/ESPEasy/tree/v2.0 (fixes only)
|
||||
|
||||
http://sourceforge.net/projects/espeasy/
|
||||
|
||||
Wiki: http://www.esp8266.nu
|
||||
Forum: http://www.esp8266.nu/forum
|
||||
|
||||
+17
-3
@@ -210,6 +210,21 @@ void handle_root() {
|
||||
reply += F(" ");
|
||||
reply += F(BUILD_NOTES);
|
||||
|
||||
reply += F("<TR><TD>GIT version:<TD>");
|
||||
reply += F(BUILD_GIT);
|
||||
|
||||
reply += F("<TR><TD>Plugin sets:<TD>");
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
reply += F("Normal, Testing, Development");
|
||||
#elif PLUGIN_BUILD_TESTING
|
||||
reply += F("Normal, Testing");
|
||||
#elif PLUGIN_BUILD_NORMAL
|
||||
reply += F("Normal");
|
||||
#else
|
||||
reply += F("Minimal");
|
||||
#endif
|
||||
|
||||
|
||||
reply += F("<TR><TD>Core Version:<TD>");
|
||||
reply += ESP.getCoreVersion();
|
||||
|
||||
@@ -823,11 +838,11 @@ void handle_devices() {
|
||||
taskdevicevaluename[varNr].toCharArray(tmpString, 41);
|
||||
strcpy(ExtraTaskSettings.TaskDeviceValueNames[varNr], tmpString);
|
||||
}
|
||||
|
||||
|
||||
TempEvent.TaskIndex = index - 1;
|
||||
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0) // if field set empty, reload defaults
|
||||
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, &TempEvent, dummyString);
|
||||
|
||||
|
||||
PluginCall(PLUGIN_WEBFORM_SAVE, &TempEvent, dummyString);
|
||||
}
|
||||
SaveTaskSettings(index - 1);
|
||||
@@ -2693,4 +2708,3 @@ String URLEncode(const char* msg)
|
||||
}
|
||||
return encodedMsg;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
string = F(CPLUGIN_NAME_001);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case CPLUGIN_PROTOCOL_SEND:
|
||||
{
|
||||
String authHeader = "";
|
||||
@@ -39,7 +39,7 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
auth += SecuritySettings.ControllerPassword;
|
||||
authHeader = "Authorization: Basic " + encoder.encode(auth) + " \r\n";
|
||||
}
|
||||
|
||||
|
||||
char log[80];
|
||||
boolean success = false;
|
||||
char host[20];
|
||||
@@ -80,13 +80,14 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
url += toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
|
||||
url += ";";
|
||||
url += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
|
||||
break;
|
||||
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";
|
||||
url += ";";
|
||||
url += humStat(UserVar[event->BaseVarIndex + 1]);
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_BARO: // temp + hum + hum_stat + bar + bar_fore, used for BMP085
|
||||
url += F("&svalue=");
|
||||
@@ -100,7 +101,9 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
url += toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
|
||||
url += ";";
|
||||
url += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
|
||||
url += ";0;";
|
||||
url += ";";
|
||||
url += humStat(UserVar[event->BaseVarIndex + 1]);
|
||||
url += ";";
|
||||
url += toString(UserVar[event->BaseVarIndex + 2],ExtraTaskSettings.TaskDeviceValueDecimals[2]);
|
||||
url += ";0";
|
||||
break;
|
||||
@@ -125,6 +128,19 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
url += UserVar[event->BaseVarIndex];
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef PLUGIN_186
|
||||
case (SENSOR_TYPE_WIND):
|
||||
url += F("&svalue=");
|
||||
url += toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
|
||||
char* bearing[] = {";N;",";NNE;",";NE;",";ENE;",";E;",";ESE;",";SE;",";SSE;",";S;",";SSW;",";SW;",";WSW;",";W;",";WNW;",";NW;",";NNW;" };
|
||||
url += bearing[int(UserVar[event->BaseVarIndex] / 22.5)];
|
||||
url += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
|
||||
url += ";";
|
||||
url += toString(UserVar[event->BaseVarIndex + 2],ExtraTaskSettings.TaskDeviceValueDecimals[2]);
|
||||
url += ";0";
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
url.toCharArray(log, 80);
|
||||
@@ -132,7 +148,7 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
// This will send the request to the server
|
||||
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
|
||||
"Host: " + host + "\r\n" + authHeader +
|
||||
"Host: " + host + "\r\n" + authHeader +
|
||||
"Connection: close\r\n\r\n");
|
||||
|
||||
unsigned long timer = millis() + 200;
|
||||
@@ -165,3 +181,17 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
return success;
|
||||
}
|
||||
|
||||
int humStat(int hum){
|
||||
int lHumStat;
|
||||
if(hum<30){
|
||||
lHumStat = 2;
|
||||
}else if(hum<40){
|
||||
lHumStat = 0;
|
||||
}else if(hum<59){
|
||||
lHumStat = 1;
|
||||
}else{
|
||||
lHumStat = 3;
|
||||
|
||||
}
|
||||
return lHumStat;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
|
||||
string = F(CPLUGIN_NAME_002);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case CPLUGIN_PROTOCOL_TEMPLATE:
|
||||
{
|
||||
strcpy_P(Settings.MQTTsubscribe, PSTR("domoticz/out"));
|
||||
@@ -60,7 +60,7 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
|
||||
nvalue = nvaluealt;
|
||||
if ((int)switchtype == 0)
|
||||
switchtype = "?";
|
||||
|
||||
|
||||
for (byte x = 0; x < TASKS_MAX; x++)
|
||||
{
|
||||
if (Settings.TaskDeviceID[x] == idx)
|
||||
@@ -150,7 +150,7 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
|
||||
values += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
|
||||
values.toCharArray(str, 80);
|
||||
root["svalue"] = str;
|
||||
break;
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM: // temp + hum + hum_stat, used for DHT11
|
||||
root["nvalue"] = 0;
|
||||
values = toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
|
||||
@@ -194,6 +194,21 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
|
||||
else
|
||||
root["Set%20Level"] = UserVar[event->BaseVarIndex];
|
||||
break;
|
||||
|
||||
#ifdef PLUGIN_186
|
||||
case SENSOR_TYPE_WIND:
|
||||
values = toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
|
||||
char* bearing[] = {";N;",";NNE;",";NE;",";ENE;",";E;",";ESE;",";SE;",";SSE;",";S;",";SSW;",";SW;",";WSW;",";W;",";WNW;",";NW;",";NNW;" };
|
||||
values += bearing[int(UserVar[event->BaseVarIndex] / 22.5)];
|
||||
values += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
|
||||
values += ";";
|
||||
values += toString(UserVar[event->BaseVarIndex + 2],ExtraTaskSettings.TaskDeviceValueDecimals[2]);
|
||||
values += ";0;0";
|
||||
values.toCharArray(str, 80);
|
||||
root["svalue"] = str;
|
||||
break;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
char json[256];
|
||||
@@ -222,4 +237,3 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
#ifdef PLUGIN_BUILD_TESTING
|
||||
|
||||
//#######################################################################################################
|
||||
//########################### Controller Plugin 011: Blynk #############################################
|
||||
//#######################################################################################################
|
||||
|
||||
#define CPLUGIN_011
|
||||
#define CPLUGIN_ID_011 11
|
||||
#define CPLUGIN_NAME_011 "Blynk HTTP [TESTING]"
|
||||
|
||||
|
||||
|
||||
|
||||
boolean CPlugin_011(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
|
||||
|
||||
switch (function)
|
||||
{
|
||||
case CPLUGIN_PROTOCOL_ADD:
|
||||
{
|
||||
Protocol[++protocolCount].Number = CPLUGIN_ID_011;
|
||||
Protocol[protocolCount].usesMQTT = false;
|
||||
Protocol[protocolCount].usesAccount = false;
|
||||
Protocol[protocolCount].usesPassword = true;
|
||||
Protocol[protocolCount].defaultPort = 8443;
|
||||
break;
|
||||
}
|
||||
|
||||
case CPLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(CPLUGIN_NAME_011);
|
||||
break;
|
||||
}
|
||||
|
||||
case CPLUGIN_PROTOCOL_SEND:
|
||||
{
|
||||
|
||||
String postDataStr = "";
|
||||
|
||||
switch (event->sensorType)
|
||||
{
|
||||
case SENSOR_TYPE_SINGLE: // single value sensor, used for Dallas, BH1750, etc
|
||||
postDataStr = F("update/V") ;
|
||||
postDataStr += event->idx;
|
||||
postDataStr += F("?value=");
|
||||
postDataStr += toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
|
||||
Blynk_get(postDataStr );
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM: // dual value
|
||||
case SENSOR_TYPE_TEMP_BARO:
|
||||
case SENSOR_TYPE_DUAL:
|
||||
postDataStr = F("update/V") ;
|
||||
postDataStr += event->idx;
|
||||
postDataStr += F("?value=");
|
||||
postDataStr += toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
|
||||
Blynk_get(postDataStr );
|
||||
|
||||
postDataStr = F("update/V") ;
|
||||
postDataStr += event->idx + 1;
|
||||
postDataStr += F("?value=");
|
||||
postDataStr += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
|
||||
Blynk_get(postDataStr );
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM_BARO:
|
||||
case SENSOR_TYPE_TRIPLE:
|
||||
postDataStr = F("update/V") ;
|
||||
postDataStr += event->idx;
|
||||
postDataStr += F("?value=");
|
||||
postDataStr += toString(UserVar[event->BaseVarIndex],ExtraTaskSettings.TaskDeviceValueDecimals[0]);
|
||||
Blynk_get(postDataStr );
|
||||
|
||||
postDataStr = F("update/V") ;
|
||||
postDataStr += event->idx + 1;
|
||||
postDataStr += F("?value=");
|
||||
postDataStr += toString(UserVar[event->BaseVarIndex + 1],ExtraTaskSettings.TaskDeviceValueDecimals[1]);
|
||||
Blynk_get(postDataStr );
|
||||
|
||||
postDataStr = F("update/V") ;
|
||||
postDataStr += event->idx + 2;
|
||||
postDataStr += F("?value=");
|
||||
postDataStr += toString(UserVar[event->BaseVarIndex + 2],ExtraTaskSettings.TaskDeviceValueDecimals[2]);
|
||||
Blynk_get(postDataStr );
|
||||
|
||||
break;
|
||||
case SENSOR_TYPE_SWITCH:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
boolean Blynk_get(String command,float *data )
|
||||
{
|
||||
boolean success = false;
|
||||
char host[20];
|
||||
char log[80];
|
||||
char command_char[50];
|
||||
|
||||
command.toCharArray(command_char, 50);
|
||||
|
||||
sprintf_P(host, PSTR("%u.%u.%u.%u"), Settings.Controller_IP[0], Settings.Controller_IP[1], Settings.Controller_IP[2], Settings.Controller_IP[3]);
|
||||
|
||||
// Use WiFiClient class to create TCP connections
|
||||
WiFiClient client;
|
||||
if (!client.connect(host, Settings.ControllerPort))
|
||||
{
|
||||
connectionFailures++;
|
||||
return false;
|
||||
}
|
||||
if (connectionFailures)
|
||||
connectionFailures--;
|
||||
|
||||
// We now create a URI for the request
|
||||
char requete[300];
|
||||
sprintf_P(requete, PSTR("GET /%s/%s HTTP/1.1\r\n Host: %s \r\n Connection: close\r\n\r\n"),SecuritySettings.ControllerPassword,command_char , host );
|
||||
addLog(LOG_LEVEL_DEBUG, requete);
|
||||
client.print(requete);
|
||||
|
||||
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');
|
||||
|
||||
// success ?
|
||||
if (line.substring(0, 15) == "HTTP/1.1 200 OK") {
|
||||
strcpy_P(log, PSTR("HTTP : Success"));
|
||||
success = true;
|
||||
}
|
||||
else if (line.substring(0, 24) == "HTTP/1.1 400 Bad Request") {
|
||||
strcpy_P(log, PSTR("HTTP : Unauthorized"));
|
||||
}
|
||||
else if (line.substring(0, 25) == "HTTP/1.1 401 Unauthorized") {
|
||||
strcpy_P(log, PSTR("HTTP : Unauthorized"));
|
||||
}
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
|
||||
// data only
|
||||
if (data && line.startsWith("["))
|
||||
{
|
||||
String strValue = line;
|
||||
byte pos = strValue.indexOf('"',2);
|
||||
strValue = strValue.substring(2, pos);
|
||||
strValue.trim();
|
||||
float value = strValue.toFloat();
|
||||
*data = value;
|
||||
success = true;
|
||||
|
||||
char value_char[5] ;
|
||||
strValue.toCharArray(value_char, 5);
|
||||
sprintf_P(log, PSTR("Blynk get - %s => %s"),command_char , value_char );
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
strcpy_P(log, PSTR("HTTP : closing connection"));
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
|
||||
client.flush();
|
||||
client.stop();
|
||||
|
||||
// important - backgroudtasks - free mem
|
||||
timer = millis() + Settings.MessageDelay;
|
||||
while (millis() < timer)
|
||||
backgroundtasks();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,182 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//########################### Controller Plugin 022: Pimatic RestApi ####################################
|
||||
//#######################################################################################################
|
||||
|
||||
/*******************************************************************************
|
||||
* Release notes:
|
||||
* V 1.0
|
||||
- First version by deejaybeam, 7 July 2016
|
||||
* V 1.01
|
||||
- Update and rename _C009.ino to _C022.ino by Wutu due to new standard protocols, 21 September 2016
|
||||
* V1.02
|
||||
- Remove ">210" core statement to comply (and function) with new standard 230 core as of R114, 24 September 2016
|
||||
*
|
||||
/******************************************************************************/
|
||||
|
||||
#define CPLUGIN_022
|
||||
#define CPLUGIN_ID_022 22
|
||||
#define CPLUGIN_NAME_022 "Pimatic RestApi [DEVELOPMENT]"
|
||||
|
||||
boolean CPlugin_022(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
case CPLUGIN_PROTOCOL_ADD:
|
||||
{
|
||||
Protocol[++protocolCount].Number = CPLUGIN_ID_022;
|
||||
Protocol[protocolCount].usesMQTT = false;
|
||||
Protocol[protocolCount].usesAccount = true;
|
||||
Protocol[protocolCount].usesPassword = true;
|
||||
Protocol[protocolCount].defaultPort = 80;
|
||||
break;
|
||||
}
|
||||
|
||||
case CPLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(CPLUGIN_NAME_022);
|
||||
break;
|
||||
}
|
||||
|
||||
case CPLUGIN_PROTOCOL_SEND:
|
||||
{
|
||||
switch (event->sensorType)
|
||||
{
|
||||
case SENSOR_TYPE_SINGLE: // single value sensor, used for Dallas, BH1750, etc
|
||||
case SENSOR_TYPE_SWITCH:
|
||||
case SENSOR_TYPE_DIMMER:
|
||||
pimaticUpdateVariable(event, 0, UserVar[event->BaseVarIndex], 0);
|
||||
break;
|
||||
case SENSOR_TYPE_LONG: // single LONG value, stored in two floats (rfid tags)
|
||||
pimaticUpdateVariable(event, 0, 0, (unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16));
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM:
|
||||
case SENSOR_TYPE_TEMP_BARO:
|
||||
{
|
||||
pimaticUpdateVariable(event, 0, UserVar[event->BaseVarIndex], 0);
|
||||
unsigned long timer = millis() + Settings.MessageDelay;
|
||||
while (millis() < timer)
|
||||
backgroundtasks();
|
||||
pimaticUpdateVariable(event, 1, UserVar[event->BaseVarIndex + 1], 0);
|
||||
break;
|
||||
}
|
||||
case SENSOR_TYPE_TEMP_HUM_BARO:
|
||||
{
|
||||
pimaticUpdateVariable(event, 0, UserVar[event->BaseVarIndex], 0);
|
||||
unsigned long timer = millis() + Settings.MessageDelay;
|
||||
while (millis() < timer)
|
||||
backgroundtasks();
|
||||
pimaticUpdateVariable(event, 1, UserVar[event->BaseVarIndex + 1], 0);
|
||||
timer = millis() + Settings.MessageDelay;
|
||||
while (millis() < timer)
|
||||
backgroundtasks();
|
||||
pimaticUpdateVariable(event, 2, UserVar[event->BaseVarIndex + 2], 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
//********************************************************************************
|
||||
// Pimatic updateVariable
|
||||
//********************************************************************************
|
||||
boolean pimaticUpdateVariable(struct EventStruct *event, byte varIndex, float value, unsigned long longValue)
|
||||
{
|
||||
|
||||
String authHeader = "";
|
||||
|
||||
if ((SecuritySettings.ControllerUser[0] != 0) && (SecuritySettings.ControllerPassword[0] != 0))
|
||||
{
|
||||
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
|
||||
WiFiClient client;
|
||||
if (!client.connect(host, Settings.ControllerPort))
|
||||
{
|
||||
connectionFailures++;
|
||||
strcpy_P(log, PSTR("HTTP : connection failed"));
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
return false;
|
||||
}
|
||||
statusLED(true);
|
||||
if (connectionFailures)
|
||||
connectionFailures--;
|
||||
|
||||
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
|
||||
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
|
||||
|
||||
String url = "/api/variables/";
|
||||
url += URLEncode(ExtraTaskSettings.TaskDeviceValueNames[varIndex]);
|
||||
url.toCharArray(log, 80);
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
|
||||
String data;
|
||||
if (longValue)
|
||||
data = String(longValue);
|
||||
else
|
||||
data = toString(value, ExtraTaskSettings.TaskDeviceValueDecimals[varIndex]);
|
||||
|
||||
String yourdata = "{\"type\": \"value\", \"valueOrExpression\": \"" + data + "\"}";
|
||||
|
||||
String hostName = host;
|
||||
if (Settings.UseDNS)
|
||||
hostName = Settings.ControllerHostName;
|
||||
|
||||
// This will send the request to the server
|
||||
client.print(String("PATCH ") + url + " HTTP/1.1\r\n" +
|
||||
authHeader +
|
||||
"Host: " + hostName + "\r\n" +
|
||||
"Content-Type:application/json\r\n" +
|
||||
"Content-Length: " + yourdata.length() + "\r\n\r\n" +
|
||||
yourdata);
|
||||
|
||||
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 : Succes!"));
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,239 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//########################### Controller Plugin 025: Generic HTTP #######################################
|
||||
//#######################################################################################################
|
||||
|
||||
#define CPLUGIN_025
|
||||
#define CPLUGIN_ID_025 25
|
||||
#define CPLUGIN_NAME_025 "Generic HTTP TEST [DEVELOPMENT]"
|
||||
|
||||
#define P025_HTTP_METHOD_MAX_LEN 16
|
||||
#define P025_HTTP_URI_MAX_LEN 240
|
||||
#define P025_HTTP_HEADER_MAX_LEN 256
|
||||
#define P025_HTTP_BODY_MAX_LEN 512
|
||||
|
||||
struct P025_ConfigStruct
|
||||
{
|
||||
char HttpMethod[P025_HTTP_METHOD_MAX_LEN];
|
||||
char HttpUri[P025_HTTP_URI_MAX_LEN];
|
||||
char HttpHeader[P025_HTTP_HEADER_MAX_LEN];
|
||||
char HttpBody[P025_HTTP_BODY_MAX_LEN];
|
||||
};
|
||||
|
||||
boolean CPlugin_025(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
case CPLUGIN_PROTOCOL_ADD:
|
||||
{
|
||||
Protocol[++protocolCount].Number = CPLUGIN_ID_025;
|
||||
Protocol[protocolCount].usesMQTT = false;
|
||||
Protocol[protocolCount].usesAccount = false;
|
||||
Protocol[protocolCount].usesPassword = false;
|
||||
Protocol[protocolCount].defaultPort = 80;
|
||||
break;
|
||||
}
|
||||
|
||||
case CPLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(CPLUGIN_NAME_025);
|
||||
break;
|
||||
}
|
||||
|
||||
case CPLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
P025_ConfigStruct customConfig;
|
||||
LoadCustomControllerSettings((byte*)&customConfig, sizeof(customConfig));
|
||||
String methods[] = { F("GET"), F("POST"), F("PUT") };
|
||||
string += F("<TR><TD>HTTP Method :<TD><select name='P025httpmethod'>");
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
string += F("<option value='");
|
||||
string += methods[i] + "'";
|
||||
string += methods[i].equals(customConfig.HttpMethod) ? F(" selected='selected'") : F("");
|
||||
string += F(">");
|
||||
string += methods[i];
|
||||
string += F("</option>");
|
||||
}
|
||||
string += F("</select>");
|
||||
|
||||
string += F("<TR><TD>HTTP URI:<TD><input type='text' name='P025httpuri' size=80 maxlength='");
|
||||
string += P025_HTTP_URI_MAX_LEN-1;
|
||||
string += F("' value='");
|
||||
string += customConfig.HttpUri;
|
||||
string += F("'>");
|
||||
|
||||
string += F("<TR><TD>HTTP Header:<TD><textarea name='P025httpheader' rows='4' cols='50' maxlength='");
|
||||
string += P025_HTTP_HEADER_MAX_LEN-1;
|
||||
string += F("'>");
|
||||
string += customConfig.HttpHeader;
|
||||
string += F("</textarea>");
|
||||
|
||||
string += F("<TR><TD>HTTP Body:<TD><textarea name='P025httpbody' rows='8' cols='50' maxlength='");
|
||||
string += P025_HTTP_BODY_MAX_LEN-1;
|
||||
string += F("'>");
|
||||
string += customConfig.HttpBody;
|
||||
string += F("</textarea>");
|
||||
break;
|
||||
}
|
||||
|
||||
case CPLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
P025_ConfigStruct customConfig;
|
||||
String httpmethod = WebServer.arg("P025httpmethod");
|
||||
String httpuri = WebServer.arg("P025httpuri");
|
||||
String httpheader = WebServer.arg("P025httpheader");
|
||||
String httpbody = WebServer.arg("P025httpbody");
|
||||
strncpy(customConfig.HttpMethod, httpmethod.c_str(), sizeof(customConfig.HttpMethod));
|
||||
strncpy(customConfig.HttpUri, httpuri.c_str(), sizeof(customConfig.HttpUri));
|
||||
strncpy(customConfig.HttpHeader, httpheader.c_str(), sizeof(customConfig.HttpHeader));
|
||||
strncpy(customConfig.HttpBody, httpbody.c_str(), sizeof(customConfig.HttpBody));
|
||||
SaveCustomControllerSettings((byte*)&customConfig, sizeof(customConfig));
|
||||
break;
|
||||
}
|
||||
|
||||
case CPLUGIN_PROTOCOL_SEND:
|
||||
{
|
||||
switch (event->sensorType)
|
||||
{
|
||||
case SENSOR_TYPE_SINGLE: // single value sensor, used for Dallas, BH1750, etc
|
||||
case SENSOR_TYPE_SWITCH:
|
||||
case SENSOR_TYPE_DIMMER:
|
||||
HTTPSend025(event, 0, UserVar[event->BaseVarIndex], 0);
|
||||
break;
|
||||
case SENSOR_TYPE_LONG: // single LONG value, stored in two floats (rfid tags)
|
||||
HTTPSend025(event, 0, 0, (unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16));
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM:
|
||||
case SENSOR_TYPE_TEMP_BARO:
|
||||
{
|
||||
HTTPSend025(event, 0, UserVar[event->BaseVarIndex], 0);
|
||||
unsigned long timer = millis() + Settings.MessageDelay;
|
||||
while (millis() < timer)
|
||||
backgroundtasks();
|
||||
HTTPSend025(event, 1, UserVar[event->BaseVarIndex + 1], 0);
|
||||
break;
|
||||
}
|
||||
case SENSOR_TYPE_TEMP_HUM_BARO:
|
||||
{
|
||||
HTTPSend025(event, 0, UserVar[event->BaseVarIndex], 0);
|
||||
unsigned long timer = millis() + Settings.MessageDelay;
|
||||
while (millis() < timer)
|
||||
backgroundtasks();
|
||||
HTTPSend025(event, 1, UserVar[event->BaseVarIndex + 1], 0);
|
||||
timer = millis() + Settings.MessageDelay;
|
||||
while (millis() < timer)
|
||||
backgroundtasks();
|
||||
HTTPSend025(event, 2, UserVar[event->BaseVarIndex + 2], 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
//********************************************************************************
|
||||
// Generic HTTP get request
|
||||
//********************************************************************************
|
||||
boolean HTTPSend025(struct EventStruct *event, byte varIndex, float value, unsigned long longValue)
|
||||
{
|
||||
P025_ConfigStruct customConfig;
|
||||
LoadCustomControllerSettings((byte*)&customConfig, sizeof(customConfig));
|
||||
|
||||
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
|
||||
WiFiClient client;
|
||||
if (!client.connect(host, Settings.ControllerPort))
|
||||
{
|
||||
connectionFailures++;
|
||||
strcpy_P(log, PSTR("HTTP : connection failed"));
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
return false;
|
||||
}
|
||||
statusLED(true);
|
||||
if (connectionFailures)
|
||||
connectionFailures--;
|
||||
|
||||
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0)
|
||||
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
|
||||
|
||||
String hostName = host;
|
||||
if (Settings.UseDNS)
|
||||
hostName = Settings.ControllerHostName;
|
||||
|
||||
String payload = String(customConfig.HttpMethod) + " /";
|
||||
payload += customConfig.HttpUri;
|
||||
payload += String(" HTTP/1.1\r\n") +
|
||||
"Host: " + hostName + ":" + Settings.ControllerPort + "\r\n" +
|
||||
"Connection: close\r\n";
|
||||
|
||||
if (strlen(customConfig.HttpHeader) > 0)
|
||||
payload += customConfig.HttpHeader;
|
||||
ReplaceTokenByValue(payload, event, varIndex, value, longValue);
|
||||
|
||||
if (strlen(customConfig.HttpBody) > 0)
|
||||
{
|
||||
String body = String(customConfig.HttpBody);
|
||||
ReplaceTokenByValue(body, event, varIndex, value, longValue);
|
||||
payload += "\r\nContent-Length: " + String(body.length());
|
||||
payload += "\r\n\r\n" + body;
|
||||
}
|
||||
|
||||
// This will send the request to the server
|
||||
client.print(payload);
|
||||
|
||||
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, 10) == "HTTP/1.1 2")
|
||||
{
|
||||
strcpy_P(log, PSTR("HTTP : Succes!"));
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
//********************************************************************************
|
||||
// Replace the token in a string by real value.
|
||||
//********************************************************************************
|
||||
void ReplaceTokenByValue(String& s, struct EventStruct *event, byte varIndex, float value, unsigned long longValue)
|
||||
{
|
||||
s.replace("%sysname%", URLEncode(Settings.Name));
|
||||
s.replace("%tskname%", URLEncode(ExtraTaskSettings.TaskDeviceName));
|
||||
s.replace("%id%", String(event->idx));
|
||||
s.replace("%valname%", URLEncode(ExtraTaskSettings.TaskDeviceValueNames[varIndex]));
|
||||
if (longValue)
|
||||
s.replace("%value%", String(longValue));
|
||||
else
|
||||
s.replace("%value%", toString(value, ExtraTaskSettings.TaskDeviceValueDecimals[varIndex]));
|
||||
}
|
||||
|
||||
#endif
|
||||
+39
-6
@@ -6,7 +6,6 @@
|
||||
#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;
|
||||
@@ -137,7 +136,7 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
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])
|
||||
{
|
||||
@@ -225,17 +224,51 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PLUGIN_BUILD_TESTING
|
||||
|
||||
if (command == F("rtttl"))
|
||||
{
|
||||
success = true;
|
||||
if (event->Par1 >= 0 && event->Par1 <= 16)
|
||||
{
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
char sng[1024] ="";
|
||||
string.replace("-","#");
|
||||
string.toCharArray(sng, 1024);
|
||||
play_rtttl(event->Par1, sng);
|
||||
setPinState(PLUGIN_ID_001, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
log = String(F("SW : ")) + string;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_001, event->Par1, log, 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (command == F("tone"))
|
||||
{
|
||||
success = true;
|
||||
if (event->Par1 >= 0 && event->Par1 <= 16)
|
||||
{
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
tone(event->Par1, event->Par2, event->Par3);
|
||||
setPinState(PLUGIN_ID_001, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
log = String(F("SW : ")) + string;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_001, event->Par1, log, 0));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (command == F("pwm"))
|
||||
{
|
||||
success = true;
|
||||
if (event->Par1 >= 0 && event->Par1 <= 16)
|
||||
{
|
||||
pinMode(event->Par1, OUTPUT);
|
||||
|
||||
|
||||
if(event->Par3 != 0)
|
||||
{
|
||||
byte prev_mode;
|
||||
uint16_t prev_value;
|
||||
uint16_t prev_value;
|
||||
getPinState(PLUGIN_ID_001, event->Par1, &prev_mode, &prev_value);
|
||||
if(prev_mode != PIN_MODE_PWM)
|
||||
prev_value = 0;
|
||||
@@ -251,7 +284,7 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
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);
|
||||
@@ -339,4 +372,4 @@ boolean Plugin_001(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin 036: MQ2 sensors ###############################################
|
||||
//#######################################################################################################
|
||||
|
||||
#define PLUGIN_036
|
||||
#define PLUGIN_ID_036 36
|
||||
#define PLUGIN_NAME_036 "MQ2 smoke sensor [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_036 "LPG"
|
||||
#define PLUGIN_VALUENAME2_036 "CO"
|
||||
#define PLUGIN_VALUENAME3_036 "SMOKE"
|
||||
|
||||
/************************Hardware Related Macros************************************/
|
||||
#define RL_VALUE (5) //define the load resistance on the board, in kilo ohms
|
||||
#define RO_CLEAN_AIR_FACTOR (9.83) //RO_CLEAR_AIR_FACTOR=(Sensor resistance in clean air)/RO,
|
||||
//which is derived from the chart in datasheet
|
||||
|
||||
/***********************Software Related Macros************************************/
|
||||
#define CALIBARAION_SAMPLE_TIMES (50) //define how many samples you are going to take in the calibration phase
|
||||
#define CALIBRATION_SAMPLE_INTERVAL (500) //define the time interal(in milisecond) between each samples in the
|
||||
//cablibration phase
|
||||
#define READ_SAMPLE_INTERVAL (20) //define how many samples you are going to take in normal operation
|
||||
#define READ_SAMPLE_TIMES (5) //define the time interal(in milisecond) between each samples in
|
||||
//normal operation
|
||||
|
||||
/**********************Application Related Macros**********************************/
|
||||
#define GAS_LPG (0)
|
||||
#define GAS_CO (1)
|
||||
#define GAS_SMOKE (2)
|
||||
|
||||
/*****************************Globals***********************************************/
|
||||
float LPGCurve[3] = {2.3,0.21,-0.47}; //two points are taken from the curve.
|
||||
//with these two points, a line is formed which is "approximately equivalent"
|
||||
//to the original curve.
|
||||
//data format:{ x, y, slope}; point1: (lg200, 0.21), point2: (lg10000, -0.59)
|
||||
float COCurve[3] = {2.3,0.72,-0.34}; //two points are taken from the curve.
|
||||
//with these two points, a line is formed which is "approximately equivalent"
|
||||
//to the original curve.
|
||||
//data format:{ x, y, slope}; point1: (lg200, 0.72), point2: (lg10000, 0.15)
|
||||
float SmokeCurve[3] ={2.3,0.53,-0.44}; //two points are taken from the curve.
|
||||
//with these two points, a line is formed which is "approximately equivalent"
|
||||
//to the original curve.
|
||||
//data format:{ x, y, slope}; point1: (lg200, 0.53), point2: (lg10000, -0.22)
|
||||
float Ro = 10; //Ro is initialized to 10 kilo ohms
|
||||
|
||||
|
||||
|
||||
boolean Plugin_036(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_036;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_ANALOG;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_TRIPLE;
|
||||
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_036);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_036));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_036));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_036));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
String log = F("MQ2 Calibrating...put it in fresh air");
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
Ro = MQCalibration(); //Calibrating the sensor. Please make sure the sensor is in clean air
|
||||
//when you perform the calibration
|
||||
log = F("MQ2 Calibration is done...");
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
log = F("Ro=");
|
||||
log += Ro;
|
||||
log += F("kohm");
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
//Read LPG value
|
||||
int value = MQGetGasPercentage(MQRead()/Ro,GAS_LPG);
|
||||
UserVar[event->BaseVarIndex] = (float)value;
|
||||
String log = F("MQ2 : LPG value: ");
|
||||
log += value;
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
//read CO value
|
||||
value = MQGetGasPercentage(MQRead()/Ro,GAS_CO);
|
||||
UserVar[event->BaseVarIndex +1] = (float)value;
|
||||
log = F("MQ2 : CO value: ");
|
||||
log += value;
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
//Read SMOKE
|
||||
value = MQGetGasPercentage(MQRead()/Ro,GAS_SMOKE);
|
||||
UserVar[event->BaseVarIndex +2] = (float)value;
|
||||
log = F("MQ2 : SMOKE value: ");
|
||||
log += value;
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
//affect result
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/****************** MQResistanceCalculation ****************************************
|
||||
Input: raw_adc - raw value read from adc, which represents the voltage
|
||||
Output: the calculated sensor resistance
|
||||
Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage
|
||||
across the load resistor and its resistance, the resistance of the sensor
|
||||
could be derived.
|
||||
************************************************************************************/
|
||||
float MQResistanceCalculation(int raw_adc)
|
||||
{
|
||||
return ( ((float)RL_VALUE*(1023-raw_adc)/raw_adc));
|
||||
}
|
||||
|
||||
/***************************** MQCalibration ****************************************
|
||||
Input: mq_pin - analog channel
|
||||
Output: Ro of the sensor
|
||||
Remarks: This function assumes that the sensor is in clean air. It use
|
||||
MQResistanceCalculation to calculates the sensor resistance in clean air
|
||||
and then divides it with RO_CLEAN_AIR_FACTOR. RO_CLEAN_AIR_FACTOR is about
|
||||
10, which differs slightly between different sensors.
|
||||
************************************************************************************/
|
||||
float MQCalibration()
|
||||
{
|
||||
int i;
|
||||
float val=0;
|
||||
|
||||
for (i=0;i<CALIBARAION_SAMPLE_TIMES;i++) { //take multiple samples
|
||||
val += MQResistanceCalculation(analogRead(A0));
|
||||
delay(CALIBRATION_SAMPLE_INTERVAL);
|
||||
}
|
||||
val = val/CALIBARAION_SAMPLE_TIMES; //calculate the average value
|
||||
|
||||
val = val/RO_CLEAN_AIR_FACTOR; //divided by RO_CLEAN_AIR_FACTOR yields the Ro
|
||||
//according to the chart in the datasheet
|
||||
|
||||
return val;
|
||||
}
|
||||
/***************************** MQRead *********************************************
|
||||
Input: mq_pin - analog channel
|
||||
Output: Rs of the sensor
|
||||
Remarks: This function use MQResistanceCalculation to caculate the sensor resistenc (Rs).
|
||||
The Rs changes as the sensor is in the different consentration of the target
|
||||
gas. The sample times and the time interval between samples could be configured
|
||||
by changing the definition of the macros.
|
||||
************************************************************************************/
|
||||
float MQRead()
|
||||
{
|
||||
int i;
|
||||
float rs=0;
|
||||
|
||||
for (i=0;i<READ_SAMPLE_TIMES;i++) {
|
||||
rs += MQResistanceCalculation(analogRead(A0));
|
||||
delay(READ_SAMPLE_INTERVAL);
|
||||
}
|
||||
|
||||
rs = rs/READ_SAMPLE_TIMES;
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
/***************************** MQGetGasPercentage **********************************
|
||||
Input: rs_ro_ratio - Rs divided by Ro
|
||||
gas_id - target gas type
|
||||
Output: ppm of the target gas
|
||||
Remarks: This function passes different curves to the MQGetPercentage function which
|
||||
calculates the ppm (parts per million) of the target gas.
|
||||
************************************************************************************/
|
||||
int MQGetGasPercentage(float rs_ro_ratio, int gas_id)
|
||||
{
|
||||
if ( gas_id == GAS_LPG ) {
|
||||
return MQGetPercentage(rs_ro_ratio,LPGCurve);
|
||||
} else if ( gas_id == GAS_CO ) {
|
||||
return MQGetPercentage(rs_ro_ratio,COCurve);
|
||||
} else if ( gas_id == GAS_SMOKE ) {
|
||||
return MQGetPercentage(rs_ro_ratio,SmokeCurve);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***************************** MQGetPercentage **********************************
|
||||
Input: rs_ro_ratio - Rs divided by Ro
|
||||
pcurve - pointer to the curve of the target gas
|
||||
Output: ppm of the target gas
|
||||
Remarks: By using the slope and a point of the line. The x(logarithmic value of ppm)
|
||||
of the line could be derived if y(rs_ro_ratio) is provided. As it is a
|
||||
logarithmic coordinate, power of 10 is used to convert the result to non-logarithmic
|
||||
value.
|
||||
************************************************************************************/
|
||||
int MQGetPercentage(float rs_ro_ratio, float *pcurve)
|
||||
{
|
||||
return (pow(10,( ((log(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0])));
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//#################### Plugin 100 SRF01 Ultrasonic Distance Sensor ######################################
|
||||
//#######################################################################################################
|
||||
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
#define PLUGIN_100
|
||||
#define PLUGIN_ID_100 100
|
||||
#define PLUGIN_NAME_100 "Ultrasonic Sensor - SRF01 [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_100 "Distance"
|
||||
|
||||
#define PLUGIN_100_srfAddress 0x01 // Address of the SFR01, default ist 0x01
|
||||
#define PLUGIN_100_getSoft 0x5D // Byte to tell SRF01 we wish to read software version
|
||||
#define PLUGIN_100_getRange 0x54 // Byte used to get range from SRF01 in cm
|
||||
#define PLUGIN_100_getStatus 0x5F // Byte used to get the status of the transducer
|
||||
|
||||
SoftwareSerial *Plugin_100_SRF;
|
||||
|
||||
uint8_t Plugin_100_SRF_Pin;
|
||||
|
||||
boolean Plugin_100(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
byte timeout;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_100;
|
||||
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 = 1;
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_100);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_100));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
addLog(LOG_LEVEL_INFO, (char*)"INIT : SRF01");
|
||||
Plugin_100_SRF_Pin = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
Plugin_100_SRF = new SoftwareSerial(Plugin_100_SRF_Pin, Plugin_100_SRF_Pin);
|
||||
Plugin_100_SRF01_Cmd(PLUGIN_100_srfAddress, PLUGIN_100_getSoft);
|
||||
for (timeout = 0; timeout < 10; timeout++) {
|
||||
if (Plugin_100_SRF->available() < 1 ) {
|
||||
delay(10);
|
||||
} else {
|
||||
int softVer = Plugin_100_SRF->read();
|
||||
String log = F("SRF01 : Firmware-Version: ");
|
||||
log += softVer;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
success = true;
|
||||
return success;
|
||||
};
|
||||
};
|
||||
addLog(LOG_LEVEL_ERROR, (char*)"SRF01-Init: protocol timeout!");
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
Plugin_100_SRF01_Cmd(PLUGIN_100_srfAddress, PLUGIN_100_getRange);
|
||||
for (timeout = 0; timeout < 10; timeout++) {
|
||||
if (Plugin_100_SRF->available() < 2 ) {
|
||||
delay(10);
|
||||
} else {
|
||||
byte highByte = Plugin_100_SRF->read();
|
||||
byte lowByte = Plugin_100_SRF->read();
|
||||
int dist = ((highByte << 8) + lowByte);
|
||||
UserVar[event->BaseVarIndex] = dist;
|
||||
String log = F("SRF1 : Distance: ");
|
||||
log += dist;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
success = true;
|
||||
return success;
|
||||
};
|
||||
};
|
||||
addLog(LOG_LEVEL_ERROR, (char*)"SRF01 : protocol timeout!");
|
||||
UserVar[event->BaseVarIndex] = NAN;
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
//**************************************************************************/
|
||||
// Send SRF01 Command
|
||||
//**************************************************************************/
|
||||
void Plugin_100_SRF01_Cmd(byte Address, byte cmd) {
|
||||
Plugin_100_SRF->flush();
|
||||
pinMode(Plugin_100_SRF_Pin, OUTPUT);
|
||||
digitalWrite(Plugin_100_SRF_Pin, LOW);
|
||||
delay(2);
|
||||
digitalWrite(Plugin_100_SRF_Pin, HIGH);
|
||||
delay(1);
|
||||
Plugin_100_SRF->write(Address);
|
||||
Plugin_100_SRF->write(cmd);
|
||||
pinMode(Plugin_100_SRF_Pin, INPUT);
|
||||
Plugin_100_SRF->flush();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin 102: Nodo Event Bridge V2 #################################
|
||||
//#######################################################################################################
|
||||
|
||||
#define PLUGIN_102
|
||||
#define PLUGIN_ID_102 102
|
||||
#define PLUGIN_NAME_102 "Nodo Event Bridge V2 [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_102 ""
|
||||
|
||||
boolean Plugin_102(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
static byte unicastTargetUnit = 0;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_102;
|
||||
Device[deviceCount].Custom = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_102);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_102));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_UDP_IN:
|
||||
{
|
||||
// event->Data is pointer to UDP buffer
|
||||
if (event->Data[0] == 255 && event->Data[1] == 254)
|
||||
{
|
||||
if (event->Data[11] == 0) // normal traffic, Nodo flags are 0
|
||||
{
|
||||
unicastTargetUnit = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unicastTargetUnit = event->Data[9]; // remote source Nodo unit will be unicast target
|
||||
}
|
||||
for (byte x = 0; x < 16; x++)
|
||||
Serial.write(event->Data[x]);
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_SERIAL_IN:
|
||||
{
|
||||
if (Serial.peek() == 255)
|
||||
{
|
||||
delay(20); // wait for message to complete
|
||||
if (Serial.read() == 255 && Serial.read() == 254)
|
||||
{
|
||||
byte data[16];
|
||||
data[0] = 255;
|
||||
data[1] = 254;
|
||||
byte count = 2;
|
||||
while (Serial.available() && count < 16)
|
||||
data[count++] = Serial.read();
|
||||
|
||||
IPAddress sendIP(255, 255, 255, 255);
|
||||
|
||||
if (data[11] != 0) // flags set, set to unicast mode
|
||||
{
|
||||
if ((unicastTargetUnit != 0) && (Nodes[unicastTargetUnit].ip[0] != 0))
|
||||
for(byte x=0; x <4; x++)
|
||||
sendIP[x] = Nodes[unicastTargetUnit].ip[x];
|
||||
}
|
||||
|
||||
portUDP.beginPacket(sendIP, Settings.UDPPort);
|
||||
portUDP.write(data, 16);
|
||||
portUDP.endPacket();
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
#endif
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//######################### Plugin 104: SRF02 Ultrasonic range finder sensor ############################
|
||||
//#######################################################################################################
|
||||
|
||||
#define PLUGIN_104
|
||||
#define PLUGIN_ID_104 104
|
||||
#define PLUGIN_NAME_104 "Ultrasonic range finder - SRF02 [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_104 "Distance"
|
||||
|
||||
#define SRF02_ADDRESS (0x70) // default address (0x70 = datasheet address 0xE0)
|
||||
|
||||
#define SRF02_REG_COMMAND (0x00) // a read on this register returns the software revision
|
||||
#define SRF02_REG_UNUSED (0x01)
|
||||
#define SRF02_REG_RANGE_HIGH_BYTE (0x02)
|
||||
#define SRF02_REG_RANGE_LOW_BYTE (0x03)
|
||||
#define SRF02_REG_AUTOTUNE_MINIMUM_HIGH_BYTE (0x04)
|
||||
#define SRF02_REG_AUTOTUNE_MINIMUM_LOW_BYTE (0x05)
|
||||
|
||||
#define SRF02_CMD_REAL_RANGING_MODE_INCH (0x50)
|
||||
#define SRF02_CMD_REAL_RANGING_MODE_CM (0x51)
|
||||
#define SRF02_CMD_REAL_RANGING_MODE_US (0x52)
|
||||
|
||||
#define SRF02_CMD_FAKE_RANGING_MODE_INCH (0x56)
|
||||
#define SRF02_CMD_FAKE_RANGING_MODE_CM (0x57)
|
||||
#define SRF02_CMD_FAKE_RANGING_MODE_US (0x58)
|
||||
|
||||
#define SRF02_CMD_FORCE_AUTOTUNE_RESTART (0x5C)
|
||||
|
||||
#define SRF02_CMD_I2C_CHANGE_SEQ_1 (0xA0)
|
||||
#define SRF02_CMD_I2C_CHANGE_SEQ_2 (0xA5)
|
||||
#define SRF02_CMD_I2C_CHANGE_SEQ_3 (0xAA)
|
||||
|
||||
|
||||
uint8_t SRF02_i2caddr;
|
||||
|
||||
boolean Plugin_104(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_104;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_I2C;
|
||||
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 = 1;
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_104);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_104));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
|
||||
String options[1];
|
||||
options[0] = F("Distance");
|
||||
|
||||
int optionValues[1];
|
||||
optionValues[0] = 0;
|
||||
|
||||
string += F("<TR><TD>Report:<TD><select name='plugin_104_value'>");
|
||||
string += F("<option value='");
|
||||
string += optionValues[0];
|
||||
string += "'";
|
||||
if (choice == optionValues[0])
|
||||
string += F(" selected");
|
||||
string += ">";
|
||||
string += options[0];
|
||||
string += F("</option>");
|
||||
string += F("</select>");
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
String plugin1 = WebServer.arg("plugin_104_value");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
Plugin_104_begin();
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
float value;
|
||||
value = Plugin_104_getDistance();
|
||||
UserVar[event->BaseVarIndex] = value;
|
||||
String log = F("SRF02 : value : ");
|
||||
log += value;
|
||||
log += F("mm");
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
//**************************************************************************/
|
||||
// I2C single byte write
|
||||
//**************************************************************************/
|
||||
void Plugin_104_wireWriteByte(uint8_t reg, uint8_t value)
|
||||
{
|
||||
Wire.beginTransmission(SRF02_i2caddr);
|
||||
Wire.write(reg);
|
||||
Wire.write(value);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
//**************************************************************************/
|
||||
// I2C two byte read
|
||||
//**************************************************************************/
|
||||
void Plugin_104_wireReadTwoBytes(uint8_t reg, uint16_t *value)
|
||||
{
|
||||
Wire.beginTransmission(SRF02_i2caddr);
|
||||
Wire.write(reg);
|
||||
Wire.endTransmission();
|
||||
|
||||
delayMicroseconds(10);
|
||||
|
||||
Wire.requestFrom(SRF02_i2caddr, (uint8_t)2);
|
||||
*value = ((Wire.read() << 8) | Wire.read());
|
||||
}
|
||||
|
||||
//**************************************************************************/
|
||||
// Sensor setup
|
||||
//**************************************************************************/
|
||||
void Plugin_104_begin(void)
|
||||
{
|
||||
SRF02_i2caddr = SRF02_ADDRESS;
|
||||
}
|
||||
|
||||
//**************************************************************************/
|
||||
// Report distance
|
||||
//**************************************************************************/
|
||||
float Plugin_104_getDistance()
|
||||
{
|
||||
uint16_t value;
|
||||
|
||||
Plugin_104_wireWriteByte(SRF02_REG_COMMAND, SRF02_CMD_REAL_RANGING_MODE_US);
|
||||
delay(70); // transmit -> receive turnaround time (up to 65ms)
|
||||
Plugin_104_wireReadTwoBytes(SRF02_REG_RANGE_HIGH_BYTE, &value);
|
||||
|
||||
return (float)value/(float)5.82750583; // distance in [mm] (2*1000/343.2)
|
||||
}
|
||||
|
||||
#endif
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin 108: WOL receiver #########################################
|
||||
//#######################################################################################################
|
||||
|
||||
// This plugin receives Wake On Lan (WOL) messages that can be used to power on a connected device.
|
||||
// Note that the ESP itself has no WOL feature. It must be active to receive this message.
|
||||
// So you can't wake an ESP from deepsleep using WOL because Wifi is down during deepsleep.
|
||||
|
||||
#define PLUGIN_108
|
||||
#define PLUGIN_ID_108 108
|
||||
#define PLUGIN_NAME_108 "WOL Receiver [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_108 "WOL"
|
||||
|
||||
WiFiUDP *WOL;
|
||||
|
||||
boolean Plugin_108(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_108;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_SINGLE;
|
||||
Device[deviceCount].Custom = true;
|
||||
Device[deviceCount].TimerOption = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_108);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_108));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
string += F("<TR><TD>Power control pin:<TD>");
|
||||
addPinSelect(false, string, "taskdevicepin1", Settings.TaskDevicePin1[event->TaskIndex]);
|
||||
|
||||
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
String options[2];
|
||||
options[0] = F("Active Low");
|
||||
options[1] = F("Active High");
|
||||
int optionValues[2];
|
||||
optionValues[0] = 0;
|
||||
optionValues[1] = 1;
|
||||
string += F("<TR><TD>Output state:<TD><select name='plugin_108_state'>");
|
||||
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>");
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
String plugin1 = WebServer.arg("plugin_108_state");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
if (!WOL)
|
||||
{
|
||||
WOL = new WiFiUDP;
|
||||
WOL->begin(7);
|
||||
}
|
||||
if (Settings.TaskDevicePin1[event->TaskIndex] != -1)
|
||||
{
|
||||
pinMode(Settings.TaskDevicePin1[event->TaskIndex], OUTPUT);
|
||||
digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], !Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
|
||||
case PLUGIN_ONCE_A_SECOND:
|
||||
{
|
||||
int packetSize = WOL->parsePacket();
|
||||
if (packetSize)
|
||||
{
|
||||
statusLED(true);
|
||||
char packetBuffer[128];
|
||||
int len = WOL->read(packetBuffer, 128);
|
||||
byte match = 0;
|
||||
uint8_t mac[] = {0, 0, 0, 0, 0, 0};
|
||||
uint8_t* macread = WiFi.macAddress(mac);
|
||||
for (byte x = 0; x < 10; x++)
|
||||
if (packetBuffer[x + 6] == macread[x])
|
||||
match++;
|
||||
if (match == 6)
|
||||
{
|
||||
String log = F("WOL : Magic packet received!");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
if (Settings.TaskDevicePin1[event->TaskIndex] != -1)
|
||||
{
|
||||
String log = F("WOL : Power cycle using pin: ");
|
||||
log += Settings.TaskDevicePin1[event->TaskIndex];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
|
||||
delay(500);
|
||||
digitalWrite(Settings.TaskDevicePin1[event->TaskIndex], !Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,260 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//################################## Plugin 109 RESOL DeltaSol Pro ######################################
|
||||
//#######################################################################################################
|
||||
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
#define PLUGIN_109
|
||||
#define PLUGIN_ID_109 109
|
||||
#define PLUGIN_NAME_109 "RESOL DeltaSol Pro [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_109 "register"
|
||||
|
||||
// uart rx-buffer size
|
||||
#define RXBUF_SIZE 32
|
||||
|
||||
// RESOL register options
|
||||
#define REG_TEMP_SENSOR_1 0
|
||||
#define REG_TEMP_SENSOR_2 1
|
||||
#define REG_TEMP_SENSOR_3 2
|
||||
#define REG_RELAIS_1 3
|
||||
#define REG_RELAIS_2 4
|
||||
|
||||
// total number of selectable registers
|
||||
#define RESOL_REGISTER_OPTIONS 5
|
||||
|
||||
uint8_t Plugin_109_UART_Pin;
|
||||
boolean Plugin_109_init = false;
|
||||
boolean valuesValid = false;
|
||||
uint8_t RXbuf[RXBUF_SIZE];
|
||||
uint8_t RXbuf_IDX;
|
||||
int16_t T1, T2 ,T3;
|
||||
uint8_t R1, R2;
|
||||
|
||||
SoftwareSerial *Plugin_109_UART;
|
||||
|
||||
|
||||
// RESOL CRC check
|
||||
uint8_t VBus_CalcCrc(uint8_t *buf, uint8_t len)
|
||||
{
|
||||
uint8_t crc;
|
||||
|
||||
crc = 0x7F;
|
||||
|
||||
for (uint8_t i = 0; i < len; i++)
|
||||
crc = (crc - buf [i]) & 0x7F;
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
boolean Plugin_109(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_109;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_DUAL;
|
||||
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 = 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_109);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_109));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
int16_t choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
|
||||
String options[RESOL_REGISTER_OPTIONS];
|
||||
uint optionValues[RESOL_REGISTER_OPTIONS];
|
||||
|
||||
optionValues[0] = REG_TEMP_SENSOR_1;
|
||||
options[0] = F("Temperature sensor 1");
|
||||
optionValues[1] = REG_TEMP_SENSOR_2;
|
||||
options[1] = F("Temperature sensor 2");
|
||||
optionValues[2] = REG_TEMP_SENSOR_3;
|
||||
options[2] = F("Temperature sensor 3");
|
||||
optionValues[3] = REG_RELAIS_1;
|
||||
options[3] = F("Relais 1");
|
||||
optionValues[4] = REG_RELAIS_2;
|
||||
options[4] = F("Relais 2");
|
||||
|
||||
string += F("<TR><TD>Register:<TD><select name='plugin_109_register'>");
|
||||
for (byte x = 0; x < RESOL_REGISTER_OPTIONS; 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 = WebServer.arg("plugin_109_register");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
|
||||
Plugin_109_init = false; // Force device setup next time
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
addLog(LOG_LEVEL_INFO, (char*)"INIT : RESOL DeltaSol Pro");
|
||||
|
||||
Plugin_109_UART = new SoftwareSerial(Settings.TaskDevicePin1[event->TaskIndex], Settings.TaskDevicePin2[event->TaskIndex], false, (2 * RXBUF_SIZE)); // set RX und Tx Pin number, no invert, buffer
|
||||
|
||||
Plugin_109_init = true;
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
if (Plugin_109_init == true)
|
||||
{
|
||||
// buffer filled with more than twize the frame size bytes? -> at least one complete frame should then be available
|
||||
if (Plugin_109_UART->available() > (2 * 30))
|
||||
{
|
||||
// search for first appearance of SOF (0xAA)
|
||||
while ((Plugin_109_UART->available()) && (Plugin_109_UART->read() != 0xAA));
|
||||
|
||||
RXbuf_IDX = 0;
|
||||
RXbuf[RXbuf_IDX] = 0;
|
||||
|
||||
while ((Plugin_109_UART->available()) && (RXbuf[RXbuf_IDX] != 0xAA) && (RXbuf_IDX < (RXBUF_SIZE - 1)))
|
||||
RXbuf[++RXbuf_IDX] = Plugin_109_UART->read();
|
||||
|
||||
if (RXbuf[RXbuf_IDX] == 0xAA)
|
||||
{
|
||||
// ********* DeltaSol Pro registers *********
|
||||
|
||||
// check CRC of first and second group
|
||||
if ( (VBus_CalcCrc(&RXbuf[10], 5) == RXbuf[15]) && (VBus_CalcCrc(&RXbuf[16], 5) == RXbuf[21]) )
|
||||
{
|
||||
// Temperature sensor 1
|
||||
T1 = RXbuf[10] + ((RXbuf[14] & (1 << 0)) << 7);
|
||||
T1 += (RXbuf[11] + ((RXbuf[14] & (1 << 1)) << 6)) << 8;
|
||||
|
||||
// Temperature sensor 2
|
||||
T2 = RXbuf[12] + ((RXbuf[14] & (1 << 2)) << 5);
|
||||
T2 += (RXbuf[13] + ((RXbuf[14] & (1 << 3)) << 4)) << 8;
|
||||
|
||||
|
||||
// Temperature sensor 3
|
||||
T3 = RXbuf[16] + ((RXbuf[20] & (1 << 0)) << 7);
|
||||
T3 += (RXbuf[17] + ((RXbuf[20] & (1 << 1)) << 6)) << 8;
|
||||
|
||||
// Relais/speed
|
||||
R1 = RXbuf[18] + ((RXbuf[20] & (1 << 2)) << 5);
|
||||
R2 = RXbuf[19] + ((RXbuf[20] & (1 << 3)) << 4);
|
||||
|
||||
valuesValid = true;
|
||||
} else
|
||||
valuesValid = false;
|
||||
}
|
||||
|
||||
Plugin_109_UART->flush();
|
||||
}
|
||||
}
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
float regValue;
|
||||
String log = F("RESOL : ");
|
||||
|
||||
if (valuesValid == true)
|
||||
{
|
||||
switch (Settings.TaskDevicePluginConfig[event->TaskIndex][0])
|
||||
{
|
||||
case REG_TEMP_SENSOR_1:
|
||||
{
|
||||
log += F("T1");
|
||||
regValue = (float)T1 / 10;
|
||||
break;
|
||||
}
|
||||
|
||||
case REG_TEMP_SENSOR_2:
|
||||
{
|
||||
log += F("T2");
|
||||
regValue = (float)T2 / 10;
|
||||
break;
|
||||
}
|
||||
|
||||
case REG_TEMP_SENSOR_3:
|
||||
{
|
||||
log += F("T3");
|
||||
regValue = (float)T3 / 10;
|
||||
break;
|
||||
}
|
||||
|
||||
case REG_RELAIS_1:
|
||||
{
|
||||
log += F("R1");
|
||||
regValue = R1;
|
||||
break;
|
||||
}
|
||||
|
||||
case REG_RELAIS_2:
|
||||
{
|
||||
log += F("R2");
|
||||
regValue = R2;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
log += F("unknown");
|
||||
regValue = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log += F("=");
|
||||
log += regValue;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
|
||||
UserVar[event->BaseVarIndex] = regValue;
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,121 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//############################# Plugin 111: SenseAir CO2 Sensors ########################################
|
||||
//#######################################################################################################
|
||||
/*
|
||||
Plugin written by: Daniel Tedenljung info__AT__tedenljungconsulting.com
|
||||
|
||||
This plugin reads the co2 value of SenseAir Co2 Sensors. (S8 works, K30 is ongoing)
|
||||
Datasheet can be found here for S8: http://www.senseair.com/products/oem-modules/senseair-s8/
|
||||
Datasheet can be found here for K30: http://www.senseair.com/products/oem-modules/k30/
|
||||
|
||||
You can buy sensor from m.nu in Sweden:
|
||||
S8 https://www.m.nu/co2matare-fran-senseair-p-1440.html
|
||||
K30 https://www.m.nu/k30-co2matare-p-302.html
|
||||
*/
|
||||
|
||||
|
||||
#define PLUGIN_111
|
||||
#define PLUGIN_ID_111 111
|
||||
#define PLUGIN_NAME_111 "SenseAir CO2 Sensor [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_111 "PPM"
|
||||
|
||||
boolean Plugin_111_init = false;
|
||||
|
||||
#include <SoftwareSerial.h>
|
||||
SoftwareSerial *Plugin_111_S8;
|
||||
|
||||
// 0xFE=Any address 0x04=Read input registers 0x0003=Starting address 0x0001=Number of registers to read 0xD5C5=CRC in reverse order
|
||||
byte cmdReadPPM[] = {0xFE, 0x04, 0x00, 0x03, 0x00, 0x01, 0xD5, 0xC5};
|
||||
|
||||
byte ReciveBuffer[7];
|
||||
|
||||
byte Data[5];
|
||||
byte co2[2];
|
||||
long ppm;
|
||||
|
||||
boolean Plugin_111(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_111;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_DUAL;
|
||||
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 = 1;
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
Device[deviceCount].TimerOption = true;
|
||||
Device[deviceCount].GlobalSyncOption = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_111);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_111));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
Plugin_111_init = true;
|
||||
Plugin_111_S8 = new SoftwareSerial(Settings.TaskDevicePin1[event->TaskIndex], Settings.TaskDevicePin2[event->TaskIndex]); // TODO: Explain this in plugin description RX=GPIO Setting 1, TX=GPIO Setting 2, Use 1kOhm in serie on datapins!
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
|
||||
if (Plugin_111_init)
|
||||
{
|
||||
Plugin_111_S8->write(cmdReadPPM, sizeof(cmdReadPPM)); //Send the byte array
|
||||
delay(50);
|
||||
|
||||
// Read answer from S8
|
||||
int ByteCounter = 0;
|
||||
while (Plugin_111_S8->available()) {
|
||||
ReciveBuffer[ByteCounter] = Plugin_111_S8->read();
|
||||
ByteCounter++;
|
||||
}
|
||||
|
||||
// Divide recived chunk in different registers
|
||||
for (int i = 0 ; i < sizeof(ReciveBuffer) - 2 ; i++)
|
||||
{
|
||||
Data[i] = ReciveBuffer[i];
|
||||
}
|
||||
|
||||
co2[0] = Data[3];
|
||||
co2[1] = Data[4];
|
||||
|
||||
ppm = (co2[0] << 8) | (co2[1]);
|
||||
|
||||
int value = ppm;
|
||||
UserVar[event->BaseVarIndex] = (float)value;
|
||||
String log = F("S8 : PPM value: "); //TODO: Different string if K30 is used.
|
||||
log += value;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
#endif
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin 112: Power Counter ########################################
|
||||
//#######################################################################################################
|
||||
//This sketch is based on Plugin 003: Pulse
|
||||
|
||||
#define PLUGIN_112
|
||||
#define PLUGIN_ID_112 112
|
||||
#define PLUGIN_NAME_112 "Power Counter [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_112 "PowerWh"
|
||||
#define PLUGIN_VALUENAME2_112 "PowerCountTotal"
|
||||
|
||||
unsigned long Plugin_112_pulseCounter[TASKS_MAX];
|
||||
unsigned long Plugin_112_pulseTotalCounter[TASKS_MAX];
|
||||
unsigned long Plugin_112_pulseTime[TASKS_MAX];
|
||||
unsigned long Plugin_112_pulseTimePrevious[TASKS_MAX];
|
||||
float Plugin_112_pulseUsage[TASKS_MAX];
|
||||
|
||||
boolean Plugin_112(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_112;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_SINGLE;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_DUAL;
|
||||
Device[deviceCount].Ports = 0;
|
||||
Device[deviceCount].PullUpOption = false;
|
||||
Device[deviceCount].InverseLogicOption = false;
|
||||
Device[deviceCount].FormulaOption = true;
|
||||
Device[deviceCount].ValueCount = 2;
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
Device[deviceCount].TimerOption = true;
|
||||
Device[deviceCount].GlobalSyncOption = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_112);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_112));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_112));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
char tmpString[128];
|
||||
|
||||
sprintf_P(tmpString, PSTR("<TR><TD>Debounce Time (mSec):<TD><input type='text' name='plugin_112_debounce' value='%u'>"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
|
||||
string += tmpString;
|
||||
|
||||
sprintf_P(tmpString, PSTR("<TR><TD>Pulses per KWh:<TD><input type='text' name='plugin_112_pulsesperkwh' value='%u'>"), Settings.TaskDevicePluginConfig[event->TaskIndex][1]);
|
||||
string += tmpString;
|
||||
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
String plugin1 = WebServer.arg("plugin_112_debounce");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
|
||||
String plugin2 = WebServer.arg("plugin_112_pulsesperkwh");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][1] = plugin2.toInt();
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_SHOW_VALUES:
|
||||
{
|
||||
string += ExtraTaskSettings.TaskDeviceValueNames[0];
|
||||
string += F(":");
|
||||
string += Plugin_112_pulseUsage[event->TaskIndex];
|
||||
string += F("<BR>");
|
||||
string += ExtraTaskSettings.TaskDeviceValueNames[1];
|
||||
string += F(":");
|
||||
string += Plugin_112_pulseTotalCounter[event->TaskIndex];
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
String log = F("INIT : Power Counter ");
|
||||
log += Settings.TaskDevicePin1[event->TaskIndex];
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
pinMode(Settings.TaskDevicePin1[event->TaskIndex], INPUT_PULLUP);
|
||||
Plugin_112_pulseinit(Settings.TaskDevicePin1[event->TaskIndex], event->TaskIndex);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
Plugin_112_idleusage(event->TaskIndex);
|
||||
UserVar[event->BaseVarIndex] = Plugin_112_pulseUsage[event->TaskIndex];
|
||||
UserVar[event->BaseVarIndex+1] = Plugin_112_pulseTotalCounter[event->TaskIndex];
|
||||
Plugin_112_pulseCounter[event->TaskIndex] = 0;
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Update usage when no pulse has been received for some time, so it will decrease on every
|
||||
* PLUGIN_READ event instead off keeping the last calculated usage value.
|
||||
\*********************************************************************************************/
|
||||
void Plugin_112_idleusage(byte Index)
|
||||
{
|
||||
unsigned long PulseTime=millis() - Plugin_112_pulseTimePrevious[Index];
|
||||
if(PulseTime > (Settings.TaskDeviceTimer[Index] * 1000) && //More than $device_delay passed since last pulse
|
||||
PulseTime > Plugin_112_pulseTime[Index] ) { //More than last pulse interval
|
||||
|
||||
// Let's prevent divison by zero
|
||||
if(Settings.TaskDevicePluginConfig[Index][1]==0) {
|
||||
Settings.TaskDevicePluginConfig[Index][1]=1000; // if not configged correctly prevent crashes and set it to 1000 as default value.
|
||||
}
|
||||
// WH = =3600000/[pulses per kwh]/[time since last pulse (ms)]
|
||||
Plugin_112_pulseUsage[Index] = (3600000000./Settings.TaskDevicePluginConfig[Index][1])/PulseTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Check Pulse Counters (called from irq handler)
|
||||
\*********************************************************************************************/
|
||||
void Plugin_112_pulsecheck(byte Index)
|
||||
{
|
||||
unsigned long PulseTime=millis() - Plugin_112_pulseTimePrevious[Index];
|
||||
if(PulseTime > Settings.TaskDevicePluginConfig[Index][0]) // check with debounce time for this task
|
||||
{
|
||||
Plugin_112_pulseTimePrevious[Index]=millis(); // moved this up as the operations below might take some time
|
||||
Plugin_112_pulseCounter[Index]++;
|
||||
Plugin_112_pulseTotalCounter[Index]++;
|
||||
Plugin_112_pulseTime[Index] = PulseTime;
|
||||
|
||||
// Let's prevent divison by zero
|
||||
if(Settings.TaskDevicePluginConfig[Index][1]==0) {
|
||||
Settings.TaskDevicePluginConfig[Index][1]=1000; // if not configged correctly prevent crashes and set it to 1000 as default value.
|
||||
}
|
||||
|
||||
// WH = =3600000/[pulses per kwh]/[time since last pulse (ms)]
|
||||
Plugin_112_pulseUsage[Index] = (3600000000./Settings.TaskDevicePluginConfig[Index][1])/PulseTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Pulse Counter IRQ handlers
|
||||
\*********************************************************************************************/
|
||||
void Plugin_112_pulse_interrupt1()
|
||||
{
|
||||
Plugin_112_pulsecheck(0);
|
||||
}
|
||||
void Plugin_112_pulse_interrupt2()
|
||||
{
|
||||
Plugin_112_pulsecheck(1);
|
||||
}
|
||||
void Plugin_112_pulse_interrupt3()
|
||||
{
|
||||
Plugin_112_pulsecheck(2);
|
||||
}
|
||||
void Plugin_112_pulse_interrupt4()
|
||||
{
|
||||
Plugin_112_pulsecheck(3);
|
||||
}
|
||||
void Plugin_112_pulse_interrupt5()
|
||||
{
|
||||
Plugin_112_pulsecheck(4);
|
||||
}
|
||||
void Plugin_112_pulse_interrupt6()
|
||||
{
|
||||
Plugin_112_pulsecheck(5);
|
||||
}
|
||||
void Plugin_112_pulse_interrupt7()
|
||||
{
|
||||
Plugin_112_pulsecheck(6);
|
||||
}
|
||||
void Plugin_112_pulse_interrupt8()
|
||||
{
|
||||
Plugin_112_pulsecheck(7);
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Init Pulse Counters
|
||||
\*********************************************************************************************/
|
||||
void Plugin_112_pulseinit(byte Par1, byte Index)
|
||||
{
|
||||
// Init IO pins
|
||||
String log = F("Power Counter: Init");
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
attachInterrupt(Par1, Plugin_112_pulse_interrupt1, FALLING);
|
||||
break;
|
||||
case 1:
|
||||
attachInterrupt(Par1, Plugin_112_pulse_interrupt2, FALLING);
|
||||
break;
|
||||
case 2:
|
||||
attachInterrupt(Par1, Plugin_112_pulse_interrupt3, FALLING);
|
||||
break;
|
||||
case 3:
|
||||
attachInterrupt(Par1, Plugin_112_pulse_interrupt4, FALLING);
|
||||
break;
|
||||
case 4:
|
||||
attachInterrupt(Par1, Plugin_112_pulse_interrupt5, FALLING);
|
||||
break;
|
||||
case 5:
|
||||
attachInterrupt(Par1, Plugin_112_pulse_interrupt6, FALLING);
|
||||
break;
|
||||
case 6:
|
||||
attachInterrupt(Par1, Plugin_112_pulse_interrupt7, FALLING);
|
||||
break;
|
||||
case 7:
|
||||
attachInterrupt(Par1, Plugin_112_pulse_interrupt8, FALLING);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,352 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin-113: SI1145 - UV index / IR / visible ####################
|
||||
//#######################################################################################################
|
||||
//#include <Adafruit_SI1145.h>
|
||||
|
||||
#define PLUGIN_113
|
||||
#define PLUGIN_ID_113 113
|
||||
#define PLUGIN_NAME_113 "SI1145 UV index (Adafruit-QA) [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_113 "VISIBLE"
|
||||
#define PLUGIN_VALUENAME2_113 "INFRA"
|
||||
#define PLUGIN_VALUENAME3_113 "UV"
|
||||
|
||||
// ADAFRUIT SI1145 LIBRARY - Adafruit_SI1145.h
|
||||
// =============================================
|
||||
#if (ARDUINO >= 100)
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
#include <Wire.h>
|
||||
|
||||
/* COMMANDS */
|
||||
#define SI1145_PARAM_QUERY 0x80
|
||||
#define SI1145_PARAM_SET 0xA0
|
||||
#define SI1145_NOP 0x0
|
||||
#define SI1145_RESET 0x01
|
||||
#define SI1145_BUSADDR 0x02
|
||||
#define SI1145_PS_FORCE 0x05
|
||||
#define SI1145_ALS_FORCE 0x06
|
||||
#define SI1145_PSALS_FORCE 0x07
|
||||
#define SI1145_PS_PAUSE 0x09
|
||||
#define SI1145_ALS_PAUSE 0x0A
|
||||
#define SI1145_PSALS_PAUSE 0xB
|
||||
#define SI1145_PS_AUTO 0x0D
|
||||
#define SI1145_ALS_AUTO 0x0E
|
||||
#define SI1145_PSALS_AUTO 0x0F
|
||||
#define SI1145_GET_CAL 0x12
|
||||
/* Parameters */
|
||||
#define SI1145_PARAM_I2CADDR 0x00
|
||||
#define SI1145_PARAM_CHLIST 0x01
|
||||
#define SI1145_PARAM_CHLIST_ENUV 0x80
|
||||
#define SI1145_PARAM_CHLIST_ENAUX 0x40
|
||||
#define SI1145_PARAM_CHLIST_ENALSIR 0x20
|
||||
#define SI1145_PARAM_CHLIST_ENALSVIS 0x10
|
||||
#define SI1145_PARAM_CHLIST_ENPS1 0x01
|
||||
#define SI1145_PARAM_CHLIST_ENPS2 0x02
|
||||
#define SI1145_PARAM_CHLIST_ENPS3 0x04
|
||||
#define SI1145_PARAM_PSLED12SEL 0x02
|
||||
#define SI1145_PARAM_PSLED12SEL_PS2NONE 0x00
|
||||
#define SI1145_PARAM_PSLED12SEL_PS2LED1 0x10
|
||||
#define SI1145_PARAM_PSLED12SEL_PS2LED2 0x20
|
||||
#define SI1145_PARAM_PSLED12SEL_PS2LED3 0x40
|
||||
#define SI1145_PARAM_PSLED12SEL_PS1NONE 0x00
|
||||
#define SI1145_PARAM_PSLED12SEL_PS1LED1 0x01
|
||||
#define SI1145_PARAM_PSLED12SEL_PS1LED2 0x02
|
||||
#define SI1145_PARAM_PSLED12SEL_PS1LED3 0x04
|
||||
#define SI1145_PARAM_PSLED3SEL 0x03
|
||||
#define SI1145_PARAM_PSENCODE 0x05
|
||||
#define SI1145_PARAM_ALSENCODE 0x06
|
||||
#define SI1145_PARAM_PS1ADCMUX 0x07
|
||||
#define SI1145_PARAM_PS2ADCMUX 0x08
|
||||
#define SI1145_PARAM_PS3ADCMUX 0x09
|
||||
#define SI1145_PARAM_PSADCOUNTER 0x0A
|
||||
#define SI1145_PARAM_PSADCGAIN 0x0B
|
||||
#define SI1145_PARAM_PSADCMISC 0x0C
|
||||
#define SI1145_PARAM_PSADCMISC_RANGE 0x20
|
||||
#define SI1145_PARAM_PSADCMISC_PSMODE 0x04
|
||||
#define SI1145_PARAM_ALSIRADCMUX 0x0E
|
||||
#define SI1145_PARAM_AUXADCMUX 0x0F
|
||||
#define SI1145_PARAM_ALSVISADCOUNTER 0x10
|
||||
#define SI1145_PARAM_ALSVISADCGAIN 0x11
|
||||
#define SI1145_PARAM_ALSVISADCMISC 0x12
|
||||
#define SI1145_PARAM_ALSVISADCMISC_VISRANGE 0x20
|
||||
#define SI1145_PARAM_ALSIRADCOUNTER 0x1D
|
||||
#define SI1145_PARAM_ALSIRADCGAIN 0x1E
|
||||
#define SI1145_PARAM_ALSIRADCMISC 0x1F
|
||||
#define SI1145_PARAM_ALSIRADCMISC_RANGE 0x20
|
||||
#define SI1145_PARAM_ADCCOUNTER_511CLK 0x70
|
||||
#define SI1145_PARAM_ADCMUX_SMALLIR 0x00
|
||||
#define SI1145_PARAM_ADCMUX_LARGEIR 0x03
|
||||
/* REGISTERS */
|
||||
#define SI1145_REG_PARTID 0x00
|
||||
#define SI1145_REG_REVID 0x01
|
||||
#define SI1145_REG_SEQID 0x02
|
||||
#define SI1145_REG_INTCFG 0x03
|
||||
#define SI1145_REG_INTCFG_INTOE 0x01
|
||||
#define SI1145_REG_INTCFG_INTMODE 0x02
|
||||
#define SI1145_REG_IRQEN 0x04
|
||||
#define SI1145_REG_IRQEN_ALSEVERYSAMPLE 0x01
|
||||
#define SI1145_REG_IRQEN_PS1EVERYSAMPLE 0x04
|
||||
#define SI1145_REG_IRQEN_PS2EVERYSAMPLE 0x08
|
||||
#define SI1145_REG_IRQEN_PS3EVERYSAMPLE 0x10
|
||||
#define SI1145_REG_IRQMODE1 0x05
|
||||
#define SI1145_REG_IRQMODE2 0x06
|
||||
#define SI1145_REG_HWKEY 0x07
|
||||
#define SI1145_REG_MEASRATE0 0x08
|
||||
#define SI1145_REG_MEASRATE1 0x09
|
||||
#define SI1145_REG_PSRATE 0x0A
|
||||
#define SI1145_REG_PSLED21 0x0F
|
||||
#define SI1145_REG_PSLED3 0x10
|
||||
#define SI1145_REG_UCOEFF0 0x13
|
||||
#define SI1145_REG_UCOEFF1 0x14
|
||||
#define SI1145_REG_UCOEFF2 0x15
|
||||
#define SI1145_REG_UCOEFF3 0x16
|
||||
#define SI1145_REG_PARAMWR 0x17
|
||||
#define SI1145_REG_COMMAND 0x18
|
||||
#define SI1145_REG_RESPONSE 0x20
|
||||
#define SI1145_REG_IRQSTAT 0x21
|
||||
#define SI1145_REG_IRQSTAT_ALS 0x01
|
||||
#define SI1145_REG_ALSVISDATA0 0x22
|
||||
#define SI1145_REG_ALSVISDATA1 0x23
|
||||
#define SI1145_REG_ALSIRDATA0 0x24
|
||||
#define SI1145_REG_ALSIRDATA1 0x25
|
||||
#define SI1145_REG_PS1DATA0 0x26
|
||||
#define SI1145_REG_PS1DATA1 0x27
|
||||
#define SI1145_REG_PS2DATA0 0x28
|
||||
#define SI1145_REG_PS2DATA1 0x29
|
||||
#define SI1145_REG_PS3DATA0 0x2A
|
||||
#define SI1145_REG_PS3DATA1 0x2B
|
||||
#define SI1145_REG_UVINDEX0 0x2C
|
||||
#define SI1145_REG_UVINDEX1 0x2D
|
||||
#define SI1145_REG_PARAMRD 0x2E
|
||||
#define SI1145_REG_CHIPSTAT 0x30
|
||||
#define SI1145_ADDR 0x60
|
||||
|
||||
class Adafruit_SI1145 {
|
||||
public:
|
||||
Adafruit_SI1145(void);
|
||||
boolean begin();
|
||||
void reset();
|
||||
uint16_t readUV();
|
||||
uint16_t readIR();
|
||||
uint16_t readVisible();
|
||||
uint16_t readProx();
|
||||
private:
|
||||
uint16_t read16(uint8_t addr);
|
||||
uint8_t read8(uint8_t addr);
|
||||
void write8(uint8_t reg, uint8_t val);
|
||||
uint8_t readParam(uint8_t p);
|
||||
uint8_t writeParam(uint8_t p, uint8_t v);
|
||||
uint8_t _addr;
|
||||
};
|
||||
|
||||
// ADAFRUIT SI1145 LIBRARY - Adafruit_SI1145.cpp
|
||||
// =============================================
|
||||
Adafruit_SI1145::Adafruit_SI1145() {
|
||||
_addr = SI1145_ADDR;
|
||||
}
|
||||
|
||||
boolean Adafruit_SI1145::begin(void) {
|
||||
Wire.begin();
|
||||
uint8_t id = read8(SI1145_REG_PARTID);
|
||||
if (id != 0x45) return false; // look for SI1145
|
||||
reset();
|
||||
/***********************************/
|
||||
// enable UVindex measurement coefficients!
|
||||
write8(SI1145_REG_UCOEFF0, 0x29);
|
||||
write8(SI1145_REG_UCOEFF1, 0x89);
|
||||
write8(SI1145_REG_UCOEFF2, 0x02);
|
||||
write8(SI1145_REG_UCOEFF3, 0x00);
|
||||
// enable UV sensor
|
||||
writeParam(SI1145_PARAM_CHLIST, SI1145_PARAM_CHLIST_ENUV |
|
||||
SI1145_PARAM_CHLIST_ENALSIR | SI1145_PARAM_CHLIST_ENALSVIS |
|
||||
SI1145_PARAM_CHLIST_ENPS1);
|
||||
// enable interrupt on every sample
|
||||
write8(SI1145_REG_INTCFG, SI1145_REG_INTCFG_INTOE);
|
||||
write8(SI1145_REG_IRQEN, SI1145_REG_IRQEN_ALSEVERYSAMPLE);
|
||||
/****************************** Prox Sense 1 */
|
||||
// program LED current
|
||||
write8(SI1145_REG_PSLED21, 0x03); // 20mA for LED 1 only
|
||||
writeParam(SI1145_PARAM_PS1ADCMUX, SI1145_PARAM_ADCMUX_LARGEIR);
|
||||
// prox sensor #1 uses LED #1
|
||||
writeParam(SI1145_PARAM_PSLED12SEL, SI1145_PARAM_PSLED12SEL_PS1LED1);
|
||||
// fastest clocks, clock div 1
|
||||
writeParam(SI1145_PARAM_PSADCGAIN, 0);
|
||||
// take 511 clocks to measure
|
||||
writeParam(SI1145_PARAM_PSADCOUNTER, SI1145_PARAM_ADCCOUNTER_511CLK);
|
||||
// in prox mode, high range
|
||||
writeParam(SI1145_PARAM_PSADCMISC, SI1145_PARAM_PSADCMISC_RANGE|
|
||||
SI1145_PARAM_PSADCMISC_PSMODE);
|
||||
writeParam(SI1145_PARAM_ALSIRADCMUX, SI1145_PARAM_ADCMUX_SMALLIR);
|
||||
// fastest clocks, clock div 1
|
||||
writeParam(SI1145_PARAM_ALSIRADCGAIN, 0);
|
||||
// take 511 clocks to measure
|
||||
writeParam(SI1145_PARAM_ALSIRADCOUNTER, SI1145_PARAM_ADCCOUNTER_511CLK);
|
||||
// in high range mode
|
||||
writeParam(SI1145_PARAM_ALSIRADCMISC, SI1145_PARAM_ALSIRADCMISC_RANGE);
|
||||
// fastest clocks, clock div 1
|
||||
writeParam(SI1145_PARAM_ALSVISADCGAIN, 0);
|
||||
// take 511 clocks to measure
|
||||
writeParam(SI1145_PARAM_ALSVISADCOUNTER, SI1145_PARAM_ADCCOUNTER_511CLK);
|
||||
// in high range mode (not normal signal)
|
||||
writeParam(SI1145_PARAM_ALSVISADCMISC, SI1145_PARAM_ALSVISADCMISC_VISRANGE);
|
||||
/************************/
|
||||
// measurement rate for auto
|
||||
write8(SI1145_REG_MEASRATE0, 0xFF); // 255 * 31.25uS = 8ms
|
||||
// auto run
|
||||
write8(SI1145_REG_COMMAND, SI1145_PSALS_AUTO);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Adafruit_SI1145::reset() {
|
||||
write8(SI1145_REG_MEASRATE0, 0);
|
||||
write8(SI1145_REG_MEASRATE1, 0);
|
||||
write8(SI1145_REG_IRQEN, 0);
|
||||
write8(SI1145_REG_IRQMODE1, 0);
|
||||
write8(SI1145_REG_IRQMODE2, 0);
|
||||
write8(SI1145_REG_INTCFG, 0);
|
||||
write8(SI1145_REG_IRQSTAT, 0xFF);
|
||||
write8(SI1145_REG_COMMAND, SI1145_RESET);
|
||||
delay(10);
|
||||
write8(SI1145_REG_HWKEY, 0x17);
|
||||
delay(10);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// returns the UV index * 100 (divide by 100 to get the index)
|
||||
uint16_t Adafruit_SI1145::readUV(void) { return read16(0x2C); }
|
||||
// returns visible+IR light levels
|
||||
uint16_t Adafruit_SI1145::readVisible(void) { return read16(0x22); }
|
||||
// returns IR light levels
|
||||
uint16_t Adafruit_SI1145::readIR(void) { return read16(0x24); }
|
||||
|
||||
// returns "Proximity" - assumes an IR LED is attached to LED
|
||||
uint16_t Adafruit_SI1145::readProx(void) {
|
||||
return read16(0x26);
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
uint8_t Adafruit_SI1145::writeParam(uint8_t p, uint8_t v) {
|
||||
//Serial.print("Param 0x"); Serial.print(p, HEX);
|
||||
//Serial.print(" = 0x"); Serial.println(v, HEX);
|
||||
write8(SI1145_REG_PARAMWR, v);
|
||||
write8(SI1145_REG_COMMAND, p | SI1145_PARAM_SET);
|
||||
return read8(SI1145_REG_PARAMRD);
|
||||
}
|
||||
|
||||
uint8_t Adafruit_SI1145::readParam(uint8_t p) {
|
||||
write8(SI1145_REG_COMMAND, p | SI1145_PARAM_QUERY);
|
||||
return read8(SI1145_REG_PARAMRD);
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
uint8_t Adafruit_SI1145::read8(uint8_t reg) {
|
||||
uint16_t val;
|
||||
Wire.beginTransmission(_addr);
|
||||
Wire.write((uint8_t)reg);
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom((uint8_t)_addr, (uint8_t)1);
|
||||
return Wire.read();
|
||||
}
|
||||
|
||||
uint16_t Adafruit_SI1145::read16(uint8_t a) {
|
||||
uint16_t ret;
|
||||
Wire.beginTransmission(_addr); // start transmission to device
|
||||
Wire.write(a); // sends register address to read from
|
||||
Wire.endTransmission(); // end transmission
|
||||
Wire.requestFrom(_addr, (uint8_t)2);// send data n-bytes read
|
||||
ret = Wire.read(); // receive DATA
|
||||
ret |= (uint16_t)Wire.read() << 8; // receive DATA
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Adafruit_SI1145::write8(uint8_t reg, uint8_t val) {
|
||||
Wire.beginTransmission(_addr); // start transmission to device
|
||||
Wire.write(reg); // sends register address to write
|
||||
Wire.write(val); // sends value
|
||||
Wire.endTransmission(); // end transmission
|
||||
}
|
||||
|
||||
Adafruit_SI1145 uv = Adafruit_SI1145();
|
||||
|
||||
boolean Plugin_113_init = false;
|
||||
|
||||
boolean Plugin_113(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success=false;
|
||||
|
||||
switch(function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_113;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_I2C;
|
||||
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_113);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_113));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_113));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_113));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
if (!Plugin_113_init)
|
||||
{
|
||||
// ziadnu EXTRA inicializaciu netreba ...
|
||||
if (uv.begin()) { Plugin_113_init = true; }
|
||||
}
|
||||
float si1145_vis = 0.0;
|
||||
float si1145_ir = 0.0;
|
||||
float si1145_uv = 0.0;
|
||||
si1145_vis = uv.readVisible();
|
||||
si1145_ir = uv.readIR();
|
||||
si1145_uv = uv.readUV();
|
||||
si1145_uv /= 100.0;
|
||||
|
||||
UserVar[event->BaseVarIndex] = si1145_vis;
|
||||
UserVar[event->BaseVarIndex+1] = si1145_ir;
|
||||
UserVar[event->BaseVarIndex+2] = si1145_uv;
|
||||
|
||||
String log = F("SI1145: Visible: ");
|
||||
log += UserVar[event->BaseVarIndex];
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
log = F("SI1145: Infrared: ");
|
||||
log += UserVar[event->BaseVarIndex+1];
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
log = F("SI1145: UV index: ");
|
||||
log += UserVar[event->BaseVarIndex+2];
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
success=true;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,178 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin 114: DMS501A #############################################
|
||||
//#################################### by serpa #############################################
|
||||
//#######################################################################################################
|
||||
|
||||
#define PLUGIN_114
|
||||
#define PLUGIN_ID_114 114
|
||||
#define PLUGIN_NAME_114 "Dust sensor - DSM501a [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_114 "PM1.0" // from the datasheet the detection is from PM1 and up. You could have from PM1 to PM2.5, on subtracting PM2.5 value on PM1 value. This value come from the pin #4
|
||||
#define PLUGIN_VALUENAME2_114 "PM2.5" // from the datasheet the detection is from PM2.5 and up. This value come from the pin #2. With different resistor topn the pin #1, you could adjust the size threshold detection
|
||||
|
||||
unsigned long Plugin_114_pulseCounter[TASKS_MAX];
|
||||
unsigned long Plugin_114_pulseTotalCounter[TASKS_MAX];
|
||||
unsigned long Plugin_114_pulseTime[TASKS_MAX];
|
||||
unsigned long Plugin_114_pulseTimePrevious[TASKS_MAX];
|
||||
unsigned long tstart1, tstart2;
|
||||
unsigned long tduration = 30000; // duration of measurement in ms
|
||||
//unsigned long triggerOn; // start of pulse time in us
|
||||
//unsigned long triggerOff; // end of pulse time in us
|
||||
//unsigned long lowpulseoccupancy; // duration of pulse in us
|
||||
volatile unsigned long thigh1, thigh2;
|
||||
volatile unsigned long tlow1, tlow2;
|
||||
volatile unsigned long startlow1, startlow2;
|
||||
volatile unsigned long starthigh1, starthigh2;
|
||||
volatile boolean done1, done2;
|
||||
volatile boolean value1, value2;
|
||||
//boolean trigger = false;
|
||||
volatile float ratio1, ratio2;
|
||||
volatile int DMSpin1, DMSpin2;
|
||||
float dens1, dens2;
|
||||
boolean Plugin_114(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_114;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_DUAL;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_TEMP_HUM;
|
||||
Device[deviceCount].Ports = 0;
|
||||
Device[deviceCount].PullUpOption = false;
|
||||
Device[deviceCount].InverseLogicOption = false;
|
||||
Device[deviceCount].FormulaOption = true;
|
||||
Device[deviceCount].ValueCount = 2;
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
Device[deviceCount].TimerOption = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_114);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_114));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_114));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
char tmpString[128];
|
||||
sprintf_P(tmpString, PSTR("<TR><TD>Averaging Time (mSec):<TD><input type='text' name='plugin_114' value='%u'>"), Settings.TaskDevicePluginConfig[event->TaskIndex][0]);
|
||||
string += tmpString;
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
String plugin1 = WebServer.arg("plugin_114");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
|
||||
// tduration= Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
String log = F("INIT : DSM501A ");
|
||||
log += Settings.TaskDevicePin1[event->TaskIndex];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
// tduration= Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
tstart1 = millis();
|
||||
startlow1 = micros();
|
||||
starthigh1 = startlow1;
|
||||
DMSpin1 = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
pinMode(DMSpin1, INPUT);
|
||||
attachInterrupt(digitalPinToInterrupt(DMSpin1), Plugin_114_ISR1, CHANGE);
|
||||
tstart2 = millis();
|
||||
startlow2 = micros();
|
||||
starthigh2 = startlow2;
|
||||
DMSpin2 = Settings.TaskDevicePin2[event->TaskIndex];
|
||||
pinMode(DMSpin2, INPUT);
|
||||
attachInterrupt(digitalPinToInterrupt(DMSpin2), Plugin_114_ISR2, CHANGE);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
if (done1 && done2) {
|
||||
done1 = FALSE;
|
||||
dens1 = ratio1 * 110; // ug/m^3
|
||||
done2 = FALSE;
|
||||
dens2 = ratio2 * 110; // ug/m^3
|
||||
|
||||
String log = F("DSM501A: PM1.0=");
|
||||
log += dens1;
|
||||
log += F(" PM2.5=");
|
||||
log += dens2;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
UserVar[event->BaseVarIndex] = (float) dens1;
|
||||
UserVar[event->BaseVarIndex + 1] = (float) dens2;
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
Check Pulse (called from irq handler)
|
||||
\*********************************************************************************************/
|
||||
void Plugin_114_ISR1()
|
||||
{
|
||||
value1 = digitalRead(DMSpin1); //read input pin just changed
|
||||
if (value1 == 0) { // gone low
|
||||
startlow1 = micros(); // record starting of low period
|
||||
thigh1 += startlow1 - starthigh1; // record duration of past high state
|
||||
} else { // gone high
|
||||
starthigh1 = micros(); // record starting of high period
|
||||
tlow1 += starthigh1 - startlow1; // record duration of past low state
|
||||
}
|
||||
if (millis() > tstart1 + tduration) { // check if average time has past
|
||||
tstart1 = millis(); // reset time period
|
||||
ratio1 = float(tlow1) / float(thigh1 + tlow1) * 100; // compute ratio low to total
|
||||
tlow1 = 0; // reset low time counter
|
||||
thigh1 = 0; // reset high time counter
|
||||
done1 = TRUE; // set reading complete flag
|
||||
}
|
||||
}
|
||||
/*********************************************************************************************\
|
||||
Check Pulse (called from irq handler)
|
||||
\*********************************************************************************************/
|
||||
void Plugin_114_ISR2()
|
||||
{
|
||||
value2 = digitalRead(DMSpin2); //read input pin just changed
|
||||
if (value2 == 0) { // gone low
|
||||
startlow2 = micros(); // record starting of low period
|
||||
thigh2 += startlow2 - starthigh2; // record duration of past high state
|
||||
} else { // gone high
|
||||
starthigh2 = micros(); // record starting of high period
|
||||
tlow2 += starthigh2 - startlow2; // record duration of past low state
|
||||
}
|
||||
if (millis() > tstart2 + tduration) { // check if average time has past
|
||||
tstart2 = millis(); // reset time period
|
||||
ratio2 = float(tlow2) / float(thigh2 + tlow2) * 100; // compute ratio low to high
|
||||
tlow2 = 0; // reset low time counter
|
||||
thigh2 = 0; // reset high time counter
|
||||
done2 = TRUE; // set reading complete flag
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin 116: Serial RFID ID-12 ####################################
|
||||
//#######################################################################################################
|
||||
|
||||
#define PLUGIN_116
|
||||
#define PLUGIN_ID_116 116
|
||||
#define PLUGIN_NAME_116 "RFID Reader - ID12LA [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_116 "Tag"
|
||||
|
||||
boolean Plugin_116_init = false;
|
||||
|
||||
boolean Plugin_116(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_116;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_LONG;
|
||||
Device[deviceCount].Ports = 0;
|
||||
Device[deviceCount].PullUpOption = false;
|
||||
Device[deviceCount].InverseLogicOption = false;
|
||||
Device[deviceCount].FormulaOption = false;
|
||||
Device[deviceCount].ValueCount = 1;
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
Device[deviceCount].TimerOption = false;
|
||||
Device[deviceCount].GlobalSyncOption = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_116);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_116));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
Plugin_116_init = true;
|
||||
Serial.begin(9600);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case PLUGIN_SERIAL_IN:
|
||||
{
|
||||
if (Plugin_116_init)
|
||||
{
|
||||
byte val = 0;
|
||||
byte code[6];
|
||||
byte checksum = 0;
|
||||
byte bytesread = 0;
|
||||
byte tempbyte = 0;
|
||||
|
||||
if ((val = Serial.read()) == 2)
|
||||
{ // check for header
|
||||
bytesread = 0;
|
||||
while (bytesread < 12) { // read 10 digit code + 2 digit checksum
|
||||
if ( Serial.available() > 0) {
|
||||
val = Serial.read();
|
||||
if ((val == 0x0D) || (val == 0x0A) || (val == 0x03) || (val == 0x02)) {
|
||||
// if header or stop bytes before the 10 digit reading
|
||||
break;
|
||||
}
|
||||
|
||||
// Do Ascii/Hex conversion:
|
||||
if ((val >= '0') && (val <= '9')) {
|
||||
val = val - '0';
|
||||
}
|
||||
else if ((val >= 'A') && (val <= 'F')) {
|
||||
val = 10 + val - 'A';
|
||||
}
|
||||
|
||||
// Every two hex-digits, add byte to code:
|
||||
if (bytesread & 1 == 1) {
|
||||
// make some space for this hex-digit by
|
||||
// shifting the previous hex-digit with 4 bits to the left:
|
||||
code[bytesread >> 1] = (val | (tempbyte << 4));
|
||||
|
||||
if (bytesread >> 1 != 5) { // If we're at the checksum byte,
|
||||
checksum ^= code[bytesread >> 1]; // Calculate the checksum... (XOR)
|
||||
};
|
||||
}
|
||||
else {
|
||||
tempbyte = val; // Store the first hex digit first...
|
||||
};
|
||||
bytesread++; // ready to read next digit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bytesread == 12)
|
||||
{
|
||||
if (code[5] == checksum)
|
||||
{
|
||||
// temp woraround, ESP Easy framework does not currently prepare this...
|
||||
byte index = 0;
|
||||
for (byte y = 0; y < TASKS_MAX; y++)
|
||||
if (Settings.TaskDeviceNumber[y] == PLUGIN_ID_116)
|
||||
index = y;
|
||||
byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[index]);
|
||||
event->TaskIndex = index;
|
||||
event->BaseVarIndex = index * VARS_PER_TASK;
|
||||
event->idx = Settings.TaskDeviceID[index];
|
||||
event->sensorType = Device[DeviceIndex].VType;
|
||||
// endof workaround
|
||||
|
||||
unsigned long key = 0;
|
||||
for (byte i = 1; i < 5; i++) key = key | (((unsigned long) code[i] << ((4 - i) * 8)));
|
||||
UserVar[event->BaseVarIndex] = (key & 0xFFFF);
|
||||
UserVar[event->BaseVarIndex + 1] = ((key >> 16) & 0xFFFF);
|
||||
String log = F("RFID : Tag: ");
|
||||
log += key;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
sendData(event);
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,434 @@
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin 118: MPU6050 [Testing] ####################################
|
||||
//#######################################################################################################
|
||||
|
||||
// Based on the works of Nolan Gilley @ https://home-assistant.io/blog/2016/08/03/laundry-automation-update/
|
||||
// falling under the following license CC-BY-SA, https://creativecommons.org/licenses/by-sa/2.0/
|
||||
// and the works of Jeff Rowberg @ https://www.i2cdevlib.com/devices/mpu6050, specifically his I2C Functions
|
||||
// in this plugin are based on or are a copy from the following two libraries:
|
||||
// I2Cdev: https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/I2Cdev
|
||||
// MPU6050: https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050
|
||||
// Which contain the following license information:
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
// associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions: The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
|
||||
|
||||
// This plugin enables the use of a MPU6050 sensor as e.g. used in the breakoutboard GY-521.
|
||||
// Using the webform you can set thresholds for the x-y-z axis and timeout values. If the thresholds are
|
||||
// exceeded the sensor is on, if the thresholds are not met during the timeout period the sensor is off.
|
||||
|
||||
// Using this plugin you can get a notification from your home automationsystem when the monitored machine or
|
||||
// device is no longer vibrating and thus this can be used as a signaling device for the end of a (dish)washer
|
||||
// or dryer cycle.
|
||||
|
||||
// You can also use the plugin to read raw sensorvalues. You can use more then one instance of the plugin and
|
||||
// you can set multiple movement alarms by giving each instance other threshold values if needed.
|
||||
|
||||
// Best practise: Create three custom sensors in your homecontroller (like domoticz) and let it plot the x, y and
|
||||
// z range. Plot the sensorvalues while you use the washingmachine and/or dryer. Also keep monitoring when they
|
||||
// are not in use so you can determine the needed thresholds. When you have these you can select the movement
|
||||
// detection function to setup the plugin for further use.
|
||||
|
||||
// The plugin can simultanious be used with two MPU6050 devices by adding multiple instances.
|
||||
|
||||
// Plugin var usage:
|
||||
// Globals - int16_t _P118_axis[3][5][2] Array to store sensorvalues of the axis
|
||||
// _P118_axis[0-2][x][x] = x, y, z axis
|
||||
// _P118_axis[x][0-4][x] = min values, max values, range (max-min), a-values, g-values.
|
||||
// _P118_axis[x][x][0-1] = device address: 0=0x68, 1=0x69
|
||||
// - long _P118_time[2] = Timer to check values each 5 seconds for each used device address.
|
||||
|
||||
// Framework - Settings.TaskDevicePluginConfig[x][0] - Device address (0x68 | 0x69)
|
||||
// Settings.TaskDevicePluginConfig[x][1] - Instance function
|
||||
// Settings.TaskDevicePluginConfig[x][2] - ax threshold value
|
||||
// Settings.TaskDevicePluginConfig[x][3] - ay threshold value
|
||||
// Settings.TaskDevicePluginConfig[x][4] - az threshold value
|
||||
// Settings.TaskDevicePluginConfig[x][5] - Minimal detection threshold value
|
||||
// Settings.TaskDevicePluginConfig[x][6] - Detection threshold window value
|
||||
// Settings.TaskDevicePluginConfig[x][7] - Last known status of switch
|
||||
// Settings.TaskDevicePluginConfigLong[x][0] - Minimal detection threshold counter
|
||||
// Settings.TaskDevicePluginConfigLong[x][1] - Detection threshold window counter
|
||||
|
||||
#ifdef PLUGIN_BUILD_TESTING
|
||||
|
||||
#define MPU6050_RA_GYRO_CONFIG 0x1B
|
||||
#define MPU6050_RA_ACCEL_CONFIG 0x1C
|
||||
#define MPU6050_RA_ACCEL_XOUT_H 0x3B
|
||||
#define MPU6050_RA_PWR_MGMT_1 0x6B
|
||||
#define MPU6050_ACONFIG_AFS_SEL_BIT 4
|
||||
#define MPU6050_ACONFIG_AFS_SEL_LENGTH 2
|
||||
#define MPU6050_GCONFIG_FS_SEL_BIT 4
|
||||
#define MPU6050_GCONFIG_FS_SEL_LENGTH 2
|
||||
#define MPU6050_CLOCK_PLL_XGYRO 0x01
|
||||
#define MPU6050_GYRO_FS_250 0x00
|
||||
#define MPU6050_ACCEL_FS_2 0x00
|
||||
#define MPU6050_PWR1_SLEEP_BIT 6
|
||||
#define MPU6050_PWR1_CLKSEL_BIT 2
|
||||
#define MPU6050_PWR1_CLKSEL_LENGTH 3
|
||||
|
||||
#define PLUGIN_118
|
||||
#define PLUGIN_ID_118 118
|
||||
#define PLUGIN_NAME_118 "MPU 6050 [TESTING]"
|
||||
#define PLUGIN_VALUENAME1_118 ""
|
||||
|
||||
int16_t _P118_axis[3][5][2]; // [xyz], [min/max/range,a,g], [0x68/0x69]
|
||||
long _P118_time[2];
|
||||
|
||||
boolean Plugin_118(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_118;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_I2C;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_SINGLE;
|
||||
Device[deviceCount].ValueCount = 1; // Unfortunatly domoticz has no custom multivalue sensors.
|
||||
Device[deviceCount].SendDataOption = true; // and I use Domoticz ... so there.
|
||||
Device[deviceCount].TimerOption = true;
|
||||
Device[deviceCount].FormulaOption = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_118);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_118));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
// Setup webform for address selection
|
||||
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
String options[10];
|
||||
options[0] = F("0x68 - default settings (ADDR Low)");
|
||||
options[1] = F("0x69 - alternate settings (ADDR High)");
|
||||
int optionValues[2];
|
||||
optionValues[0] = 0x68;
|
||||
optionValues[1] = 0x69;
|
||||
string += F("<TR><TD>I2C Address:<TD><select name='plugin_118_address'>");
|
||||
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>");
|
||||
// Setup webform for instance function
|
||||
choice = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
|
||||
string += F("<TR><TD>Function:<TD><select name='plugin_118_function'>");
|
||||
options[0] = F("Movement detection");
|
||||
options[1] = F("Range acceleration X");
|
||||
options[2] = F("Range acceleration Y");
|
||||
options[3] = F("Range acceleration Z");
|
||||
options[4] = F("Acceleration X");
|
||||
options[5] = F("Acceleration Y");
|
||||
options[6] = F("Acceleration Z");
|
||||
options[7] = F("G-force X");
|
||||
options[8] = F("G-force Y");
|
||||
options[9] = F("G-force Z");
|
||||
for (byte x = 0; x < 10; x++)
|
||||
{
|
||||
string += F("<option value='");
|
||||
string += x;
|
||||
string += "'";
|
||||
if (choice == x)
|
||||
string += F(" selected");
|
||||
string += ">";
|
||||
string += options[x];
|
||||
string += F("</option>");
|
||||
}
|
||||
string += F("</select>");
|
||||
if (choice == 0) {
|
||||
// If this is instance function 0, setup webform for additional vars
|
||||
// Show some user information about the webform and what the vars mean.
|
||||
string += F("<TR><TD><TD>The thresholdvalues (0-65535) can be used to set a threshold for one or more<br>");
|
||||
string += F("axis. The axis will trigger when the range for that axis exceeds the threshold<br>");
|
||||
string += F("value. A value of 0 disables movement detection for that axis.");
|
||||
string += F("<TR><TD>Detection threshold X:<TD><input type='text' size='6' maxlength='6' name='plugin_118_threshold_x' value='");
|
||||
string += Settings.TaskDevicePluginConfig[event->TaskIndex][2];
|
||||
string += F("'>");
|
||||
string += F("<TR><TD>Detection threshold Y:<TD><input type='text' size='6' maxlength='6' name='plugin_118_threshold_y' value='");
|
||||
string += Settings.TaskDevicePluginConfig[event->TaskIndex][3];
|
||||
string += F("'>");
|
||||
string += F("<TR><TD>Detection threshold Z:<TD><input type='text' size='6' maxlength='6' name='plugin_118_threshold_z' value='");
|
||||
string += Settings.TaskDevicePluginConfig[event->TaskIndex][4];
|
||||
string += F("'>");
|
||||
string += F("<TR><TD><TD>Each 30 seconds a counter for the detection window is increased plus all axis<br>");
|
||||
string += F("are checked and if they *all* exceeded the threshold values, a counter is increased.<br>");
|
||||
string += F("Each period, defined by the [detection window], the counter is checked against<br>");
|
||||
string += F("the [min. detection count] and if found equal or larger, movement is detected.<br>");
|
||||
string += F("If in the next window the [min. detection count] value is not met, movement has stopped.");
|
||||
string += F("The [detection window] cannot be smaller than the [min. detection count].");
|
||||
string += F("<TR><TD>Min. detection count:<TD><input type='text' size='6' maxlength='6' name='plugin_118_threshold_counter' value='");
|
||||
string += Settings.TaskDevicePluginConfig[event->TaskIndex][5];
|
||||
string += F("'>");
|
||||
string += F("<TR><TD>Detection window:<TD><input type='text' size='6' maxlength='6' name='plugin_118_threshold_window' value='");
|
||||
string += Settings.TaskDevicePluginConfig[event->TaskIndex][6];
|
||||
string += F("'>");
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
// Save the vars
|
||||
String plugin1 = WebServer.arg("plugin_118_address");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
|
||||
plugin1 = WebServer.arg("plugin_118_function");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][1] = plugin1.toInt();
|
||||
plugin1 = WebServer.arg("plugin_118_threshold_x");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][2] = plugin1.toInt();
|
||||
plugin1 = WebServer.arg("plugin_118_threshold_y");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = plugin1.toInt();
|
||||
plugin1 = WebServer.arg("plugin_118_threshold_z");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = plugin1.toInt();
|
||||
plugin1 = WebServer.arg("plugin_118_threshold_counter");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][5] = plugin1.toInt();
|
||||
plugin1 = WebServer.arg("plugin_118_threshold_window");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = plugin1.toInt();
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][6] < Settings.TaskDevicePluginConfig[event->TaskIndex][5]) {
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][6] = Settings.TaskDevicePluginConfig[event->TaskIndex][5];
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
// Initialize the MPU6050. This *can* be done multiple times per instance and device address.
|
||||
// We could make sure that this is only done once per device address, but why bother?
|
||||
uint8_t devAddr = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
if ((devAddr < 0x68) || (devAddr > 0x69)) { // Just in case the address is not initialized, set it anyway.
|
||||
devAddr = 0x68;
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = devAddr;
|
||||
}
|
||||
// Initialize the MPU6050, for details look at the MPU6050 library: MPU6050::Initialize
|
||||
_P118_writeBits(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_CLKSEL_BIT, MPU6050_PWR1_CLKSEL_LENGTH, MPU6050_CLOCK_PLL_XGYRO);
|
||||
_P118_writeBits(devAddr, MPU6050_RA_GYRO_CONFIG, MPU6050_GCONFIG_FS_SEL_BIT, MPU6050_GCONFIG_FS_SEL_LENGTH, MPU6050_GYRO_FS_250);
|
||||
_P118_writeBits(devAddr, MPU6050_RA_ACCEL_CONFIG, MPU6050_ACONFIG_AFS_SEL_BIT, MPU6050_ACONFIG_AFS_SEL_LENGTH, MPU6050_ACCEL_FS_2);
|
||||
_P118_writeBits(devAddr, MPU6050_RA_PWR_MGMT_1, MPU6050_PWR1_SLEEP_BIT, 1, 0);
|
||||
|
||||
// Read the MPU6050 once to clear out zeros (1st time reading MPU6050 returns all 0s)
|
||||
int16_t ax, ay, az, gx, gy, gz;
|
||||
_P118_getMotion6(devAddr, &ax, &ay, &az, &gx, &gy, &gz);
|
||||
|
||||
// Reset vars
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][7] = 0; // Last known value of "switch" is off
|
||||
UserVar[event->BaseVarIndex] = 0; // Switch is off
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = 0; // Minimal detection counter is zero
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1] = 0; // Detection window counter is zero
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_ONCE_A_SECOND:
|
||||
{
|
||||
uint8_t devAddr = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
byte dev = devAddr & 1;
|
||||
|
||||
// Read the sensorvalues, we run this bit every 1/10th of a second
|
||||
_P118_getMotion6(devAddr, &_P118_axis[0][3][dev], &_P118_axis[1][3][dev], &_P118_axis[2][3][dev], &_P118_axis[0][4][dev], &_P118_axis[1][4][dev], &_P118_axis[2][4][dev]);
|
||||
// Set the minimum and maximum value for each axis a-value, overwrite previous values if smaller/larger
|
||||
_P118_trackMinMax(_P118_axis[0][3][dev], &_P118_axis[0][0][dev], &_P118_axis[0][1][dev]);
|
||||
_P118_trackMinMax(_P118_axis[1][3][dev], &_P118_axis[1][0][dev], &_P118_axis[1][1][dev]);
|
||||
_P118_trackMinMax(_P118_axis[2][3][dev], &_P118_axis[2][0][dev], &_P118_axis[2][1][dev]);
|
||||
// ^ current value @ 3 ^ min val @ 0 ^ max val @ 1
|
||||
|
||||
/* // Uncomment this block if you want to debug your MPU6050, but be prepared for a log overload
|
||||
String log = F("MPU6050 : axis values: ");
|
||||
log += _P118_axis[0][3][dev]
|
||||
log += F(", ");
|
||||
log += _P118_axis[1][3][dev];
|
||||
log += F(", ");
|
||||
log += _P118_axis[2][3][dev];
|
||||
log += F(", g values: ");
|
||||
log += _P118_axis[0][4][dev];
|
||||
log += F(", ");
|
||||
log += _P118_axis[1][4][dev];
|
||||
log += F(", ");
|
||||
log += _P118_axis[2][4][dev];
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
*/
|
||||
// Run this bit every 5 seconds per deviceaddress (not per instance)
|
||||
if ((_P118_time[dev] + 5000) < millis())
|
||||
{
|
||||
_P118_time[dev] = millis();
|
||||
|
||||
// Determine the maximum measured range of each axis
|
||||
for (uint8_t i=0; i<3; i++) {
|
||||
_P118_axis[i][2][dev] = abs(_P118_axis[i][1][dev] - _P118_axis[i][0][dev]);
|
||||
_P118_axis[i][0][dev] = _P118_axis[i][3][dev];
|
||||
_P118_axis[i][1][dev] = _P118_axis[i][3][dev];
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
int devAddr = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
byte dev = devAddr & 1;
|
||||
int _P118_Function = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
|
||||
switch (_P118_Function)
|
||||
{
|
||||
// Function 0 is for movement detection
|
||||
case 0:
|
||||
{
|
||||
// Check if all (enabled, so !=0) thresholds are exceeded, if one fails then thresexceed (thesholds exceeded) is reset to false;
|
||||
boolean thresexceed = true;
|
||||
byte count = 0; // Counter to check if not all thresholdvalues are set to 0 or disabled
|
||||
for (byte i=0; i<3; i++)
|
||||
{
|
||||
// for each axis:
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][i + 2] != 0) { // not disabled, check threshold
|
||||
if (_P118_axis[i][2][dev] < Settings.TaskDevicePluginConfig[event->TaskIndex][i + 2]) { thresexceed = false; }
|
||||
} else { count++; } // If disabled count + 1
|
||||
}
|
||||
if (count == 3) { thresexceed = false; } // If we counted to three, all three axis are disabled.
|
||||
|
||||
// If all enabled thresholds are exceeded the increase the counter
|
||||
if (thresexceed) { Settings.TaskDevicePluginConfigLong[event->TaskIndex][0]++; }
|
||||
// And increase the window counter
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1]++;
|
||||
|
||||
if (Settings.TaskDevicePluginConfigLong[event->TaskIndex][1] >= Settings.TaskDevicePluginConfig[event->TaskIndex][6]) {
|
||||
// Detection window has passed.
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][1] = 0; // reset window counter
|
||||
|
||||
// Did we count more times exceeded then the minimum detection value?
|
||||
if (Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] >= Settings.TaskDevicePluginConfig[event->TaskIndex][5]) {
|
||||
UserVar[event->BaseVarIndex] = 1; // x times threshold exceeded within window.
|
||||
} else {
|
||||
UserVar[event->BaseVarIndex] = 0; // reset because x times threshold within window not met.
|
||||
}
|
||||
|
||||
// Check if UserVar changed so we do not overload homecontroller with the same readings
|
||||
if (Settings.TaskDevicePluginConfigLong[event->TaskIndex][7] != UserVar[event->BaseVarIndex]) {
|
||||
success = true; // Update switch status
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][7] = UserVar[event->BaseVarIndex];
|
||||
} else {
|
||||
success = false; // Do not update switch status
|
||||
}
|
||||
Settings.TaskDevicePluginConfigLong[event->TaskIndex][0] = 0; // reset threshold exceeded counter
|
||||
}
|
||||
// The default sensorType of the device is a single sensor value. But for detection movement we want it to be
|
||||
// a switch so we change the sensortype here. Looks like a legal thing to do because _P001_Switch does it as well.
|
||||
event->sensorType = SENSOR_TYPE_SWITCH;
|
||||
break;
|
||||
}
|
||||
// All other functions are reading values. So extract xyz value and wanted type from function number:
|
||||
default: // [1-3]: range-values, [4-6]: a-values, [7-9]: g-values
|
||||
{
|
||||
uint8_t reqaxis = (_P118_Function - 1) % 3; // xyz -> eg: function 5(ay) (5-1) % 3 = 1 (y)
|
||||
uint8_t reqvar = ((_P118_Function - 1) / 3) + 2; // range, a, g -> eg: function 9(gz) ((9-1) / 3 = 2) + 2 = 4 (g)
|
||||
UserVar[event->BaseVarIndex] = float(_P118_axis[reqaxis][reqvar][dev]);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void _P118_trackMinMax(int16_t current, int16_t *min, int16_t *max)
|
||||
// From nodemcu-laundry.ino by Nolan Gilley
|
||||
{
|
||||
if (current > *max)
|
||||
{
|
||||
*max = current;
|
||||
}
|
||||
else if (current < *min)
|
||||
{
|
||||
*min = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Get raw 6-axis motion sensor readings (accel/gyro).
|
||||
* Retrieves all currently available motion sensor values.
|
||||
* @param devAddr I2C slave device address
|
||||
* @param ax 16-bit signed integer container for accelerometer X-axis value
|
||||
* @param ay 16-bit signed integer container for accelerometer Y-axis value
|
||||
* @param az 16-bit signed integer container for accelerometer Z-axis value
|
||||
* @param gx 16-bit signed integer container for gyroscope X-axis value
|
||||
* @param gy 16-bit signed integer container for gyroscope Y-axis value
|
||||
* @param gz 16-bit signed integer container for gyroscope Z-axis value
|
||||
*/
|
||||
void _P118_getMotion6(uint8_t devAddr, int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz) {
|
||||
// From I2Cdev::readBytes and MPU6050::getMotion6, both by Jeff Rowberg
|
||||
uint8_t buffer[14];
|
||||
uint8_t count = 0;
|
||||
Wire.beginTransmission(devAddr);
|
||||
Wire.write(MPU6050_RA_ACCEL_XOUT_H);
|
||||
Wire.endTransmission();
|
||||
Wire.beginTransmission(devAddr);
|
||||
Wire.requestFrom(devAddr, (uint8_t)14);
|
||||
for (; Wire.available(); count++) {
|
||||
buffer[count] = Wire.read();
|
||||
}
|
||||
*ax = (((int16_t)buffer[0]) << 8) | buffer[1];
|
||||
*ay = (((int16_t)buffer[2]) << 8) | buffer[3];
|
||||
*az = (((int16_t)buffer[4]) << 8) | buffer[5];
|
||||
*gx = (((int16_t)buffer[8]) << 8) | buffer[9];
|
||||
*gy = (((int16_t)buffer[10]) << 8) | buffer[11];
|
||||
*gz = (((int16_t)buffer[12]) << 8) | buffer[13];
|
||||
}
|
||||
|
||||
/** Write multiple bits in an 8-bit device register.
|
||||
* @param devAddr I2C slave device address
|
||||
* @param regAddr Register regAddr to write to
|
||||
* @param bitStart First bit position to write (0-7)
|
||||
* @param length Number of bits to write (not more than 8)
|
||||
* @param data Right-aligned value to write
|
||||
*/
|
||||
void _P118_writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data) {
|
||||
// From I2Cdev::writeBits by Jeff Rowberg
|
||||
// 010 value to write
|
||||
// 76543210 bit numbers
|
||||
// xxx args: bitStart=4, length=3
|
||||
// 00011100 mask byte
|
||||
// 10101111 original value (sample)
|
||||
// 10100011 original & ~mask
|
||||
// 10101011 masked | value
|
||||
uint8_t b;
|
||||
Wire.beginTransmission(devAddr);
|
||||
Wire.write(regAddr);
|
||||
Wire.endTransmission();
|
||||
Wire.requestFrom(devAddr, uint8_t(1));
|
||||
if (Wire.available()) {
|
||||
b = Wire.read();
|
||||
uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1);
|
||||
data <<= (bitStart - length + 1); // shift data into correct position
|
||||
data &= mask; // zero all non-important bits in data
|
||||
b &= ~(mask); // zero all important bits in existing byte
|
||||
b |= data; // combine data with existing byte
|
||||
Wire.beginTransmission(devAddr);
|
||||
Wire.write(regAddr);
|
||||
Wire.write(b);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,292 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//######################## Plugin 120: Thermocouple (MAX6675 / MAX31855) ################################
|
||||
//#######################################################################################################
|
||||
|
||||
// Plugin Description
|
||||
// This Plugin reads the data from Thermocouples. You have to use an Adapter Board with a
|
||||
// MAX6675 or MAX31855 in order to read the values. Take a look at ebay to find such boards :-)
|
||||
// You can only use ESP8266 boards which expose the SPI Interface. This Plugin uses only the Hardware
|
||||
// SPI Interface - no software SPI at the moment.
|
||||
// But neverless you need at least 3 Pins to use SPI. So using an very simple ESP-01 is no option - Sorry.
|
||||
// The Wiring ist straight forward ...
|
||||
//
|
||||
// If you like to send suggestions feel free to send me an email : dominik@logview.info
|
||||
// Have fun ... Dominik
|
||||
|
||||
// Wiring
|
||||
// https://de.wikipedia.org/wiki/Serial_Peripheral_Interface
|
||||
// You need an ESP8266 device with accessable SPI Pins. These are:
|
||||
// Name Description GPIO NodeMCU Notes
|
||||
// MOSI Master Output GPIO13 D7 Not used (No Data sending to MAX)
|
||||
// MISO Master Input GPIO12 D6 Hardware SPI
|
||||
// SCK Clock Output GPIO14 D5 Hardware SPI
|
||||
// CS Chip Select GPIO15 D8 Hardware SPI (CS is configurable through the web interface)
|
||||
|
||||
// Thermocouple Infos
|
||||
// http://www.bristolwatch.com/ele2/therc.htm
|
||||
|
||||
// Chips
|
||||
// MAX6675 - Cold-Junction-Compensated K-Thermocouple-to-Digital Converter ( 0°C to +1024°C)
|
||||
// https://cdn-shop.adafruit.com/datasheets/MAX6675.pdf (only
|
||||
// MAX31855 - Cold-Junction Compensated Thermocouple-to-Digital Converter (-270°C to +1800°C)
|
||||
// https://cdn-shop.adafruit.com/datasheets/MAX31855.pdf
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
#define PLUGIN_120
|
||||
#define PLUGIN_ID_120 120
|
||||
#define PLUGIN_NAME_120 "Temperature Thermocouple [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_120 "Temperature C"
|
||||
#define PLUGIN_VALUENAME2_120 "Temperature K"
|
||||
|
||||
uint8_t Plugin_120_SPI_CS_Pin = 15; // D8
|
||||
bool Plugin_120_SensorAttached = true;
|
||||
double Plugin_120_Celsius = 0.0;
|
||||
|
||||
boolean Plugin_120(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_120;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_SINGLE;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_DUAL; // 2 Messwerte
|
||||
Device[deviceCount].Ports = 0;
|
||||
Device[deviceCount].PullUpOption = false;
|
||||
Device[deviceCount].InverseLogicOption = false;
|
||||
Device[deviceCount].FormulaOption = true;
|
||||
Device[deviceCount].ValueCount = 2;
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
Device[deviceCount].TimerOption = true;
|
||||
Device[deviceCount].GlobalSyncOption = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_120);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_120));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_120));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
// Get CS Pin
|
||||
// If no Pin is in Config we use 15 as default -> Hardware Chip Select on ESP8266
|
||||
if (Settings.TaskDevicePin1[event->TaskIndex] != 0)
|
||||
{
|
||||
// Konvert the GPIO Pin to a Dogotal Puin Number first ...
|
||||
Plugin_120_SPI_CS_Pin = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
}
|
||||
|
||||
// set the slaveSelectPin as an output:
|
||||
pinMode(Plugin_120_SPI_CS_Pin, OUTPUT);
|
||||
// initialize SPI:
|
||||
SPI.setHwCs(false);
|
||||
SPI.begin();
|
||||
|
||||
addLog(LOG_LEVEL_INFO, (char*)"P120 : SPI Init");
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
string += F("<TR><TD>Info GPIO:<TD><b>1st GPIO</b> = CS (Usable GPIOs : 0, 2, 4, 5, 15)");
|
||||
|
||||
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
String options[2];
|
||||
options[0] = F("MAX 6675");
|
||||
options[1] = F("MAX 31855");
|
||||
//options[2] = F("MAX 31865");
|
||||
int optionValues[2];
|
||||
optionValues[0] = 1;
|
||||
optionValues[1] = 2;
|
||||
//optionValues[2] = 3;
|
||||
string += F("<TR><TD>Adapter IC:<TD><select name='plugin_120_maxtype'>");
|
||||
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>");
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
String plugin1 = WebServer.arg("plugin_120_maxtype");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
// Get the MAX Type (6675 / 31855)
|
||||
// TBD ... Auswertung je nach Chip !!!
|
||||
byte MaxType = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
|
||||
// Get CS Pin
|
||||
// Konvert the GPIO Pin to a Dogotal Puin Number first ...
|
||||
Plugin_120_SPI_CS_Pin = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
|
||||
switch (MaxType) {
|
||||
case 1: // MAX6675
|
||||
Plugin_120_Celsius = readMax6675();
|
||||
break;
|
||||
case 2: // MAX31855
|
||||
Plugin_120_Celsius = readMax31855();
|
||||
break;
|
||||
case 3: // MAX31865 (not implemented yet)
|
||||
//do something when var equals 2
|
||||
break;
|
||||
}
|
||||
|
||||
if (Plugin_120_Celsius != NAN)
|
||||
{
|
||||
UserVar[event->BaseVarIndex] = Plugin_120_Celsius;
|
||||
UserVar[event->BaseVarIndex + 1] = CelsiusToFahrenheit(Plugin_120_Celsius);
|
||||
String log = F("P120 : C : ");
|
||||
log += UserVar[event->BaseVarIndex];
|
||||
log += F(" - F : ");
|
||||
log += UserVar[event->BaseVarIndex + 1];
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
UserVar[event->BaseVarIndex] = NAN;
|
||||
UserVar[event->BaseVarIndex + 1] = NAN;
|
||||
String log = F("P120 : No Sensor attached !");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
success = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
double readMax6675()
|
||||
{
|
||||
uint16_t rawvalue = 0;
|
||||
// take the SS pin low to select the chip:
|
||||
digitalWrite(Plugin_120_SPI_CS_Pin, LOW);
|
||||
// String log = F("P120 : CS Pin : ");
|
||||
// log += Plugin_120_SPI_CS_Pin;
|
||||
// addLog(LOG_LEVEL_INFO, log);
|
||||
// "transfer" 0x0 and read the Data from the Chip
|
||||
rawvalue = SPI.transfer16(0x0);
|
||||
// take the SS pin high to de-select the chip:
|
||||
digitalWrite(Plugin_120_SPI_CS_Pin, HIGH);
|
||||
|
||||
String log = F("P120 : MAX6675 : RAW - BIN:");
|
||||
log += String(rawvalue, BIN);
|
||||
log += " HEX:";
|
||||
log += String(rawvalue, HEX);
|
||||
log += " DEC:";
|
||||
log += String(rawvalue);
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
|
||||
// Open Thermocouple
|
||||
// Bit D2 is normally low and goes high if the thermocouple input is open. In order to allow the operation of the
|
||||
// open thermocouple detector, T- must be grounded. Make the ground connection as close to the GND pin
|
||||
// as possible.
|
||||
Plugin_120_SensorAttached = !(rawvalue & 0x0004);
|
||||
|
||||
if (Plugin_120_SensorAttached)
|
||||
{
|
||||
// Shift RAW value 3 Bits to the right to get the data
|
||||
rawvalue >>= 3;
|
||||
|
||||
// Calculate Celsius
|
||||
return rawvalue * 0.25;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NAN;
|
||||
}
|
||||
}
|
||||
|
||||
double readMax31855()
|
||||
{
|
||||
uint32_t rawvalue = 0;
|
||||
// take the SS pin low to select the chip:
|
||||
digitalWrite(Plugin_120_SPI_CS_Pin, LOW);
|
||||
// "transfer" 0x0 and read the MSB Data from the Chip
|
||||
rawvalue = SPI.transfer16(0x0);
|
||||
// Shift MSB 16 Bits to the left
|
||||
rawvalue <<= 16;
|
||||
// "transfer" 0x0 and read the LSB Data from the Chip
|
||||
rawvalue |= SPI.transfer16(0x0);
|
||||
// take the SS pin high to de-select the chip:
|
||||
digitalWrite(Plugin_120_SPI_CS_Pin, HIGH);
|
||||
|
||||
String log = F("P120 : MAX31855 : RAW - BIN:");
|
||||
log += String(rawvalue, BIN);
|
||||
log += " HEX:";
|
||||
log += String(rawvalue, HEX);
|
||||
log += " DEC:";
|
||||
log += String(rawvalue);
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
|
||||
// D16 - This bit reads at 1 when any of the SCV, SCG, or OC faults are active. Default value is 0.
|
||||
Plugin_120_SensorAttached = !(rawvalue & 0x00010000);
|
||||
|
||||
if (Plugin_120_SensorAttached)
|
||||
{
|
||||
// Data is D[31:18]
|
||||
// Shift RAW value 18 Bits to the right to get the data
|
||||
rawvalue >>= 18;
|
||||
|
||||
// Check for negative Values
|
||||
// +25.00 0000 0001 1001 00
|
||||
// 0.00 0000 0000 0000 00
|
||||
// -0.25 1111 1111 1111 11
|
||||
// -1.00 1111 1111 1111 00
|
||||
// -250.00 1111 0000 0110 00
|
||||
if (rawvalue & 0x2000) // Bit 31=1 -> neg Values
|
||||
{
|
||||
// Negate all Bits
|
||||
rawvalue = ~rawvalue;
|
||||
// Add 1 and make negative
|
||||
rawvalue = (rawvalue + 1) * -1;
|
||||
}
|
||||
|
||||
// Calculate Celsius
|
||||
return rawvalue * 0.25;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NAN;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert Celsius to Fahrenheit
|
||||
double CelsiusToFahrenheit(double celsius) {
|
||||
return celsius * 9.0 / 5.0 + 32;
|
||||
}
|
||||
|
||||
#endif
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
/*
|
||||
|
||||
This plug in is written by Dmitry (rel22 ___ inbox.ru)
|
||||
Plugin is based upon SenseAir plugin by Daniel Tedenljung info__AT__tedenljungconsulting.com
|
||||
|
||||
This plugin reads the CO2 value from MH-Z19 NDIR Sensor
|
||||
DevicePin1 - is RX for ESP
|
||||
DevicePin2 - is TX for ESP
|
||||
*/
|
||||
|
||||
|
||||
#define PLUGIN_149
|
||||
#define PLUGIN_ID_149 149
|
||||
#define PLUGIN_NAME_149 "NDIR CO2 Sensor MH-Z19 [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_149 "PPM"
|
||||
|
||||
boolean Plugin_149_init = false;
|
||||
|
||||
#include <SoftwareSerial.h>
|
||||
SoftwareSerial *Plugin_149_S8;
|
||||
|
||||
// 9-bytes CMD PPM read command
|
||||
byte mhzCmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
|
||||
byte mhzResp[9]; // 9 bytes bytes response
|
||||
|
||||
boolean Plugin_149(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_149;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_DUAL;
|
||||
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 = 1;
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
Device[deviceCount].TimerOption = true;
|
||||
Device[deviceCount].GlobalSyncOption = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_149);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_149));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
Plugin_149_init = true;
|
||||
Plugin_149_S8 = new SoftwareSerial(Settings.TaskDevicePin1[event->TaskIndex], Settings.TaskDevicePin2[event->TaskIndex]);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
|
||||
if (Plugin_149_init)
|
||||
{
|
||||
Plugin_149_S8->write(mhzCmd, 9);
|
||||
memset(mhzResp, 0, 9);
|
||||
Plugin_149_S8->readBytes(mhzResp, 9);
|
||||
int i;
|
||||
unsigned int ppm = 0;
|
||||
byte crc = 0;
|
||||
for (i = 1; i < 8; i++) crc+=mhzResp[i];
|
||||
crc = 255 - crc;
|
||||
crc++;
|
||||
|
||||
if ( !(mhzResp[0] == 0xFF && mhzResp[1] == 0x86 && mhzResp[8] == crc) ) {
|
||||
String log = F("MHZ19: CRC error: ");
|
||||
log += String(crc); log += " / "; log += String(mhzResp[8]);
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
success = false;
|
||||
break;
|
||||
|
||||
} else {
|
||||
|
||||
unsigned int mhzRespHigh = (unsigned int) mhzResp[2];
|
||||
unsigned int mhzRespLow = (unsigned int) mhzResp[3];
|
||||
ppm = (256*mhzRespHigh) + mhzRespLow;
|
||||
}
|
||||
|
||||
UserVar[event->BaseVarIndex] = (float)ppm;
|
||||
String log = F("MHZ19: PPM value: ");
|
||||
log += ppm;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,525 @@
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin 186: Ventus W266 [Testing] ################################
|
||||
//#######################################################################################################
|
||||
|
||||
// Purpose: Sniff the data received by the Ventus W266 display unit and send it to Domoticz
|
||||
// Status : "Initial release"
|
||||
|
||||
// This plugin can be used on a esp8266 connected to the recievermodule inside a Ventus W266 or
|
||||
// Renkforce W205GU display unit. The plugin then reads the data send from the remote sensorunit and
|
||||
// can send the data to Domoticz or an other controller. The plugin does not read the data of the
|
||||
// sensors in the display unit such as indoor temperature and airpressure.
|
||||
|
||||
// The displaynit has a RFM31 reciever but the pinout of this receiver is identical to the pinout
|
||||
// of a RFM01 reciever. We need to connect 4 wires to read the SPI exchange between the host and the
|
||||
// reciever. Because we need to read both MOSI and MISO signals the hardware SPI is unusable and we
|
||||
// use bitbanging to achive the same result.
|
||||
|
||||
// Connect pins 5-8 from the "RFM31BJ-S1" to the pins you defined in de webgui.
|
||||
// Pinout of the RFM31: 5-MOSI, 6-SCLK, 7-nSEL (active low CS), 8-MISO, 14-GND.
|
||||
// In my original setup these were connected to 4, 12, 14 and 5.
|
||||
// Try to avoid GPIO 15 as nSEL line because if the line is high during a reboot, and it is mostly high,
|
||||
// the boot will fail!
|
||||
|
||||
// The Ventus W266 remote has the following sensor outputs:
|
||||
// Humidity, Temperature, Wind direction, Wind avarage, Wind gust, Rainfall, UV and Lightning. That is
|
||||
// more than te maximum of 4 values per device for espeasy. The plugins functionality is therefore
|
||||
// devided per sensorgroup. To read all the sensor data you need to run 6 instances of the plugin.
|
||||
|
||||
// The plugin can (and should) be used more then one time, however only one plugin instance can be
|
||||
// the main plugin. The plugin function can be selected by a dropdown and only the main plugin has
|
||||
// the ability to set the I/O lines.
|
||||
|
||||
// The plugin uses two buffers, ine for the ISR routines and one for the other plugin instances.
|
||||
|
||||
// Why plugin nr 186? Well 266 is not possible, 206 (id on the back of the unit) is nit within the
|
||||
// playground range and the W186 is an additinal external thermometer for the Ventus W266.
|
||||
// The Ventus W266 is also known as the Renkforce W205GU.
|
||||
|
||||
// TaskDevicePluginConfig[x][0] = Instance function
|
||||
// TaskDevicePluginConfig[x][1] = MOSI (eg. GPOI pin 4)
|
||||
// TaskDevicePluginConfig[x][2] = SCLK (eg. GPIO pin 12)
|
||||
// TaskDevicePluginConfig[x][3] = nSEL (eg. GPIO pin 14)
|
||||
// TaskDevicePluginConfig[x][4] = MISO (eg. GPIO pin 5)
|
||||
// If you use GPIO 4&5, please disable the I2C/SPI option in the hardware tab.
|
||||
|
||||
// The buffers contain the following data:
|
||||
// hhIDhh 1A tlth ?b tlth wb alahglgh rlrh?? uv ld?? lllhcrc
|
||||
// 7F9827 1A B100 00 B100 06 00000000 1E0000 00 3F8A 2A0017
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
|
||||
// hh=header > This is actualy not part of the real payload but a wanted artifact of the sniffing method.
|
||||
// hh=humidity (bcd) > Humidity is bcd encoded
|
||||
// tlth=temperature-low/temphigh (*10) > Temperature is stored as a 16bit integer holding the temperature in Celcius * 10 but low byte first.
|
||||
// b=battery (1=low) > This byte is 00 but 01 when the battery of the transmitter runs low
|
||||
// wb=bearing (cw0-15) > The windbearing in 16 clockwise steps (0 = north, 4 = east, 8 = south and C = west)
|
||||
// alah=windaverage-low/high (m/s/2) > A 16 bit int holding the wind avarage in m/s * 2, low byte first
|
||||
// rlrh=rainfall-low/high (1/4mm) > A 16 bit int holding the wind gust in m/s * 2, low byte first
|
||||
// uv=uvindex (*10) > The UV value * 10
|
||||
// ld=lightningstorm-distance (km, 3F is max) > The distance to the stormfront in km
|
||||
// lllh=strikecount-low/high (#) > A 16 bit integer holding the number of detected lightning strikes, low byte first
|
||||
// crc > poly 0x31, init 0xff, revin&revout, xorout 0x00. Like Maxim 1-wire but with a 0xff initvalue. Crc is calculated over bytes 1-22
|
||||
//
|
||||
|
||||
// Events:
|
||||
// None
|
||||
|
||||
// Commands:
|
||||
// None
|
||||
|
||||
// Current state / limitations:
|
||||
// 1.0 Initial release. All values are always visible although sometimes
|
||||
// only one is really used with domoticz.
|
||||
// Needs work on a sliding window for the lightning detection.
|
||||
// Exploits the fact that event->sensorType is not reset after PLUGIN_READ.
|
||||
|
||||
// This plugin is based on the work of the Plugin 199: RF KaKu receiver/sender and Plugin 026: Analog.
|
||||
// CRC calculation is based on the works by Paul Stoffregen from the 1-Wire arduino library. Special
|
||||
// thanks to Greg Cook and the team behind reveng.sourceforge.net.
|
||||
|
||||
#ifdef PLUGIN_BUILD_TESTING
|
||||
|
||||
#define PLUGIN_186_DEBUG true // Shows recieved frames and crc in log@INFO
|
||||
|
||||
#define PLUGIN_186 // Mandatory framework constants
|
||||
#define PLUGIN_ID_186 186
|
||||
#define PLUGIN_NAME_186 "Ventus W266 [TESTING]"
|
||||
#define PLUGIN_VALUENAME1_186 ""
|
||||
#define PLUGIN_VALUENAME2_186 ""
|
||||
#define PLUGIN_VALUENAME3_186 ""
|
||||
|
||||
#define Plugin_186_MagicByte 0x7F // When we read this byte on MOSI, switch to MISO
|
||||
#define Plugin_186_RAW_BUFFER_SIZE 24 // Payload is 23 bytes, added space for header
|
||||
#define Plugin_186_Payload 23
|
||||
|
||||
int8_t Plugin_186_MOSIpin = -1; // GPIO pins
|
||||
int8_t Plugin_186_SCLKpin = -1;
|
||||
int8_t Plugin_186_nSELpin = -1;
|
||||
int8_t Plugin_186_MISOpin = -1;
|
||||
// Vars used in data collection:
|
||||
byte Plugin_186_ISR_Buffer[Plugin_186_RAW_BUFFER_SIZE]; // Buffer used in ISR routine
|
||||
//Test data: volatile byte Plugin_186_databuffer[] = {0x7F, 0x98, 0x33, 0x1A, 0xB0, 0x00, 0x00, 0xB0, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3F, 0x8A, 0x25, 0x00, 0x49, 0x00}; // Buffer used by other instances
|
||||
byte Plugin_186_databuffer[Plugin_186_RAW_BUFFER_SIZE]; // Buffer used by other instances
|
||||
boolean Plugin_186_RecieveActive = false; // Active session in progress
|
||||
boolean Plugin_186_MasterSlave = false; // Which pin o read? false=MOSI, true=MISO
|
||||
boolean Plugin_186_newData = false; // "Valid" data ready, please process
|
||||
byte Plugin_186_bitpointer; // Pointer for recieved bit
|
||||
byte Plugin_186_bytepointer; // Pointe for ISR recieve buffer
|
||||
byte Plugin_186_recievedData; // Byte to store recieved bits
|
||||
|
||||
// Vars used for interpreting the data:
|
||||
volatile unsigned long Plugin_186_lastrainctr; // Keep track of wdcounter (1/2 min tick)
|
||||
volatile int Plugin_186_lastraincount; // Last rain count
|
||||
volatile float Plugin_186_rainmmph = 0;
|
||||
volatile unsigned long Plugin_186_laststrikectr; // Keep track of wdcounter (1/2 min tick)
|
||||
volatile unsigned int Plugin_186_laststrikecount; // Last number of strikes
|
||||
volatile int Plugin_186_strikesph = 0;
|
||||
|
||||
void Plugin_186_ISR_nSEL() ICACHE_RAM_ATTR; // Interrupt routines
|
||||
void Plugin_186_ISR_SCLK() ICACHE_RAM_ATTR;
|
||||
|
||||
boolean Plugin_186(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_186;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_DUMMY; // Nothing else really fit the bill ...
|
||||
Device[deviceCount].VType = SENSOR_TYPE_DUAL; // New type, see ESPEasy.ino
|
||||
Device[deviceCount].Ports = 0;
|
||||
Device[deviceCount].PullUpOption = false;
|
||||
Device[deviceCount].InverseLogicOption = false;
|
||||
Device[deviceCount].FormulaOption = true;
|
||||
Device[deviceCount].SendDataOption = true;
|
||||
Device[deviceCount].ValueCount = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
byte nrchoices = 9;
|
||||
String options[nrchoices];
|
||||
options[0] = F("Main + Temp/Hygro");
|
||||
options[1] = F("Wind");
|
||||
options[2] = F("Rain");
|
||||
options[3] = F("UV");
|
||||
options[4] = F("Lightning strikes");
|
||||
options[5] = F("Lightning distance");
|
||||
|
||||
options[6] = F("Unknown 1, byte 6");
|
||||
options[7] = F("Unknown 2, byte 16");
|
||||
options[8] = F("Unknown 3, byte 19");
|
||||
|
||||
int optionValues[nrchoices];
|
||||
for (byte x = 0; x < nrchoices; x++) {
|
||||
optionValues[x] = x;
|
||||
}
|
||||
string += F("<TR><TD>Plugin function:<TD><select name='plugin_186'>");
|
||||
for (byte x = 0; x < nrchoices; 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("<TR><TD>1st GPIO (5-MOSI):<TD>");
|
||||
addPinSelect(false, string, "taskdevicepin1", Settings.TaskDevicePluginConfig[event->TaskIndex][1]);
|
||||
string += F("<TR><TD>2nd GPIO (6-SCLK):<TD>");
|
||||
addPinSelect(false, string, "taskdevicepin2", Settings.TaskDevicePluginConfig[event->TaskIndex][2]);
|
||||
string += F("<TR><TD>3rd GPIO (7-nSEL):<TD>");
|
||||
addPinSelect(false, string, "taskdevicepin3", Settings.TaskDevicePluginConfig[event->TaskIndex][3]);
|
||||
string += F("<TR><TD>4th GPIO (8-MISO):<TD>");
|
||||
addPinSelect(false, string, "taskdeviceport", Settings.TaskDevicePluginConfig[event->TaskIndex][4]);
|
||||
}
|
||||
|
||||
switch (choice)
|
||||
{
|
||||
case (0):
|
||||
{
|
||||
string += F("<TR><TD><B>Be sure you only have 1 main plugin!</B></TD>");
|
||||
string += F("<TR><TD>Value 1: Temperature, 1 decimal<BR>Value 2: Humidity, 0 decimals");
|
||||
string += F("<BR>Value 3: not used</TD>");
|
||||
break;
|
||||
}
|
||||
case (1):
|
||||
{
|
||||
string += F("<TR><TD>Value 1: Direction, 0 decimals<BR>");
|
||||
string += F("Value 2: Average, 1 decimal<Br>Value 3: Gust, 1 decimal</TD>");
|
||||
break;
|
||||
}
|
||||
case (2):
|
||||
{
|
||||
string += F("<TR><TD>Value 1: Rain in mm per hour<BR>Value 2: Total rain in mm");
|
||||
string += F("<BR>Value 3: not used</TD>");
|
||||
break;
|
||||
}
|
||||
case (3):
|
||||
{
|
||||
string += F("<TR><TD>Value 1: UV, 1 decimal");
|
||||
string += F("<BR>Values 2, 3</TD>");
|
||||
break;
|
||||
}
|
||||
case (4):
|
||||
{
|
||||
string += F("<TR><TD>Value 1: Strikes this hour, 0 decimals");
|
||||
string += F("<BR>Values 2, 3: not used</TD>");
|
||||
break;
|
||||
}
|
||||
case (5):
|
||||
{
|
||||
string += F("<TR><TD>Value 1: Distance in km, 0 decimals");
|
||||
string += F("<BR>Values 2, 3: not used</TD>");
|
||||
break;
|
||||
}
|
||||
case (6):
|
||||
{
|
||||
string += F("<TR><TD>Value 1: Batterybyte, 0 decimals");
|
||||
string += F("<BR>Values 2, 3: not used</TD>");
|
||||
break;
|
||||
}
|
||||
case (7):
|
||||
{
|
||||
string += F("<TR><TD>Value 1: Last rainbyte, 0 decimals");
|
||||
string += F("<BR>Values 2, 3: not used</TD>");
|
||||
break;
|
||||
}
|
||||
case (8):
|
||||
{
|
||||
string += F("<TR><TD>Value 1: Last lightningbyte, 0 decimals");
|
||||
string += F("<BR>Values 2, 3: not used</TD>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
String plugin1 = WebServer.arg("plugin_186");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][0] = plugin1.toInt();
|
||||
if (plugin1.toInt()==0) {
|
||||
String plugin2 = WebServer.arg("taskdevicepin1");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][1] = plugin2.toInt();
|
||||
String plugin3 = WebServer.arg("taskdevicepin2");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][2] = plugin3.toInt();
|
||||
String plugin4 = WebServer.arg("taskdevicepin3");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][3] = plugin4.toInt();
|
||||
String plugin5 = WebServer.arg("taskdeviceport");
|
||||
Settings.TaskDevicePluginConfig[event->TaskIndex][4] = plugin5.toInt();
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_186);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_186));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_186));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_186));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
|
||||
switch (choice)
|
||||
{
|
||||
case (0):
|
||||
{
|
||||
Plugin_186_MOSIpin = Settings.TaskDevicePluginConfig[event->TaskIndex][1];
|
||||
Plugin_186_SCLKpin = Settings.TaskDevicePluginConfig[event->TaskIndex][2];
|
||||
Plugin_186_nSELpin = Settings.TaskDevicePluginConfig[event->TaskIndex][3];
|
||||
Plugin_186_MISOpin = Settings.TaskDevicePluginConfig[event->TaskIndex][4];
|
||||
int8_t total = Plugin_186_MOSIpin + Plugin_186_SCLKpin + Plugin_186_nSELpin + Plugin_186_MISOpin;
|
||||
if (total > 6) { // All pins configured?
|
||||
pinMode(Plugin_186_MOSIpin, INPUT);
|
||||
pinMode(Plugin_186_SCLKpin, INPUT);
|
||||
pinMode(Plugin_186_nSELpin, INPUT);
|
||||
pinMode(Plugin_186_MISOpin, INPUT);
|
||||
Plugin_186_databuffer[0] = 0; // buffer is "empty"
|
||||
Plugin_186_lastrainctr = 0;
|
||||
Plugin_186_lastraincount = -1;
|
||||
Plugin_186_laststrikectr = 0;
|
||||
Plugin_186_laststrikecount = -1;
|
||||
attachInterrupt(Plugin_186_SCLKpin, Plugin_186_ISR_SCLK, RISING);
|
||||
attachInterrupt(Plugin_186_nSELpin, Plugin_186_ISR_nSEL, CHANGE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
if (Settings.TaskDevicePluginConfig[event->TaskIndex][0] == 0) {
|
||||
if (Plugin_186_newData) {
|
||||
uint8_t crc = 0xff; // init = 0xff
|
||||
char data; // CRC = MAXIM with modified init: poly 0x31, init 0xff, refin 1; refout 1, xorout 0x00
|
||||
// Copy recieved data to buffer and check CRC
|
||||
Plugin_186_databuffer[0] = Plugin_186_ISR_Buffer[0];
|
||||
for (int i = 1; i < Plugin_186_bytepointer; i++) {
|
||||
data = Plugin_186_ISR_Buffer[i];
|
||||
Plugin_186_databuffer[i] = data;
|
||||
for (int j = 0; j < 8; j++) // crc routine from Jim Studt
|
||||
{ // Onewire library
|
||||
uint8_t mix = (crc ^ data) & 0x01;
|
||||
crc >>= 1;
|
||||
if (mix) crc ^= 0x8C;
|
||||
data >>= 1;
|
||||
}
|
||||
}
|
||||
Plugin_186_MasterSlave = false;
|
||||
Plugin_186_newData = false;
|
||||
if (PLUGIN_186_DEBUG) {
|
||||
String log = "Ventus W266 Rcvd(";
|
||||
log += hour();
|
||||
log += ":";
|
||||
if (minute() < 10) { log += "0"; }
|
||||
log += minute();
|
||||
log += ":";
|
||||
if (tm.Second < 10) { log += "0"; }
|
||||
log += tm.Second;
|
||||
log += ") ";
|
||||
for (int i = 0; i < Plugin_186_Payload; i++) {
|
||||
if ((i==2)||(i==3)||(i==4)||(i==9)||(i==10)||(i==14)||(i==17)||(i==18)||(i==20)) {
|
||||
log += ":";
|
||||
}
|
||||
char myHex = (Plugin_186_databuffer[i] >> 4) + 0x30;
|
||||
if (myHex > 0x39) { myHex += 7; }
|
||||
log += myHex;
|
||||
myHex = (Plugin_186_databuffer[i] & 0x0f) + 0x30;
|
||||
if (myHex > 0x39) { myHex += 7; }
|
||||
log += myHex;
|
||||
}
|
||||
log += " > ";
|
||||
char myHex = (crc >> 4) + 0x30;
|
||||
if (myHex > 0x39) { myHex += 7; }
|
||||
log += myHex;
|
||||
myHex = (crc & 0x0f) + 0x30;
|
||||
if (myHex > 0x39) { myHex += 7; }
|
||||
log += myHex;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
if (crc != 00)
|
||||
{
|
||||
Plugin_186_databuffer[0] = 0; // Not MagicByte, so not valid.
|
||||
}
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
if (Plugin_186_databuffer[0] == Plugin_186_MagicByte) // buffer[0] should be the MagicByte if valid
|
||||
{
|
||||
UserVar[event->BaseVarIndex + 1] = 0;
|
||||
byte choice = Settings.TaskDevicePluginConfig[event->TaskIndex][0]; // Which instance?
|
||||
switch (choice)
|
||||
{
|
||||
case (0):
|
||||
{
|
||||
int myTemp = int((Plugin_186_databuffer[5] * 256) + Plugin_186_databuffer[4]);
|
||||
if (myTemp > 0x8000) { myTemp |= 0xffff0000; } // int @ esp8266 = 32 bits!
|
||||
float temperature = float(myTemp) / 10.0; // Temperature
|
||||
byte myHum = (Plugin_186_databuffer[2] >> 4) * 10 + (Plugin_186_databuffer[2] & 0x0f);
|
||||
float humidity = float(myHum);
|
||||
UserVar[event->BaseVarIndex] = temperature;
|
||||
UserVar[event->BaseVarIndex + 1] = humidity;
|
||||
event->sensorType = SENSOR_TYPE_TEMP_HUM;
|
||||
break;
|
||||
}
|
||||
case (1):
|
||||
{
|
||||
float average = float((Plugin_186_databuffer[11]) * 256 + Plugin_186_databuffer[10]); // Wind speed average in 10 * m/s
|
||||
float gust = float((Plugin_186_databuffer[13]) * 256 + Plugin_186_databuffer[12]); // Wind speed gust in 10 * m/s
|
||||
float bearing = float(Plugin_186_databuffer[9] & 0x0f) * 22.5; // Wind bearing (0-359)
|
||||
UserVar[event->BaseVarIndex] = bearing; // degrees
|
||||
UserVar[event->BaseVarIndex + 1] = average;
|
||||
UserVar[event->BaseVarIndex + 2] = gust;
|
||||
event->sensorType = SENSOR_TYPE_WIND;
|
||||
break;
|
||||
}
|
||||
case (2):
|
||||
{
|
||||
float raincnt = float(((Plugin_186_databuffer[15]) * 256 + Plugin_186_databuffer[14]) / 4);
|
||||
int rainnow = int(raincnt);
|
||||
if (wdcounter < Plugin_186_lastrainctr) { Plugin_186_lastrainctr = wdcounter; }
|
||||
if (Plugin_186_lastrainctr > (wdcounter + 10)) // 5 min interval
|
||||
{
|
||||
Plugin_186_lastrainctr = wdcounter;
|
||||
if (rainnow > Plugin_186_lastraincount)
|
||||
{ // per 5 min * 12 = per hour
|
||||
Plugin_186_rainmmph = float(rainnow - Plugin_186_lastraincount) * 12;
|
||||
Plugin_186_lastraincount = rainnow;
|
||||
} else {
|
||||
Plugin_186_rainmmph = 0;
|
||||
}
|
||||
}
|
||||
UserVar[event->BaseVarIndex] = Plugin_186_rainmmph;
|
||||
UserVar[event->BaseVarIndex + 1] = raincnt;
|
||||
break;
|
||||
}
|
||||
case (3):
|
||||
{
|
||||
float uvindex = float((Plugin_186_databuffer[17]) / 10);
|
||||
UserVar[event->BaseVarIndex] = uvindex;
|
||||
break;
|
||||
}
|
||||
case (4):
|
||||
{
|
||||
int strikes = 0;
|
||||
int strikesnow = int((Plugin_186_databuffer[21]) * 256 + Plugin_186_databuffer[20]);
|
||||
if (wdcounter < Plugin_186_laststrikectr) { Plugin_186_laststrikectr = wdcounter; }
|
||||
if (Plugin_186_laststrikectr > (wdcounter + 10)) // 5 min interval
|
||||
{
|
||||
Plugin_186_laststrikectr = wdcounter;
|
||||
if (strikesnow > Plugin_186_laststrikecount)
|
||||
{
|
||||
Plugin_186_strikesph = strikesnow - Plugin_186_laststrikecount;
|
||||
Plugin_186_laststrikecount = strikesnow;
|
||||
} else {
|
||||
Plugin_186_strikesph = 0;
|
||||
}
|
||||
}
|
||||
UserVar[event->BaseVarIndex] = float(Plugin_186_strikesph);
|
||||
break;
|
||||
}
|
||||
case (5):
|
||||
{
|
||||
float distance = float(-1);
|
||||
if (Plugin_186_databuffer[18] != 0x3F )
|
||||
{
|
||||
distance = float(Plugin_186_databuffer[18]);
|
||||
}
|
||||
UserVar[event->BaseVarIndex] = distance;
|
||||
break;
|
||||
}
|
||||
case (6):
|
||||
{
|
||||
UserVar[event->BaseVarIndex] = float(Plugin_186_databuffer[6]);
|
||||
break;
|
||||
}
|
||||
case (7):
|
||||
{
|
||||
UserVar[event->BaseVarIndex] = float(Plugin_186_databuffer[16]);
|
||||
break;
|
||||
}
|
||||
case (8):
|
||||
{
|
||||
UserVar[event->BaseVarIndex] = float(Plugin_186_databuffer[19]);
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
success = true;
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
break;
|
||||
} // case READ
|
||||
} // switch
|
||||
return success;
|
||||
}
|
||||
|
||||
void Plugin_186_ISR_nSEL() // Interrupt on nSEL change
|
||||
{
|
||||
if (digitalRead(Plugin_186_nSELpin)) {
|
||||
Plugin_186_RecieveActive = false; // nSEL high? Recieve done.
|
||||
if (Plugin_186_MasterSlave) { // If MISO not active, no data recieved
|
||||
if (Plugin_186_bytepointer == Plugin_186_Payload) { // If not 23 then bad datapacket
|
||||
Plugin_186_newData = true; // We have new data!
|
||||
}
|
||||
}
|
||||
} else { // nSEL low? Start recieve
|
||||
if (!Plugin_186_newData) { // Only accept new data if the old is processed
|
||||
Plugin_186_bitpointer = 7; // reset pointer (MSB first)
|
||||
Plugin_186_bytepointer = 0; // reset pointers & flags
|
||||
Plugin_186_MasterSlave = false;
|
||||
Plugin_186_RecieveActive = true; // We are now recieving data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Plugin_186_ISR_SCLK() // Interrupt on SCLK rising
|
||||
{
|
||||
if (Plugin_186_RecieveActive) { // Are we recieving or glitch?
|
||||
if (Plugin_186_MasterSlave) { // Read MISO or MOSI?
|
||||
bitWrite(Plugin_186_recievedData, Plugin_186_bitpointer, digitalRead(Plugin_186_MISOpin));
|
||||
} else {
|
||||
bitWrite(Plugin_186_recievedData, Plugin_186_bitpointer, digitalRead(Plugin_186_MOSIpin));
|
||||
}
|
||||
if (Plugin_186_bitpointer == 0) { // 8 bits done?
|
||||
Plugin_186_bitpointer = 7;
|
||||
if (Plugin_186_recievedData==Plugin_186_MagicByte) { // Switch data pins?
|
||||
Plugin_186_MasterSlave = true;
|
||||
}
|
||||
Plugin_186_ISR_Buffer[Plugin_186_bytepointer] = Plugin_186_recievedData;
|
||||
Plugin_186_bytepointer++; // TReady for the next byte ...
|
||||
if (Plugin_186_bytepointer > Plugin_186_RAW_BUFFER_SIZE) {
|
||||
Plugin_186_RecieveActive = false; // We don't want a bufferoverflow, so abort
|
||||
Plugin_186_MasterSlave = false;
|
||||
}
|
||||
} else {
|
||||
Plugin_186_bitpointer--; // Not yet done with all bits ...
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,584 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin 199: RF KaKu receiver/sender ##############################
|
||||
//#######################################################################################################
|
||||
|
||||
// Purpose: Control Klik-Aan-Klik-Uit RF 433MHz devices directly from ESP Easy (receive and send)
|
||||
// Status : "Proof of concept"
|
||||
|
||||
// Connect the RF Receiver data pin to the first pin selected in the webgui
|
||||
// Connect the RF Transmitter data pin to the second pin selected in the webgui
|
||||
|
||||
// Events:
|
||||
// newKaku_<address>#<Channel>=<state> (0=off, 1-15=dimvalue, 16=on)
|
||||
// Kaku_<address>#<Channel>=<state> (0=off, 1=on)
|
||||
// HE300EU_<address>#<Channel>=<state> (0=off, 1=on)
|
||||
|
||||
// Commands:
|
||||
// newKakuSend <address>, <Channel>, <state/dim>
|
||||
// KakuSend <address>, <Channel>, <state>
|
||||
|
||||
// This is a Work in Progress mini project!
|
||||
// It has limited use because in most cases, your fancy Home Automation controller can handle 433MHz devices quite well using RFLink.
|
||||
// It was implemented because in some cases i would like to have local "Klik-Aan-Klik-Uit" support using a standalone ESP Easy.
|
||||
// (Just because i own quite a lot of these Kaku devices)
|
||||
|
||||
// Current state / limitations:
|
||||
// Implemented send and receive support for KaKu with automatic code (no code wheel)
|
||||
// Implemented send and receive support for old KaKu unit's with code wheels
|
||||
// Implemented receive support for HomeEasy HE300EU remotes
|
||||
// RF Sender and RF receiver each need their own antenna! (as opposed to using a transceiver)
|
||||
|
||||
#define MIN_PULSE_LENGTH 100 // Too short pulses are considered to be noise...
|
||||
#define SIGNAL_TIMEOUT 5 // gap between transmissions
|
||||
#define MIN_RAW_PULSES 32 // Minimum number of pulses to be received, otherwise considered to be noise...
|
||||
#define RAW_BUFFER_SIZE 256
|
||||
#define RAWSIGNAL_MULTIPLY 25
|
||||
|
||||
void RF_ISR() ICACHE_RAM_ATTR;
|
||||
|
||||
// We need our own rawsignal buffer here.
|
||||
// During plugin rawsignal checks, the IRQ routine will alPlugin_199_ready be working on the next signal burst...
|
||||
volatile byte Plugin_199_RFBuffer[RAW_BUFFER_SIZE];
|
||||
volatile boolean Plugin_199_ready = false;
|
||||
volatile byte Plugin_199_pulses[RAW_BUFFER_SIZE + 2];
|
||||
volatile int Plugin_199_number;
|
||||
unsigned long Plugin_199_codeHash;
|
||||
unsigned long Plugin_199_lastTime;
|
||||
int8_t Plugin_199_RXpin = -1;
|
||||
int8_t Plugin_199_TXpin = -1;
|
||||
|
||||
#define PLUGIN_199
|
||||
#define PLUGIN_ID_199 199
|
||||
#define PLUGIN_NAME_199 "RF Receiver/Sender [DEVELOPMENT]"
|
||||
#define PLUGIN_VALUENAME1_199 "Address"
|
||||
#define PLUGIN_VALUENAME2_199 "Channel"
|
||||
#define PLUGIN_VALUENAME3_199 "State"
|
||||
|
||||
boolean Plugin_199(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_199;
|
||||
Device[deviceCount].Type = DEVICE_TYPE_DUAL;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_DUAL;
|
||||
Device[deviceCount].Ports = 0;
|
||||
Device[deviceCount].PullUpOption = false;
|
||||
Device[deviceCount].InverseLogicOption = false;
|
||||
Device[deviceCount].FormulaOption = false;
|
||||
Device[deviceCount].ValueCount = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_199);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_199));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_199));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_199));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
if (Settings.TaskDevicePin1[event->TaskIndex] != -1)
|
||||
{
|
||||
Plugin_199_RXpin = Settings.TaskDevicePin1[event->TaskIndex];
|
||||
pinMode(Plugin_199_RXpin, INPUT_PULLUP);
|
||||
attachInterrupt(Plugin_199_RXpin, RF_ISR, CHANGE);
|
||||
success = true;
|
||||
}
|
||||
if (Settings.TaskDevicePin1[event->TaskIndex] != -1)
|
||||
{
|
||||
Plugin_199_TXpin = Settings.TaskDevicePin2[event->TaskIndex];
|
||||
pinMode(Plugin_199_TXpin, OUTPUT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
if (Plugin_199_ready)
|
||||
{
|
||||
if (decodeNewKaku(event->BaseVarIndex));
|
||||
else if (decodeKaku());
|
||||
else if (decodeHE300EU());
|
||||
Plugin_199_ready = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WRITE:
|
||||
{
|
||||
String command = parseString(string, 1);
|
||||
if (command == F("newkakusend"))
|
||||
{
|
||||
success = true;
|
||||
sendNewKaku(event->Par1, event->Par2, event->Par3);
|
||||
}
|
||||
if (command == F("kakusend"))
|
||||
{
|
||||
success = true;
|
||||
sendKaku(event->Par1, event->Par2, event->Par3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
//********************************************************************************
|
||||
// Interrupt handler for RF messages
|
||||
//********************************************************************************
|
||||
void RF_ISR()
|
||||
{
|
||||
static unsigned int counter = 0;
|
||||
static unsigned long TimeStamp = 0;
|
||||
unsigned long TimeElapsed = 0;
|
||||
|
||||
TimeElapsed = micros() - TimeStamp;
|
||||
TimeStamp = micros();
|
||||
|
||||
if (TimeElapsed > MIN_PULSE_LENGTH && counter < RAW_BUFFER_SIZE)
|
||||
{
|
||||
counter++;
|
||||
Plugin_199_RFBuffer[counter] = TimeElapsed / 25;
|
||||
}
|
||||
else
|
||||
counter = 0;
|
||||
|
||||
if (TimeElapsed > (SIGNAL_TIMEOUT * 1000) )
|
||||
{
|
||||
if (counter > MIN_RAW_PULSES)
|
||||
{
|
||||
Plugin_199_number = counter;
|
||||
|
||||
// copy IRQ RF working buffer to RawSignal struct
|
||||
for (unsigned int x = 0; x <= counter; x++)
|
||||
Plugin_199_pulses[x] = Plugin_199_RFBuffer[x];
|
||||
|
||||
Plugin_199_ready = true;
|
||||
counter = 0;
|
||||
}
|
||||
else
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
if (counter >= RAW_BUFFER_SIZE)
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
|
||||
//********************************************************************************
|
||||
// Transmit pulses using pulse array
|
||||
//********************************************************************************
|
||||
void RawSendRF(void)
|
||||
{
|
||||
if (Plugin_199_RXpin != -1)
|
||||
detachInterrupt(Plugin_199_RXpin);
|
||||
int x;
|
||||
//digitalWrite(PIN_RF_RX_VCC,LOW); // Turn off RF receiver
|
||||
//digitalWrite(PIN_RF_TX_VCC,HIGH); // Turn on RF sender
|
||||
|
||||
delay(5); // small delay between switching send/receive
|
||||
|
||||
Plugin_199_pulses[Plugin_199_number] = 1; // force last duration as 1 msec
|
||||
|
||||
for (byte y = 0; y < 7; y++) // repeats RF code
|
||||
{
|
||||
x = 1;
|
||||
noInterrupts();
|
||||
while (x < Plugin_199_number)
|
||||
{
|
||||
digitalWrite(Plugin_199_TXpin, HIGH);
|
||||
delayMicroseconds(Plugin_199_pulses[x++] * 25 - 5);
|
||||
digitalWrite(Plugin_199_TXpin, LOW);
|
||||
delayMicroseconds(Plugin_199_pulses[x++] * 25 - 7);
|
||||
}
|
||||
interrupts();
|
||||
delay(20);// Delay must run outside interrupt blocked code.
|
||||
}
|
||||
|
||||
delay(5);
|
||||
|
||||
//digitalWrite(PIN_RF_TX_VCC,LOW); // turn off RF sender
|
||||
//digitalWrite(PIN_RF_RX_VCC,HIGH); // turn on RF receiver
|
||||
if (Plugin_199_RXpin != -1)
|
||||
attachInterrupt(Plugin_199_RXpin, RF_ISR, CHANGE);
|
||||
}
|
||||
|
||||
|
||||
//********************************************************************************
|
||||
// Decode NewKaku protocol (without code wheel)
|
||||
//********************************************************************************
|
||||
#define NewKAKU_RawSignalLength 132
|
||||
#define NewKAKUdim_RawSignalLength 148
|
||||
#define NewKAKU_1T 275 // us
|
||||
#define NewKAKU_mT 650 // us
|
||||
#define NewKAKU_4T 1100 // us
|
||||
#define NewKAKU_8T 2200 // us
|
||||
|
||||
boolean decodeNewKaku(byte BaseVarIndex)
|
||||
{
|
||||
boolean success = false;
|
||||
byte Par1 = 0;
|
||||
unsigned long Par2 = 0;
|
||||
|
||||
unsigned long bitstream = 0L;
|
||||
unsigned long address = 0L;
|
||||
byte channel = 0;
|
||||
byte command = 0;
|
||||
boolean Bit;
|
||||
int i;
|
||||
int P0, P1, P2, P3;
|
||||
Par1 = 0;
|
||||
|
||||
if (Plugin_199_number == NewKAKU_RawSignalLength || Plugin_199_number == NewKAKUdim_RawSignalLength)
|
||||
{
|
||||
i = 3; // Plugin_199_pulses[3] is de eerste van een T,xT,T,xT combinatie
|
||||
|
||||
do
|
||||
{
|
||||
P0 = Plugin_199_pulses[i] * 25;
|
||||
P1 = Plugin_199_pulses[i + 1] * 25;
|
||||
P2 = Plugin_199_pulses[i + 2] * 25;
|
||||
P3 = Plugin_199_pulses[i + 3] * 25;
|
||||
|
||||
if (P0 < NewKAKU_mT && P1 < NewKAKU_mT && P2 < NewKAKU_mT && P3 > NewKAKU_mT)Bit = 0; // T,T,T,4T
|
||||
else if (P0 < NewKAKU_mT && P1 > NewKAKU_mT && P2 < NewKAKU_mT && P3 < NewKAKU_mT)Bit = 1; // T,4T,T,T
|
||||
else if (P0 < NewKAKU_mT && P1 < NewKAKU_mT && P2 < NewKAKU_mT && P3 < NewKAKU_mT) // T,T,T,T Deze hoort te zitten op i=111 want: 27e NewKAKU bit maal 4 plus 2 posities voor startbit
|
||||
{
|
||||
if (Plugin_199_number != NewKAKUdim_RawSignalLength) // als de dim-bits er niet zijn
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false; // andere mogelijkheden zijn niet geldig in NewKAKU signaal.
|
||||
|
||||
if (i < 130) // alle bits die tot de 32-bit pulstrein behoren 32bits * 4posities per bit + pulse/space voor startbit
|
||||
bitstream = (bitstream << 1) | Bit;
|
||||
else // de resterende vier bits die tot het dimlevel behoren
|
||||
Par1 = (Par1 << 1) | Bit;
|
||||
|
||||
i += 4; // volgende pulsenquartet
|
||||
} while (i < Plugin_199_number - 2); //-2 omdat de space/pulse van de stopbit geen deel meer van signaal uit maakt.
|
||||
|
||||
if (i > 140) // Commando en Dim deel
|
||||
Par1++; // Dim level. +1 omdat gebruiker dim level begint bij één.
|
||||
else
|
||||
Par1 = ((bitstream >> 4) & 0x01) ? 16 : 0; // On/Off bit omzetten naar een Nodo waarde.
|
||||
|
||||
Par2 = bitstream;
|
||||
address = bitstream >> 6;
|
||||
channel = (bitstream & 0x0f) + 1;
|
||||
command = (bitstream >> 4) & 0x03;
|
||||
if (command > 1)
|
||||
channel = 0;
|
||||
// valid signal, remember timestamp to suppress repeats...
|
||||
elapsed = millis() - Plugin_199_lastTime;
|
||||
Plugin_199_lastTime = millis();
|
||||
unsigned long codeHash = Par2 + Par1;
|
||||
if (codeHash != Plugin_199_codeHash || (codeHash == Plugin_199_codeHash && elapsed > 250))
|
||||
{
|
||||
UserVar[BaseVarIndex] = address;
|
||||
UserVar[BaseVarIndex + 1] = channel;
|
||||
UserVar[BaseVarIndex + 2] = Par1;
|
||||
String eventString = F("NewKaku_");
|
||||
eventString += address;
|
||||
eventString += F("#");
|
||||
eventString += channel;
|
||||
eventString += F("=");
|
||||
eventString += Par1;
|
||||
rulesProcessing(eventString);
|
||||
success = true;
|
||||
}
|
||||
Plugin_199_codeHash = Par2 + Par1;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
//********************************************************************************
|
||||
// Send NewKaku protocol (without code wheel)
|
||||
//********************************************************************************
|
||||
void sendNewKaku(unsigned long address, byte channel, byte state)
|
||||
{
|
||||
unsigned long bitstream = 0L;
|
||||
byte i = 1;
|
||||
byte x; // aantal posities voor pulsen/spaces in RawSignal
|
||||
|
||||
bitstream = address << 6;
|
||||
bitstream |= (channel - 1);
|
||||
|
||||
//RawSignal.Repeats = 7; // Aantal herhalingen van het signaal.
|
||||
//RawSignal.Delay = 20; // Tussen iedere pulsenreeks enige tijd rust.
|
||||
|
||||
if (state == 16 || state == 0)
|
||||
{
|
||||
bitstream |= (state == 16) << 4; // bit-5 is het on/off commando in KAKU signaal
|
||||
x = 130; // verzend startbit + 32-bits = 130
|
||||
}
|
||||
else
|
||||
x = 146; // verzend startbit + 32-bits = 130 + 4dimbits = 146
|
||||
|
||||
// bitstream bevat nu de KAKU-bits die verzonden moeten worden.
|
||||
|
||||
for (i = 3; i <= x; i++)Plugin_199_pulses[i] = NewKAKU_1T / 25; // De meeste tijden in signaal zijn T. Vul alle pulstijden met deze waarde. Later worden de 4T waarden op hun plek gezet
|
||||
|
||||
i = 1;
|
||||
Plugin_199_pulses[i++] = NewKAKU_1T / 25; //pulse van de startbit
|
||||
Plugin_199_pulses[i++] = NewKAKU_8T / 25; //space na de startbit
|
||||
|
||||
byte y = 31; // bit uit de bitstream
|
||||
while (i < x)
|
||||
{
|
||||
if ((bitstream >> (y--)) & 1)
|
||||
Plugin_199_pulses[i + 1] = NewKAKU_4T / 25; // Bit=1; // T,4T,T,T
|
||||
else
|
||||
Plugin_199_pulses[i + 3] = NewKAKU_4T / 25; // Bit=0; // T,T,T,4T
|
||||
|
||||
if (x == 146) // als het een dim opdracht betreft
|
||||
{
|
||||
if (i == 111) // Plaats van de Commando-bit uit KAKU
|
||||
Plugin_199_pulses[i + 3] = NewKAKU_1T / 25; // moet een T,T,T,T zijn bij een dim commando.
|
||||
if (i == 127) // als alle pulsen van de 32-bits weggeschreven zijn
|
||||
{
|
||||
bitstream = (unsigned long)state; // nog vier extra dim-bits om te verzenden.
|
||||
y = 3;
|
||||
}
|
||||
}
|
||||
i += 4;
|
||||
}
|
||||
Plugin_199_pulses[i++] = NewKAKU_1T / 25; //pulse van de stopbit
|
||||
Plugin_199_pulses[i] = 0; //space van de stopbit
|
||||
Plugin_199_number = i; // aantal bits*2 die zich in het opgebouwde RawSignal bevinden
|
||||
RawSendRF();
|
||||
}
|
||||
|
||||
|
||||
//********************************************************************************
|
||||
// Decode Kaku protocol (with code wheel)
|
||||
//********************************************************************************
|
||||
#define KAKU_CodeLength 12
|
||||
#define KAKU_T 350
|
||||
boolean decodeKaku()
|
||||
{
|
||||
boolean success = false;
|
||||
byte Par1 = 0;
|
||||
unsigned long Par2 = 0;
|
||||
|
||||
int i, j;
|
||||
unsigned long bitstream = 0;
|
||||
|
||||
if (Plugin_199_number != (KAKU_CodeLength * 4) + 2)return false; // conventionele KAKU bestaat altijd uit 12 data bits plus stop. Ongelijk, dan geen KAKU!
|
||||
|
||||
for (i = 0; i < KAKU_CodeLength; i++)
|
||||
{
|
||||
j = (KAKU_T * 2) / 25;
|
||||
|
||||
if (Plugin_199_pulses[4 * i + 1] < j && Plugin_199_pulses[4 * i + 2] > j && Plugin_199_pulses[4 * i + 3] < j && Plugin_199_pulses[4 * i + 4] > j) {
|
||||
bitstream = (bitstream >> 1); // 0
|
||||
}
|
||||
else if (Plugin_199_pulses[4 * i + 1] < j && Plugin_199_pulses[4 * i + 2] > j && Plugin_199_pulses[4 * i + 3] > j && Plugin_199_pulses[4 * i + 4] < j) {
|
||||
bitstream = (bitstream >> 1 | (1 << (KAKU_CodeLength - 1))); // 1
|
||||
}
|
||||
else if (Plugin_199_pulses[4 * i + 1] < j && Plugin_199_pulses[4 * i + 2] > j && Plugin_199_pulses[4 * i + 3] < j && Plugin_199_pulses[4 * i + 4] < j) {
|
||||
bitstream = (bitstream >> 1); // Short 0, Groep commando op 2e bit.
|
||||
Par1 = 2;
|
||||
}
|
||||
else {
|
||||
return false; // foutief signaal
|
||||
}
|
||||
}
|
||||
|
||||
if ((bitstream & 0x600) == 0x600) // twee vaste bits van KAKU gebruiken als checksum
|
||||
{ // Alles is in orde, bouw event op
|
||||
Par2 = bitstream & 0xFF;
|
||||
Par1 |= (bitstream >> 11) & 0x01;
|
||||
|
||||
// valid signal, remember timestamp to suppress repeats...
|
||||
elapsed = millis() - Plugin_199_lastTime;
|
||||
Plugin_199_lastTime = millis();
|
||||
unsigned long codeHash = Par2 + Par1;
|
||||
if (codeHash != Plugin_199_codeHash || (codeHash == Plugin_199_codeHash && elapsed > 250))
|
||||
{
|
||||
String eventString = F("Kaku_");
|
||||
eventString += (Par2 & 0x0f) + 1;
|
||||
eventString += F("#");
|
||||
eventString += (Par2 >> 4) + 1;
|
||||
eventString += F("=");
|
||||
eventString += Par1;
|
||||
rulesProcessing(eventString);
|
||||
success = true;
|
||||
}
|
||||
Plugin_199_codeHash = Par2 + Par1;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
//********************************************************************************
|
||||
// Send Kaku protocol (with code wheel)
|
||||
//********************************************************************************
|
||||
void sendKaku(unsigned long address, byte channel, byte state)
|
||||
{
|
||||
byte Par1 = state;
|
||||
unsigned long Par2 = ((channel-1) << 4) + address-1;
|
||||
|
||||
unsigned long Bitstream = Par2 | (0x600 | ((Par1 & 1 /*Commando*/) << 11)); // Stel een bitstream samen
|
||||
|
||||
// loop de 12-bits langs en vertaal naar pulse/space signalen.
|
||||
for (byte i = 0; i < KAKU_CodeLength; i++)
|
||||
{
|
||||
Plugin_199_pulses[4 * i + 1] = KAKU_T / RAWSIGNAL_MULTIPLY;
|
||||
Plugin_199_pulses[4 * i + 2] = (KAKU_T * 3) / RAWSIGNAL_MULTIPLY;
|
||||
|
||||
if (((Par1 >> 1) & 1) /* Groep */ && i >= 4 && i < 8)
|
||||
{
|
||||
Plugin_199_pulses[4 * i + 3] = KAKU_T / RAWSIGNAL_MULTIPLY;
|
||||
Plugin_199_pulses[4 * i + 4] = KAKU_T / RAWSIGNAL_MULTIPLY;
|
||||
} // short 0
|
||||
else
|
||||
{
|
||||
if ((Bitstream >> i) & 1) // 1
|
||||
{
|
||||
Plugin_199_pulses[4 * i + 3] = (KAKU_T * 3) / RAWSIGNAL_MULTIPLY;
|
||||
Plugin_199_pulses[4 * i + 4] = KAKU_T / RAWSIGNAL_MULTIPLY;
|
||||
}
|
||||
else //0
|
||||
{
|
||||
Plugin_199_pulses[4 * i + 3] = KAKU_T / RAWSIGNAL_MULTIPLY;
|
||||
Plugin_199_pulses[4 * i + 4] = (KAKU_T * 3) / RAWSIGNAL_MULTIPLY;
|
||||
}
|
||||
}
|
||||
// Stopbit
|
||||
Plugin_199_pulses[4 * KAKU_CodeLength + 1] = KAKU_T / RAWSIGNAL_MULTIPLY;
|
||||
Plugin_199_pulses[4 * KAKU_CodeLength + 2] = KAKU_T / RAWSIGNAL_MULTIPLY;
|
||||
}
|
||||
|
||||
Plugin_199_number=KAKU_CodeLength*4+2;
|
||||
RawSendRF();
|
||||
}
|
||||
|
||||
|
||||
//********************************************************************************
|
||||
// Decode HomeEasy 3xx series EU protocol (without code wheel)
|
||||
//********************************************************************************
|
||||
boolean decodeHE300EU()
|
||||
{
|
||||
boolean success = false;
|
||||
byte Par1 = 0;
|
||||
unsigned long Par2 = 0;
|
||||
|
||||
unsigned long address = 0;
|
||||
unsigned long bitstream = 0;
|
||||
int counter = 0;
|
||||
byte rfbit = 0;
|
||||
byte state = 0;
|
||||
unsigned long channel = 0;
|
||||
|
||||
// valid messages are 116 pulses
|
||||
if (Plugin_199_number != 116) return false;
|
||||
|
||||
for (byte x = 1; x <= Plugin_199_number; x = x + 2)
|
||||
{
|
||||
if ((Plugin_199_pulses[x] * 25 < 500) & (Plugin_199_pulses[x + 1] * 25 > 500))
|
||||
rfbit = 1;
|
||||
else
|
||||
rfbit = 0;
|
||||
|
||||
if ((x >= 23) && (x <= 86)) address = (address << 1) | rfbit;
|
||||
if ((x >= 87) && (x <= 114)) bitstream = (bitstream << 1) | rfbit;
|
||||
|
||||
}
|
||||
state = ((bitstream >> 8) & 0x3) - 1;
|
||||
channel = (bitstream) & 0x3f;
|
||||
|
||||
Par1 = state;
|
||||
Par2 = address + channel;
|
||||
|
||||
// valid signal, remember timestamp to suppress repeats...
|
||||
elapsed = millis() - Plugin_199_lastTime;
|
||||
Plugin_199_lastTime = millis();
|
||||
unsigned long codeHash = Par2 + Par1;
|
||||
if (codeHash != Plugin_199_codeHash || (codeHash == Plugin_199_codeHash && elapsed > 250))
|
||||
{
|
||||
String eventString = F("HE300EU_");
|
||||
eventString += address;
|
||||
eventString += F("#");
|
||||
eventString += channel;
|
||||
eventString += F("=");
|
||||
eventString += Par1;
|
||||
rulesProcessing(eventString);
|
||||
success = true;
|
||||
}
|
||||
Plugin_199_codeHash = Par2 + Par1;
|
||||
}
|
||||
|
||||
|
||||
//********************************************************************************
|
||||
// Decode generic protocol, deriving hash value
|
||||
//********************************************************************************
|
||||
boolean decodeUnknown()
|
||||
{
|
||||
boolean success = false;
|
||||
byte Par1 = 0;
|
||||
unsigned long Par2 = 0;
|
||||
|
||||
int x;
|
||||
unsigned int MinPulse = 0xffff;
|
||||
unsigned int MinSpace = 0xffff;
|
||||
unsigned long CodeM = 0L;
|
||||
unsigned long CodeS = 0L;
|
||||
|
||||
if (Plugin_199_number < MIN_RAW_PULSES) return false;
|
||||
|
||||
for (x = 5; x < Plugin_199_number - 2; x += 2)
|
||||
{
|
||||
if (Plugin_199_pulses[x] < MinPulse)MinPulse = Plugin_199_pulses[x];
|
||||
if (Plugin_199_pulses[x + 1] < MinSpace)MinSpace = Plugin_199_pulses[x + 1];
|
||||
}
|
||||
|
||||
MinPulse += (MinPulse * 100) / 100;
|
||||
MinSpace += (MinSpace * 100) / 100;
|
||||
|
||||
// Data kan zowel in de mark als de space zitten. Daarom pakken we beide voor data opbouw.
|
||||
for (x = 3; x <= Plugin_199_number; x += 2)
|
||||
{
|
||||
CodeM = (CodeM << 1) | (Plugin_199_pulses[x] > MinPulse);
|
||||
CodeS = (CodeS << 1) | (Plugin_199_pulses[x + 1] > MinSpace);
|
||||
}
|
||||
|
||||
// Data kan zowel in de mark als de space zitten. We nemen de grootste waarde voor de data.
|
||||
if (CodeM > CodeS)
|
||||
Par2 = CodeM;
|
||||
else
|
||||
Par2 = CodeS;
|
||||
|
||||
// valid signal, remember timestamp to suppress repeats...
|
||||
elapsed = millis() - Plugin_199_lastTime;
|
||||
Plugin_199_lastTime = millis();
|
||||
unsigned long codeHash = Par2;
|
||||
if (codeHash != Plugin_199_codeHash || (codeHash == Plugin_199_codeHash && elapsed > 250))
|
||||
{
|
||||
String eventString = F("UnknownRF_");
|
||||
eventString += Par2;
|
||||
rulesProcessing(eventString);
|
||||
success = true;
|
||||
}
|
||||
Plugin_199_codeHash = Par2;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,490 @@
|
||||
#ifdef PLUGIN_BUILD_DEV
|
||||
|
||||
//#######################################################################################################
|
||||
//#################################### Plugin 210: MQTT Import ##########################################
|
||||
//#################################### Version 0.4 5-Aug-2016 ###########################################
|
||||
//## This Version Requires ESPEasy 114+, Core 2.3 and PubSub 2.6 #######
|
||||
//#######################################################################################################
|
||||
|
||||
// This task reads data from the MQTT Import input stream and saves the value
|
||||
|
||||
#define PLUGIN_210
|
||||
#define PLUGIN_ID_210 210
|
||||
#define PLUGIN_NAME_210 "MQTT Import [DEVELOPMENT]"
|
||||
|
||||
#define PLUGIN_VALUENAME1_210 "Value1"
|
||||
#define PLUGIN_VALUENAME2_210 "Value2"
|
||||
#define PLUGIN_VALUENAME3_210 "Value3"
|
||||
#define PLUGIN_VALUENAME4_210 "Value4"
|
||||
|
||||
#define PLUGIN_IMPORT 210 // This is a 'private' function used only by this import module
|
||||
|
||||
// Declare a Wifi client for this plugin only
|
||||
|
||||
WiFiClient espclient_210;
|
||||
PubSubClient MQTTclient_210(espclient_210); // Create a new pubsub instance
|
||||
|
||||
boolean Plugin_210(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
boolean success = false;
|
||||
|
||||
char deviceTemplate[4][41]; // variable for saving the subscription topics
|
||||
//
|
||||
// Generate the MQTT import client name from the system name and a suffix
|
||||
//
|
||||
String tmpClientName = "%sysname%-Import";
|
||||
String ClientName = parseTemplate(tmpClientName, 20);
|
||||
|
||||
switch (function)
|
||||
{
|
||||
|
||||
case PLUGIN_DEVICE_ADD:
|
||||
{
|
||||
Device[++deviceCount].Number = PLUGIN_ID_210;
|
||||
Device[deviceCount].Type = SENSOR_TYPE_SWITCH;
|
||||
Device[deviceCount].VType = SENSOR_TYPE_SINGLE; // This means it has a single pin
|
||||
Device[deviceCount].Ports = 0;
|
||||
Device[deviceCount].PullUpOption = false;
|
||||
Device[deviceCount].InverseLogicOption = false;
|
||||
Device[deviceCount].FormulaOption = true; // Need this in order to get the decimals option
|
||||
Device[deviceCount].ValueCount = 4;
|
||||
Device[deviceCount].SendDataOption = false;
|
||||
Device[deviceCount].TimerOption=false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICENAME:
|
||||
{
|
||||
string = F(PLUGIN_NAME_210);
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_GET_DEVICEVALUENAMES:
|
||||
{
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_210));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_210));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_210));
|
||||
strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_210));
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD:
|
||||
{
|
||||
|
||||
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
|
||||
for (byte varNr = 0; varNr < 4; varNr++)
|
||||
{
|
||||
string += F("<TR><TD>MQTT Topic ");
|
||||
string += varNr + 1;
|
||||
string += F(":<TD><input type='text' size='40' maxlength='40' name='Plugin_210_template");
|
||||
string += varNr + 1;
|
||||
string += F("' value='");
|
||||
string += deviceTemplate[varNr];
|
||||
string += F("'>");
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_SAVE:
|
||||
{
|
||||
String argName;
|
||||
|
||||
for (byte varNr = 0; varNr < 4; varNr++)
|
||||
{
|
||||
argName = F("Plugin_210_template");
|
||||
argName += varNr + 1;
|
||||
strncpy(deviceTemplate[varNr], WebServer.arg(argName).c_str(), sizeof(deviceTemplate[varNr]));
|
||||
}
|
||||
|
||||
Settings.TaskDeviceID[event->TaskIndex] = 1; // temp fix, needs a dummy value
|
||||
|
||||
SaveCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_INIT:
|
||||
{
|
||||
|
||||
// When we edit the subscription data from the webserver, the plugin is called again with init.
|
||||
// In order to resubscribe we have to disconnect and reconnect in order to get rid of any obsolete subscriptions
|
||||
|
||||
MQTTclient_210.disconnect();
|
||||
|
||||
if (MQTTConnect_210(ClientName))
|
||||
{
|
||||
// Subscribe to ALL the topics from ALL instance of this import module
|
||||
MQTTSubscribe_210();
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
success=false;
|
||||
}
|
||||
}
|
||||
|
||||
case PLUGIN_TEN_PER_SECOND:
|
||||
{
|
||||
MQTTclient_210.loop(); // Listen out for callbacks
|
||||
success=true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_ONCE_A_SECOND:
|
||||
{
|
||||
// Here we check that the MQTT client is alive.
|
||||
|
||||
if (!MQTTclient_210.connected()){
|
||||
|
||||
String log = F("IMPT : MQTT 210 Connection lost");
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
|
||||
MQTTclient_210.disconnect();
|
||||
delay(1000);
|
||||
|
||||
if (! MQTTConnect_210(ClientName)){
|
||||
success=false;
|
||||
break;
|
||||
}
|
||||
|
||||
MQTTSubscribe_210();
|
||||
}
|
||||
|
||||
success=true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_READ:
|
||||
{
|
||||
// This routine does not output any data and so we do not need to respond to regular read requests
|
||||
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLUGIN_IMPORT:
|
||||
{
|
||||
// This is a private option only used by the MQTT 210 callback function
|
||||
|
||||
// Get the payload and check it out
|
||||
|
||||
String Payload=event->String2;
|
||||
float floatPayload=string2float(Payload);
|
||||
|
||||
LoadTaskSettings(event->TaskIndex);
|
||||
|
||||
if (floatPayload == -999){
|
||||
String log=F("IMPT : Bad Import MQTT Command ");
|
||||
log+=event->String1;
|
||||
addLog(LOG_LEVEL_ERROR,log);
|
||||
log="ERR : Illegal Payload ";
|
||||
log+=Payload;
|
||||
log+=" ";
|
||||
log+=ExtraTaskSettings.TaskDeviceName;
|
||||
addLog(LOG_LEVEL_INFO,log);
|
||||
success=false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the Topic and see if it matches any of the subscriptions
|
||||
|
||||
String Topic=event->String1;
|
||||
|
||||
LoadCustomTaskSettings(event->TaskIndex, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
|
||||
for (byte x = 0; x < 4; x++)
|
||||
{
|
||||
String subscriptionTopic = deviceTemplate[x];
|
||||
subscriptionTopic.trim();
|
||||
if (subscriptionTopic.length() == 0) continue; // skip blank subscriptions
|
||||
|
||||
// Now check if the incoming topic matches one of our subscriptions
|
||||
|
||||
if (MQTTCheckSubscription_210(Topic,subscriptionTopic))
|
||||
{
|
||||
UserVar[event->BaseVarIndex+x]=floatPayload; // Save the new value
|
||||
|
||||
// Log the event
|
||||
|
||||
String log =F("IMPT : [");
|
||||
log += ExtraTaskSettings.TaskDeviceName;
|
||||
log += F("#");
|
||||
log += ExtraTaskSettings.TaskDeviceValueNames[x];
|
||||
log += F("] : ");
|
||||
log += floatPayload;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
|
||||
// Generate event for rules processing - proposed by TridentTD
|
||||
|
||||
if (Settings.UseRules)
|
||||
{
|
||||
String RuleEvent = F("");
|
||||
RuleEvent += ExtraTaskSettings.TaskDeviceName;
|
||||
RuleEvent += F("#");
|
||||
RuleEvent += ExtraTaskSettings.TaskDeviceValueNames[x];
|
||||
RuleEvent += F("=");
|
||||
RuleEvent += floatPayload;
|
||||
rulesProcessing(RuleEvent);
|
||||
}
|
||||
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
boolean MQTTSubscribe_210()
|
||||
{
|
||||
|
||||
// Subscribe to the topics requested by ALL calls to this plugin.
|
||||
// We do this because if the connection to the broker is lost, we want to resubscribe for all instances.
|
||||
|
||||
char deviceTemplate[4][41];
|
||||
|
||||
// Loop over all tasks looking for a 210 instance
|
||||
|
||||
for (byte y = 0; y < TASKS_MAX; y++)
|
||||
{
|
||||
if (Settings.TaskDeviceNumber[y] == 210)
|
||||
{
|
||||
LoadCustomTaskSettings(y, (byte*)&deviceTemplate, sizeof(deviceTemplate));
|
||||
|
||||
// Now loop over all import variables and subscribe to those that are not blank
|
||||
|
||||
for (byte x = 0; x < 4; x++)
|
||||
{
|
||||
String subscribeTo = deviceTemplate[x];
|
||||
|
||||
if (subscribeTo.length() > 0)
|
||||
{
|
||||
if (MQTTclient_210.subscribe(subscribeTo.c_str()))
|
||||
{
|
||||
String log = F("IMPT : [");
|
||||
LoadTaskSettings(y);
|
||||
log += ExtraTaskSettings.TaskDeviceName;
|
||||
log += F("#");
|
||||
log += ExtraTaskSettings.TaskDeviceValueNames[x];
|
||||
log += F("] subscribed to ");
|
||||
log += subscribeTo;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
else
|
||||
{
|
||||
String log = F("IMPT : Error subscribing to ");
|
||||
log += subscribeTo;
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//
|
||||
// handle MQTT messages
|
||||
//
|
||||
void mqttcallback_210(char* c_topic, byte* b_payload, unsigned int length)
|
||||
{
|
||||
// Here we have incomng MQTT messages from the mqtt import module
|
||||
|
||||
String topic = c_topic;
|
||||
|
||||
char cpayload[256];
|
||||
strncpy(cpayload, (char*)b_payload, length);
|
||||
cpayload[length] = 0;
|
||||
String payload = cpayload; // convert byte to char string
|
||||
payload.trim();
|
||||
|
||||
byte DeviceIndex = getDeviceIndex(210); // This is the device index of 210 modules -there should be one!
|
||||
|
||||
// We generate a temp event structure to pass to the plugins
|
||||
|
||||
struct EventStruct TempEvent;
|
||||
|
||||
TempEvent.String1 = topic; // This is the topic of the message
|
||||
TempEvent.String2 = payload; // This is the payload
|
||||
|
||||
// Here we loop over all tasks and call each 210 plugin with function PLUGIN_IMPORT
|
||||
|
||||
for (byte y = 0; y < TASKS_MAX; y++)
|
||||
{
|
||||
if (Settings.TaskDeviceNumber[y] == 210) // if we have found a 210 device, then give it something to think about!
|
||||
{
|
||||
TempEvent.TaskIndex = y;
|
||||
TempEvent.BaseVarIndex = y * VARS_PER_TASK; // This is the index in Uservar where values for this task are stored
|
||||
Plugin_ptr[DeviceIndex](PLUGIN_IMPORT, &TempEvent, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Make a new client connection to the mqtt broker.
|
||||
// For some reason this seems to failduring the call in INIT- however it succeeds later during recovery
|
||||
// It would be nice to understand this....
|
||||
|
||||
boolean MQTTConnect_210(String clientid)
|
||||
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
// Do nothing if already connected
|
||||
|
||||
if (MQTTclient_210.connected())return true;
|
||||
|
||||
IPAddress MQTTBrokerIP(Settings.Controller_IP);
|
||||
|
||||
// define stuff for the client - this could also be done in the intial declaration of MQTTclient_210
|
||||
|
||||
MQTTclient_210.setServer(MQTTBrokerIP, Settings.ControllerPort);
|
||||
MQTTclient_210.setCallback(mqttcallback_210);
|
||||
|
||||
// Try three times for a connection
|
||||
|
||||
for (byte x = 1; x < 4; x++)
|
||||
{
|
||||
String log = "";
|
||||
|
||||
if ((SecuritySettings.ControllerUser[0] != 0) && (SecuritySettings.ControllerPassword[0] != 0))
|
||||
result = MQTTclient_210.connect(clientid.c_str(), SecuritySettings.ControllerUser, SecuritySettings.ControllerPassword);
|
||||
else
|
||||
result = MQTTclient_210.connect(clientid.c_str());
|
||||
|
||||
|
||||
if (result)
|
||||
{
|
||||
log = F("IMPT : Connected to MQTT broker with Client ID=");
|
||||
log += clientid;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
|
||||
break; // end loop if succesfull
|
||||
}
|
||||
else
|
||||
{
|
||||
log = F("IMPT : Failed to connect to MQTT broker - attempt ");
|
||||
log += x;
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
}
|
||||
|
||||
delay(500);
|
||||
}
|
||||
|
||||
return MQTTclient_210.connected();
|
||||
}
|
||||
|
||||
//
|
||||
// Check to see if Topic matches the MQTT subscription
|
||||
//
|
||||
boolean MQTTCheckSubscription_210(String Topic, String Subscription) {
|
||||
|
||||
String tmpTopic = Topic;
|
||||
String tmpSub = Subscription;
|
||||
|
||||
tmpTopic.trim();
|
||||
tmpSub.trim();
|
||||
|
||||
// Get rid of any initial /
|
||||
|
||||
if (tmpTopic.substring(0, 1) == "/")tmpTopic = tmpTopic.substring(1);
|
||||
if (tmpSub.substring(0, 1) == "/")tmpSub = tmpSub.substring(1);
|
||||
|
||||
// Add trailing / if required
|
||||
|
||||
int lenTopic = tmpTopic.length();
|
||||
if (tmpTopic.substring(lenTopic - 1, lenTopic) != "/")tmpTopic += F("/");
|
||||
|
||||
int lenSub = tmpSub.length();
|
||||
if (tmpSub.substring(lenSub - 1, lenSub) != "/")tmpSub += F("/");
|
||||
|
||||
// Now get first part
|
||||
|
||||
int SlashTopic;
|
||||
int SlashSub;
|
||||
int count = 0;
|
||||
|
||||
String pTopic;
|
||||
String pSub;
|
||||
|
||||
while (count < 10) {
|
||||
|
||||
// Get locations of the first /
|
||||
|
||||
SlashTopic = tmpTopic.indexOf('/');
|
||||
SlashSub = tmpSub.indexOf('/');
|
||||
|
||||
// If no slashes found then match is OK
|
||||
// If only one slash found then not OK
|
||||
|
||||
if ((SlashTopic == -1) && (SlashSub == -1)) return true;
|
||||
if ((SlashTopic == -1) && (SlashSub != -1)) return false;
|
||||
if ((SlashTopic != -1) && (SlashSub == -1)) return false;
|
||||
|
||||
// Get the values for the current subtopic
|
||||
|
||||
pTopic = tmpTopic.substring(0, SlashTopic);
|
||||
pSub = tmpSub.substring(0, SlashSub);
|
||||
|
||||
// And strip the subtopic from the topic
|
||||
|
||||
tmpTopic = tmpTopic.substring(SlashTopic + 1);
|
||||
tmpSub = tmpSub.substring(SlashSub + 1);
|
||||
|
||||
// If the subtopics match then OK - otherwise fail
|
||||
if (pSub == "#") return true;
|
||||
if ((pTopic != pSub) && (pSub != "+"))return false;
|
||||
|
||||
count = count + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
// Convert String to float - returns -999 in case of error
|
||||
float string2float(String myString) {
|
||||
int i, len;
|
||||
float value;
|
||||
len = myString.length();
|
||||
char tmp[(len + 1)]; // one extra for the zero termination
|
||||
byte start = 0;
|
||||
|
||||
// Look for decimal point - they can be anywhere but no more than one of them!
|
||||
|
||||
int dotIndex = myString.indexOf(".");
|
||||
//Serial.println(dotIndex);
|
||||
|
||||
if (dotIndex != -1)
|
||||
{
|
||||
int dotIndex2 = (myString.substring(dotIndex + 1)).indexOf(".");
|
||||
//Serial.println(dotIndex2);
|
||||
if (dotIndex2 != -1)return -999.00; // Give error if there is more than one dot
|
||||
}
|
||||
|
||||
if (myString.substring(0, 1) == "-") {
|
||||
start = 1; //allow a minus in front of string
|
||||
tmp[i] = '-';
|
||||
}
|
||||
|
||||
for (i = start; i<len; i++)
|
||||
{
|
||||
tmp[i] = myString.charAt(i);
|
||||
if (!isdigit(tmp[i]))
|
||||
{
|
||||
if (tmp[i] != '.')return -999;
|
||||
}
|
||||
}
|
||||
|
||||
tmp[i] = 0;
|
||||
value = atof(tmp);
|
||||
return value;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user