mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Compare commits
132
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e2a5d3b3d | ||
|
|
a4dcc304a9 | ||
|
|
a938bc78c1 | ||
|
|
d2c7743197 | ||
|
|
f0fc20de57 | ||
|
|
d35a50be17 | ||
|
|
537d4cd1e9 | ||
|
|
62422f4924 | ||
|
|
04de145dbb | ||
|
|
21840d0b26 | ||
|
|
076ce0127b | ||
|
|
226c0a6f83 | ||
|
|
6ffa8d92a6 | ||
|
|
dae2cc2f4a | ||
|
|
555ccbe229 | ||
|
|
d277d22914 | ||
|
|
b5e4994392 | ||
|
|
cbdddf8c06 | ||
|
|
8b5d22a081 | ||
|
|
88fc0a8445 | ||
|
|
ec9187d038 | ||
|
|
51ede17d2a | ||
|
|
97ee69b371 | ||
|
|
f77ce9c8f7 | ||
|
|
99cc6f72af | ||
|
|
848f43b704 | ||
|
|
3a5ef5da81 | ||
|
|
3527b69718 | ||
|
|
54628bfca9 | ||
|
|
72ff745eff | ||
|
|
aed566b6db | ||
|
|
504ea0c813 | ||
|
|
4ca5ef3f72 | ||
|
|
dd9db3b557 | ||
|
|
9c531b30bc | ||
|
|
67c8477a61 | ||
|
|
cd30b81202 | ||
|
|
550881faa5 | ||
|
|
3d07466904 | ||
|
|
ed45945f21 | ||
|
|
f13abe8922 | ||
|
|
34b0a683a9 | ||
|
|
9ab10437b5 | ||
|
|
b0b7408522 | ||
|
|
b104942db9 | ||
|
|
a694b658b1 | ||
|
|
cb07a0d342 | ||
|
|
1ceaad04ec | ||
|
|
5cbf0c1e38 | ||
|
|
ef0ef092f0 | ||
|
|
46d3ae6a2b | ||
|
|
da54ddda78 | ||
|
|
c24205713d | ||
|
|
52d4cb804e | ||
|
|
ae077de72b | ||
|
|
86fa5ec6ae | ||
|
|
41c9d68422 | ||
|
|
e713da5d79 | ||
|
|
edc261ca2b | ||
|
|
8f4a52a6f1 | ||
|
|
af519748a6 | ||
|
|
c04b80b2f1 | ||
|
|
cdbc2498c1 | ||
|
|
78997b4329 | ||
|
|
2bece45712 | ||
|
|
10c73a001a | ||
|
|
790a946cf6 | ||
|
|
69c5be6489 | ||
|
|
233ca2bf43 | ||
|
|
3de400fc91 | ||
|
|
edeea02f71 | ||
|
|
6fae1f79d8 | ||
|
|
1222e04caf | ||
|
|
f38973aad6 | ||
|
|
b31b1d913f | ||
|
|
fa3d4af8cb | ||
|
|
29320aab90 | ||
|
|
3bc8609611 | ||
|
|
c43d6b3769 | ||
|
|
e9e2ea32ed | ||
|
|
24b7d1f438 | ||
|
|
4febf08501 | ||
|
|
7f4eae19f4 | ||
|
|
c13952d04c | ||
|
|
3032463917 | ||
|
|
bbb97bfc7e | ||
|
|
e51f7683a5 | ||
|
|
e747756dd2 | ||
|
|
1ad6fd6ea0 | ||
|
|
d2ec15a4bf | ||
|
|
f23d9fa3d3 | ||
|
|
6df5840db9 | ||
|
|
5f79e4b922 | ||
|
|
e058507b9a | ||
|
|
3cea522ec4 | ||
|
|
5c4defdf6e | ||
|
|
e58ea2a3fe | ||
|
|
a93a078c0b | ||
|
|
0e0e7be95a | ||
|
|
2b7a61469f | ||
|
|
ce02c9e039 | ||
|
|
1e9dc1d14c | ||
|
|
75f1c6c140 | ||
|
|
9a6f7360da | ||
|
|
4e537461f2 | ||
|
|
dc7bb12581 | ||
|
|
0c80cdc119 | ||
|
|
37210031b7 | ||
|
|
baa1df620f | ||
|
|
a310811919 | ||
|
|
e9dd5da690 | ||
|
|
f9c25489d1 | ||
|
|
46ee928b07 | ||
|
|
3af102f2ad | ||
|
|
ca53ec0263 | ||
|
|
5ca7c073f1 | ||
|
|
3f68154819 | ||
|
|
7b1c4483bb | ||
|
|
572d9c0714 | ||
|
|
48643cf87e | ||
|
|
9ea594afeb | ||
|
|
49a783c381 | ||
|
|
c9b81f7ab9 | ||
|
|
91c6e3af61 | ||
|
|
faeb921bcf | ||
|
|
9928f56ad3 | ||
|
|
61d27084c1 | ||
|
|
e5393a9fa4 | ||
|
|
1cbc49021a | ||
|
|
84f389b44e | ||
|
|
765775e22f | ||
|
|
02c97c4513 |
+3
-1
@@ -12,11 +12,13 @@ for ENV in \
|
||||
normal_ESP8285_1024 \
|
||||
test_ESP8266_1024\
|
||||
test_ESP8266_4096\
|
||||
test_ESP8266_4096_VCC\
|
||||
test_ESP8285_1024\
|
||||
dev_ESP8266_1024\
|
||||
dev_ESP8266_4096\
|
||||
dev_ESP8285_1024\
|
||||
dev_ESP8266PUYA_1024;\
|
||||
dev_ESP8266PUYA_1024\
|
||||
dev_ESP8266PUYA_1024_VCC;\
|
||||
do
|
||||
echo
|
||||
echo "### Deploying environment $ENV for version $VERSION"
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
|
||||
|
||||
import sys
|
||||
import binascii
|
||||
import struct
|
||||
import md5
|
||||
MD5DUMMY = "MD5_MD5_MD5_MD5_BoundariesOfTheSegmentsGoHere..."
|
||||
import os
|
||||
MD5DUMMY = "MD5_MD5_MD5_MD5_BoundariesOfTheSegmentsGoHere..." #48 chars
|
||||
FILENAMEDUMMY = "ThisIsTheDummyPlaceHolderForTheBinaryFilename..." #48 chars
|
||||
|
||||
MemorySegmentStart,MemorySegmentEnd,MemoryContent=[],[],[]
|
||||
|
||||
##################################################################
|
||||
@@ -14,12 +16,15 @@ def showSegments (fileContent,offset):
|
||||
global MemorySegmentStart, MemorySegmentEnd, MemoryContent
|
||||
header = struct.unpack("ii", fileContent[offset:offset+8])
|
||||
herestr =""
|
||||
herestr2 =""
|
||||
MemorySegmentStart.append(struct.pack("I",header[0]))
|
||||
MemorySegmentEnd.append(struct.pack("I",header[0]+header[1]))
|
||||
MemoryContent.append(fileContent[offset+8:offset+8+header[1]])
|
||||
if fileContent.find( MD5DUMMY, offset+8, offset+8+header[1]) >0 :
|
||||
herestr= " <-- CRC is here."
|
||||
print ("SEGMENT "+ str(len(MemorySegmentStart)-1)+ ": memory position: " + hex(header[0])+" to " + hex(header[0]+header[1]) + " length: " + hex(header[1])+herestr)
|
||||
if fileContent.find( FILENAMEDUMMY, offset+8, offset+8+header[1]) >0 :
|
||||
herestr2= " <-- filename is here."
|
||||
print ("SEGMENT "+ str(len(MemorySegmentStart)-1)+ ": memory position: " + hex(header[0])+" to " + hex(header[0]+header[1]) + " length: " + hex(header[1])+herestr+herestr2)
|
||||
#print ("first byte positoin in file: " + hex( offset+8))
|
||||
#print ("last byte postion in file: " + hex(offset+8+header[1]-1))
|
||||
return (8+offset+ header[1]); # return start of next segment
|
||||
@@ -82,6 +87,17 @@ while len(startArray) < 16 :
|
||||
if (len(endArray) + len (startArray)) != 32 :
|
||||
print("ERROR: please make sure you add / remove padding if you change the semgents.")
|
||||
|
||||
BinaryFileName = "";
|
||||
if fileContent.find( FILENAMEDUMMY) < 0:
|
||||
print("ERROR: FILENAMEDUMMY dummy not found in binary")
|
||||
else:
|
||||
BinaryFileName=os.path.basename(FileName) +"\0"
|
||||
if len(BinaryFileName) >48: # check that filename is <48 chars
|
||||
BinaryFileName=BinaryFileName[0:48] # truncate if necessary. 49th char in ESP is zero already
|
||||
else:
|
||||
BinaryFileName= BinaryFileName.ljust(48,'\0'); # pad with zeros.
|
||||
|
||||
|
||||
if fileContent.find( MD5DUMMY) < 0:
|
||||
print("ERROR: MD5 dummy not found in binary")
|
||||
else:
|
||||
@@ -91,6 +107,10 @@ else:
|
||||
md5hash = m.digest()
|
||||
print("MD5 hash: "+ m.hexdigest())
|
||||
print("\nwriting output file:\n" + FileName)
|
||||
|
||||
fileContent=fileContent.replace(MD5DUMMY,md5hash+startArray+endArray)
|
||||
fileContent=fileContent.replace(FILENAMEDUMMY,BinaryFileName)
|
||||
|
||||
with open(FileName, mode='wb') as file: # b is important -> binary
|
||||
file.write(fileContent.replace(MD5DUMMY,md5hash+startArray+endArray))
|
||||
file.write(fileContent)
|
||||
#k=input("press close to exit")
|
||||
|
||||
Vendored
+222
@@ -1,3 +1,225 @@
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180228 (since mega-20180227)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Wed Feb 28 04:00:09 CET 2018
|
||||
|
||||
Bartłomiej Zimoń (1):
|
||||
fix #964, add timeStringToSeconds and use it to compare time conditions (#965)
|
||||
|
||||
ESPEasy release bot (1):
|
||||
automaticly updated release notes for v2.0-20180227
|
||||
|
||||
Gijs Noorlander (2):
|
||||
[issue #966] Add %sysweekday% and %sysweekday_s% (#973)
|
||||
[issue #968] Use template parser for OLED commands (#974)
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180227 (since mega-20180226)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Tue Feb 27 04:00:11 CET 2018
|
||||
|
||||
Bartłomiej Zimoń (1):
|
||||
add format #O to templates to show OFF/ON instead 0/1 (#949)
|
||||
|
||||
ESPEasy release bot (1):
|
||||
automaticly updated release notes for v2.0-20180226
|
||||
|
||||
Edwin Eefting (2):
|
||||
[test suite] changes from mega to 2.0
|
||||
[test suite] changes from mega to 2.0
|
||||
|
||||
Gijs Noorlander (2):
|
||||
[BuildError] StreamingBuffer code cleanup + fix order member initialize (#961)
|
||||
[build conflict] Renamed ESP32 version of ping (#963)
|
||||
|
||||
mvdbro (1):
|
||||
Fix ESP32 compile errors
|
||||
|
||||
s0170071 (1):
|
||||
Add Factory Reset button to web interface (#954)
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180226 (since mega-20180224)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Feb 26 04:00:08 CET 2018
|
||||
|
||||
Bartłomiej Zimoń (2):
|
||||
provide System@Sleep event (#875) (#931)
|
||||
- extend conditions with AND/OR, now it is possible to simplify rules (#932)
|
||||
|
||||
ESPEasy release bot (1):
|
||||
automaticly updated release notes for v2.0-20180221
|
||||
|
||||
Edwin Eefting (1):
|
||||
device page doesnt need next/previous page buttons anymore
|
||||
|
||||
Gijs Noorlander (7):
|
||||
[MQTT] LWT "Connected" message doesn't have the retained flag (#925)
|
||||
[v2.0] Added Vcc-binaries to deploy process #933 (#935)
|
||||
[v2.0 merge] 2kwebserver_merge_to_mega #929 (#934)
|
||||
[v2.0] Use ESPeasySoftwareSerial (#936)
|
||||
[Mega] rename SoftwareSerial lib #930 (#937)
|
||||
[timeouts] Improve response when hosts are not reachable (#939)
|
||||
[2k Webserver] Proper initialisation of StreamingBuffer members. (#941)
|
||||
|
||||
Michael Schröder (1):
|
||||
Added Vcc-binaries to deploy process (#933)
|
||||
|
||||
Unknown (2):
|
||||
[ping lib] Added ping library
|
||||
[softwareSerial] Remove duplicate with only capilization as difference
|
||||
|
||||
s0170071 (33):
|
||||
Webserver min memory
|
||||
Adjusted to min 7k free
|
||||
Timeout 100ms
|
||||
tcp_cleanup for LWIP1 & 2
|
||||
Adapted timeout
|
||||
trigger travis re-compile
|
||||
try fix travis
|
||||
Does Travis like extern "C" ?
|
||||
Wait for memory be like before send chunked
|
||||
"Content-Type","text/html"
|
||||
Optimized memory usage
|
||||
2k Webserver
|
||||
fix travis
|
||||
fix travis
|
||||
Unused variable
|
||||
navMenuIndex
|
||||
fix download page
|
||||
Add Ram usage debug message
|
||||
fix Travis
|
||||
Travis...
|
||||
Cleanup and +1k Ram
|
||||
4->12 Devices per page
|
||||
Fix travis
|
||||
Update .gitignore
|
||||
Merge Webserver
|
||||
Fixed String redeclaration
|
||||
Merge into mega
|
||||
fix travis
|
||||
Removed double entry on info page
|
||||
Fix compile error when SD card is enabled
|
||||
Memtracker (#927)
|
||||
V20 Webserver@LowMemory
|
||||
Update WebServer.ino
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180224 (since mega-20180223)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Feb 24 04:00:06 CET 2018
|
||||
|
||||
mvdbro (2):
|
||||
Fix ESP32 compile errors
|
||||
ESP32 fixed P020 and P044
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180223 (since mega-20180221)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Fri Feb 23 04:00:07 CET 2018
|
||||
|
||||
GHolli (1):
|
||||
* BugFix: According to the data sheet, the offset for the detection range values (command byte 0x99) is 3..4 instead of 6..7 (#921)
|
||||
|
||||
s0170071 (1):
|
||||
Include name of binary file in crc2.py and sysinfo (#918)
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180221 (since mega-20180220)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Wed Feb 21 04:00:09 CET 2018
|
||||
|
||||
ESPEasy release bot (1):
|
||||
automaticly updated release notes for v2.0-20180220
|
||||
|
||||
Edwin Eefting (3):
|
||||
SENSOR_TYPE_TEMP_BARO changed, was probably implemented incorrectly before
|
||||
apply PUYA patch to all available frameworks
|
||||
[test suite] corrected PUYA test. added erase/build/flash for all chips. preparations for full automatic hardware regression testing
|
||||
|
||||
GHolli (1):
|
||||
Optimisation: Save RAM (freeing RAM by moving static data from RAM to program memory). (#910)
|
||||
|
||||
Gijs Noorlander (1):
|
||||
[issue #899 ] Backport of DHT22 implementation on Mega (#913)
|
||||
|
||||
Stanislav Mihic (1):
|
||||
Frontend (#906)
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180220 (since mega-20180219)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Tue Feb 20 04:00:08 CET 2018
|
||||
|
||||
Bartłomiej Zimoń (1):
|
||||
- pcfgpio,port,2 sets pin as input (#897)
|
||||
|
||||
ESPEasy release bot (1):
|
||||
automaticly updated release notes for v2.0-20180219
|
||||
|
||||
Edwin Eefting (1):
|
||||
function was moved to StringConverter.inp
|
||||
|
||||
Gijs Noorlander (3):
|
||||
[isue #877] Add VCC enable firmware file to release list (#894)
|
||||
[Cleanup] Move String manipulating functions to single file (#895)
|
||||
[issue #893] Fix globalSync not working when no controllers active (#896)
|
||||
|
||||
Unknown (3):
|
||||
[issue #847] Fix last letter of command is cut in OpenHAB MQTT
|
||||
[Event Log] Add event name to event processing time log
|
||||
[issue #899] DHT22 readings not received
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180219 (since mega-20180217)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Mon Feb 19 04:00:09 CET 2018
|
||||
|
||||
ESPEasy release bot (2):
|
||||
automaticly updated release notes for v2.0-20180217
|
||||
automaticly updated release notes for v2.0-20180218
|
||||
|
||||
Gijs Noorlander (4):
|
||||
[issue #847 ] OpenHAB MQTT rewrite topic parser (#886)
|
||||
[issue #885] MAC address as system variable (#887)
|
||||
[Build Mega] Fix for Build Mega 20180217 (#889)
|
||||
[issue #890] Split overlap for both Domoticz controllers to helper (#891)
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180217 (since mega-20180214)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: Sat Feb 17 04:00:07 CET 2018
|
||||
|
||||
Bartłomiej Zimoń (1):
|
||||
provide System@Sleep event (#875)
|
||||
|
||||
ESPEasy release bot (1):
|
||||
automaticly updated release notes for v2.0-20180214
|
||||
|
||||
Gijs Noorlander (4):
|
||||
[issue 866] Try to stop logging to serial when serial is not read. (#868)
|
||||
[issue #853] Favicon.ico (#874)
|
||||
[OLED Framed] ESP Easy logo (#876)
|
||||
[issue #869] Added 'LWT' to last will topic and improved CPU load (#883)
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20180214 (since mega-20180213)
|
||||
-------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Name: PingTest.ino
|
||||
Created: 05/08/2017 10:20:19
|
||||
Author: pbecc
|
||||
*/
|
||||
#undef PROVA_
|
||||
|
||||
|
||||
#include <WiFi.h>
|
||||
#include "esp32_ping.h"
|
||||
|
||||
// the setup function runs once when you press reset or power the board
|
||||
const char ssid[] = "TP-LINK_C20B"; // your network SSID (name)
|
||||
const char password[] = "paolo-48"; // your network password
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.print("Connecting t ");
|
||||
Serial.println(ssid);
|
||||
// WiFi.mode(WIFI_STA);
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
|
||||
|
||||
}
|
||||
Serial.println("Pinging address: 192.168.1.1");
|
||||
}
|
||||
|
||||
// the loop function runs over and over again until power down or reset
|
||||
void loop() {
|
||||
int ia[4] = { 192,168,1,1 };
|
||||
int i = 0;
|
||||
while (Serial.available()) {
|
||||
|
||||
char c = Serial.read();
|
||||
delay(100);
|
||||
int val = 0;
|
||||
while (c != '.' && c != 10 && c!=255) {
|
||||
if (c >= '0'&& c<='9') {
|
||||
val = val*10+(c-'0');
|
||||
}
|
||||
c = Serial.read();
|
||||
}
|
||||
|
||||
ia[i++] =val ;
|
||||
}
|
||||
IPAddress adr = IPAddress(ia[0], ia[1], ia[2], ia[3]);
|
||||
Serial.printf("Ping : %d . %d . %d . %d ->", ia[0], ia[1], ia[2], ia[3]);
|
||||
if (ping_start(adr, 4, 0, 0, 5))
|
||||
Serial.println("OK");
|
||||
else
|
||||
Serial.println("FAILED");
|
||||
delay(10000);
|
||||
|
||||
}
|
||||
int readnu(char s) {
|
||||
char c = Serial.read();
|
||||
Serial.print(c);
|
||||
int digit = 1,val=0;
|
||||
while (c != s && c != 10&&c>0) {
|
||||
if(c>'0') val += digit*(c - '0');
|
||||
digit *= 10;
|
||||
c=Serial.read();
|
||||
Serial.print(int(c));
|
||||
}
|
||||
Serial.println(digit);
|
||||
return digit;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# ESP32_ping
|
||||
Ping library for ESP32
|
||||
|
||||
WORK IN PROGRESS
|
||||
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* ESP32 Ping library
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* and its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that the above copyright notice appear in all
|
||||
* copies and that both that the copyright notice and this
|
||||
* permission notice and warranty disclaimer appear in supporting
|
||||
* documentation, and that the name of the author not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
*
|
||||
* The author disclaim all warranties with regard to this
|
||||
* software, including all implied warranties of merchantability
|
||||
* and fitness. In no event shall the author be liable for any
|
||||
* special, indirect or consequential damages or any damages
|
||||
* whatsoever resulting from loss of use, data or profits, whether
|
||||
* in an action of contract, negligence or other tortious action,
|
||||
* arising out of or in connection with the use or performance of
|
||||
* this software.
|
||||
*
|
||||
* --------------------------------------------------------------------------------
|
||||
* Ping Library is based on the following source code:
|
||||
*
|
||||
* Lua RTOS, ping utility
|
||||
*
|
||||
*
|
||||
* Author: Jaume Oliv� (jolive@iberoxarxa.com / jolive@whitecatboard.org)
|
||||
*
|
||||
* --------------------------------------------------------------------------------
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "esp32_ping.h"
|
||||
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/ip4.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/icmp.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
static uint16_t ping_seq_num;
|
||||
static uint8_t stopped = 0;
|
||||
|
||||
/*
|
||||
* Statistics
|
||||
*/
|
||||
static uint32_t transmitted = 0;
|
||||
static uint32_t received = 0;
|
||||
static float min_time = 0;
|
||||
static float max_time = 0;
|
||||
static float mean_time = 0;
|
||||
static float last_mean_time = 0;
|
||||
static float var_time = 0;
|
||||
|
||||
#define PING_ID 0xAFAF
|
||||
|
||||
#define PING_DEFAULT_COUNT 10
|
||||
#define PING_DEFAULT_INTERVAL 1
|
||||
#define PING_DEFAULT_SIZE 32
|
||||
#define PING_DEFAULT_TIMEOUT 1
|
||||
|
||||
/*
|
||||
* Helper functions
|
||||
*
|
||||
*/
|
||||
static void ping_prepare_echo(struct icmp_echo_hdr *iecho, uint16_t len) {
|
||||
size_t i;
|
||||
size_t data_len = len - sizeof(struct icmp_echo_hdr);
|
||||
|
||||
ICMPH_TYPE_SET(iecho, ICMP_ECHO);
|
||||
ICMPH_CODE_SET(iecho, 0);
|
||||
iecho->chksum = 0;
|
||||
iecho->id = PING_ID;
|
||||
iecho->seqno = htons(++ping_seq_num);
|
||||
|
||||
/* fill the additional data buffer with some data */
|
||||
for (i = 0; i < data_len; i++) {
|
||||
((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i;
|
||||
}
|
||||
|
||||
iecho->chksum = inet_chksum(iecho, len);
|
||||
}
|
||||
|
||||
static err_t ping_send(int s, ip4_addr_t *addr, int size) {
|
||||
struct icmp_echo_hdr *iecho;
|
||||
struct sockaddr_in to;
|
||||
size_t ping_size = sizeof(struct icmp_echo_hdr) + size;
|
||||
int err;
|
||||
|
||||
iecho = (struct icmp_echo_hdr *)mem_malloc((mem_size_t)ping_size);
|
||||
if (!iecho) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
ping_prepare_echo(iecho, (uint16_t)ping_size);
|
||||
|
||||
to.sin_len = sizeof(to);
|
||||
to.sin_family = AF_INET;
|
||||
inet_addr_from_ipaddr(&to.sin_addr, addr);
|
||||
|
||||
if ((err = sendto(s, iecho, ping_size, 0, (struct sockaddr*)&to, sizeof(to)))) {
|
||||
transmitted++;
|
||||
}
|
||||
|
||||
return (err ? ERR_OK : ERR_VAL);
|
||||
}
|
||||
|
||||
static void ping_recv(int s) {
|
||||
char buf[64];
|
||||
int fromlen, len;
|
||||
struct sockaddr_in from;
|
||||
struct ip_hdr *iphdr;
|
||||
struct icmp_echo_hdr *iecho = NULL;
|
||||
char ipa[16];
|
||||
struct timeval begin;
|
||||
struct timeval end;
|
||||
uint64_t micros_begin;
|
||||
uint64_t micros_end;
|
||||
float elapsed;
|
||||
|
||||
// Register begin time
|
||||
gettimeofday(&begin, NULL);
|
||||
|
||||
// Send
|
||||
while ((len = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, (socklen_t*)&fromlen)) > 0) {
|
||||
if (len >= (int)(sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr))) {
|
||||
// Register end time
|
||||
gettimeofday(&end, NULL);
|
||||
|
||||
/// Get from IP address
|
||||
ip4_addr_t fromaddr;
|
||||
inet_addr_to_ipaddr(&fromaddr, &from.sin_addr);
|
||||
|
||||
strcpy(ipa, inet_ntoa(fromaddr));
|
||||
|
||||
// Get echo
|
||||
iphdr = (struct ip_hdr *)buf;
|
||||
iecho = (struct icmp_echo_hdr *)(buf + (IPH_HL(iphdr) * 4));
|
||||
|
||||
// Print ....
|
||||
if ((iecho->id == PING_ID) && (iecho->seqno == htons(ping_seq_num))) {
|
||||
received++;
|
||||
|
||||
// Get elapsed time in milliseconds
|
||||
micros_begin = begin.tv_sec * 1000000;
|
||||
micros_begin += begin.tv_usec;
|
||||
|
||||
micros_end = end.tv_sec * 1000000;
|
||||
micros_end += end.tv_usec;
|
||||
|
||||
elapsed = (float)(micros_end - micros_begin) / (float)1000.0;
|
||||
|
||||
// Update statistics
|
||||
// Mean and variance are computed in an incremental way
|
||||
if (elapsed < min_time) {
|
||||
min_time = elapsed;
|
||||
}
|
||||
|
||||
if (elapsed > max_time) {
|
||||
max_time = elapsed;
|
||||
}
|
||||
|
||||
last_mean_time = mean_time;
|
||||
mean_time = (((received - 1) * mean_time) + elapsed) / received;
|
||||
|
||||
if (received > 1) {
|
||||
var_time = var_time + ((elapsed - last_mean_time) * (elapsed - mean_time));
|
||||
}
|
||||
|
||||
// Print ...
|
||||
log_d("%d bytes from %s: icmp_seq=%d time=%.3f ms\r\n", len, ipa,
|
||||
ntohs(iecho->seqno), elapsed
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
log_d("Request timeout for icmp_seq %d\r\n", ping_seq_num);
|
||||
}
|
||||
}
|
||||
/*
|
||||
static void stop_action(int i) {
|
||||
signal(i, SIG_DFL);
|
||||
|
||||
stopped = 1;
|
||||
}
|
||||
+/
|
||||
/*
|
||||
* Operation functions
|
||||
*
|
||||
*/
|
||||
void ping(const char *name, int count, int interval, int size, int timeout) {
|
||||
// Resolve name
|
||||
hostent * target = gethostbyname(name);
|
||||
IPAddress adr = *target->h_addr_list[0];
|
||||
if (target->h_length == 0) {
|
||||
// TODO: error not found target?????
|
||||
return;
|
||||
}
|
||||
ping_start(adr, count, interval, size, timeout);
|
||||
}
|
||||
bool ping_start(struct ping_option *ping_o) {
|
||||
|
||||
|
||||
return ping_start(ping_o->ip,ping_o->count,0,0,0);
|
||||
|
||||
}
|
||||
bool ping_start(IPAddress adr, int count=0, int interval=0, int size=0, int timeout=0) {
|
||||
// driver_error_t *error;
|
||||
struct sockaddr_in address;
|
||||
ip4_addr_t ping_target;
|
||||
int s;
|
||||
// Get default values if argument are not provided
|
||||
if (count == 0) {
|
||||
count = PING_DEFAULT_COUNT;
|
||||
}
|
||||
|
||||
if (interval == 0) {
|
||||
interval = PING_DEFAULT_INTERVAL;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
size = PING_DEFAULT_SIZE;
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
timeout = PING_DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
// Create socket
|
||||
if ((s = socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP)) < 0) {
|
||||
// TODO: error
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
address.sin_addr.s_addr = adr;
|
||||
ping_target.addr = address.sin_addr.s_addr;
|
||||
|
||||
// Setup socket
|
||||
struct timeval tout;
|
||||
|
||||
// Timeout
|
||||
tout.tv_sec = timeout;
|
||||
tout.tv_usec = 0;
|
||||
|
||||
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tout, sizeof(tout)) < 0) {
|
||||
closesocket(s);
|
||||
// TODO: error
|
||||
return false;
|
||||
}
|
||||
|
||||
stopped = 0;
|
||||
transmitted = 0;
|
||||
received = 0;
|
||||
min_time = 1.E+9;// FLT_MAX;
|
||||
max_time = 0.0;
|
||||
mean_time = 0.0;
|
||||
var_time = 0.0;
|
||||
|
||||
// Register signal for stop ping
|
||||
//signal(SIGINT, stop_action);
|
||||
|
||||
// Begin ping ...
|
||||
char ipa[16];
|
||||
|
||||
strcpy(ipa, inet_ntoa(ping_target));
|
||||
log_i("PING %s: %d data bytes\r\n", ipa, size);
|
||||
|
||||
ping_seq_num = 0;
|
||||
|
||||
while ((ping_seq_num < count) && (!stopped)) {
|
||||
if (ping_send(s, &ping_target, size) == ERR_OK) {
|
||||
ping_recv(s);
|
||||
}
|
||||
delay( interval*1000L);
|
||||
}
|
||||
|
||||
closesocket(s);
|
||||
|
||||
log_i("%d packets transmitted, %d packets received, %.1f%% packet loss\r\n",
|
||||
transmitted,
|
||||
received,
|
||||
((((float)transmitted - (float)received) / (float)transmitted) * 100.0)
|
||||
);
|
||||
|
||||
if (received) {
|
||||
ping_resp pingresp;
|
||||
log_i("round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\r\n", min_time, mean_time, max_time, sqrt(var_time / received));
|
||||
pingresp.total_count = 10;
|
||||
pingresp.timeout_count = 10;
|
||||
pingresp.total_bytes = 1;
|
||||
pingresp.total_time = mean_time;
|
||||
pingresp.ping_err = 0;
|
||||
return true;
|
||||
// ping_o->sent_function(ping_o, (uint8*)&pingresp);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ping_regist_recv(struct ping_option *ping_opt, ping_recv_function ping_recv)
|
||||
{
|
||||
if (ping_opt == NULL)
|
||||
return false;
|
||||
|
||||
ping_opt->recv_function = ping_recv;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ping_regist_sent(struct ping_option *ping_opt, ping_sent_function ping_sent)
|
||||
{
|
||||
if (ping_opt == NULL)
|
||||
return false;
|
||||
|
||||
ping_opt->sent_function = ping_sent;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
#ifndef _PING_
|
||||
#define _PING_
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ESP32
|
||||
|
||||
typedef void(*ping_recv_function)(void* arg, void *pdata);
|
||||
typedef void(*ping_sent_function)(void* arg, void *pdata);
|
||||
|
||||
struct ping_option {
|
||||
uint32_t count;
|
||||
uint32_t ip;
|
||||
uint32_t coarse_time;
|
||||
ping_recv_function recv_function;
|
||||
ping_sent_function sent_function;
|
||||
void* reverse;
|
||||
};
|
||||
|
||||
struct ping_resp {
|
||||
uint32_t total_count;
|
||||
uint32_t resp_time;
|
||||
uint32_t seqno;
|
||||
uint32_t timeout_count;
|
||||
uint32_t bytes;
|
||||
uint32_t total_bytes;
|
||||
uint32_t total_time;
|
||||
int8_t ping_err;
|
||||
};
|
||||
|
||||
bool ping_start(struct ping_option *ping_opt);
|
||||
void ping(const char *name, int count, int interval, int size, int timeout);
|
||||
bool ping_start(IPAddress adr, int count, int interval, int size, int timeout);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,456 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
@@ -0,0 +1,40 @@
|
||||
# ESP8266Ping
|
||||
Let the ESP8266 ping a remote machine.
|
||||
|
||||
With this library an ESP8266 can ping a remote machine and know if it's reachable.
|
||||
It provide some basic measurements on ping messages (avg response time).
|
||||
|
||||
## Usage
|
||||
|
||||
First, include the library in your sketch along with WiFi library:
|
||||
|
||||
```Arduino
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266Ping.h>
|
||||
```
|
||||
|
||||
Next, simply call the `Ping.ping()` function
|
||||
|
||||
```Arduino
|
||||
IPAddress ip (192, 168, 0, 1); // The remote ip to ping
|
||||
bool ret = Ping.ping(ip);
|
||||
```
|
||||
|
||||
`ret` will be true if the remote responded to pings, false if not reachable.
|
||||
The library supports hostname too, just pass a string instead of the ip address:
|
||||
|
||||
```Arduino
|
||||
bool ret = Ping.ping("www.google.com");
|
||||
```
|
||||
|
||||
Additionally, the function accept a second integer parameter `count` that specify how many pings has to be sent:
|
||||
|
||||
```Arduino
|
||||
bool ret = Ping.ping(ip_or_host, 10);
|
||||
```
|
||||
|
||||
After `Ping.ping()` has been called, the average response time (in milliseconds) can be retrieved with
|
||||
|
||||
```Arduino
|
||||
int avg_time_ms = Ping.averageTime();
|
||||
```
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This example show how to ping a remote machine using it's hostname
|
||||
*/
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266Ping.h>
|
||||
|
||||
const char* ssid = "ssid";
|
||||
const char* password = "passphrase";
|
||||
|
||||
const char* remote_host = "www.google.com";
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
|
||||
// We start by connecting to a WiFi network
|
||||
|
||||
Serial.println();
|
||||
Serial.println("Connecting to WiFi");
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(100);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.print("WiFi connected with ip ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
Serial.print("Pinging host ");
|
||||
Serial.println(remote_host);
|
||||
|
||||
if(Ping.ping(remote_host)) {
|
||||
Serial.println("Success!!");
|
||||
} else {
|
||||
Serial.println("Error :(");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() { }
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* With this library an ESP8266 can ping a remote machine and know if it's reachable.
|
||||
* It provides some basic measurements on ping messages (avg response time).
|
||||
*/
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266Ping.h>
|
||||
|
||||
const char* ssid = "ssid";
|
||||
const char* password = "passphrase";
|
||||
|
||||
const IPAddress remote_ip(192, 168, 0, 1);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(10);
|
||||
|
||||
// We start by connecting to a WiFi network
|
||||
|
||||
Serial.println();
|
||||
Serial.println("Connecting to WiFi");
|
||||
|
||||
WiFi.begin(ssid, password);
|
||||
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
delay(100);
|
||||
Serial.print(".");
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.print("WiFi connected with ip ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
Serial.print("Pinging ip ");
|
||||
Serial.println(remote_ip);
|
||||
|
||||
if(Ping.ping(remote_ip)) {
|
||||
Serial.println("Success!!");
|
||||
} else {
|
||||
Serial.println("Error :(");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() { }
|
||||
@@ -0,0 +1,26 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For ESP8266Ping
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Library (KEYWORD3)
|
||||
#######################################
|
||||
|
||||
ESP8266Ping KEYWORD3
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
Ping KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
ping KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
name=ESPEasy_ESP8266Ping
|
||||
version=1.0
|
||||
author=Daniele Colanardi
|
||||
maintainer=Daniele Colanardi <dancol90@gmail.com>
|
||||
sentence=Let the ESP8266 ping a remote machine.
|
||||
paragraph=With this library an ESP8266 can ping a remote machine and know if it's reachable. It provide some basic measurements on ping messages (avg response time). Lib name renamed to avoid conflicts in Arduino IDE.
|
||||
category=Communication
|
||||
url=
|
||||
architectures=esp8266
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
ESP8266Ping - Ping library for ESP8266
|
||||
Copyright (c) 2015 Daniele Colanardi. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef ESP8266Ping_H
|
||||
#define ESP8266Ping_H
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
|
||||
extern "C" {
|
||||
#include <ping.h>
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DEBUG_PING
|
||||
#define DEBUG_PING(...) Serial.printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PING(...)
|
||||
#endif
|
||||
|
||||
class PingClass {
|
||||
public:
|
||||
PingClass();
|
||||
|
||||
bool ping(IPAddress dest, byte count = 5);
|
||||
bool ping(const char* host, byte count = 5);
|
||||
|
||||
int averageTime();
|
||||
|
||||
protected:
|
||||
static void _ping_sent_cb(void *opt, void *pdata);
|
||||
static void _ping_recv_cb(void *opt, void *pdata);
|
||||
|
||||
IPAddress _dest;
|
||||
ping_option _options;
|
||||
|
||||
static byte _expected_count, _errors, _success;
|
||||
static int _avg_time;
|
||||
};
|
||||
|
||||
#include "ESP8266Ping.impl.h"
|
||||
PingClass Ping;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
ESP8266Ping - Ping library for ESP8266
|
||||
Copyright (c) 2015 Daniele Colanardi. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
extern "C" void esp_schedule();
|
||||
extern "C" void esp_yield();
|
||||
|
||||
PingClass::PingClass() {}
|
||||
|
||||
bool PingClass::ping(IPAddress dest, byte count) {
|
||||
_expected_count = count;
|
||||
_errors = 0;
|
||||
_success = 0;
|
||||
|
||||
_avg_time = 0;
|
||||
|
||||
memset(&_options, 0, sizeof(struct ping_option));
|
||||
|
||||
// Repeat count (how many time send a ping message to destination)
|
||||
_options.count = count;
|
||||
// Time interval between two ping (seconds??)
|
||||
_options.coarse_time = 1;
|
||||
// Destination machine
|
||||
_options.ip = dest;
|
||||
|
||||
// Callbacks
|
||||
_options.recv_function = reinterpret_cast<ping_recv_function>(&PingClass::_ping_recv_cb);
|
||||
_options.sent_function = NULL; //reinterpret_cast<ping_sent_function>(&_ping_sent_cb);
|
||||
|
||||
// Let's go!
|
||||
if(ping_start(&_options)) {
|
||||
// Suspend till the process end
|
||||
esp_yield();
|
||||
}
|
||||
|
||||
return (_success > 0);
|
||||
}
|
||||
|
||||
bool PingClass::ping(const char* host, byte count) {
|
||||
IPAddress remote_addr;
|
||||
|
||||
if (WiFi.hostByName(host, remote_addr))
|
||||
return ping(remote_addr, count);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int PingClass::averageTime() {
|
||||
return _avg_time;
|
||||
}
|
||||
|
||||
void PingClass::_ping_recv_cb(void *opt, void *resp) {
|
||||
// Cast the parameters to get some usable info
|
||||
ping_resp* ping_resp = reinterpret_cast<struct ping_resp*>(resp);
|
||||
//ping_option* ping_opt = reinterpret_cast<struct ping_option*>(opt);
|
||||
|
||||
// Error or success?
|
||||
if (ping_resp->ping_err == -1)
|
||||
_errors++;
|
||||
else {
|
||||
_success++;
|
||||
_avg_time += ping_resp->resp_time;
|
||||
}
|
||||
|
||||
// Some debug info
|
||||
DEBUG_PING(
|
||||
"DEBUG: ping reply\n"
|
||||
"\ttotal_count = %d \n"
|
||||
"\tresp_time = %d \n"
|
||||
"\tseqno = %d \n"
|
||||
"\ttimeout_count = %d \n"
|
||||
"\tbytes = %d \n"
|
||||
"\ttotal_bytes = %d \n"
|
||||
"\ttotal_time = %d \n"
|
||||
"\tping_err = %d \n",
|
||||
ping_resp->total_count, ping_resp->resp_time, ping_resp->seqno,
|
||||
ping_resp->timeout_count, ping_resp->bytes, ping_resp->total_bytes,
|
||||
ping_resp->total_time, ping_resp->ping_err
|
||||
);
|
||||
|
||||
// Is it time to end?
|
||||
// Don't using seqno because it does not increase on error
|
||||
if (_success + _errors == _expected_count) {
|
||||
_avg_time = _avg_time / _expected_count;
|
||||
|
||||
DEBUG_PING("Avg resp time %d ms\n", _avg_time);
|
||||
|
||||
// Done, return to main functiom
|
||||
esp_schedule();
|
||||
}
|
||||
}
|
||||
|
||||
byte PingClass::_expected_count = 0;
|
||||
byte PingClass::_errors = 0;
|
||||
byte PingClass::_success = 0;
|
||||
int PingClass::_avg_time = 0;
|
||||
@@ -1,31 +1,21 @@
|
||||
#define WiFi_Logo_width 60
|
||||
#define WiFi_Logo_height 36
|
||||
const char WiFi_Logo_bits[] PROGMEM = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
|
||||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF,
|
||||
0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00,
|
||||
0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C,
|
||||
0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00,
|
||||
0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C,
|
||||
0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00,
|
||||
0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C,
|
||||
0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00,
|
||||
0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C,
|
||||
0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00,
|
||||
0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F,
|
||||
0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00,
|
||||
0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF,
|
||||
0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
|
||||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
#define espeasy_logo_width 36
|
||||
#define espeasy_logo_height 36
|
||||
const char espeasy_logo_bits[] PROGMEM= {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x60, 0x40, 0xc0, 0x00, 0x00, 0x70, 0xf0, 0xe0,
|
||||
0x01, 0x00, 0x78, 0xf8, 0xf0, 0x01, 0x00, 0x3c, 0x7c, 0xf8, 0x00, 0x00,
|
||||
0x1e, 0x3e, 0x7c, 0x00, 0x00, 0x0f, 0x1f, 0x3e, 0x00, 0x80, 0x87, 0x0f,
|
||||
0x1f, 0x00, 0xc0, 0xc3, 0x87, 0x0f, 0x00, 0xe0, 0xe1, 0xc3, 0x07, 0x00,
|
||||
0xf0, 0xf0, 0xe1, 0xc3, 0x00, 0x78, 0xf8, 0xf0, 0xe1, 0x01, 0x30, 0x7c,
|
||||
0xf8, 0xf0, 0x00, 0x00, 0x3e, 0x7c, 0x78, 0x00, 0x00, 0x1f, 0x3e, 0x3c,
|
||||
0x00, 0x80, 0x0f, 0x1f, 0x1e, 0x00, 0xc0, 0x87, 0x0f, 0x0f, 0x00, 0xe0,
|
||||
0xc3, 0x87, 0x07, 0x00, 0xf0, 0xe1, 0xc3, 0x03, 0x00, 0xf0, 0xf0, 0xe1,
|
||||
0x01, 0x00, 0x78, 0xf8, 0xf0, 0x00, 0x00, 0x30, 0x7c, 0x78, 0x00, 0x00,
|
||||
0x00, 0x3e, 0x3c, 0x00, 0x00, 0x00, 0x1f, 0x1e, 0x00, 0x00, 0x80, 0x0f,
|
||||
0x0f, 0x78, 0x00, 0xc0, 0x87, 0x07, 0xfc, 0x00, 0xe0, 0xc3, 0x03, 0xfc,
|
||||
0x01, 0xf0, 0xe1, 0x01, 0xfc, 0x01, 0xf8, 0xf0, 0x00, 0xfc, 0x00, 0x78,
|
||||
0x70, 0x00, 0x78, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
const char activeSymbole[] PROGMEM = {
|
||||
B00000000,
|
||||
@@ -48,4 +38,3 @@ const char inactiveSymbole[] PROGMEM = {
|
||||
B00000000,
|
||||
B00000000
|
||||
};
|
||||
|
||||
|
||||
@@ -420,7 +420,7 @@ boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsig
|
||||
|
||||
lastOutActivity = millis();
|
||||
|
||||
return rc == tlen + 4 + plength;
|
||||
return rc == tlen + 3 + llen + plength;
|
||||
}
|
||||
|
||||
boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
#define MQTTQOS1 (1 << 1)
|
||||
#define MQTTQOS2 (2 << 1)
|
||||
|
||||
#ifdef ESP8266
|
||||
#if defined(ESP8266) || defined(ESP32)
|
||||
#include <functional>
|
||||
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
|
||||
#else
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
DIR=~/.platformio/packages/framework-arduinoespressif8266
|
||||
PATCH=puya.patch
|
||||
|
||||
#not applied yet? (in upstream or otherwise)
|
||||
if ! grep FLASH_QUIRK_WRITE_0_TO_1 $DIR/cores/esp8266/Esp.cpp >/dev/null; then
|
||||
patch -p1 -d $DIR < $PATCH
|
||||
fi
|
||||
for DIR in ~/.platformio/packages/framework-arduinoespressif8266*/; do
|
||||
|
||||
#not applied yet? (in upstream or otherwise)
|
||||
if ! grep FLASH_QUIRK_WRITE_0_TO_1 $DIR/cores/esp8266/Esp.cpp >/dev/null; then
|
||||
echo "Patching $DIR"
|
||||
patch -p1 -d $DIR < $PATCH
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -22,11 +22,13 @@
|
||||
|
||||
[common]
|
||||
build_flags = -D BUILD_GIT='"${env.TRAVIS_TAG}"'
|
||||
lib_deps = ""
|
||||
|
||||
|
||||
#normal version with stable plugins, 1024k version
|
||||
[env:normal_ESP8266_1024]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp12e
|
||||
upload_speed=460800
|
||||
@@ -35,6 +37,7 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld
|
||||
|
||||
[env:normal_ESP8266_1024_DOUT]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
board_flash_mode = dout
|
||||
framework = arduino
|
||||
board = esp12e
|
||||
@@ -45,6 +48,7 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld
|
||||
#normal version with stable plugins, 4096k version
|
||||
[env:normal_ESP8266_4096]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp12e
|
||||
upload_speed=460800
|
||||
@@ -54,6 +58,7 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.4m1m.ld
|
||||
#normal version with stable plugins, 4096k version for esp8285
|
||||
[env:normal_ESP8285_1024]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp8285
|
||||
upload_speed=460800
|
||||
@@ -64,6 +69,7 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld -DESP8285
|
||||
#version with additional plugins (and dependend code) that are in test-stadium 1024k
|
||||
[env:test_ESP8266_1024]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp12e
|
||||
upload_speed=460800
|
||||
@@ -72,14 +78,24 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld -D PLUGIN_BUILD
|
||||
#version with additional plugins (and dependend code) that are in test-stadium 4096k
|
||||
[env:test_ESP8266_4096]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp12e
|
||||
upload_speed=460800
|
||||
build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.4m1m.ld -D PLUGIN_BUILD_TESTING
|
||||
|
||||
[env:test_ESP8266_4096_VCC]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp12e
|
||||
upload_speed=460800
|
||||
build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.4m1m.ld -D PLUGIN_BUILD_TESTING -D FEATURE_ADC_VCC=true
|
||||
|
||||
#version with additional plugins (and dependend code) that are in test-stadium 4096k for esp8285
|
||||
[env:test_ESP8285_1024]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp8285
|
||||
upload_speed=460800
|
||||
@@ -89,6 +105,7 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld -D PLUGIN_BUILD
|
||||
#version with additional plugins (and dependend code) that is in development (probably broken or incomplete) 1024k
|
||||
[env:dev_ESP8266_1024]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp12e
|
||||
upload_speed=460800
|
||||
@@ -97,6 +114,7 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld -D PLUGIN_BUILD
|
||||
#version with additional plugins (and dependend code) that is in development (probably broken or incomplete) 4096k
|
||||
[env:dev_ESP8266_4096]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp12e
|
||||
upload_speed=460800
|
||||
@@ -115,6 +133,7 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.4m1m.ld -D PLUGIN_BUILD_
|
||||
#version with additional plugins (and dependend code) that is in development (probably broken or incomplete) 4096k for esp8285
|
||||
[env:dev_ESP8285_1024]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp8285
|
||||
upload_speed=460800
|
||||
@@ -125,7 +144,17 @@ build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld -D PLUGIN_BUILD
|
||||
#special patched version for PUYA flash chips, see https://github.com/letscontrolit/ESPEasy/issues/650
|
||||
[env:dev_ESP8266PUYA_1024]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp12e
|
||||
upload_speed=460800
|
||||
build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld -D PLUGIN_BUILD_TESTING -D PLUGIN_BUILD_DEV -D FLASH_QUIRK_WRITE_0_TO_1
|
||||
|
||||
#special patched version for PUYA flash chips, see https://github.com/letscontrolit/ESPEasy/issues/650
|
||||
[env:dev_ESP8266PUYA_1024_VCC]
|
||||
platform = espressif8266@1.5.0
|
||||
lib_deps = ${common.lib_deps}
|
||||
framework = arduino
|
||||
board = esp12e
|
||||
upload_speed=460800
|
||||
build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld -D PLUGIN_BUILD_TESTING -D PLUGIN_BUILD_DEV -D FLASH_QUIRK_WRITE_0_TO_1 -D FEATURE_ADC_VCC=true
|
||||
|
||||
@@ -42,6 +42,7 @@ bool safeReadStringUntil(Stream &input, String &str, char terminator, unsigned i
|
||||
#define INPUT_COMMAND_SIZE 80
|
||||
void ExecuteCommand(byte source, const char *Line)
|
||||
{
|
||||
checkRAM(F("ExecuteCommand"));
|
||||
String status = "";
|
||||
boolean success = false;
|
||||
char TmpStr1[80];
|
||||
@@ -584,6 +585,13 @@ void ExecuteCommand(byte source, const char *Line)
|
||||
{
|
||||
success = true;
|
||||
ResetFactory();
|
||||
#if defined(ESP8266)
|
||||
ESP.reset();
|
||||
#endif
|
||||
#if defined(ESP32)
|
||||
ESP.restart();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
if (strcasecmp_P(Command, PSTR("Save")) == 0)
|
||||
|
||||
+47
-54
@@ -3,7 +3,8 @@
|
||||
//********************************************************************************
|
||||
void sendData(struct EventStruct *event)
|
||||
{
|
||||
LoadTaskSettings(event->TaskIndex);
|
||||
checkRAM(F("sendData"));
|
||||
LoadTaskSettings(event->TaskIndex);
|
||||
if (Settings.UseRules)
|
||||
createRuleEvents(event->TaskIndex);
|
||||
|
||||
@@ -62,11 +63,7 @@ void sendData(struct EventStruct *event)
|
||||
boolean validUserVar(struct EventStruct *event) {
|
||||
for (int i = 0; i < VARS_PER_TASK; ++i) {
|
||||
const float f(UserVar[event->BaseVarIndex + i]);
|
||||
if (f == NAN) return false; //("NaN");
|
||||
if (f == INFINITY) return false; //("INFINITY");
|
||||
if (-f == INFINITY)return false; //("-INFINITY");
|
||||
if (isnan(f)) return false; //("isnan");
|
||||
if (isinf(f)) return false; //("isinf");
|
||||
if (!isValidFloat(f)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -120,13 +117,14 @@ void callback(char* c_topic, byte* b_payload, unsigned int length) {
|
||||
/*********************************************************************************************\
|
||||
* Connect to MQTT message broker
|
||||
\*********************************************************************************************/
|
||||
void MQTTConnect(int controller_idx)
|
||||
bool MQTTConnect(int controller_idx)
|
||||
{
|
||||
if (!WiFiConnected(100)) return;
|
||||
if (MQTTclient.connected())
|
||||
MQTTclient.disconnect();
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(controller_idx, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
if (!ControllerSettings.checkHostReachable(true))
|
||||
return false;
|
||||
if (MQTTclient.connected())
|
||||
MQTTclient.disconnect();
|
||||
if (ControllerSettings.UseDNS) {
|
||||
MQTTclient.setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port);
|
||||
} else {
|
||||
@@ -137,62 +135,56 @@ void MQTTConnect(int controller_idx)
|
||||
// MQTT needs a unique clientname to subscribe to broker
|
||||
String clientid = F("ESPClient_");
|
||||
clientid += WiFi.macAddress();
|
||||
String subscribeTo = "";
|
||||
|
||||
String LWTTopic = ControllerSettings.Subscribe;
|
||||
LWTTopic.replace(F("/#"), F("/status"));
|
||||
LWTTopic.replace(F("%sysname%"), Settings.Name);
|
||||
LWTTopic += F("/LWT"); // Extend the topic for status updates of connected/disconnected status.
|
||||
|
||||
for (byte x = 1; x < 3; x++)
|
||||
{
|
||||
String log = "";
|
||||
boolean MQTTresult = false;
|
||||
boolean MQTTresult = false;
|
||||
uint8_t willQos = 0;
|
||||
boolean willRetain = true;
|
||||
|
||||
if ((SecuritySettings.ControllerUser[controller_idx] != 0) && (SecuritySettings.ControllerPassword[controller_idx] != 0))
|
||||
MQTTresult = MQTTclient.connect(clientid.c_str(), SecuritySettings.ControllerUser[controller_idx], SecuritySettings.ControllerPassword[controller_idx], LWTTopic.c_str(), 0, 0, "Connection Lost");
|
||||
else
|
||||
MQTTresult = MQTTclient.connect(clientid.c_str(), LWTTopic.c_str(), 0, 0, "Connection Lost");
|
||||
yield();
|
||||
|
||||
if (MQTTresult)
|
||||
{
|
||||
MQTTclient_should_reconnect = false;
|
||||
log = F("MQTT : Connected to broker with client ID: ");
|
||||
log += clientid;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
subscribeTo = ControllerSettings.Subscribe;
|
||||
subscribeTo.replace(F("%sysname%"), Settings.Name);
|
||||
MQTTclient.subscribe(subscribeTo.c_str());
|
||||
log = F("Subscribed to: ");
|
||||
log += subscribeTo;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
|
||||
MQTTclient.publish(LWTTopic.c_str(), "Connected", 1);
|
||||
|
||||
statusLED(true);
|
||||
return; // end loop if succesfull
|
||||
}
|
||||
else
|
||||
{
|
||||
log = F("MQTT : ClientID ");
|
||||
log += clientid;
|
||||
log +=" failed to connected to broker";
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
}
|
||||
|
||||
delay(500);
|
||||
if ((SecuritySettings.ControllerUser[controller_idx] != 0) && (SecuritySettings.ControllerPassword[controller_idx] != 0)) {
|
||||
MQTTresult = MQTTclient.connect(clientid.c_str(), SecuritySettings.ControllerUser[controller_idx], SecuritySettings.ControllerPassword[controller_idx],
|
||||
LWTTopic.c_str(), willQos, willRetain, "Connection Lost");
|
||||
} else {
|
||||
MQTTresult = MQTTclient.connect(clientid.c_str(), LWTTopic.c_str(), willQos, willRetain, "Connection Lost");
|
||||
}
|
||||
yield();
|
||||
|
||||
if (!MQTTresult) {
|
||||
addLog(LOG_LEVEL_ERROR, F("MQTT : Failed to connect to broker"));
|
||||
return false;
|
||||
}
|
||||
MQTTclient_should_reconnect = false;
|
||||
String log = F("MQTT : Connected to broker with client ID: ");
|
||||
log += clientid;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
String subscribeTo = ControllerSettings.Subscribe;
|
||||
subscribeTo.replace(F("%sysname%"), Settings.Name);
|
||||
MQTTclient.subscribe(subscribeTo.c_str());
|
||||
log = F("Subscribed to: ");
|
||||
log += subscribeTo;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
|
||||
if (MQTTclient.publish(LWTTopic.c_str(), "Connected", 1)) {
|
||||
statusLED(true);
|
||||
return true; // end loop if succesfull
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Check connection MQTT message broker
|
||||
\*********************************************************************************************/
|
||||
void MQTTCheck(int controller_idx)
|
||||
bool MQTTCheck(int controller_idx)
|
||||
{
|
||||
byte ProtocolIndex = getProtocolIndex(Settings.Protocol[controller_idx]);
|
||||
if (Protocol[ProtocolIndex].usesMQTT)
|
||||
{
|
||||
if (MQTTclient_should_reconnect || !WiFiConnected(100) || !MQTTclient.connected())
|
||||
if (MQTTclient_should_reconnect || !WiFiConnected(10) || !MQTTclient.connected())
|
||||
{
|
||||
if (MQTTclient_should_reconnect) {
|
||||
addLog(LOG_LEVEL_ERROR, F("MQTT : Intentional reconnect"));
|
||||
@@ -200,11 +192,13 @@ void MQTTCheck(int controller_idx)
|
||||
addLog(LOG_LEVEL_ERROR, F("MQTT : Connection lost"));
|
||||
connectionFailures += 2;
|
||||
}
|
||||
MQTTConnect(controller_idx);
|
||||
}
|
||||
else if (connectionFailures)
|
||||
return MQTTConnect(controller_idx);
|
||||
} else if (connectionFailures) {
|
||||
connectionFailures--;
|
||||
}
|
||||
}
|
||||
// When no MQTT protocol is enabled, all is fine.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -234,7 +228,6 @@ boolean MQTTpublish(int controller_idx, const char* topic, const char* payload,
|
||||
if (MQTTclient.publish(topic, payload, retained))
|
||||
return true;
|
||||
addLog(LOG_LEVEL_DEBUG, F("MQTT : publish failed"));
|
||||
MQTTConnect(controller_idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+129
-25
@@ -132,6 +132,10 @@
|
||||
#define DEFAULT_TIME_ZONE 0 // Time Offset (in minutes)
|
||||
#define DEFAULT_USE_DST false // (true|false) Use Daily Time Saving
|
||||
|
||||
#define LOG_TO_SERIAL 1
|
||||
#define LOG_TO_SYSLOG 2
|
||||
#define LOG_TO_WEBLOG 3
|
||||
#define LOG_TO_SDCARD 4
|
||||
#define DEFAULT_SYSLOG_IP "" // Syslog IP Address
|
||||
#define DEFAULT_SYSLOG_LEVEL 0 // Syslog Log Level
|
||||
#define DEFAULT_SERIAL_LOG_LEVEL LOG_LEVEL_INFO // Serial Log Level
|
||||
@@ -159,7 +163,9 @@
|
||||
// Enable FEATURE_ADC_VCC to measure supply voltage using the analog pin
|
||||
// Please note that the TOUT pin has to be disconnected in this mode
|
||||
// Use the "System Info" device to read the VCC value
|
||||
#define FEATURE_ADC_VCC false
|
||||
#ifndef FEATURE_ADC_VCC
|
||||
#define FEATURE_ADC_VCC false
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(ESP8266)
|
||||
@@ -387,6 +393,7 @@
|
||||
#include <lwip/tcp_impl.h>
|
||||
#endif
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266Ping.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
ESP8266WebServer WebServer(80);
|
||||
#include <DNSServer.h>
|
||||
@@ -429,6 +436,7 @@
|
||||
#define FILE_NOTIFICATION "/notification.dat"
|
||||
#define FILE_RULES "/rules1.dat"
|
||||
#include <WiFi.h>
|
||||
#include "esp32_ping.h"
|
||||
#include <ESP32WebServer.h>
|
||||
#include "SPIFFS.h"
|
||||
ESP32WebServer WebServer(80);
|
||||
@@ -479,6 +487,7 @@ WiFiUDP portUDP;
|
||||
|
||||
struct CRCStruct{
|
||||
char compileTimeMD5[16+32+1]= "MD5_MD5_MD5_MD5_BoundariesOfTheSegmentsGoHere...";
|
||||
char binaryFilename[16+32+1]= "ThisIsTheDummyPlaceHolderForTheBinaryFilename...";
|
||||
char compileTime[16]= __TIME__;
|
||||
char compileDate[16]= __DATE__;
|
||||
uint8_t runTimeMD5[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||
@@ -486,15 +495,12 @@ struct CRCStruct{
|
||||
uint32_t numberOfCRCBytes=0;
|
||||
}CRCValues;
|
||||
|
||||
// Forward declarations.
|
||||
bool WiFiConnected(uint32_t timeout_ms);
|
||||
|
||||
extern "C" {
|
||||
#include "spi_flash.h"
|
||||
}
|
||||
extern "C" uint32_t _SPIFFS_start;
|
||||
extern "C" uint32_t _SPIFFS_end;
|
||||
extern "C" uint32_t _SPIFFS_page;
|
||||
extern "C" uint32_t _SPIFFS_block;
|
||||
bool hostReachable(const IPAddress& ip);
|
||||
bool hostReachable(const String& hostname);
|
||||
void formatMAC(const uint8_t* mac, char (&strMAC)[20]);
|
||||
void formatIP(const IPAddress& ip, char (&strIP)[20]);
|
||||
|
||||
struct SecurityStruct
|
||||
{
|
||||
@@ -677,24 +683,55 @@ struct ControllerSettingsStruct
|
||||
return getIP().toString();
|
||||
}
|
||||
|
||||
boolean connectToHost(WiFiClient &client) {
|
||||
if (!WiFiConnected(100)) {
|
||||
void setHostname(const String& controllerhostname) {
|
||||
strncpy(HostName, controllerhostname.c_str(), sizeof(HostName));
|
||||
updateIPcache();
|
||||
}
|
||||
|
||||
boolean checkHostReachable(bool quick) {
|
||||
if (!WiFiConnected(10)) {
|
||||
return false; // Not connected, so no use in wasting time to connect to a host.
|
||||
}
|
||||
if (quick) return true;
|
||||
if (UseDNS) {
|
||||
return client.connect(HostName, Port);
|
||||
if (!updateIPcache()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return client.connect(getIP(), Port);
|
||||
return hostReachable(getIP());
|
||||
}
|
||||
|
||||
boolean connectToHost(WiFiClient &client) {
|
||||
if (!checkHostReachable(true)) {
|
||||
return false; // Host not reachable
|
||||
}
|
||||
byte retry = 2;
|
||||
bool connected = false;
|
||||
while (retry > 0 && !connected) {
|
||||
--retry;
|
||||
connected = client.connect(getIP(), Port);
|
||||
if (connected) return true;
|
||||
if (!checkHostReachable(false))
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int beginPacket(WiFiUDP &client) {
|
||||
if (!WiFiConnected(100)) {
|
||||
return 0; // Not connected, so no use in wasting time to connect to a host.
|
||||
if (!checkHostReachable(true)) {
|
||||
return 0; // Host not reachable
|
||||
}
|
||||
if (UseDNS) {
|
||||
return client.beginPacket(HostName, Port);
|
||||
byte retry = 2;
|
||||
int connected = 0;
|
||||
while (retry > 0 && !connected) {
|
||||
--retry;
|
||||
connected = client.beginPacket(getIP(), Port);
|
||||
if (connected != 0) return connected;
|
||||
if (!checkHostReachable(false))
|
||||
return false;
|
||||
delay(10);
|
||||
}
|
||||
return client.beginPacket(getIP(), Port);
|
||||
return false;
|
||||
}
|
||||
|
||||
String getHostPortString() const {
|
||||
@@ -703,6 +740,22 @@ struct ControllerSettingsStruct
|
||||
result += Port;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
bool updateIPcache() {
|
||||
if (!UseDNS) {
|
||||
return true;
|
||||
}
|
||||
IPAddress tmpIP;
|
||||
if (WiFi.hostByName(HostName, tmpIP)) {
|
||||
for (byte x = 0; x < 4; x++) {
|
||||
IP[x] = tmpIP[x];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct NotificationSettingsStruct
|
||||
@@ -817,6 +870,14 @@ struct LogStruct {
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
bool get(String& output, const String& lineEnd, int line) {
|
||||
int tmpread((write_idx + 1+line) % LOG_STRUCT_MESSAGE_LINES);
|
||||
if (timeStamp[tmpread] != 0) {
|
||||
output += formatLine(tmpread, lineEnd);
|
||||
}
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
bool getAll(String& output, const String& lineEnd) {
|
||||
int tmpread((write_idx + 1) % LOG_STRUCT_MESSAGE_LINES);
|
||||
bool someAdded = false;
|
||||
@@ -975,6 +1036,8 @@ unsigned long timer100ms;
|
||||
unsigned long timer20ms;
|
||||
unsigned long timer1s;
|
||||
unsigned long timerwd;
|
||||
unsigned long timermqtt;
|
||||
unsigned long timermqtt_interval;
|
||||
unsigned long lastSend;
|
||||
unsigned long lastWeb;
|
||||
unsigned int NC_Count = 0;
|
||||
@@ -1035,11 +1098,15 @@ int firstEnabledBlynkController() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//void checkRAM( const __FlashStringHelper* flashString);
|
||||
/*********************************************************************************************\
|
||||
* SETUP
|
||||
\*********************************************************************************************/
|
||||
void setup()
|
||||
{
|
||||
|
||||
|
||||
checkRAM(F("setup"));
|
||||
#if defined(ESP32)
|
||||
for(byte x = 0; x < 16; x++)
|
||||
ledChannelPin[x] = -1;
|
||||
@@ -1140,6 +1207,7 @@ void setup()
|
||||
if (Settings.UseSerial && Settings.SerialLogLevel >= LOG_LEVEL_DEBUG_MORE)
|
||||
Serial.setDebugOutput(true);
|
||||
|
||||
checkRAM(F("hardwareInit"));
|
||||
hardwareInit();
|
||||
|
||||
WiFi.persistent(false); // Do not use SDK storage of SSID/WPA parameters
|
||||
@@ -1175,6 +1243,8 @@ void setup()
|
||||
timer100ms = 0; // timer for periodic actions 10 x per/sec
|
||||
timer1s = 0; // timer for periodic actions once per/sec
|
||||
timerwd = 0; // timer for watchdog once per 30 sec
|
||||
timermqtt = 0; // Timer for the MQTT keep alive loop.
|
||||
timermqtt_interval = 250; // Interval for checking MQTT
|
||||
|
||||
PluginInit();
|
||||
CPluginInit();
|
||||
@@ -1190,12 +1260,14 @@ void setup()
|
||||
if (Settings.UDPPort != 0)
|
||||
portUDP.begin(Settings.UDPPort);
|
||||
|
||||
/*
|
||||
// Setup MQTT Client
|
||||
// ToDo TD-er: Controller index is forced to the first enabled MQTT controller.
|
||||
int enabledMqttController = firstEnabledMQTTController();
|
||||
if (enabledMqttController >= 0) {
|
||||
MQTTConnect(enabledMqttController);
|
||||
}
|
||||
*/
|
||||
|
||||
sendSysInfoUDP(3);
|
||||
|
||||
@@ -1243,6 +1315,7 @@ bool getControllerProtocolDisplayName(byte ProtocolIndex, byte parameterIdx, Str
|
||||
\*********************************************************************************************/
|
||||
void loop()
|
||||
{
|
||||
//checkRAM(F("loop"));
|
||||
loopCounter++;
|
||||
|
||||
if (wifiSetupConnect)
|
||||
@@ -1259,8 +1332,11 @@ void loop()
|
||||
run10TimesPerSecond();
|
||||
runEach30Seconds();
|
||||
runOncePerSecond();
|
||||
deepSleep(Settings.Delay);
|
||||
//deepsleep will never return, its a special kind of reboot
|
||||
if (Settings.UseRules)
|
||||
{
|
||||
String event = F("System#Sleep");
|
||||
rulesProcessing(event);
|
||||
}
|
||||
}
|
||||
//normal mode, run each task when its time
|
||||
else
|
||||
@@ -1277,16 +1353,32 @@ void loop()
|
||||
|
||||
if (timeOutReached(timer1s))
|
||||
runOncePerSecond();
|
||||
}
|
||||
|
||||
//dont do this in backgroundtasks(), otherwise causes crashes. (https://github.com/letscontrolit/ESPEasy/issues/683)
|
||||
int enabledMqttController = firstEnabledMQTTController();
|
||||
if (enabledMqttController >= 0) {
|
||||
MQTTclient.loop();
|
||||
if (timeOutReached(timermqtt)) {
|
||||
// MQTT_KEEPALIVE = 15 seconds.
|
||||
timermqtt = millis() + timermqtt_interval;
|
||||
//dont do this in backgroundtasks(), otherwise causes crashes. (https://github.com/letscontrolit/ESPEasy/issues/683)
|
||||
int enabledMqttController = firstEnabledMQTTController();
|
||||
if (enabledMqttController >= 0) {
|
||||
if (!MQTTclient.loop()) {
|
||||
if (!MQTTCheck(enabledMqttController)) {
|
||||
// Check failed, no need to retry it immediately.
|
||||
if (timermqtt_interval < 2000)
|
||||
timermqtt_interval += 250;
|
||||
} else {
|
||||
timermqtt_interval = 250;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
backgroundtasks();
|
||||
|
||||
if (isDeepSleepEnabled()){
|
||||
deepSleep(Settings.Delay);
|
||||
//deepsleep will never return, its a special kind of reboot
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1316,6 +1408,7 @@ void run10TimesPerSecond()
|
||||
eventBuffer = "";
|
||||
}
|
||||
elapsed = micros() - start;
|
||||
WebServer.handleClient();
|
||||
}
|
||||
|
||||
|
||||
@@ -1415,6 +1508,8 @@ void runOncePerSecond()
|
||||
\*********************************************************************************************/
|
||||
void runEach30Seconds()
|
||||
{
|
||||
extern void checkRAMtoLog();
|
||||
checkRAMtoLog();
|
||||
wdcounter++;
|
||||
timerwd = millis() + 30000;
|
||||
char str[60];
|
||||
@@ -1457,6 +1552,7 @@ void runEach30Seconds()
|
||||
\*********************************************************************************************/
|
||||
void checkSensors()
|
||||
{
|
||||
checkRAM(F("checkSensors"));
|
||||
bool isDeepSleep = isDeepSleepEnabled();
|
||||
//check all the devices and only run the sendtask if its time, or we if we used deep sleep mode
|
||||
for (byte x = 0; x < TASKS_MAX; x++)
|
||||
@@ -1493,6 +1589,7 @@ void checkSensors()
|
||||
\*********************************************************************************************/
|
||||
void SensorSendTask(byte TaskIndex)
|
||||
{
|
||||
checkRAM(F("SensorSendTask"));
|
||||
if (Settings.TaskDeviceEnabled[TaskIndex])
|
||||
{
|
||||
byte varIndex = TaskIndex * VARS_PER_TASK;
|
||||
@@ -1535,6 +1632,12 @@ void SensorSendTask(byte TaskIndex)
|
||||
}
|
||||
}
|
||||
sendData(&TempEvent);
|
||||
} else if (!anyControllerEnabled() && Settings.GlobalSync) {
|
||||
// No other controller enabled thus need to make sure global sync is still performed.
|
||||
if (Settings.TaskDeviceGlobalSync[TaskIndex]) {
|
||||
LoadTaskSettings(TaskIndex);
|
||||
SendUDPTaskData(0, TaskIndex, TaskIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1633,6 +1736,7 @@ void checkSystemTimers()
|
||||
bool runningBackgroundTasks=false;
|
||||
void backgroundtasks()
|
||||
{
|
||||
//checkRAM(F("backgroundtasks"));
|
||||
//always start with a yield
|
||||
yield();
|
||||
|
||||
|
||||
+310
-302
@@ -7,17 +7,20 @@
|
||||
#include <md5.h>
|
||||
#endif
|
||||
#if defined(ESP8266)
|
||||
|
||||
struct tcp_pcb;
|
||||
extern struct tcp_pcb* tcp_tw_pcbs;
|
||||
extern "C" void tcp_abort (struct tcp_pcb* pcb);
|
||||
|
||||
void tcpCleanup()
|
||||
{
|
||||
#if LWIP_VERSION_MAJOR == 2
|
||||
// is it still needed ?
|
||||
#else
|
||||
while(tcp_tw_pcbs!=NULL)
|
||||
|
||||
while(tcp_tw_pcbs!=NULL)
|
||||
{
|
||||
tcp_abort(tcp_tw_pcbs);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
bool isDeepSleepEnabled()
|
||||
@@ -40,6 +43,7 @@ bool isDeepSleepEnabled()
|
||||
void deepSleep(int delay)
|
||||
{
|
||||
|
||||
checkRAM(F("deepSleep"));
|
||||
if (!isDeepSleepEnabled())
|
||||
{
|
||||
//Deep sleep canceled by GPIO16(D0)=LOW
|
||||
@@ -87,6 +91,7 @@ void deepSleepStart(int delay)
|
||||
|
||||
boolean remoteConfig(struct EventStruct *event, String& string)
|
||||
{
|
||||
checkRAM(F("remoteConfig"));
|
||||
boolean success = false;
|
||||
String command = parseString(string, 1);
|
||||
|
||||
@@ -138,6 +143,7 @@ void flashCount()
|
||||
|
||||
String flashGuard()
|
||||
{
|
||||
checkRAM(F("flashGuard"));
|
||||
if (RTC.flashDayCounter > MAX_FLASHWRITES_PER_DAY)
|
||||
{
|
||||
String log = F("FS : Daily flash write rate exceeded! (powercycle to reset this)");
|
||||
@@ -187,72 +193,6 @@ byte getValueCountFromSensorType(byte sensorType)
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
Workaround for removing trailing white space when String() converts a float with 0 decimals
|
||||
\*********************************************************************************************/
|
||||
String toString(float value, byte decimals)
|
||||
{
|
||||
String sValue = String(value, decimals);
|
||||
sValue.trim();
|
||||
return sValue;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
Format a value to the set number of decimals
|
||||
\*********************************************************************************************/
|
||||
String formatUserVar(struct EventStruct *event, byte rel_index)
|
||||
{
|
||||
return toString(
|
||||
UserVar[event->BaseVarIndex + rel_index],
|
||||
ExtraTaskSettings.TaskDeviceValueDecimals[rel_index]);
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
Parse a string and get the xth command or parameter
|
||||
\*********************************************************************************************/
|
||||
String parseString(String& string, byte indexFind)
|
||||
{
|
||||
String tmpString = string;
|
||||
tmpString += ",";
|
||||
tmpString.replace(" ", ",");
|
||||
String locateString = "";
|
||||
byte count = 0;
|
||||
int index = tmpString.indexOf(',');
|
||||
while (index > 0)
|
||||
{
|
||||
count++;
|
||||
locateString = tmpString.substring(0, index);
|
||||
tmpString = tmpString.substring(index + 1);
|
||||
index = tmpString.indexOf(',');
|
||||
if (count == indexFind)
|
||||
{
|
||||
locateString.toLowerCase();
|
||||
return locateString;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
Parse a string and get the xth command or parameter
|
||||
\*********************************************************************************************/
|
||||
int getParamStartPos(String& string, byte indexFind)
|
||||
{
|
||||
String tmpString = string;
|
||||
byte count = 0;
|
||||
tmpString.replace(" ", ",");
|
||||
for (unsigned int x = 0; x < tmpString.length(); x++)
|
||||
{
|
||||
if (tmpString.charAt(x) == ',')
|
||||
{
|
||||
count++;
|
||||
if (count == (indexFind - 1))
|
||||
return x + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
@@ -322,6 +262,7 @@ boolean hasPinState(byte plugin, byte index)
|
||||
\*********************************************************************************************/
|
||||
String getPinStateJSON(boolean search, byte plugin, byte index, String& log, uint16_t noSearchValue)
|
||||
{
|
||||
checkRAM(F("getPinStateJSON"));
|
||||
printToWebJSON = true;
|
||||
byte mode = PIN_MODE_INPUT;
|
||||
uint16_t value = noSearchValue;
|
||||
@@ -462,6 +403,7 @@ void delayBackground(unsigned long delay)
|
||||
\*********************************************************************************************/
|
||||
void parseCommandString(struct EventStruct *event, String& string)
|
||||
{
|
||||
checkRAM(F("parseCommandString"));
|
||||
char command[80];
|
||||
command[0] = 0;
|
||||
char TmpStr1[80];
|
||||
@@ -486,6 +428,7 @@ void parseCommandString(struct EventStruct *event, String& string)
|
||||
\*********************************************************************************************/
|
||||
void taskClear(byte taskIndex, boolean save)
|
||||
{
|
||||
checkRAM(F("taskClear"));
|
||||
Settings.TaskDeviceNumber[taskIndex] = 0;
|
||||
ExtraTaskSettings.TaskDeviceName[0] = 0;
|
||||
Settings.TaskDeviceDataFeed[taskIndex] = 0;
|
||||
@@ -547,6 +490,7 @@ String FileError(int line, const char * fname)
|
||||
\*********************************************************************************************/
|
||||
String BuildFixes()
|
||||
{
|
||||
checkRAM(F("BuildFixes"));
|
||||
Serial.println(F("\nBuild changed!"));
|
||||
|
||||
if (Settings.Build < 145)
|
||||
@@ -574,6 +518,7 @@ String BuildFixes()
|
||||
\*********************************************************************************************/
|
||||
void fileSystemCheck()
|
||||
{
|
||||
checkRAM(F("fileSystemCheck"));
|
||||
addLog(LOG_LEVEL_INFO, F("FS : Mounting..."));
|
||||
if (SPIFFS.begin())
|
||||
{
|
||||
@@ -704,34 +649,7 @@ boolean GetArgv(const char *string, char *argv, unsigned int argc)
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************\
|
||||
Convert a char string to integer
|
||||
\*********************************************************************************************/
|
||||
//FIXME: change original code so it uses String and String.toInt()
|
||||
unsigned long str2int(char *string)
|
||||
{
|
||||
unsigned long temp = atof(string);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
Convert a char string to IP byte array
|
||||
\*********************************************************************************************/
|
||||
boolean str2ip(const String& string, byte* IP) {
|
||||
return str2ip(string.c_str(), IP);
|
||||
}
|
||||
|
||||
boolean str2ip(const char *string, byte* IP)
|
||||
{
|
||||
IPAddress tmpip; // Default constructor => set to 0.0.0.0
|
||||
if (*string == 0 || tmpip.fromString(string)) {
|
||||
// Eiher empty string or a valid IP addres, so copy value.
|
||||
for (byte i = 0; i < 4; ++i)
|
||||
IP[i] = tmpip[i];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
check the program memory hash
|
||||
@@ -760,6 +678,7 @@ void dump (uint32_t addr) { //Seems already included in core 2.4 ...
|
||||
#endif
|
||||
|
||||
uint32_t progMemMD5check(){
|
||||
checkRAM(F("progMemMD5check"));
|
||||
#define BufSize 10
|
||||
uint32_t calcBuffer[BufSize];
|
||||
CRCValues.numberOfCRCBytes = 0;
|
||||
@@ -799,6 +718,7 @@ uint32_t progMemMD5check(){
|
||||
\*********************************************************************************************/
|
||||
String SaveSettings(void)
|
||||
{
|
||||
checkRAM(F("SaveSettings"));
|
||||
MD5Builder md5;
|
||||
memcpy( Settings.ProgmemMd5, CRCValues.runTimeMD5, 16);
|
||||
md5.begin();
|
||||
@@ -826,6 +746,7 @@ String SaveSettings(void)
|
||||
\*********************************************************************************************/
|
||||
String LoadSettings()
|
||||
{
|
||||
checkRAM(F("LoadSettings"));
|
||||
String err;
|
||||
uint8_t calculatedMd5[16];
|
||||
MD5Builder md5;
|
||||
@@ -870,6 +791,7 @@ String LoadSettings()
|
||||
\*********************************************************************************************/
|
||||
String SaveTaskSettings(byte TaskIndex)
|
||||
{
|
||||
checkRAM(F("SaveTaskSettings"));
|
||||
ExtraTaskSettings.TaskIndex = TaskIndex;
|
||||
return(SaveToFile((char*)FILE_CONFIG, DAT_OFFSET_TASKS + (TaskIndex * DAT_TASKS_SIZE), (byte*)&ExtraTaskSettings, sizeof(struct ExtraTaskSettingsStruct)));
|
||||
}
|
||||
@@ -880,6 +802,7 @@ String SaveTaskSettings(byte TaskIndex)
|
||||
\*********************************************************************************************/
|
||||
String LoadTaskSettings(byte TaskIndex)
|
||||
{
|
||||
checkRAM(F("LoadTaskSettings"));
|
||||
//already loaded
|
||||
if (ExtraTaskSettings.TaskIndex == TaskIndex)
|
||||
return(String());
|
||||
@@ -896,6 +819,7 @@ String LoadTaskSettings(byte TaskIndex)
|
||||
\*********************************************************************************************/
|
||||
String SaveCustomTaskSettings(int TaskIndex, byte* memAddress, int datasize)
|
||||
{
|
||||
checkRAM(F("SaveCustomTaskSettings"));
|
||||
if (datasize > DAT_TASKS_SIZE)
|
||||
return F("SaveCustomTaskSettings too big");
|
||||
return(SaveToFile((char*)FILE_CONFIG, DAT_OFFSET_TASKS + (TaskIndex * DAT_TASKS_SIZE) + DAT_TASKS_CUSTOM_OFFSET, memAddress, datasize));
|
||||
@@ -908,7 +832,7 @@ String SaveCustomTaskSettings(int TaskIndex, byte* memAddress, int datasize)
|
||||
String ClearCustomTaskSettings(int TaskIndex)
|
||||
{
|
||||
// addLog(LOG_LEVEL_DEBUG, F("Clearing custom task settings"));
|
||||
return(ClearInFile((char*)"config.dat", DAT_OFFSET_TASKS + (TaskIndex * DAT_TASKS_SIZE) + DAT_TASKS_CUSTOM_OFFSET, DAT_TASKS_SIZE));
|
||||
return(ClearInFile((char*)FILE_CONFIG, DAT_OFFSET_TASKS + (TaskIndex * DAT_TASKS_SIZE) + DAT_TASKS_CUSTOM_OFFSET, DAT_TASKS_SIZE));
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
@@ -916,6 +840,7 @@ String ClearCustomTaskSettings(int TaskIndex)
|
||||
\*********************************************************************************************/
|
||||
String LoadCustomTaskSettings(int TaskIndex, byte* memAddress, int datasize)
|
||||
{
|
||||
checkRAM(F("LoadCustomTaskSettings"));
|
||||
if (datasize > DAT_TASKS_SIZE)
|
||||
return (String(F("LoadCustomTaskSettings too big")));
|
||||
return(LoadFromFile((char*)FILE_CONFIG, DAT_OFFSET_TASKS + (TaskIndex * DAT_TASKS_SIZE) + DAT_TASKS_CUSTOM_OFFSET, memAddress, datasize));
|
||||
@@ -926,6 +851,7 @@ String LoadCustomTaskSettings(int TaskIndex, byte* memAddress, int datasize)
|
||||
\*********************************************************************************************/
|
||||
String SaveControllerSettings(int ControllerIndex, byte* memAddress, int datasize)
|
||||
{
|
||||
checkRAM(F("SaveControllerSettings"));
|
||||
if (datasize > DAT_CONTROLLER_SIZE)
|
||||
return F("SaveControllerSettings too big");
|
||||
return SaveToFile((char*)FILE_CONFIG, DAT_OFFSET_CONTROLLER + (ControllerIndex * DAT_CONTROLLER_SIZE), memAddress, datasize);
|
||||
@@ -937,6 +863,7 @@ String SaveControllerSettings(int ControllerIndex, byte* memAddress, int datasiz
|
||||
\*********************************************************************************************/
|
||||
String LoadControllerSettings(int ControllerIndex, byte* memAddress, int datasize)
|
||||
{
|
||||
checkRAM(F("LoadControllerSettings"));
|
||||
if (datasize > DAT_CONTROLLER_SIZE)
|
||||
return F("LoadControllerSettings too big");
|
||||
|
||||
@@ -949,8 +876,9 @@ String LoadControllerSettings(int ControllerIndex, byte* memAddress, int datasiz
|
||||
\*********************************************************************************************/
|
||||
String ClearCustomControllerSettings(int ControllerIndex)
|
||||
{
|
||||
checkRAM(F("ClearCustomControllerSettings"));
|
||||
// addLog(LOG_LEVEL_DEBUG, F("Clearing custom controller settings"));
|
||||
return(ClearInFile((char*)"config.dat", DAT_OFFSET_CUSTOM_CONTROLLER + (ControllerIndex * DAT_CUSTOM_CONTROLLER_SIZE), DAT_CUSTOM_CONTROLLER_SIZE));
|
||||
return(ClearInFile((char*)FILE_CONFIG, DAT_OFFSET_CUSTOM_CONTROLLER + (ControllerIndex * DAT_CUSTOM_CONTROLLER_SIZE), DAT_CUSTOM_CONTROLLER_SIZE));
|
||||
}
|
||||
|
||||
|
||||
@@ -959,6 +887,7 @@ String ClearCustomControllerSettings(int ControllerIndex)
|
||||
\*********************************************************************************************/
|
||||
String SaveCustomControllerSettings(int ControllerIndex,byte* memAddress, int datasize)
|
||||
{
|
||||
checkRAM(F("SaveCustomControllerSettings"));
|
||||
if (datasize > DAT_CUSTOM_CONTROLLER_SIZE)
|
||||
return F("SaveCustomControllerSettings too big");
|
||||
return SaveToFile((char*)FILE_CONFIG, DAT_OFFSET_CUSTOM_CONTROLLER + (ControllerIndex * DAT_CUSTOM_CONTROLLER_SIZE), memAddress, datasize);
|
||||
@@ -970,6 +899,7 @@ String SaveCustomControllerSettings(int ControllerIndex,byte* memAddress, int da
|
||||
\*********************************************************************************************/
|
||||
String LoadCustomControllerSettings(int ControllerIndex,byte* memAddress, int datasize)
|
||||
{
|
||||
checkRAM(F("LoadCustomControllerSettings"));
|
||||
if (datasize > DAT_CUSTOM_CONTROLLER_SIZE)
|
||||
return(F("LoadCustomControllerSettings too big"));
|
||||
return(LoadFromFile((char*)FILE_CONFIG, DAT_OFFSET_CUSTOM_CONTROLLER + (ControllerIndex * DAT_CUSTOM_CONTROLLER_SIZE), memAddress, datasize));
|
||||
@@ -980,6 +910,7 @@ String LoadCustomControllerSettings(int ControllerIndex,byte* memAddress, int da
|
||||
\*********************************************************************************************/
|
||||
String SaveNotificationSettings(int NotificationIndex, byte* memAddress, int datasize)
|
||||
{
|
||||
checkRAM(F("SaveNotificationSettings"));
|
||||
if (datasize > DAT_NOTIFICATION_SIZE)
|
||||
return F("SaveNotificationSettings too big");
|
||||
return SaveToFile((char*)FILE_NOTIFICATION, NotificationIndex * DAT_NOTIFICATION_SIZE, memAddress, datasize);
|
||||
@@ -991,6 +922,7 @@ String SaveNotificationSettings(int NotificationIndex, byte* memAddress, int dat
|
||||
\*********************************************************************************************/
|
||||
String LoadNotificationSettings(int NotificationIndex, byte* memAddress, int datasize)
|
||||
{
|
||||
checkRAM(F("LoadNotificationSettings"));
|
||||
if (datasize > DAT_NOTIFICATION_SIZE)
|
||||
return(F("LoadNotificationSettings too big"));
|
||||
return(LoadFromFile((char*)FILE_NOTIFICATION, NotificationIndex * DAT_NOTIFICATION_SIZE, memAddress, datasize));
|
||||
@@ -1004,7 +936,7 @@ String LoadNotificationSettings(int NotificationIndex, byte* memAddress, int dat
|
||||
\*********************************************************************************************/
|
||||
String InitFile(const char* fname, int datasize)
|
||||
{
|
||||
|
||||
checkRAM(F("InitFile"));
|
||||
FLASH_GUARD();
|
||||
|
||||
fs::File f = SPIFFS.open(fname, "w");
|
||||
@@ -1025,7 +957,7 @@ String InitFile(const char* fname, int datasize)
|
||||
\*********************************************************************************************/
|
||||
String SaveToFile(char* fname, int index, byte* memAddress, int datasize)
|
||||
{
|
||||
|
||||
checkRAM(F("SaveToFile"));
|
||||
FLASH_GUARD();
|
||||
|
||||
fs::File f = SPIFFS.open(fname, "r+");
|
||||
@@ -1052,6 +984,7 @@ String SaveToFile(char* fname, int index, byte* memAddress, int datasize)
|
||||
\*********************************************************************************************/
|
||||
String ClearInFile(char* fname, int index, int datasize)
|
||||
{
|
||||
checkRAM(F("ClearInFile"));
|
||||
FLASH_GUARD();
|
||||
|
||||
fs::File f = SPIFFS.open(fname, "r+");
|
||||
@@ -1074,6 +1007,7 @@ String ClearInFile(char* fname, int index, int datasize)
|
||||
\*********************************************************************************************/
|
||||
String LoadFromFile(char* fname, int index, byte* memAddress, int datasize)
|
||||
{
|
||||
checkRAM(F("LoadFromFile"));
|
||||
// addLog(LOG_LEVEL_INFO, String(F("FILE : Load size "))+datasize);
|
||||
|
||||
fs::File f = SPIFFS.open(fname, "r+");
|
||||
@@ -1101,6 +1035,7 @@ String LoadFromFile(char* fname, int index, byte* memAddress, int datasize)
|
||||
\*********************************************************************************************/
|
||||
int SpiffsSectors()
|
||||
{
|
||||
checkRAM(F("SpiffsSectors"));
|
||||
#if defined(ESP8266)
|
||||
uint32_t _sectorStart = ((uint32_t)&_SPIFFS_start - 0x40200000) / SPI_FLASH_SEC_SIZE;
|
||||
uint32_t _sectorEnd = ((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE;
|
||||
@@ -1118,6 +1053,7 @@ int SpiffsSectors()
|
||||
void ResetFactory(void)
|
||||
{
|
||||
|
||||
checkRAM(F("ResetFactory"));
|
||||
// Direct Serial is allowed here, since this is only an emergency task.
|
||||
Serial.println(F("RESET: Resetting factory defaults..."));
|
||||
delay(1000);
|
||||
@@ -1263,7 +1199,7 @@ void ResetFactory(void)
|
||||
ControllerSettings.Port = DEFAULT_PORT;
|
||||
SaveControllerSettings(0, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
#endif
|
||||
|
||||
checkRAM(F("ResetFactory2"));
|
||||
Serial.println("RESET: Succesful, rebooting. (you might need to press the reset button if you've justed flashed the firmware)");
|
||||
//NOTE: this is a known ESP8266 bug, not our fault. :)
|
||||
delay(1000);
|
||||
@@ -1336,6 +1272,63 @@ float ul2float(unsigned long ul)
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************\
|
||||
Check if string is valid float
|
||||
\*********************************************************************************************/
|
||||
boolean isFloat(const String& tBuf) {
|
||||
return isNumerical(tBuf, false);
|
||||
}
|
||||
|
||||
boolean isValidFloat(float f) {
|
||||
if (f == NAN) return false; //("NaN");
|
||||
if (f == INFINITY) return false; //("INFINITY");
|
||||
if (-f == INFINITY)return false; //("-INFINITY");
|
||||
if (isnan(f)) return false; //("isnan");
|
||||
if (isinf(f)) return false; //("isinf");
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isInt(const String& tBuf) {
|
||||
return isNumerical(tBuf, true);
|
||||
}
|
||||
|
||||
boolean isNumerical(const String& tBuf, bool mustBeInteger) {
|
||||
boolean decPt = false;
|
||||
int firstDec = 0;
|
||||
if(tBuf.charAt(0) == '+' || tBuf.charAt(0) == '-')
|
||||
firstDec = 1;
|
||||
for(unsigned int x=firstDec; x < tBuf.length(); ++x) {
|
||||
if(tBuf.charAt(x) == '.') {
|
||||
if (mustBeInteger) return false;
|
||||
// Only one decimal point allowed
|
||||
if(decPt) return false;
|
||||
else decPt = true;
|
||||
}
|
||||
else if(tBuf.charAt(x) < '0' || tBuf.charAt(x) > '9') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// convert old and new time string to nr of seconds
|
||||
float timeStringToSeconds(String tBuf) {
|
||||
float sec = 0;
|
||||
int split = tBuf.indexOf(':');
|
||||
if (split < 0) { // assume only hours
|
||||
sec += tBuf.toFloat() * 60 * 60;
|
||||
} else {
|
||||
sec += tBuf.substring(0, split).toFloat() * 60 * 60;
|
||||
tBuf = tBuf.substring(split +1);
|
||||
split = tBuf.indexOf(':');
|
||||
if (split < 0) { //old format
|
||||
sec += tBuf.toFloat() * 60;
|
||||
} else { //new format
|
||||
sec += tBuf.substring(0, split).toFloat() * 60;
|
||||
sec += tBuf.substring(split +1).toFloat();
|
||||
}
|
||||
}
|
||||
return sec;
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
Init critical variables for logging (important during initial factory reset stuff )
|
||||
\*********************************************************************************************/
|
||||
@@ -1359,31 +1352,64 @@ void addLog(byte loglevel, String& string)
|
||||
|
||||
void addLog(byte logLevel, const __FlashStringHelper* flashString)
|
||||
{
|
||||
checkRAM(F("addLog"));
|
||||
String s(flashString);
|
||||
addLog(logLevel, s.c_str());
|
||||
}
|
||||
|
||||
bool SerialAvailableForWrite() {
|
||||
if (!Settings.UseSerial) return false;
|
||||
#if defined(ESP8266)
|
||||
if (!Serial.availableForWrite()) return false; // UART FIFO overflow or TX disabled.
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean loglevelActiveFor(byte destination, byte logLevel) {
|
||||
byte logLevelSettings = 0;
|
||||
switch (destination) {
|
||||
case LOG_TO_SERIAL: {
|
||||
if (!SerialAvailableForWrite()) return false;
|
||||
logLevelSettings = Settings.SerialLogLevel;
|
||||
break;
|
||||
}
|
||||
case LOG_TO_SYSLOG: {
|
||||
logLevelSettings = Settings.SyslogLevel;
|
||||
break;
|
||||
}
|
||||
case LOG_TO_WEBLOG: {
|
||||
logLevelSettings = Settings.WebLogLevel;
|
||||
break;
|
||||
}
|
||||
case LOG_TO_SDCARD: {
|
||||
logLevelSettings = Settings.SDLogLevel;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return loglevelActive(logLevel, logLevelSettings);
|
||||
}
|
||||
|
||||
|
||||
boolean loglevelActive(byte logLevel, byte logLevelSettings) {
|
||||
return (logLevel <= logLevelSettings);
|
||||
}
|
||||
|
||||
void addLog(byte loglevel, const char *line)
|
||||
void addLog(byte logLevel, const char *line)
|
||||
{
|
||||
if (Settings.UseSerial)
|
||||
if (loglevelActive(loglevel, Settings.SerialLogLevel))
|
||||
Serial.println(line);
|
||||
|
||||
if (loglevelActive(loglevel, Settings.SyslogLevel))
|
||||
if (loglevelActiveFor(LOG_TO_SERIAL, logLevel)) {
|
||||
Serial.println(line);
|
||||
}
|
||||
if (loglevelActiveFor(LOG_TO_SYSLOG, logLevel)) {
|
||||
syslog(line);
|
||||
|
||||
if (loglevelActive(loglevel, Settings.WebLogLevel))
|
||||
{
|
||||
}
|
||||
if (loglevelActiveFor(LOG_TO_WEBLOG, logLevel)) {
|
||||
Logging.add(line);
|
||||
}
|
||||
|
||||
#ifdef FEATURE_SD
|
||||
if (loglevelActive(loglevel, Settings.SDLogLevel))
|
||||
{
|
||||
if (loglevelActiveFor(LOG_TO_SDCARD, logLevel)) {
|
||||
File logFile = SD.open("log.dat", FILE_WRITE);
|
||||
if (logFile)
|
||||
logFile.println(line);
|
||||
@@ -1531,25 +1557,11 @@ uint32_t getChecksum(byte* buffer, size_t size)
|
||||
Parse string template
|
||||
\*********************************************************************************************/
|
||||
|
||||
// Call this by first declaring a char array of size 20, like:
|
||||
// char strIP[20];
|
||||
// formatIP(ip, strIP);
|
||||
void formatIP(const IPAddress& ip, char (&strIP)[20]) {
|
||||
sprintf_P(strIP, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
|
||||
}
|
||||
|
||||
String formatIP(const IPAddress& ip) {
|
||||
char strIP[20];
|
||||
formatIP(ip, strIP);
|
||||
return String(strIP);
|
||||
}
|
||||
|
||||
void formatMAC(const uint8_t* mac, char (&strMAC)[20]) {
|
||||
sprintf_P(strMAC, PSTR("%02X:%02X:%02X:%02X:%02X:%02X"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
}
|
||||
|
||||
String parseTemplate(String &tmpString, byte lineSize)
|
||||
{
|
||||
checkRAM(F("parseTemplate"));
|
||||
String newString = "";
|
||||
String tmpStringMid = "";
|
||||
newString.reserve(lineSize);
|
||||
@@ -1604,6 +1616,20 @@ String parseTemplate(String &tmpString, byte lineSize)
|
||||
else
|
||||
value = toString(UserVar[y * VARS_PER_TASK + z], ExtraTaskSettings.TaskDeviceValueDecimals[z]);
|
||||
|
||||
int oidx;
|
||||
if ((oidx = valueFormat.indexOf('O'))>=0) // Output
|
||||
{
|
||||
valueFormat.remove(oidx);
|
||||
oidx = valueFormat.indexOf('!'); // inverted or active low
|
||||
float val = value.toFloat();
|
||||
if (oidx >= 0) {
|
||||
valueFormat.remove(oidx);
|
||||
value = val == 0 ? " ON" : "OFF";
|
||||
} else {
|
||||
value = val == 0 ? "OFF" : " ON";
|
||||
}
|
||||
}
|
||||
|
||||
if (valueFormat == "R")
|
||||
{
|
||||
int filler = lineSize - newString.length() - value.length() - tmpString.length() ;
|
||||
@@ -1630,6 +1656,7 @@ String parseTemplate(String &tmpString, byte lineSize)
|
||||
leftBracketIndex = tmpString.indexOf('[');
|
||||
count++;
|
||||
}
|
||||
checkRAM(F("parseTemplate2"));
|
||||
newString += tmpString;
|
||||
|
||||
if (currentTaskIndex!=255)
|
||||
@@ -1641,163 +1668,10 @@ String parseTemplate(String &tmpString, byte lineSize)
|
||||
// padding spaces
|
||||
while (newString.length() < lineSize)
|
||||
newString += " ";
|
||||
|
||||
checkRAM(F("parseTemplate3"));
|
||||
return newString;
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
replace other system variables like %sysname%, %systime%, %ip%
|
||||
\*********************************************************************************************/
|
||||
void repl(const String& key, const String& val, String& s, boolean useURLencode)
|
||||
{
|
||||
if (useURLencode) {
|
||||
s.replace(key, URLEncode(val.c_str()));
|
||||
} else {
|
||||
s.replace(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
void parseSpecialCharacters(String& s, boolean useURLencode)
|
||||
{
|
||||
bool no_accolades = s.indexOf('{') == -1 || s.indexOf('}') == -1;
|
||||
bool no_html_entity = s.indexOf('&') == -1 || s.indexOf(';') == -1;
|
||||
if (no_accolades && no_html_entity)
|
||||
return; // Nothing to replace
|
||||
|
||||
{
|
||||
// Degree
|
||||
const char degree[3] = {0xc2, 0xb0, 0}; // Unicode degree symbol
|
||||
const char degreeC[4] = {0xe2, 0x84, 0x83, 0}; // Unicode degreeC symbol
|
||||
const char degree_C[4] = {0xc2, 0xb0, 'C', 0}; // Unicode degree symbol + captial C
|
||||
repl(F("{D}"), degree, s, useURLencode);
|
||||
repl(F("°"), degree, s, useURLencode);
|
||||
repl(degreeC, degree_C, s, useURLencode);
|
||||
}
|
||||
{
|
||||
// Angle quotes
|
||||
const char laquo[3] = {0xc2, 0xab, 0}; // Unicode left angle quotes symbol
|
||||
const char raquo[3] = {0xc2, 0xbb, 0}; // Unicode right angle quotes symbol
|
||||
repl(F("{<<}"), laquo, s, useURLencode);
|
||||
repl(F("«"), laquo, s, useURLencode);
|
||||
repl(F("{>>}"), raquo, s, useURLencode);
|
||||
repl(F("»"), raquo, s, useURLencode);
|
||||
}
|
||||
{
|
||||
// Greek letter Mu
|
||||
const char mu[3] = {0xc2, 0xb5, 0}; // Unicode greek letter mu
|
||||
repl(F("{u}"), mu, s, useURLencode);
|
||||
repl(F("µ"), mu, s, useURLencode);
|
||||
}
|
||||
{
|
||||
// Currency
|
||||
const char euro[4] = {0xe2, 0x82, 0xac, 0}; // Unicode euro symbol
|
||||
const char yen[3] = {0xc2, 0xa5, 0}; // Unicode yen symbol
|
||||
const char pound[3] = {0xc2, 0xa3, 0}; // Unicode pound symbol
|
||||
const char cent[3] = {0xc2, 0xa2, 0}; // Unicode cent symbol
|
||||
repl(F("{E}"), euro, s, useURLencode);
|
||||
repl(F("€"), euro, s, useURLencode);
|
||||
repl(F("{Y}"), yen, s, useURLencode);
|
||||
repl(F("¥"), yen, s, useURLencode);
|
||||
repl(F("{P}"), pound, s, useURLencode);
|
||||
repl(F("£"), pound, s, useURLencode);
|
||||
repl(F("{c}"), cent, s, useURLencode);
|
||||
repl(F("¢"), cent, s, useURLencode);
|
||||
}
|
||||
{
|
||||
// Math symbols
|
||||
const char sup1[3] = {0xc2, 0xb9, 0}; // Unicode sup1 symbol
|
||||
const char sup2[3] = {0xc2, 0xb2, 0}; // Unicode sup2 symbol
|
||||
const char sup3[3] = {0xc2, 0xb3, 0}; // Unicode sup3 symbol
|
||||
const char frac14[3] = {0xc2, 0xbc, 0}; // Unicode frac14 symbol
|
||||
const char frac12[3] = {0xc2, 0xbd, 0}; // Unicode frac12 symbol
|
||||
const char frac34[3] = {0xc2, 0xbe, 0}; // Unicode frac34 symbol
|
||||
const char plusmn[3] = {0xc2, 0xb1, 0}; // Unicode plusmn symbol
|
||||
const char times[3] = {0xc3, 0x97, 0}; // Unicode times symbol
|
||||
const char divide[3] = {0xc3, 0xb7, 0}; // Unicode divide symbol
|
||||
repl(F("{^1}"), sup1, s, useURLencode);
|
||||
repl(F("¹"), sup1, s, useURLencode);
|
||||
repl(F("{^2}"), sup2, s, useURLencode);
|
||||
repl(F("²"), sup2, s, useURLencode);
|
||||
repl(F("{^3}"), sup3, s, useURLencode);
|
||||
repl(F("³"), sup3, s, useURLencode);
|
||||
repl(F("{1_4}"), frac14, s, useURLencode);
|
||||
repl(F("¼"), frac14, s, useURLencode);
|
||||
repl(F("{1_2}"), frac12, s, useURLencode);
|
||||
repl(F("½"), frac12, s, useURLencode);
|
||||
repl(F("{3_4}"), frac34, s, useURLencode);
|
||||
repl(F("¾"), frac34, s, useURLencode);
|
||||
repl(F("{+-}"), plusmn, s, useURLencode);
|
||||
repl(F("±"), plusmn, s, useURLencode);
|
||||
repl(F("{x}"), times, s, useURLencode);
|
||||
repl(F("×"), times, s, useURLencode);
|
||||
repl(F("{..}"), divide, s, useURLencode);
|
||||
repl(F("÷"), divide, s, useURLencode);
|
||||
}
|
||||
}
|
||||
|
||||
// Simple macro to create the replacement string only when needed.
|
||||
#define SMART_REPL(T,S) if (s.indexOf(T) != -1) { repl((T), (S), s, useURLencode);}
|
||||
void parseSystemVariables(String& s, boolean useURLencode)
|
||||
{
|
||||
parseSpecialCharacters(s, useURLencode);
|
||||
if (s.indexOf('%') == -1)
|
||||
return; // Nothing to replace
|
||||
|
||||
#if FEATURE_ADC_VCC
|
||||
repl(F("%vcc%"), String(vcc), s, useURLencode);
|
||||
#endif
|
||||
repl(F("%CR%"), F("\r"), s, useURLencode);
|
||||
repl(F("%LF%"), F("\n"), s, useURLencode);
|
||||
SMART_REPL(F("%ip%"),WiFi.localIP().toString())
|
||||
SMART_REPL(F("%rssi%"), String((WiFi.status() == WL_CONNECTED) ? WiFi.RSSI() : 0))
|
||||
SMART_REPL(F("%ssid%"), (WiFi.status() == WL_CONNECTED) ? WiFi.SSID() : F("--"))
|
||||
SMART_REPL(F("%unit%"), String(Settings.Unit))
|
||||
|
||||
if (s.indexOf(F("%sys")) != -1) {
|
||||
SMART_REPL(F("%sysload%"), String(100 - (100 * loopCounterLast / loopCounterMax)))
|
||||
SMART_REPL(F("%systime%"), getTimeString(':'))
|
||||
repl(F("%sysname%"), Settings.Name, s, useURLencode);
|
||||
|
||||
// valueString is being used by the macro.
|
||||
char valueString[5];
|
||||
#define SMART_REPL_TIME(T,F,V) if (s.indexOf(T) != -1) { sprintf_P(valueString, (F), (V)); repl((T),valueString, s, useURLencode);}
|
||||
SMART_REPL_TIME(F("%syshour%"), PSTR("%02d"), hour())
|
||||
SMART_REPL_TIME(F("%sysmin%"), PSTR("%02d"), minute())
|
||||
SMART_REPL_TIME(F("%syssec%"),PSTR("%02d"), second())
|
||||
SMART_REPL_TIME(F("%sysday%"), PSTR("%02d"), day())
|
||||
SMART_REPL_TIME(F("%sysmonth%"),PSTR("%02d"), month())
|
||||
SMART_REPL_TIME(F("%sysyear%"), PSTR("%04d"), year())
|
||||
SMART_REPL_TIME(F("%sysyears%"),PSTR("%02d"), year()%100)
|
||||
#undef SMART_REPL_TIME
|
||||
}
|
||||
SMART_REPL(F("%lcltime%"), getDateTimeString('-',':',' '))
|
||||
SMART_REPL(F("%uptime%"), String(wdcounter / 2))
|
||||
|
||||
repl(F("%tskname%"), ExtraTaskSettings.TaskDeviceName, s, useURLencode);
|
||||
if (s.indexOf("%vname") != -1) {
|
||||
repl(F("%vname1%"), ExtraTaskSettings.TaskDeviceValueNames[0], s, useURLencode);
|
||||
repl(F("%vname2%"), ExtraTaskSettings.TaskDeviceValueNames[1], s, useURLencode);
|
||||
repl(F("%vname3%"), ExtraTaskSettings.TaskDeviceValueNames[2], s, useURLencode);
|
||||
repl(F("%vname4%"), ExtraTaskSettings.TaskDeviceValueNames[3], s, useURLencode);
|
||||
}
|
||||
}
|
||||
|
||||
void parseEventVariables(String& s, struct EventStruct *event, boolean useURLencode)
|
||||
{
|
||||
SMART_REPL(F("%id%"), String(event->idx))
|
||||
if (s.indexOf("%val") != -1) {
|
||||
if (event->sensorType == SENSOR_TYPE_LONG) {
|
||||
SMART_REPL(F("%val1%"), String((unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16)))
|
||||
} else {
|
||||
SMART_REPL(F("%val1%"), formatUserVar(event, 0))
|
||||
SMART_REPL(F("%val2%"), formatUserVar(event, 1))
|
||||
SMART_REPL(F("%val3%"), formatUserVar(event, 2))
|
||||
SMART_REPL(F("%val4%"), formatUserVar(event, 3))
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef SMART_REPL
|
||||
|
||||
|
||||
/********************************************************************************************\
|
||||
Calculate function for simple expressions
|
||||
@@ -1935,6 +1809,7 @@ unsigned int op_arg_count(const char c)
|
||||
|
||||
int Calculate(const char *input, float* result)
|
||||
{
|
||||
checkRAM(F("Calculate"));
|
||||
const char *strpos = input, *strend = input + strlen(input);
|
||||
char token[25];
|
||||
char c, oc, *TokenPos = token;
|
||||
@@ -2067,6 +1942,7 @@ int Calculate(const char *input, float* result)
|
||||
return error;
|
||||
}
|
||||
*result = *sp;
|
||||
checkRAM(F("Calculate2"));
|
||||
return CALCULATE_OK;
|
||||
}
|
||||
|
||||
@@ -2077,6 +1953,7 @@ int Calculate(const char *input, float* result)
|
||||
\*********************************************************************************************/
|
||||
void rulesProcessing(String& event)
|
||||
{
|
||||
checkRAM(F("rulesProcessing"));
|
||||
unsigned long timer = millis();
|
||||
String log = "";
|
||||
|
||||
@@ -2098,7 +1975,7 @@ void rulesProcessing(String& event)
|
||||
rulesProcessingFile(fileName, event);
|
||||
}
|
||||
|
||||
log = F("EVENT: Processing time:");
|
||||
log += F(" Processing time:");
|
||||
log += timePassedSince(timer);
|
||||
log += F(" milliSeconds");
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
@@ -2110,10 +1987,11 @@ void rulesProcessing(String& event)
|
||||
\*********************************************************************************************/
|
||||
String rulesProcessingFile(String fileName, String& event)
|
||||
{
|
||||
checkRAM(F("rulesProcessingFile"));
|
||||
fs::File f = SPIFFS.open(fileName, "r+");
|
||||
SPIFFS_CHECK(f, fileName.c_str());
|
||||
|
||||
static byte nestingLevel;
|
||||
static byte nestingLevel = 0;
|
||||
int data = 0;
|
||||
String log = "";
|
||||
|
||||
@@ -2213,7 +2091,7 @@ String rulesProcessingFile(String fileName, String& event)
|
||||
{
|
||||
conditional = true;
|
||||
String check = lcAction.substring(split + 3);
|
||||
condition = conditionMatch(check);
|
||||
condition = conditionMatchExtended(check);
|
||||
ifBranche = true;
|
||||
isCommand = false;
|
||||
}
|
||||
@@ -2266,6 +2144,7 @@ String rulesProcessingFile(String fileName, String& event)
|
||||
}
|
||||
|
||||
nestingLevel--;
|
||||
checkRAM(F("rulesProcessingFile2"));
|
||||
return(String());
|
||||
}
|
||||
|
||||
@@ -2275,6 +2154,7 @@ String rulesProcessingFile(String fileName, String& event)
|
||||
\*********************************************************************************************/
|
||||
boolean ruleMatch(String& event, String& rule)
|
||||
{
|
||||
checkRAM(F("ruleMatch"));
|
||||
boolean match = false;
|
||||
String tmpEvent = event;
|
||||
String tmpRule = rule;
|
||||
@@ -2381,7 +2261,7 @@ boolean ruleMatch(String& event, String& rule)
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
|
||||
checkRAM(F("ruleMatch2"));
|
||||
return match;
|
||||
}
|
||||
|
||||
@@ -2389,6 +2269,32 @@ boolean ruleMatch(String& event, String& rule)
|
||||
/********************************************************************************************\
|
||||
Check expression
|
||||
\*********************************************************************************************/
|
||||
|
||||
boolean conditionMatchExtended(String& check) {
|
||||
int condAnd = -1;
|
||||
int condOr = -1;
|
||||
boolean rightcond = false;
|
||||
boolean leftcond = conditionMatch(check); // initial check
|
||||
|
||||
do {
|
||||
condAnd = check.indexOf(F(" and "));
|
||||
condOr = check.indexOf(F(" or "));
|
||||
|
||||
if (condAnd > 0 || condOr > 0) { // we got AND/OR
|
||||
if (condAnd > 0 && ((condOr < 0 && condOr < condAnd) || (condOr > 0 && condOr > condAnd))) { //AND is first
|
||||
check = check.substring(condAnd + 5);
|
||||
rightcond = conditionMatch(check);
|
||||
leftcond = (leftcond && rightcond);
|
||||
} else { //OR is first
|
||||
check = check.substring(condOr + 4);
|
||||
rightcond = conditionMatch(check);
|
||||
leftcond = (leftcond || rightcond);
|
||||
}
|
||||
}
|
||||
} while (condAnd > 0 || condOr > 0);
|
||||
return leftcond;
|
||||
}
|
||||
|
||||
boolean conditionMatch(String& check)
|
||||
{
|
||||
boolean match = false;
|
||||
@@ -2422,10 +2328,15 @@ boolean conditionMatch(String& check)
|
||||
|
||||
if (comparePos > 0)
|
||||
{
|
||||
String tmpCheck = check.substring(comparePos + 1);
|
||||
Value2 = tmpCheck.toFloat();
|
||||
tmpCheck = check.substring(0, comparePos);
|
||||
Value1 = tmpCheck.toFloat();
|
||||
String tmpCheck1 = check.substring(0, comparePos);
|
||||
String tmpCheck2 = check.substring(comparePos + 1);
|
||||
if (!isFloat(tmpCheck1) || !isFloat(tmpCheck2)) {
|
||||
Value1 = timeStringToSeconds(tmpCheck1);
|
||||
Value2 = timeStringToSeconds(tmpCheck2);
|
||||
} else {
|
||||
Value1 = tmpCheck1.toFloat();
|
||||
Value2 = tmpCheck2.toFloat();
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
@@ -2539,8 +2450,107 @@ void SendValueLogger(byte TaskIndex)
|
||||
}
|
||||
|
||||
|
||||
#define TRACES 3 // number of memory traces
|
||||
#define TRACEENTRIES 15 // entries per trace
|
||||
|
||||
class RamTracker{
|
||||
private:
|
||||
String traces[TRACES] ; // trace of latest memory checks
|
||||
unsigned int tracesMemory[TRACES] ; // lowest memory for that trace
|
||||
unsigned int readPtr, writePtr; // pointer to cyclic buffer
|
||||
String nextAction[TRACEENTRIES]; // buffer to record the names of functions before they are transfered to a trace
|
||||
unsigned int nextActionStartMemory[TRACEENTRIES]; // memory levels for the functions.
|
||||
|
||||
unsigned int bestCaseTrace (void){ // find highest the trace with the largest minimum memory (gets replaced by worse one)
|
||||
unsigned int lowestMemoryInTrace = 0;
|
||||
unsigned int lowestMemoryInTraceIndex=0;
|
||||
for (int i = 0; i<TRACES; i++) {
|
||||
if (tracesMemory[i] > lowestMemoryInTrace){
|
||||
lowestMemoryInTrace= tracesMemory[i];
|
||||
lowestMemoryInTraceIndex=i;
|
||||
}
|
||||
}
|
||||
//Serial.println(lowestMemoryInTraceIndex);
|
||||
return lowestMemoryInTraceIndex;
|
||||
}
|
||||
|
||||
public:
|
||||
RamTracker(void){ // constructor
|
||||
readPtr=0;
|
||||
writePtr=0;
|
||||
for (int i = 0; i< TRACES; i++) {
|
||||
traces[i]="";
|
||||
tracesMemory[i]=0xffff; // init with best case memory values, so they get replaced if memory goes lower
|
||||
}
|
||||
for (int i = 0; i< TRACEENTRIES; i++) {
|
||||
nextAction[i]="startup";
|
||||
nextActionStartMemory[i] = ESP.getFreeHeap(); // init with best case memory values, so they get replaced if memory goes lower
|
||||
}
|
||||
};
|
||||
|
||||
void registerRamState(String &s){ // store function
|
||||
nextAction[writePtr]=s; // name and mem
|
||||
nextActionStartMemory[writePtr]=ESP.getFreeHeap(); // in cyclic buffer.
|
||||
int bestCase = bestCaseTrace(); // find best case memory trace
|
||||
if ( ESP.getFreeHeap() < tracesMemory[bestCase]){ // compare to current memory value
|
||||
traces[bestCase]="";
|
||||
readPtr = writePtr+1; // read out buffer, oldest value first
|
||||
if (readPtr>=TRACEENTRIES) readPtr=0; // read pointer wrap around
|
||||
tracesMemory[bestCase] = ESP.getFreeHeap(); // store new lowest value of that trace
|
||||
|
||||
for (int i = 0; i<TRACEENTRIES; i++) { // tranfer cyclic buffer strings and mem values to this trace
|
||||
traces[bestCase]+= nextAction[readPtr];
|
||||
traces[bestCase]+= "-> ";
|
||||
traces[bestCase]+= String(nextActionStartMemory[readPtr]);
|
||||
traces[bestCase]+= " ";
|
||||
readPtr++;
|
||||
if (readPtr >=TRACEENTRIES) readPtr=0; // wrap around read pointer
|
||||
}
|
||||
}
|
||||
writePtr++;
|
||||
if (writePtr >= TRACEENTRIES) writePtr=0; // inc write pointer and wrap around too.
|
||||
};
|
||||
void getTraceBuffer(){ // return giant strings, one line per trace. Add stremToWeb method to avoid large strings.
|
||||
String retval="Memtrace\n";
|
||||
for (int i = 0; i< TRACES; i++){
|
||||
retval += String(i);
|
||||
retval += ": lowest: ";
|
||||
retval += String(tracesMemory[i]);
|
||||
retval += " ";
|
||||
retval += traces[i];
|
||||
addLog(LOG_LEVEL_DEBUG_DEV, retval);
|
||||
retval="";
|
||||
}
|
||||
}
|
||||
}myRamTracker; // instantiate class. (is global now)
|
||||
|
||||
void checkRAMtoLog(void){
|
||||
myRamTracker.getTraceBuffer();
|
||||
}
|
||||
|
||||
void checkRAM(const __FlashStringHelper* flashString, int a ) {
|
||||
String s=String(a);
|
||||
checkRAM(flashString,s);
|
||||
}
|
||||
|
||||
void checkRAM(const __FlashStringHelper* flashString, String &a ) {
|
||||
String s = flashString;
|
||||
checkRAM(s,a);
|
||||
}
|
||||
|
||||
void checkRAM(String &flashString, String &a ) {
|
||||
String s = flashString;
|
||||
s+=" (";
|
||||
s+=a;
|
||||
s+=")";
|
||||
checkRAM(s);
|
||||
}
|
||||
|
||||
void checkRAM( const __FlashStringHelper* flashString)
|
||||
{
|
||||
String s = flashString;
|
||||
myRamTracker.registerRamState(s);
|
||||
|
||||
uint32_t freeRAM = FreeMem();
|
||||
|
||||
if (freeRAM < lowestRAM)
|
||||
@@ -2550,6 +2560,11 @@ void checkRAM( const __FlashStringHelper* flashString)
|
||||
}
|
||||
}
|
||||
|
||||
void checkRAM( String &a ) {
|
||||
myRamTracker.registerRamState(a);
|
||||
}
|
||||
|
||||
|
||||
#ifdef PLUGIN_BUILD_TESTING
|
||||
|
||||
#define isdigit(n) (n >= '0' && n <= '9')
|
||||
@@ -2570,6 +2585,7 @@ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) {
|
||||
\*********************************************************************************************/
|
||||
void play_rtttl(uint8_t _pin, const char *p )
|
||||
{
|
||||
checkRAM(F("play_rtttl"));
|
||||
#define OCTAVE_OFFSET 0
|
||||
// FIXME: Absolutely no error checking in here
|
||||
|
||||
@@ -2720,6 +2736,7 @@ void play_rtttl(uint8_t _pin, const char *p )
|
||||
delay(duration/10);
|
||||
}
|
||||
}
|
||||
checkRAM(F("play_rtttl2"));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -2732,6 +2749,7 @@ void play_rtttl(uint8_t _pin, const char *p )
|
||||
|
||||
void ArduinoOTAInit()
|
||||
{
|
||||
checkRAM(F("ArduinoOTAInit"));
|
||||
// Default port is 8266
|
||||
ArduinoOTA.setPort(8266);
|
||||
ArduinoOTA.setHostname(Settings.Name);
|
||||
@@ -2815,16 +2833,6 @@ String getBearing(int degrees)
|
||||
|
||||
}
|
||||
|
||||
//escapes special characters in strings for use in html-forms
|
||||
void htmlEscape(String & html)
|
||||
{
|
||||
html.replace("&", "&");
|
||||
html.replace("\"", """);
|
||||
html.replace("'", "'");
|
||||
html.replace("<", "<");
|
||||
html.replace(">", ">");
|
||||
}
|
||||
|
||||
// Compute the dew point temperature, given temperature and humidity (temp in Celcius)
|
||||
// Formula: http://www.ajdesigner.com/phphumidity/dewpoint_equation_dewpoint_temperature.php
|
||||
// Td = (f/100)^(1/8) * (112 + 0.9*T) + 0.1*T - 112
|
||||
|
||||
+33
-4
@@ -7,7 +7,6 @@
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
Syslog client
|
||||
\*********************************************************************************************/
|
||||
@@ -752,11 +751,12 @@ void SSDP_update() {
|
||||
_server->flush();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check WiFi connection. Maximum timeout 2000 msec.
|
||||
// Check WiFi connection. Maximum timeout 500 msec.
|
||||
bool WiFiConnected(uint32_t timeout_ms) {
|
||||
uint32_t timer = millis() + (timeout_ms > 2000 ? 2000 : timeout_ms);
|
||||
uint32_t min_delay = timeout_ms / 10;
|
||||
uint32_t timer = millis() + (timeout_ms > 500 ? 500 : timeout_ms);
|
||||
uint32_t min_delay = timeout_ms / 20;
|
||||
if (min_delay < 10) {
|
||||
yield(); // Allow at least once time for backgroundtasks
|
||||
min_delay = 10;
|
||||
@@ -769,4 +769,33 @@ bool WiFiConnected(uint32_t timeout_ms) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hostReachable(const IPAddress& ip) {
|
||||
// Only do 1 ping at a time to return early
|
||||
byte retry = 3;
|
||||
while (retry > 0) {
|
||||
#if defined(ESP8266)
|
||||
if (Ping.ping(ip, 1)) return true;
|
||||
#endif
|
||||
#if defined(ESP32)
|
||||
if (ping_start(ip, 4, 0, 0, 5)) return true;
|
||||
#endif
|
||||
delay(50);
|
||||
--retry;
|
||||
}
|
||||
String log = F("Host unreachable: ");
|
||||
log += ip;
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hostReachable(const String& hostname) {
|
||||
IPAddress remote_addr;
|
||||
if (WiFi.hostByName(hostname.c_str(), remote_addr))
|
||||
return hostReachable(remote_addr);
|
||||
String log = F("Hostname cannot be resolved: ");
|
||||
log += hostname;
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
/********************************************************************************************\
|
||||
Convert a char string to integer
|
||||
\*********************************************************************************************/
|
||||
//FIXME: change original code so it uses String and String.toInt()
|
||||
unsigned long str2int(char *string)
|
||||
{
|
||||
unsigned long temp = atof(string);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
Check if valid float and convert string to float.
|
||||
\*********************************************************************************************/
|
||||
bool string2float(const String& string, float& floatvalue) {
|
||||
if (!isFloat(string)) return false;
|
||||
floatvalue = atof(string.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************\
|
||||
Convert a char string to IP byte array
|
||||
\*********************************************************************************************/
|
||||
boolean str2ip(const String& string, byte* IP) {
|
||||
return str2ip(string.c_str(), IP);
|
||||
}
|
||||
|
||||
boolean str2ip(const char *string, byte* IP)
|
||||
{
|
||||
IPAddress tmpip; // Default constructor => set to 0.0.0.0
|
||||
if (*string == 0 || tmpip.fromString(string)) {
|
||||
// Eiher empty string or a valid IP addres, so copy value.
|
||||
for (byte i = 0; i < 4; ++i)
|
||||
IP[i] = tmpip[i];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call this by first declaring a char array of size 20, like:
|
||||
// char strIP[20];
|
||||
// formatIP(ip, strIP);
|
||||
void formatIP(const IPAddress& ip, char (&strIP)[20]) {
|
||||
sprintf_P(strIP, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
|
||||
}
|
||||
|
||||
String formatIP(const IPAddress& ip) {
|
||||
char strIP[20];
|
||||
formatIP(ip, strIP);
|
||||
return String(strIP);
|
||||
}
|
||||
|
||||
void formatMAC(const uint8_t* mac, char (&strMAC)[20]) {
|
||||
sprintf_P(strMAC, PSTR("%02X:%02X:%02X:%02X:%02X:%02X"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
Workaround for removing trailing white space when String() converts a float with 0 decimals
|
||||
\*********************************************************************************************/
|
||||
String toString(float value, byte decimals)
|
||||
{
|
||||
String sValue = String(value, decimals);
|
||||
sValue.trim();
|
||||
return sValue;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
Format a value to the set number of decimals
|
||||
\*********************************************************************************************/
|
||||
String formatUserVar(struct EventStruct *event, byte rel_index)
|
||||
{
|
||||
float f(UserVar[event->BaseVarIndex + rel_index]);
|
||||
if (!isValidFloat(f)) {
|
||||
String log = F("Invalid float value for TaskIndex: ");
|
||||
log += event->TaskIndex;
|
||||
log += F(" varnumber: ");
|
||||
log += rel_index;
|
||||
addLog(LOG_LEVEL_DEBUG, log);
|
||||
f = 0;
|
||||
}
|
||||
return toString(f, ExtraTaskSettings.TaskDeviceValueDecimals[rel_index]);
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
Parse a string and get the xth command or parameter
|
||||
\*********************************************************************************************/
|
||||
String parseString(String& string, byte indexFind)
|
||||
{
|
||||
String tmpString = string;
|
||||
tmpString += ",";
|
||||
tmpString.replace(" ", ",");
|
||||
String locateString = "";
|
||||
byte count = 0;
|
||||
int index = tmpString.indexOf(',');
|
||||
while (index > 0)
|
||||
{
|
||||
count++;
|
||||
locateString = tmpString.substring(0, index);
|
||||
tmpString = tmpString.substring(index + 1);
|
||||
index = tmpString.indexOf(',');
|
||||
if (count == indexFind)
|
||||
{
|
||||
locateString.toLowerCase();
|
||||
return locateString;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
Parse a string and get the xth command or parameter
|
||||
\*********************************************************************************************/
|
||||
int getParamStartPos(String& string, byte indexFind)
|
||||
{
|
||||
String tmpString = string;
|
||||
byte count = 0;
|
||||
tmpString.replace(" ", ",");
|
||||
for (unsigned int x = 0; x < tmpString.length(); x++)
|
||||
{
|
||||
if (tmpString.charAt(x) == ',')
|
||||
{
|
||||
count++;
|
||||
if (count == (indexFind - 1))
|
||||
return x + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//escapes special characters in strings for use in html-forms
|
||||
void htmlEscape(String & html)
|
||||
{
|
||||
html.replace("&", "&");
|
||||
html.replace("\"", """);
|
||||
html.replace("'", "'");
|
||||
html.replace("<", "<");
|
||||
html.replace(">", ">");
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
replace other system variables like %sysname%, %systime%, %ip%
|
||||
\*********************************************************************************************/
|
||||
void repl(const String& key, const String& val, String& s, boolean useURLencode)
|
||||
{
|
||||
if (useURLencode) {
|
||||
s.replace(key, URLEncode(val.c_str()));
|
||||
} else {
|
||||
s.replace(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
void parseSpecialCharacters(String& s, boolean useURLencode)
|
||||
{
|
||||
bool no_accolades = s.indexOf('{') == -1 || s.indexOf('}') == -1;
|
||||
bool no_html_entity = s.indexOf('&') == -1 || s.indexOf(';') == -1;
|
||||
if (no_accolades && no_html_entity)
|
||||
return; // Nothing to replace
|
||||
|
||||
{
|
||||
// Degree
|
||||
const char degree[3] = {0xc2, 0xb0, 0}; // Unicode degree symbol
|
||||
const char degreeC[4] = {0xe2, 0x84, 0x83, 0}; // Unicode degreeC symbol
|
||||
const char degree_C[4] = {0xc2, 0xb0, 'C', 0}; // Unicode degree symbol + captial C
|
||||
repl(F("{D}"), degree, s, useURLencode);
|
||||
repl(F("°"), degree, s, useURLencode);
|
||||
repl(degreeC, degree_C, s, useURLencode);
|
||||
}
|
||||
{
|
||||
// Angle quotes
|
||||
const char laquo[3] = {0xc2, 0xab, 0}; // Unicode left angle quotes symbol
|
||||
const char raquo[3] = {0xc2, 0xbb, 0}; // Unicode right angle quotes symbol
|
||||
repl(F("{<<}"), laquo, s, useURLencode);
|
||||
repl(F("«"), laquo, s, useURLencode);
|
||||
repl(F("{>>}"), raquo, s, useURLencode);
|
||||
repl(F("»"), raquo, s, useURLencode);
|
||||
}
|
||||
{
|
||||
// Greek letter Mu
|
||||
const char mu[3] = {0xc2, 0xb5, 0}; // Unicode greek letter mu
|
||||
repl(F("{u}"), mu, s, useURLencode);
|
||||
repl(F("µ"), mu, s, useURLencode);
|
||||
}
|
||||
{
|
||||
// Currency
|
||||
const char euro[4] = {0xe2, 0x82, 0xac, 0}; // Unicode euro symbol
|
||||
const char yen[3] = {0xc2, 0xa5, 0}; // Unicode yen symbol
|
||||
const char pound[3] = {0xc2, 0xa3, 0}; // Unicode pound symbol
|
||||
const char cent[3] = {0xc2, 0xa2, 0}; // Unicode cent symbol
|
||||
repl(F("{E}"), euro, s, useURLencode);
|
||||
repl(F("€"), euro, s, useURLencode);
|
||||
repl(F("{Y}"), yen, s, useURLencode);
|
||||
repl(F("¥"), yen, s, useURLencode);
|
||||
repl(F("{P}"), pound, s, useURLencode);
|
||||
repl(F("£"), pound, s, useURLencode);
|
||||
repl(F("{c}"), cent, s, useURLencode);
|
||||
repl(F("¢"), cent, s, useURLencode);
|
||||
}
|
||||
{
|
||||
// Math symbols
|
||||
const char sup1[3] = {0xc2, 0xb9, 0}; // Unicode sup1 symbol
|
||||
const char sup2[3] = {0xc2, 0xb2, 0}; // Unicode sup2 symbol
|
||||
const char sup3[3] = {0xc2, 0xb3, 0}; // Unicode sup3 symbol
|
||||
const char frac14[3] = {0xc2, 0xbc, 0}; // Unicode frac14 symbol
|
||||
const char frac12[3] = {0xc2, 0xbd, 0}; // Unicode frac12 symbol
|
||||
const char frac34[3] = {0xc2, 0xbe, 0}; // Unicode frac34 symbol
|
||||
const char plusmn[3] = {0xc2, 0xb1, 0}; // Unicode plusmn symbol
|
||||
const char times[3] = {0xc3, 0x97, 0}; // Unicode times symbol
|
||||
const char divide[3] = {0xc3, 0xb7, 0}; // Unicode divide symbol
|
||||
repl(F("{^1}"), sup1, s, useURLencode);
|
||||
repl(F("¹"), sup1, s, useURLencode);
|
||||
repl(F("{^2}"), sup2, s, useURLencode);
|
||||
repl(F("²"), sup2, s, useURLencode);
|
||||
repl(F("{^3}"), sup3, s, useURLencode);
|
||||
repl(F("³"), sup3, s, useURLencode);
|
||||
repl(F("{1_4}"), frac14, s, useURLencode);
|
||||
repl(F("¼"), frac14, s, useURLencode);
|
||||
repl(F("{1_2}"), frac12, s, useURLencode);
|
||||
repl(F("½"), frac12, s, useURLencode);
|
||||
repl(F("{3_4}"), frac34, s, useURLencode);
|
||||
repl(F("¾"), frac34, s, useURLencode);
|
||||
repl(F("{+-}"), plusmn, s, useURLencode);
|
||||
repl(F("±"), plusmn, s, useURLencode);
|
||||
repl(F("{x}"), times, s, useURLencode);
|
||||
repl(F("×"), times, s, useURLencode);
|
||||
repl(F("{..}"), divide, s, useURLencode);
|
||||
repl(F("÷"), divide, s, useURLencode);
|
||||
}
|
||||
}
|
||||
|
||||
// Simple macro to create the replacement string only when needed.
|
||||
#define SMART_REPL(T,S) if (s.indexOf(T) != -1) { repl((T), (S), s, useURLencode);}
|
||||
void parseSystemVariables(String& s, boolean useURLencode)
|
||||
{
|
||||
parseSpecialCharacters(s, useURLencode);
|
||||
if (s.indexOf('%') == -1)
|
||||
return; // Nothing to replace
|
||||
|
||||
#if FEATURE_ADC_VCC
|
||||
repl(F("%vcc%"), String(vcc), s, useURLencode);
|
||||
#endif
|
||||
repl(F("%CR%"), F("\r"), s, useURLencode);
|
||||
repl(F("%LF%"), F("\n"), s, useURLencode);
|
||||
SMART_REPL(F("%ip%"),WiFi.localIP().toString())
|
||||
SMART_REPL(F("%rssi%"), String((WiFi.status() == WL_CONNECTED) ? WiFi.RSSI() : 0))
|
||||
SMART_REPL(F("%ssid%"), (WiFi.status() == WL_CONNECTED) ? WiFi.SSID() : F("--"))
|
||||
SMART_REPL(F("%unit%"), String(Settings.Unit))
|
||||
SMART_REPL(F("%mac%"), String(WiFi.macAddress()))
|
||||
#if defined(ESP8266)
|
||||
SMART_REPL(F("%mac_int%"), String(ESP.getChipId())) // Last 24 bit of MAC address as integer, to be used in rules.
|
||||
#endif
|
||||
|
||||
if (s.indexOf(F("%sys")) != -1) {
|
||||
SMART_REPL(F("%sysload%"), String(100 - (100 * loopCounterLast / loopCounterMax)))
|
||||
SMART_REPL(F("%systime%"), getTimeString(':'))
|
||||
repl(F("%sysname%"), Settings.Name, s, useURLencode);
|
||||
|
||||
// valueString is being used by the macro.
|
||||
char valueString[5];
|
||||
#define SMART_REPL_TIME(T,F,V) if (s.indexOf(T) != -1) { sprintf_P(valueString, (F), (V)); repl((T),valueString, s, useURLencode);}
|
||||
SMART_REPL_TIME(F("%syshour%"), PSTR("%02d"), hour())
|
||||
SMART_REPL_TIME(F("%sysmin%"), PSTR("%02d"), minute())
|
||||
SMART_REPL_TIME(F("%syssec%"),PSTR("%02d"), second())
|
||||
SMART_REPL_TIME(F("%sysday%"), PSTR("%02d"), day())
|
||||
SMART_REPL_TIME(F("%sysmonth%"),PSTR("%02d"), month())
|
||||
SMART_REPL_TIME(F("%sysyear%"), PSTR("%04d"), year())
|
||||
SMART_REPL_TIME(F("%sysyears%"),PSTR("%02d"), year()%100)
|
||||
SMART_REPL(F("%sysweekday%"), String(weekday()))
|
||||
SMART_REPL(F("%sysweekday_s%"), weekday_str())
|
||||
#undef SMART_REPL_TIME
|
||||
}
|
||||
SMART_REPL(F("%lcltime%"), getDateTimeString('-',':',' '))
|
||||
SMART_REPL(F("%uptime%"), String(wdcounter / 2))
|
||||
|
||||
repl(F("%tskname%"), ExtraTaskSettings.TaskDeviceName, s, useURLencode);
|
||||
if (s.indexOf("%vname") != -1) {
|
||||
repl(F("%vname1%"), ExtraTaskSettings.TaskDeviceValueNames[0], s, useURLencode);
|
||||
repl(F("%vname2%"), ExtraTaskSettings.TaskDeviceValueNames[1], s, useURLencode);
|
||||
repl(F("%vname3%"), ExtraTaskSettings.TaskDeviceValueNames[2], s, useURLencode);
|
||||
repl(F("%vname4%"), ExtraTaskSettings.TaskDeviceValueNames[3], s, useURLencode);
|
||||
}
|
||||
}
|
||||
|
||||
void parseEventVariables(String& s, struct EventStruct *event, boolean useURLencode)
|
||||
{
|
||||
SMART_REPL(F("%id%"), String(event->idx))
|
||||
if (s.indexOf("%val") != -1) {
|
||||
if (event->sensorType == SENSOR_TYPE_LONG) {
|
||||
SMART_REPL(F("%val1%"), String((unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16)))
|
||||
} else {
|
||||
SMART_REPL(F("%val1%"), formatUserVar(event, 0))
|
||||
SMART_REPL(F("%val2%"), formatUserVar(event, 1))
|
||||
SMART_REPL(F("%val3%"), formatUserVar(event, 2))
|
||||
SMART_REPL(F("%val4%"), formatUserVar(event, 3))
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef SMART_REPL
|
||||
+61
-54
@@ -89,7 +89,8 @@ unsigned long now() {
|
||||
setTime(t);
|
||||
applyTimeZone(t);
|
||||
} else {
|
||||
nextSyncTime = sysTime + syncInterval;
|
||||
// Unable to sync, retry again in a minute
|
||||
nextSyncTime = sysTime + 60;
|
||||
}
|
||||
}
|
||||
uint32_t localSystime = toLocal(sysTime);
|
||||
@@ -141,11 +142,19 @@ byte second()
|
||||
return tm.Second;
|
||||
}
|
||||
|
||||
// day of week, sunday is day 1
|
||||
int weekday()
|
||||
{
|
||||
return tm.Wday;
|
||||
}
|
||||
|
||||
String weekday_str()
|
||||
{
|
||||
const int wday(weekday() - 1); // here: Count from Sunday = 0
|
||||
const String weekDays = F("SunMonTueWedThuFriSat");
|
||||
return weekDays.substring(wday * 3, wday * 3 + 3);
|
||||
}
|
||||
|
||||
void initTime()
|
||||
{
|
||||
nextSyncTime = 0;
|
||||
@@ -161,9 +170,10 @@ void checkTime()
|
||||
PrevMinutes = tm.Minute;
|
||||
if (Settings.UseRules)
|
||||
{
|
||||
String weekDays = F("AllSunMonTueWedThuFriSat");
|
||||
String event = F("Clock#Time=");
|
||||
event += weekDays.substring(weekday() * 3, weekday() * 3 + 3);
|
||||
String event;
|
||||
event.reserve(21);
|
||||
event = F("Clock#Time=");
|
||||
event += weekday_str();
|
||||
event += ",";
|
||||
if (hour() < 10)
|
||||
event += "0";
|
||||
@@ -183,65 +193,62 @@ unsigned long getNtpTime()
|
||||
if (!Settings.UseNTP || !WiFiConnected(100)) {
|
||||
return 0;
|
||||
}
|
||||
IPAddress timeServerIP;
|
||||
const char* ntpServerName = "pool.ntp.org";
|
||||
// Have to do a lookup eacht time, since the NTP pool always returns another IP
|
||||
if (Settings.NTPHost[0] != 0)
|
||||
WiFi.hostByName(Settings.NTPHost, timeServerIP);
|
||||
else
|
||||
WiFi.hostByName(ntpServerName, timeServerIP);
|
||||
|
||||
if (!hostReachable(timeServerIP))
|
||||
return 0;
|
||||
|
||||
WiFiUDP udp;
|
||||
udp.begin(123);
|
||||
for (byte x = 1; x < 4; x++)
|
||||
{
|
||||
String log = F("NTP : NTP sync request:");
|
||||
log += x;
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
|
||||
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
|
||||
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
|
||||
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
|
||||
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
|
||||
|
||||
IPAddress timeServerIP;
|
||||
const char* ntpServerName = "pool.ntp.org";
|
||||
String log = F("NTP : NTP send to ");
|
||||
log += timeServerIP.toString();
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
|
||||
if (Settings.NTPHost[0] != 0)
|
||||
WiFi.hostByName(Settings.NTPHost, timeServerIP);
|
||||
else
|
||||
WiFi.hostByName(ntpServerName, timeServerIP);
|
||||
while (udp.parsePacket() > 0) ; // discard any previously received packets
|
||||
|
||||
log = F("NTP : NTP send to ");
|
||||
log += timeServerIP.toString();
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
memset(packetBuffer, 0, NTP_PACKET_SIZE);
|
||||
packetBuffer[0] = 0b11100011; // LI, Version, Mode
|
||||
packetBuffer[1] = 0; // Stratum, or type of clock
|
||||
packetBuffer[2] = 6; // Polling Interval
|
||||
packetBuffer[3] = 0xEC; // Peer Clock Precision
|
||||
packetBuffer[12] = 49;
|
||||
packetBuffer[13] = 0x4E;
|
||||
packetBuffer[14] = 49;
|
||||
packetBuffer[15] = 52;
|
||||
udp.beginPacket(timeServerIP, 123); //NTP requests are to port 123
|
||||
udp.write(packetBuffer, NTP_PACKET_SIZE);
|
||||
udp.endPacket();
|
||||
|
||||
while (udp.parsePacket() > 0) ; // discard any previously received packets
|
||||
|
||||
memset(packetBuffer, 0, NTP_PACKET_SIZE);
|
||||
packetBuffer[0] = 0b11100011; // LI, Version, Mode
|
||||
packetBuffer[1] = 0; // Stratum, or type of clock
|
||||
packetBuffer[2] = 6; // Polling Interval
|
||||
packetBuffer[3] = 0xEC; // Peer Clock Precision
|
||||
packetBuffer[12] = 49;
|
||||
packetBuffer[13] = 0x4E;
|
||||
packetBuffer[14] = 49;
|
||||
packetBuffer[15] = 52;
|
||||
udp.beginPacket(timeServerIP, 123); //NTP requests are to port 123
|
||||
udp.write(packetBuffer, NTP_PACKET_SIZE);
|
||||
udp.endPacket();
|
||||
|
||||
uint32_t beginWait = millis();
|
||||
while (!timeOutReached(beginWait + 1000)) {
|
||||
int size = udp.parsePacket();
|
||||
if (size >= NTP_PACKET_SIZE) {
|
||||
udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
|
||||
unsigned long secsSince1900;
|
||||
// convert four bytes starting at location 40 to a long integer
|
||||
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[43];
|
||||
log = F("NTP : NTP replied: ");
|
||||
log += timePassedSince(beginWait);
|
||||
log += F(" mSec");
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
return secsSince1900 - 2208988800UL;
|
||||
}
|
||||
uint32_t beginWait = millis();
|
||||
while (!timeOutReached(beginWait + 1000)) {
|
||||
int size = udp.parsePacket();
|
||||
if (size >= NTP_PACKET_SIZE) {
|
||||
udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
|
||||
unsigned long secsSince1900;
|
||||
// convert four bytes starting at location 40 to a long integer
|
||||
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
|
||||
secsSince1900 |= (unsigned long)packetBuffer[43];
|
||||
log = F("NTP : NTP replied: ");
|
||||
log += timePassedSince(beginWait);
|
||||
log += F(" mSec");
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
return secsSince1900 - 2208988800UL;
|
||||
}
|
||||
log = F("NTP : No reply");
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
}
|
||||
log = F("NTP : No reply");
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+1600
-1191
File diff suppressed because it is too large
Load Diff
+25
-88
@@ -76,64 +76,6 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
switch (event->sensorType)
|
||||
{
|
||||
case SENSOR_TYPE_SINGLE: // single value sensor, used for Dallas, BH1750, etc
|
||||
url += F("&svalue=");
|
||||
url += formatUserVar(event, 0);
|
||||
break;
|
||||
case SENSOR_TYPE_LONG: // single LONG value, stored in two floats (rfid tags)
|
||||
url += F("&svalue=");
|
||||
url += (unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16);
|
||||
break;
|
||||
case SENSOR_TYPE_DUAL: // any sensor that uses two simple values
|
||||
url += F("&svalue=");
|
||||
url += formatUserVar(event, 0);
|
||||
url += (";");
|
||||
url += formatUserVar(event, 1);
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM: // temp + hum + hum_stat, used for DHT11
|
||||
url += F("&svalue=");
|
||||
url += formatUserVar(event, 0);
|
||||
url += F(";");
|
||||
url += formatUserVar(event, 1);
|
||||
url += F(";");
|
||||
url += humStat(UserVar[event->BaseVarIndex + 1]);
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_BARO: // temp + bar used for BMP085 and BMP280
|
||||
url += F("&svalue=");
|
||||
url += formatUserVar(event, 0);
|
||||
url += F(";0;0;");
|
||||
url += formatUserVar(event, 1);
|
||||
url += F(";0");
|
||||
break;
|
||||
case SENSOR_TYPE_TRIPLE:
|
||||
url += F("&svalue=");
|
||||
url += formatUserVar(event, 0);
|
||||
url += F(";");
|
||||
url += formatUserVar(event, 1);
|
||||
url += F(";");
|
||||
url += formatUserVar(event, 2);
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM_BARO: // temp + hum + hum_stat + bar + bar_fore, used for BME280
|
||||
url += F("&svalue=");
|
||||
url += formatUserVar(event, 0);
|
||||
url += F(";");
|
||||
url += formatUserVar(event, 1);
|
||||
url += F(";");
|
||||
url += humStat(UserVar[event->BaseVarIndex + 1]);
|
||||
url += F(";");
|
||||
url += formatUserVar(event, 2);
|
||||
url += F(";0");
|
||||
break;
|
||||
case SENSOR_TYPE_QUAD:
|
||||
url += F("&svalue=");
|
||||
url += formatUserVar(event, 0);
|
||||
url += F(";");
|
||||
url += formatUserVar(event, 1);
|
||||
url += F(";");
|
||||
url += formatUserVar(event, 2);
|
||||
url += F(";");
|
||||
url += formatUserVar(event, 3);
|
||||
break;
|
||||
case SENSOR_TYPE_SWITCH:
|
||||
url = F("/json.htm?type=command¶m=switchlight&idx=");
|
||||
url += event->idx;
|
||||
@@ -147,28 +89,39 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
|
||||
url = F("/json.htm?type=command¶m=switchlight&idx=");
|
||||
url += event->idx;
|
||||
url += F("&switchcmd=");
|
||||
if (UserVar[event->BaseVarIndex] == 0)
|
||||
if (UserVar[event->BaseVarIndex] == 0) {
|
||||
url += ("Off");
|
||||
else
|
||||
{
|
||||
} else {
|
||||
url += F("Set%20Level&level=");
|
||||
url += UserVar[event->BaseVarIndex];
|
||||
}
|
||||
break;
|
||||
case (SENSOR_TYPE_WIND):
|
||||
url += F("&svalue="); // WindDir in degrees; WindDir as text; Wind speed average ; Wind speed gust; 0
|
||||
url += formatUserVar(event, 0);
|
||||
url += ";";
|
||||
url += getBearing(UserVar[event->BaseVarIndex]);
|
||||
url += ";";
|
||||
// Domoticz expects the wind speed in (m/s * 10)
|
||||
url += toString((UserVar[event->BaseVarIndex + 1] * 10),ExtraTaskSettings.TaskDeviceValueDecimals[1]);
|
||||
url += ";";
|
||||
url += toString((UserVar[event->BaseVarIndex + 2] * 10),ExtraTaskSettings.TaskDeviceValueDecimals[2]);
|
||||
url += ";0;0";
|
||||
|
||||
case SENSOR_TYPE_SINGLE:
|
||||
case SENSOR_TYPE_LONG:
|
||||
case SENSOR_TYPE_DUAL:
|
||||
case SENSOR_TYPE_TRIPLE:
|
||||
case SENSOR_TYPE_QUAD:
|
||||
case SENSOR_TYPE_TEMP_HUM:
|
||||
case SENSOR_TYPE_TEMP_BARO:
|
||||
case SENSOR_TYPE_TEMP_HUM_BARO:
|
||||
case SENSOR_TYPE_WIND:
|
||||
default:
|
||||
url = F("/json.htm?type=command¶m=udevice&idx=");
|
||||
url += event->idx;
|
||||
url += F("&nvalue=0");
|
||||
url += F("&svalue=");
|
||||
url += formatDomoticzSensorType(event);
|
||||
break;
|
||||
}
|
||||
|
||||
// Add WiFi reception quality
|
||||
url += F("&rssi=");
|
||||
url += mapRSSItoDomoticz();
|
||||
#if FEATURE_ADC_VCC
|
||||
url += F("&battery=");
|
||||
url += mapVccToDomoticz();
|
||||
#endif
|
||||
|
||||
// This will send the request to the server
|
||||
String request = F("GET ");
|
||||
@@ -212,19 +165,3 @@ 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;
|
||||
}
|
||||
|
||||
+20
-103
@@ -135,104 +135,23 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
|
||||
{
|
||||
if (event->idx != 0)
|
||||
{
|
||||
if (!WiFiConnected(100)) {
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
if (!ControllerSettings.checkHostReachable(true)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
ControllerSettingsStruct ControllerSettings;
|
||||
LoadControllerSettings(event->ControllerIndex, (byte*)&ControllerSettings, sizeof(ControllerSettings));
|
||||
|
||||
StaticJsonBuffer<200> jsonBuffer;
|
||||
|
||||
JsonObject& root = jsonBuffer.createObject();
|
||||
|
||||
root[F("idx")] = event->idx;
|
||||
|
||||
String values;
|
||||
// char str[80];
|
||||
root[F("RSSI")] = mapRSSItoDomoticz();
|
||||
#if FEATURE_ADC_VCC
|
||||
root[F("Battery")] = mapVccToDomoticz();
|
||||
#endif
|
||||
|
||||
switch (event->sensorType)
|
||||
{
|
||||
case SENSOR_TYPE_SINGLE: // single value sensor, used for Dallas, BH1750, etc
|
||||
root[F("nvalue")] = 0;
|
||||
values = formatUserVar(event, 0);
|
||||
// values.toCharArray(str, 80);
|
||||
root[F("svalue")] = values.c_str();
|
||||
break;
|
||||
case SENSOR_TYPE_LONG: // single LONG value, stored in two floats (rfid tags)
|
||||
root[F("nvalue")] = 0;
|
||||
values = (unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16);
|
||||
// values.toCharArray(str, 80);
|
||||
root[F("svalue")] = values.c_str();
|
||||
break;
|
||||
case SENSOR_TYPE_DUAL: // any sensor that uses two simple values
|
||||
root[F("nvalue")] = 0;
|
||||
values = formatUserVar(event, 0);
|
||||
values += ";";
|
||||
values += formatUserVar(event, 1);
|
||||
// values.toCharArray(str, 80);
|
||||
root[F("svalue")] = values.c_str();
|
||||
// root[F("svalue")] = str;
|
||||
break;
|
||||
case SENSOR_TYPE_TRIPLE: // any sensor that uses three simple values
|
||||
root[F("nvalue")] = 0;
|
||||
values = formatUserVar(event, 0);
|
||||
values += ";";
|
||||
values += formatUserVar(event, 1);
|
||||
values += ";";
|
||||
values += formatUserVar(event, 2);
|
||||
// values.toCharArray(str, 80);
|
||||
root[F("svalue")] = values.c_str();
|
||||
// root[F("svalue")] = str;
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM: // temp + hum + hum_stat, used for DHT11
|
||||
root[F("nvalue")] = 0;
|
||||
values = formatUserVar(event, 0);
|
||||
values += ";";
|
||||
values += formatUserVar(event, 1);
|
||||
//FIXME: this should be the same as in C001?, instead of 0:
|
||||
// url += humStat(UserVar[event->BaseVarIndex + 1]);
|
||||
values += ";0";
|
||||
// values.toCharArray(str, 80);
|
||||
root[F("svalue")] = values.c_str();
|
||||
// root[F("svalue")] = str;
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_BARO: // temp + hum + hum_stat + bar + bar_fore, used for BMP085
|
||||
root[F("nvalue")] = 0;
|
||||
values = formatUserVar(event, 0);
|
||||
values += ";0;0;";
|
||||
values += formatUserVar(event, 1);
|
||||
values += ";0";
|
||||
// values.toCharArray(str, 80);
|
||||
root[F("svalue")] = values.c_str();
|
||||
// root[F("svalue")] = str;
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM_BARO: // temp + hum + hum_stat + bar + bar_fore, used for BME280
|
||||
root[F("nvalue")] = 0;
|
||||
values = formatUserVar(event, 0);
|
||||
values += ";";
|
||||
values += formatUserVar(event, 1);
|
||||
values += ";0;";
|
||||
values += formatUserVar(event, 2);
|
||||
values += ";0";
|
||||
root[F("svalue")] = values.c_str();
|
||||
// values.toCharArray(str, 80);
|
||||
// root[F("svalue")] = str;
|
||||
break;
|
||||
case SENSOR_TYPE_QUAD:
|
||||
root[F("nvalue")] = 0;
|
||||
values = formatUserVar(event, 0);
|
||||
values += ";";
|
||||
values += formatUserVar(event, 1);
|
||||
values += ";";
|
||||
values += formatUserVar(event, 2);
|
||||
values += ";";
|
||||
values += formatUserVar(event, 3);
|
||||
root[F("svalue")] = values.c_str();
|
||||
// values.toCharArray(str, 80);
|
||||
// root[F("svalue")] = str;
|
||||
break;
|
||||
case SENSOR_TYPE_SWITCH:
|
||||
root[F("command")] = String(F("switchlight"));
|
||||
if (UserVar[event->BaseVarIndex] == 0)
|
||||
@@ -247,22 +166,20 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
|
||||
else
|
||||
root[F("Set%20Level")] = UserVar[event->BaseVarIndex];
|
||||
break;
|
||||
case SENSOR_TYPE_WIND: // WindDir in degrees; WindDir as text; Wind speed average ; Wind speed gust
|
||||
values = formatUserVar(event, 0);
|
||||
values += ";";
|
||||
values += getBearing(UserVar[event->BaseVarIndex]);
|
||||
values += ";";
|
||||
// Domoticz expects the wind speed in (m/s * 10)
|
||||
values += toString((UserVar[event->BaseVarIndex + 1] * 10),ExtraTaskSettings.TaskDeviceValueDecimals[1]);
|
||||
values += ";";
|
||||
values += toString((UserVar[event->BaseVarIndex + 2] * 10),ExtraTaskSettings.TaskDeviceValueDecimals[2]);
|
||||
values += ";0;0";
|
||||
root[F("svalue")] = values.c_str();
|
||||
// values.toCharArray(str, 80);
|
||||
// root["svalue"] = str;
|
||||
|
||||
case SENSOR_TYPE_SINGLE:
|
||||
case SENSOR_TYPE_LONG:
|
||||
case SENSOR_TYPE_DUAL:
|
||||
case SENSOR_TYPE_TRIPLE:
|
||||
case SENSOR_TYPE_QUAD:
|
||||
case SENSOR_TYPE_TEMP_HUM:
|
||||
case SENSOR_TYPE_TEMP_BARO:
|
||||
case SENSOR_TYPE_TEMP_HUM_BARO:
|
||||
case SENSOR_TYPE_WIND:
|
||||
default:
|
||||
root[F("nvalue")] = 0;
|
||||
root[F("svalue")] = formatDomoticzSensorType(event);
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
String json;
|
||||
|
||||
+26
-32
@@ -44,44 +44,38 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
|
||||
// Controller is not enabled.
|
||||
break;
|
||||
} else {
|
||||
// Split topic into array
|
||||
String tmpTopic = event->String1.substring(1);
|
||||
String topicSplit[10];
|
||||
int SlashIndex = tmpTopic.indexOf('/');
|
||||
byte count = 0;
|
||||
while (SlashIndex > 0 && (count < (10 - 1)))
|
||||
{
|
||||
topicSplit[count] = tmpTopic.substring(0, SlashIndex);
|
||||
tmpTopic = tmpTopic.substring(SlashIndex + 1);
|
||||
SlashIndex = tmpTopic.indexOf('/');
|
||||
count++;
|
||||
}
|
||||
topicSplit[count] = tmpTopic;
|
||||
|
||||
String cmd = "";
|
||||
String cmd;
|
||||
struct EventStruct TempEvent;
|
||||
|
||||
if (topicSplit[count] == F("cmd"))
|
||||
{
|
||||
bool validTopic = false;
|
||||
const int lastindex = event->String1.lastIndexOf('/');
|
||||
const String lastPartTopic = event->String1.substring(lastindex + 1);
|
||||
if (lastPartTopic == F("cmd")) {
|
||||
cmd = event->String2;
|
||||
parseCommandString(&TempEvent, cmd);
|
||||
TempEvent.Source = VALUE_SOURCE_MQTT;
|
||||
validTopic = true;
|
||||
} else {
|
||||
if (lastindex > 0) {
|
||||
// Topic has at least one separator
|
||||
if (isFloat(event->String2) && isInt(lastPartTopic)) {
|
||||
int prevLastindex = event->String1.lastIndexOf('/', lastindex - 1);
|
||||
cmd = event->String1.substring(prevLastindex + 1, lastindex);
|
||||
TempEvent.Par1 = lastPartTopic.toInt();
|
||||
TempEvent.Par2 = event->String2.toFloat();
|
||||
TempEvent.Par3 = 0;
|
||||
validTopic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd = topicSplit[count - 1];
|
||||
TempEvent.Par1 = topicSplit[count].toInt();
|
||||
TempEvent.Par2 = event->String2.toFloat();
|
||||
TempEvent.Par3 = 0;
|
||||
}
|
||||
// in case of event, store to buffer and return...
|
||||
String command = parseString(cmd, 1);
|
||||
if (command == F("event"))
|
||||
if (validTopic) {
|
||||
// in case of event, store to buffer and return...
|
||||
String command = parseString(cmd, 1);
|
||||
if (command == F("event")) {
|
||||
eventBuffer = cmd.substring(6);
|
||||
else if
|
||||
(PluginCall(PLUGIN_WRITE, &TempEvent, cmd));
|
||||
else
|
||||
remoteConfig(&TempEvent, cmd);
|
||||
} else if (!PluginCall(PLUGIN_WRITE, &TempEvent, cmd)) {
|
||||
remoteConfig(&TempEvent, cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
// HUM_STAT can be one of:
|
||||
// 0=Normal
|
||||
// 1=Comfortable
|
||||
// 2=Dry
|
||||
// 3=Wet
|
||||
String humStatDomoticz(struct EventStruct *event, byte rel_index){
|
||||
const int hum = UserVar[event->BaseVarIndex + rel_index];
|
||||
if (hum < 30) { return formatUserVarDomoticz(2); }
|
||||
if (hum < 40) { return formatUserVarDomoticz(0); }
|
||||
if (hum < 59) { return formatUserVarDomoticz(1); }
|
||||
return formatUserVarDomoticz(3);
|
||||
}
|
||||
|
||||
int mapRSSItoDomoticz() {
|
||||
long rssi = WiFi.RSSI();
|
||||
if (-50 < rssi) { return 10; }
|
||||
if (rssi <= -98) { return 0; }
|
||||
rssi = rssi + 97; // Range 0..47 => 1..9
|
||||
return (rssi / 5) + 1;
|
||||
}
|
||||
|
||||
int mapVccToDomoticz() {
|
||||
#if FEATURE_ADC_VCC
|
||||
// Voltage range from 2.6V .. 3.6V => 0..100%
|
||||
if (vcc < 2.6) return 0;
|
||||
return (vcc - 2.6) * 100;
|
||||
#else
|
||||
return 255;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Format including trailing semi colon
|
||||
String formatUserVarDomoticz(struct EventStruct *event, byte rel_index) {
|
||||
String text = formatUserVar(event, rel_index);
|
||||
text += F(";");
|
||||
return text;
|
||||
}
|
||||
|
||||
String formatUserVarDomoticz(int value) {
|
||||
String text;
|
||||
text += value;
|
||||
text.trim();
|
||||
text += F(";");
|
||||
return text;
|
||||
}
|
||||
|
||||
String formatDomoticzSensorType(struct EventStruct *event) {
|
||||
String values;
|
||||
switch (event->sensorType)
|
||||
{
|
||||
case SENSOR_TYPE_SINGLE: // single value sensor, used for Dallas, BH1750, etc
|
||||
values = formatUserVarDomoticz(event, 0);
|
||||
break;
|
||||
case SENSOR_TYPE_LONG: // single LONG value, stored in two floats (rfid tags)
|
||||
values = (unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16);
|
||||
break;
|
||||
case SENSOR_TYPE_DUAL: // any sensor that uses two simple values
|
||||
values = formatUserVarDomoticz(event, 0);
|
||||
values += formatUserVarDomoticz(event, 1);
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM:
|
||||
// temp + hum + hum_stat, used for DHT11
|
||||
// http://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s#Temperature.2Fhumidity
|
||||
values = formatUserVarDomoticz(event, 0); // TEMP = Temperature
|
||||
values += formatUserVarDomoticz(event, 1); // HUM = Humidity
|
||||
values += humStatDomoticz(event, 1); // HUM_STAT = Humidity status
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_HUM_BARO:
|
||||
// temp + hum + hum_stat + bar + bar_fore, used for BME280
|
||||
// http://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s#Temperature.2Fhumidity.2Fbarometer
|
||||
values = formatUserVarDomoticz(event, 0); // TEMP = Temperature
|
||||
values += formatUserVarDomoticz(event, 1); // HUM = Humidity
|
||||
values += humStatDomoticz(event, 1); // HUM_STAT = Humidity status
|
||||
values += formatUserVarDomoticz(event, 2); // BAR = Barometric pressure
|
||||
values += formatUserVarDomoticz(0); // BAR_FOR = Barometer forecast
|
||||
break;
|
||||
case SENSOR_TYPE_TEMP_BARO:
|
||||
// temp + hum + hum_stat + bar + bar_fore, used for BMP085
|
||||
// http://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s#Temperature.2Fbarometer
|
||||
values = formatUserVarDomoticz(event, 0); // TEMP = Temperature
|
||||
values += formatUserVarDomoticz(event, 1); // BAR = Barometric pressure
|
||||
values += formatUserVarDomoticz(0); // BAR_FOR = Barometer forecast
|
||||
values += formatUserVarDomoticz(0); // ALTITUDE= Not used at the moment, can be 0
|
||||
break;
|
||||
case SENSOR_TYPE_TRIPLE:
|
||||
values = formatUserVarDomoticz(event, 0);
|
||||
values += formatUserVarDomoticz(event, 1);
|
||||
values += formatUserVarDomoticz(event, 2);
|
||||
break;
|
||||
case SENSOR_TYPE_QUAD:
|
||||
values = formatUserVarDomoticz(event, 0);
|
||||
values += formatUserVarDomoticz(event, 1);
|
||||
values += formatUserVarDomoticz(event, 2);
|
||||
values += formatUserVarDomoticz(event, 3);
|
||||
break;
|
||||
case SENSOR_TYPE_WIND:
|
||||
// WindDir in degrees; WindDir as text; Wind speed average ; Wind speed gust; 0
|
||||
// http://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s#Wind
|
||||
values = formatUserVarDomoticz(event, 0); // WB = Wind bearing (0-359)
|
||||
values += getBearing(UserVar[event->BaseVarIndex]); // WD = Wind direction (S, SW, NNW, etc.)
|
||||
values += ";"; // Needed after getBearing
|
||||
// Domoticz expects the wind speed in (m/s * 10)
|
||||
values += toString((UserVar[event->BaseVarIndex + 1] * 10),ExtraTaskSettings.TaskDeviceValueDecimals[1]);
|
||||
values += ";"; // WS = 10 * Wind speed [m/s]
|
||||
values += toString((UserVar[event->BaseVarIndex + 2] * 10),ExtraTaskSettings.TaskDeviceValueDecimals[2]);
|
||||
values += ";"; // WG = 10 * Gust [m/s]
|
||||
values += formatUserVarDomoticz(0); // Temperature
|
||||
values += formatUserVarDomoticz(0); // Temperature Windchill
|
||||
break;
|
||||
case SENSOR_TYPE_SWITCH:
|
||||
case SENSOR_TYPE_DIMMER:
|
||||
// Too specific for HTTP/MQTT
|
||||
break;
|
||||
default:
|
||||
{
|
||||
String log = F("Domoticz Controller: Not yet implemented sensor type: ");
|
||||
log += event->sensorType;
|
||||
log += F(" idx: ");
|
||||
log += event->idx;
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Now strip trailing semi colon.
|
||||
int index_last_char = values.length() -1;
|
||||
if (index_last_char > 0 && values.charAt(index_last_char) == ';') {
|
||||
values.setCharAt(index_last_char, ' ');
|
||||
}
|
||||
values.trim();
|
||||
{
|
||||
String log = F(" Domoticz: Sensortype: ");
|
||||
log += event->sensorType;
|
||||
log += F(" idx: ");
|
||||
log += event->idx;
|
||||
log += F(" values: ");
|
||||
log += values;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
// DS Temp:[Dallas1#Temperature#R]
|
||||
// Lux:[Lux#Lux#R]
|
||||
// Baro:[Baro#Pressure#R]
|
||||
// Pump:[Pump#on#O] -> ON/OFF
|
||||
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
|
||||
|
||||
+34
-21
@@ -112,9 +112,17 @@ boolean Plugin_019(byte function, struct EventStruct *event, String& string)
|
||||
if (command == F("pcfgpio"))
|
||||
{
|
||||
success = true;
|
||||
Plugin_019_Write(event->Par1, event->Par2);
|
||||
setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
log = String(F("PCF : GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(event->Par2);
|
||||
if (event->Par2 == 2) { //INPUT
|
||||
// PCF8574 specific: only can read 0/low state, so we must send 1
|
||||
setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_INPUT, 1);
|
||||
Plugin_019_Write(event->Par1,1);
|
||||
log = String(F("PCF : GPIO ")) + String(event->Par1) + String(F(" Set to 1"));
|
||||
}
|
||||
else { // OUTPUT
|
||||
setPinState(PLUGIN_ID_019, event->Par1, PIN_MODE_OUTPUT, event->Par2);
|
||||
Plugin_019_Write(event->Par1, event->Par2);
|
||||
log = String(F("PCF : GPIO ")) + String(event->Par1) + String(F(" Set to ")) + String(event->Par2);
|
||||
}
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
SendStatus(event->Source, getPinStateJSON(SEARCH_PIN_STATE, PLUGIN_ID_019, event->Par1, log, 0));
|
||||
}
|
||||
@@ -200,28 +208,33 @@ int Plugin_019_Read(byte Par1)
|
||||
//********************************************************************************
|
||||
boolean Plugin_019_Write(byte Par1, byte Par2)
|
||||
{
|
||||
boolean success = false;
|
||||
byte portvalue = 0;
|
||||
byte unit = (Par1 - 1) / 8;
|
||||
byte port = Par1 - (unit * 8);
|
||||
uint8_t address = 0x20 + unit;
|
||||
if (unit > 7) address += 0x10;
|
||||
|
||||
// get the current pin status
|
||||
Wire.requestFrom(address, (uint8_t)0x1);
|
||||
if (Wire.available())
|
||||
{
|
||||
portvalue = Wire.read();
|
||||
if (Par2 == 1)
|
||||
portvalue |= (1 << (port - 1));
|
||||
else
|
||||
portvalue &= ~(1 << (port - 1));
|
||||
|
||||
// write back new data
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(portvalue);
|
||||
Wire.endTransmission();
|
||||
success = true;
|
||||
//generate bitmask
|
||||
int i = 0;
|
||||
byte portmask = 0;
|
||||
byte mode = 0;
|
||||
uint16_t value = 0;
|
||||
unit *= 8; // calculate first pin
|
||||
unit += 1;
|
||||
for(i =0;i<8;i++){
|
||||
mode =0;
|
||||
if(!getPinState(PLUGIN_ID_019, unit, &mode, &value) || mode == PIN_MODE_INPUT || (mode == PIN_MODE_OUTPUT && value == 1))
|
||||
portmask |= (1 << i);
|
||||
unit++;
|
||||
}
|
||||
return(success);
|
||||
|
||||
if (Par2 == 1)
|
||||
portmask |= (1 << (port - 1));
|
||||
else
|
||||
portmask &= ~(1 << (port - 1));
|
||||
|
||||
Wire.beginTransmission(address);
|
||||
Wire.write(portmask);
|
||||
Wire.endTransmission();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,12 @@ boolean Plugin_020(byte function, struct EventStruct *event, String& string)
|
||||
serialconfig += (ExtraTaskSettings.TaskDevicePluginConfigLong[2] - 5) << 2;
|
||||
if (ExtraTaskSettings.TaskDevicePluginConfigLong[4] == 2)
|
||||
serialconfig += 0x20;
|
||||
Serial.begin(ExtraTaskSettings.TaskDevicePluginConfigLong[1], (SerialConfig)serialconfig);
|
||||
#if defined(ESP8266)
|
||||
Serial.begin(ExtraTaskSettings.TaskDevicePluginConfigLong[1], (SerialConfig)serialconfig);
|
||||
#endif
|
||||
#if defined(ESP32)
|
||||
Serial.begin(ExtraTaskSettings.TaskDevicePluginConfigLong[1], serialconfig);
|
||||
#endif
|
||||
ser2netServer = new WiFiServer(ExtraTaskSettings.TaskDevicePluginConfigLong[0]);
|
||||
ser2netServer->begin();
|
||||
|
||||
|
||||
+2
-1
@@ -213,7 +213,8 @@ boolean Plugin_023(byte function, struct EventStruct *event, String& string)
|
||||
success = true;
|
||||
argIndex = string.lastIndexOf(',');
|
||||
tmpString = string.substring(argIndex + 1);
|
||||
Plugin_023_sendStrXY(tmpString.c_str(), event->Par1 - 1, event->Par2 - 1);
|
||||
String newString = P023_parseTemplate(tmpString, 16);
|
||||
Plugin_023_sendStrXY(newString.c_str(), event->Par1 - 1, event->Par2 - 1);
|
||||
}
|
||||
if (tmpString.equalsIgnoreCase(F("OLEDCMD")))
|
||||
{
|
||||
|
||||
@@ -225,7 +225,7 @@ boolean Plugin_036(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
// Display the device name, logo, time and wifi
|
||||
display_header();
|
||||
// display_logo();
|
||||
display_logo();
|
||||
display->display();
|
||||
|
||||
// Set up the display timer
|
||||
@@ -503,8 +503,14 @@ void display_title(String& title) {
|
||||
}
|
||||
|
||||
void display_logo() {
|
||||
// draw an xbm image.
|
||||
display->drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(ArialMT_Plain_16);
|
||||
display->setColor(BLACK);
|
||||
display->fillRect(0, 14, 128, 64);
|
||||
display->setColor(WHITE);
|
||||
display->drawString(65, 15, F("ESP"));
|
||||
display->drawString(65, 34, F("Easy"));
|
||||
display->drawXbm(24, 14, espeasy_logo_width, espeasy_logo_height, espeasy_logo_bits);
|
||||
}
|
||||
|
||||
// Draw the frame position
|
||||
|
||||
@@ -166,13 +166,11 @@ boolean Plugin_037(byte function, struct EventStruct *event, String& string)
|
||||
// This is a private option only used by the MQTT 037 callback function
|
||||
|
||||
// Get the payload and check it out
|
||||
|
||||
String Payload = event->String2;
|
||||
float floatPayload = string2float(Payload);
|
||||
|
||||
LoadTaskSettings(event->TaskIndex);
|
||||
|
||||
if (floatPayload == -999) {
|
||||
String Payload = event->String2;
|
||||
float floatPayload;
|
||||
if (!string2float(Payload, floatPayload)) {
|
||||
String log = F("IMPT : Bad Import MQTT Command ");
|
||||
log += event->String1;
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
@@ -450,41 +448,3 @@ boolean MQTTCheckSubscription_037(String Topic, String Subscription) {
|
||||
}
|
||||
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.charAt(0) == '-') {
|
||||
tmp[0] = '-';
|
||||
start = 1; //allow a minus in front of string
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,12 @@ boolean Plugin_044(byte function, struct EventStruct *event, String& string)
|
||||
serialconfig += (ExtraTaskSettings.TaskDevicePluginConfigLong[2] - 5) << 2;
|
||||
if (ExtraTaskSettings.TaskDevicePluginConfigLong[4] == 2)
|
||||
serialconfig += 0x20;
|
||||
Serial.begin(ExtraTaskSettings.TaskDevicePluginConfigLong[1], (SerialConfig)serialconfig);
|
||||
#if defined(ESP8266)
|
||||
Serial.begin(ExtraTaskSettings.TaskDevicePluginConfigLong[1], (SerialConfig)serialconfig);
|
||||
#endif
|
||||
#if defined(ESP32)
|
||||
Serial.begin(ExtraTaskSettings.TaskDevicePluginConfigLong[1], serialconfig);
|
||||
#endif
|
||||
if (P1GatewayServer) P1GatewayServer->close();
|
||||
P1GatewayServer = new WiFiServer(ExtraTaskSettings.TaskDevicePluginConfigLong[0]);
|
||||
P1GatewayServer->begin();
|
||||
|
||||
+77
-39
@@ -31,17 +31,38 @@ boolean Plugin_049_ABC_MustApply = false;
|
||||
#include <ESPeasySoftwareSerial.h>
|
||||
ESPeasySoftwareSerial *Plugin_049_SoftSerial;
|
||||
|
||||
// 9-bytes CMD PPM read command
|
||||
byte mhzCmdReadPPM[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
|
||||
byte mhzResp[9]; // 9 bytes bytes response
|
||||
byte mhzCmdCalibrateZero[9] = {0xFF,0x01,0x87,0x00,0x00,0x00,0x00,0x00,0x78};
|
||||
byte mhzCmdABCEnable[9] = {0xFF,0x01,0x79,0xA0,0x00,0x00,0x00,0x00,0xE6};
|
||||
byte mhzCmdABCDisable[9] = {0xFF,0x01,0x79,0x00,0x00,0x00,0x00,0x00,0x86};
|
||||
byte mhzCmdReset[9] = {0xFF,0x01,0x8d,0x00,0x00,0x00,0x00,0x00,0x72};
|
||||
byte mhzCmdMeasurementRange1000[9] = {0xFF,0x01,0x99,0x00,0x00,0x00,0x03,0xE8,0x7B};
|
||||
byte mhzCmdMeasurementRange2000[9] = {0xFF,0x01,0x99,0x00,0x00,0x00,0x07,0xD0,0x8F};
|
||||
byte mhzCmdMeasurementRange3000[9] = {0xFF,0x01,0x99,0x00,0x00,0x00,0x0B,0xB8,0xA3};
|
||||
byte mhzCmdMeasurementRange5000[9] = {0xFF,0x01,0x99,0x00,0x00,0x00,0x13,0x88,0xCB};
|
||||
enum mhzCommands : byte { mhzCmdReadPPM,
|
||||
mhzCmdCalibrateZero,
|
||||
mhzCmdABCEnable,
|
||||
mhzCmdABCDisable,
|
||||
mhzCmdReset,
|
||||
mhzCmdMeasurementRange1000,
|
||||
mhzCmdMeasurementRange2000,
|
||||
mhzCmdMeasurementRange3000,
|
||||
mhzCmdMeasurementRange5000 };
|
||||
// 9 byte commands:
|
||||
// mhzCmdReadPPM[] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
|
||||
// mhzCmdCalibrateZero[] = {0xFF,0x01,0x87,0x00,0x00,0x00,0x00,0x00,0x78};
|
||||
// mhzCmdABCEnable[] = {0xFF,0x01,0x79,0xA0,0x00,0x00,0x00,0x00,0xE6};
|
||||
// mhzCmdABCDisable[] = {0xFF,0x01,0x79,0x00,0x00,0x00,0x00,0x00,0x86};
|
||||
// mhzCmdReset[] = {0xFF,0x01,0x8d,0x00,0x00,0x00,0x00,0x00,0x72};
|
||||
// mhzCmdMeasurementRange1000[] = {0xFF,0x01,0x99,0x03,0xE8,0x00,0x00,0x00,0x7B};
|
||||
// mhzCmdMeasurementRange2000[] = {0xFF,0x01,0x99,0x07,0xD0,0x00,0x00,0x00,0x8F};
|
||||
// mhzCmdMeasurementRange3000[] = {0xFF,0x01,0x99,0x0B,0xB8,0x00,0x00,0x00,0xA3};
|
||||
// mhzCmdMeasurementRange5000[] = {0xFF,0x01,0x99,0x13,0x88,0x00,0x00,0x00,0xCB};
|
||||
// Removing redundant data, just keeping offsets [2]..[4]:
|
||||
const PROGMEM byte mhzCmdData[][3] = {
|
||||
{0x86,0x00,0x00},
|
||||
{0x87,0x00,0x00},
|
||||
{0x79,0xA0,0x00},
|
||||
{0x79,0x00,0x00},
|
||||
{0x8d,0x00,0x00},
|
||||
{0x99,0x03,0xE8},
|
||||
{0x99,0x07,0xD0},
|
||||
{0x99,0x0B,0xB8},
|
||||
{0x99,0x13,0x88}};
|
||||
|
||||
byte mhzResp[9]; // 9 byte response buffer
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -179,7 +200,7 @@ boolean Plugin_049(byte function, struct EventStruct *event, String& string)
|
||||
// No guarantee the correct state is active on the sensor after reboot.
|
||||
Plugin_049_ABC_MustApply = true;
|
||||
}
|
||||
Plugin_049_SoftSerial = new ESPeasySoftwareSerial(Settings.TaskDevicePin1[event->TaskIndex], Settings.TaskDevicePin2[event->TaskIndex], false, 18);
|
||||
Plugin_049_SoftSerial = new ESPeasySoftwareSerial(Settings.TaskDevicePin1[event->TaskIndex], Settings.TaskDevicePin2[event->TaskIndex]);
|
||||
Plugin_049_SoftSerial->begin(9600);
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Init OK "));
|
||||
|
||||
@@ -198,56 +219,56 @@ boolean Plugin_049(byte function, struct EventStruct *event, String& string)
|
||||
|
||||
if (command == F("mhzcalibratezero"))
|
||||
{
|
||||
Plugin_049_SoftSerial->write(mhzCmdCalibrateZero, 9);
|
||||
_P049_send_mhzCmd(mhzCmdCalibrateZero);
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Calibrated zero point!"));
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (command == F("mhzreset"))
|
||||
{
|
||||
Plugin_049_SoftSerial->write(mhzCmdReset, 9);
|
||||
_P049_send_mhzCmd(mhzCmdReset);
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Sent sensor reset!"));
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (command == F("mhzabcenable"))
|
||||
{
|
||||
Plugin_049_SoftSerial->write(mhzCmdABCEnable, 9);
|
||||
_P049_send_mhzCmd(mhzCmdABCEnable);
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Sent sensor ABC Enable!"));
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (command == F("mhzabcdisable"))
|
||||
{
|
||||
Plugin_049_SoftSerial->write(mhzCmdABCDisable, 9);
|
||||
_P049_send_mhzCmd(mhzCmdABCDisable);
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Sent sensor ABC Disable!"));
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (command == F("mhzmeasurementrange1000"))
|
||||
{
|
||||
Plugin_049_SoftSerial->write(mhzCmdMeasurementRange1000, 9);
|
||||
_P049_send_mhzCmd(mhzCmdMeasurementRange1000);
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Sent measurement range 0-1000PPM!"));
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (command == F("mhzmeasurementrange2000"))
|
||||
{
|
||||
Plugin_049_SoftSerial->write(mhzCmdMeasurementRange2000, 9);
|
||||
_P049_send_mhzCmd(mhzCmdMeasurementRange2000);
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Sent measurement range 0-2000PPM!"));
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (command == F("mhzmeasurementrange3000"))
|
||||
{
|
||||
Plugin_049_SoftSerial->write(mhzCmdMeasurementRange3000, 9);
|
||||
_P049_send_mhzCmd(mhzCmdMeasurementRange3000);
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Sent measurement range 0-3000PPM!"));
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (command == F("mhzmeasurementrange5000"))
|
||||
{
|
||||
Plugin_049_SoftSerial->write(mhzCmdMeasurementRange5000, 9);
|
||||
_P049_send_mhzCmd(mhzCmdMeasurementRange5000);
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Sent measurement range 0-5000PPM!"));
|
||||
success = true;
|
||||
}
|
||||
@@ -261,7 +282,7 @@ boolean Plugin_049(byte function, struct EventStruct *event, String& string)
|
||||
if (Plugin_049_init)
|
||||
{
|
||||
//send read PPM command
|
||||
int nbBytesSent = Plugin_049_SoftSerial->write(mhzCmdReadPPM, 9);
|
||||
byte nbBytesSent = _P049_send_mhzCmd(mhzCmdReadPPM);
|
||||
if (nbBytesSent != 9) {
|
||||
String log = F("MHZ19: Error, nb bytes sent != 9 : ");
|
||||
log += nbBytesSent;
|
||||
@@ -285,19 +306,15 @@ boolean Plugin_049(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
|
||||
unsigned int ppm = 0;
|
||||
int i;
|
||||
signed int temp = 0;
|
||||
unsigned int s = 0;
|
||||
float u = 0;
|
||||
byte crc = 0;
|
||||
for (i = 1; i < 8; i++) crc+=mhzResp[i];
|
||||
crc = 255 - crc;
|
||||
crc++;
|
||||
byte checksum = _P049_calculateChecksum(mhzResp);
|
||||
|
||||
if ( !(mhzResp[8] == crc) ) {
|
||||
String log = F("MHZ19: Read error : CRC = ");
|
||||
log += String(crc); log += " / "; log += String(mhzResp[8]);
|
||||
log += " bytes read => ";for (i = 0; i < 9; i++) {log += mhzResp[i];log += "/" ;}
|
||||
if ( !(mhzResp[8] == checksum) ) {
|
||||
String log = F("MHZ19: Read error: checksum = ");
|
||||
log += String(checksum); log += " / "; log += String(mhzResp[8]);
|
||||
log += " bytes read => ";for (byte i = 0; i < 9; i++) {log += mhzResp[i];log += "/" ;}
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
|
||||
// Sometimes there is a misalignment in the serial read
|
||||
@@ -305,24 +322,24 @@ boolean Plugin_049(byte function, struct EventStruct *event, String& string)
|
||||
// This goes on forever.
|
||||
// There must be a better way to handle this, but here
|
||||
// we're trying to shift it so that 0xFF is the next byte
|
||||
byte crcshift;
|
||||
for (i = 1; i < 8; i++) {
|
||||
crcshift = Plugin_049_SoftSerial->peek();
|
||||
if (crcshift == 0xFF) {
|
||||
byte checksum_shift;
|
||||
for (byte i = 1; i < 8; i++) {
|
||||
checksum_shift = Plugin_049_SoftSerial->peek();
|
||||
if (checksum_shift == 0xFF) {
|
||||
String log = F("MHZ19: Shifted ");
|
||||
log += i;
|
||||
log += F(" bytes to attempt to fix buffer alignment");
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
break;
|
||||
} else {
|
||||
crcshift = Plugin_049_SoftSerial->read();
|
||||
checksum_shift = Plugin_049_SoftSerial->read();
|
||||
}
|
||||
}
|
||||
success = false;
|
||||
break;
|
||||
|
||||
// Process responses to 0x86
|
||||
} else if (mhzResp[0] == 0xFF && mhzResp[1] == 0x86 && mhzResp[8] == crc) {
|
||||
} else if (mhzResp[0] == 0xFF && mhzResp[1] == 0x86 && mhzResp[8] == checksum) {
|
||||
|
||||
//calculate CO2 PPM
|
||||
unsigned int mhzRespHigh = (unsigned int) mhzResp[2];
|
||||
@@ -368,10 +385,10 @@ boolean Plugin_049(byte function, struct EventStruct *event, String& string)
|
||||
if (Plugin_049_ABC_MustApply) {
|
||||
// Send ABC enable/disable command based on the desired state.
|
||||
if (Plugin_049_ABC_Disable) {
|
||||
Plugin_049_SoftSerial->write(mhzCmdABCDisable, 9);
|
||||
_P049_send_mhzCmd(mhzCmdABCDisable);
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Sent sensor ABC Disable!"));
|
||||
} else {
|
||||
Plugin_049_SoftSerial->write(mhzCmdABCEnable, 9);
|
||||
_P049_send_mhzCmd(mhzCmdABCEnable);
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Sent sensor ABC Enable!"));
|
||||
}
|
||||
Plugin_049_ABC_MustApply = false;
|
||||
@@ -396,7 +413,7 @@ boolean Plugin_049(byte function, struct EventStruct *event, String& string)
|
||||
break;
|
||||
|
||||
// Sensor responds with 0x99 whenever we send it a measurement range adjustment
|
||||
} else if (mhzResp[0] == 0xFF && mhzResp[1] == 0x99 && mhzResp[8] == crc) {
|
||||
} else if (mhzResp[0] == 0xFF && mhzResp[1] == 0x99 && mhzResp[8] == checksum) {
|
||||
|
||||
addLog(LOG_LEVEL_INFO, F("MHZ19: Received measurement range acknowledgment! "));
|
||||
addLog(LOG_LEVEL_INFO, F("Expecting sensor reset..."));
|
||||
@@ -423,3 +440,24 @@ boolean Plugin_049(byte function, struct EventStruct *event, String& string)
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
byte _P049_calculateChecksum(byte *array)
|
||||
{
|
||||
byte checksum = 0;
|
||||
for (byte i = 1; i < 8; i++)
|
||||
checksum+=array[i];
|
||||
checksum = 0xFF - checksum;
|
||||
return (checksum+1);
|
||||
}
|
||||
|
||||
size_t _P049_send_mhzCmd(byte CommandId)
|
||||
{
|
||||
// The receive buffer "mhzResp" is re-used to send a command here:
|
||||
mhzResp[0] = 0xFF; // Start byte, fixed
|
||||
mhzResp[1] = 0x01; // Sensor number, 0x01 by default
|
||||
memcpy_P(&mhzResp[2], mhzCmdData[CommandId], sizeof(mhzCmdData[0]));
|
||||
mhzResp[5] = mhzResp[6] = mhzResp[7] = 0x00;
|
||||
mhzResp[8] = _P049_calculateChecksum(mhzResp);
|
||||
|
||||
return Plugin_049_SoftSerial->write(mhzResp, sizeof(mhzResp));
|
||||
}
|
||||
|
||||
+13
-1
@@ -148,8 +148,10 @@ byte CPluginCall(byte Function, struct EventStruct *event)
|
||||
// Unconditional calls to all plugins
|
||||
case CPLUGIN_PROTOCOL_ADD:
|
||||
for (x = 0; x < CPLUGIN_MAX; x++)
|
||||
if (CPlugin_id[x] != 0)
|
||||
if (CPlugin_id[x] != 0){
|
||||
checkRAM(F("CPluginCallADD"),x);
|
||||
CPlugin_ptr[x](Function, event, dummyString);
|
||||
}
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
@@ -157,6 +159,16 @@ byte CPluginCall(byte Function, struct EventStruct *event)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if there is any controller enabled.
|
||||
bool anyControllerEnabled() {
|
||||
for (byte i=0; i < CONTROLLER_MAX; i++) {
|
||||
if (Settings.Protocol[i] != 0 && Settings.ControllerEnabled[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find first enabled controller index with this protocol
|
||||
byte findFirstEnabledControllerWithId(byte cpluginid) {
|
||||
for (byte i=0; i < CONTROLLER_MAX; i++) {
|
||||
|
||||
+5
-1
@@ -1104,8 +1104,10 @@ byte PluginCall(byte Function, struct EventStruct *event, String& str)
|
||||
TempEvent.BaseVarIndex = y * VARS_PER_TASK;
|
||||
//TempEvent.idx = Settings.TaskDeviceID[y]; todo check
|
||||
TempEvent.sensorType = Device[DeviceIndex].VType;
|
||||
if (Plugin_ptr[x](Function, event, str))
|
||||
if (Plugin_ptr[x](Function, event, str)){
|
||||
checkRAM(F("PluginCallUDP"),x);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1140,6 +1142,7 @@ byte PluginCall(byte Function, struct EventStruct *event, String& str)
|
||||
{
|
||||
if (Plugin_id[x] == Settings.TaskDeviceNumber[y])
|
||||
{
|
||||
checkRAM(F("PluginCall_s"),x);
|
||||
Plugin_ptr[x](Function, &TempEvent, str);
|
||||
}
|
||||
}
|
||||
@@ -1167,6 +1170,7 @@ byte PluginCall(byte Function, struct EventStruct *event, String& str)
|
||||
if ((Plugin_id[x] != 0 ) && (Plugin_id[x] == Settings.TaskDeviceNumber[event->TaskIndex]))
|
||||
{
|
||||
event->BaseVarIndex = event->TaskIndex * VARS_PER_TASK;
|
||||
checkRAM(F("PluginCall_init"),x);
|
||||
return Plugin_ptr[x](Function, event, str);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -5,7 +5,7 @@ nodes=[
|
||||
'port' : '/dev/serial/by-path/pci-0000:00:14.0-usb-0:3.1:1.0-port0',
|
||||
'ip' : '192.168.13.91',
|
||||
'flash_cmd' : 'esptool.py --port {port} -b 1500000 write_flash 0x0 .pioenvs/dev_ESP8266_4096/firmware.bin --flash_size=32m -p',
|
||||
'build_cmd' : 'platformio run --environment dev_ESP8266_4096'
|
||||
'build_cmd' : 'platformio run --environment dev_ESP8266_4096 -s'
|
||||
},
|
||||
|
||||
# node 1
|
||||
@@ -14,16 +14,16 @@ nodes=[
|
||||
'port' : '/dev/serial/by-path/pci-0000:00:14.0-usb-0:3.2:1.0-port0',
|
||||
'ip' : '192.168.13.92',
|
||||
'flash_cmd' : 'esptool.py --port {port} -b 1500000 write_flash 0x0 .pioenvs/dev_ESP8266_4096/firmware.bin --flash_size=32m -p',
|
||||
'build_cmd' : 'platformio run --environment dev_ESP8266_4096'
|
||||
'build_cmd' : 'platformio run --environment dev_ESP8266_4096 -s'
|
||||
},
|
||||
|
||||
# node 2
|
||||
{
|
||||
'type' : 'ESP-01S 1Mb PUYA flash',
|
||||
'port' : '/dev/serial/by-path/pci-0000:00:14.0-usb-0:3.3:1.0-port0',
|
||||
'ip' : '192.168.13.92',
|
||||
'flash_cmd' : 'esptool.py --port {port} -b 1500000 write_flash 0x0 .pioenvs/dev_ESP8266_1024/firmware.bin --flash_size=detect -p',
|
||||
'build_cmd' : 'platformio run --environment dev_ESP8266_1024'
|
||||
'ip' : '192.168.13.93',
|
||||
'flash_cmd' : 'esptool.py --port {port} -b 115200 write_flash 0x0 .pioenvs/dev_ESP8266PUYA_1024/firmware.bin --flash_size=1MB -p',
|
||||
'build_cmd' : 'platformio run --environment dev_ESP8266PUYA_1024 -s'
|
||||
},
|
||||
|
||||
# {
|
||||
@@ -39,10 +39,10 @@ nodes=[
|
||||
|
||||
|
||||
#an mqtt broker that both ESPEasy and the test suite are connecting to
|
||||
mqtt_broker="192.168.13.236"
|
||||
mqtt_broker="192.168.13.159"
|
||||
mqtt_port=1883
|
||||
|
||||
#ip of the server running this script
|
||||
test_server="192.168.13.236"
|
||||
test_server="192.168.13.159"
|
||||
http_port=8080
|
||||
linebased_port=8181
|
||||
|
||||
+17
-12
@@ -29,6 +29,7 @@ class ControllerEmu:
|
||||
self.start_mqtt()
|
||||
self.start_http()
|
||||
self.start_linebased()
|
||||
self.log_enabled=True
|
||||
|
||||
|
||||
def start_mqtt(self):
|
||||
@@ -44,8 +45,9 @@ class ControllerEmu:
|
||||
|
||||
self.mqtt_messages=Queue()
|
||||
def mqtt_on_message(client, userdata, message):
|
||||
logging.getLogger("mqtt").debug("Received message '" + str(message.payload) + "' on topic '"
|
||||
+ message.topic + "' with QoS " + str(message.qos))
|
||||
if self.log_enabled:
|
||||
logging.getLogger("mqtt").debug("Received message '" + str(message.payload) + "' on topic '"
|
||||
+ message.topic + "' with QoS " + str(message.qos))
|
||||
self.mqtt_messages.put(message)
|
||||
|
||||
mqtt_client.on_message=mqtt_on_message
|
||||
@@ -66,12 +68,12 @@ class ControllerEmu:
|
||||
@bottle.post('<filename:path>')
|
||||
@bottle.get('<filename:path>')
|
||||
def urlhandler(filename):
|
||||
logging.getLogger("http").debug(bottle.request.method+" "+str(dict(bottle.request.params)))
|
||||
if self.log_enabled:
|
||||
logging.getLogger("http").debug(bottle.request.method+" "+str(dict(bottle.request.params)))
|
||||
self.http_requests.put(bottle.request.copy())
|
||||
|
||||
|
||||
|
||||
http_thread=threading.Thread(target=bottle.run, kwargs=dict(host='0.0.0.0', port=config.http_port, reloader=False))
|
||||
http_thread=threading.Thread(target=bottle.run, kwargs=dict(host='0.0.0.0', port=config.http_port, reloader=False, quiet=True))
|
||||
http_thread.daemon=True
|
||||
http_thread.start()
|
||||
|
||||
@@ -92,15 +94,18 @@ class ControllerEmu:
|
||||
|
||||
|
||||
def handle_connect(connection, client_address):
|
||||
logging.getLogger("linebased").debug("Connect from "+str(client_address))
|
||||
if self.log_enabled:
|
||||
logging.getLogger("linebased").debug("Connect from "+str(client_address))
|
||||
|
||||
fh = connection.makefile()
|
||||
for line in fh:
|
||||
line=line.rstrip()
|
||||
logging.getLogger("linebased").debug("Recv from "+str(client_address)+" :"+line)
|
||||
if self.log_enabled:
|
||||
logging.getLogger("linebased").debug("Recv from "+str(client_address)+" :"+line)
|
||||
self.linebased_lines.put( ( client_address, line ) )
|
||||
|
||||
logging.getLogger("linebased").debug("Disconnect from "+str(client_address))
|
||||
if self.log_enabled:
|
||||
logging.getLogger("linebased").debug("Disconnect from "+str(client_address))
|
||||
|
||||
def wait_accept():
|
||||
# Create a TCP/IP socket
|
||||
@@ -166,8 +171,8 @@ class ControllerEmu:
|
||||
return svalues
|
||||
elif sensor_type==SENSOR_TYPE_TEMP_HUM and len(svalues)==3:
|
||||
return [svalues[0], svalues[1]]
|
||||
elif sensor_type==SENSOR_TYPE_TEMP_BARO and len(svalues)==5:
|
||||
return [svalues[0], svalues[3]]
|
||||
elif sensor_type==SENSOR_TYPE_TEMP_BARO and len(svalues)==4:
|
||||
return [svalues[0], svalues[1]]
|
||||
elif sensor_type==SENSOR_TYPE_TRIPLE and len(svalues)==3:
|
||||
return svalues
|
||||
elif sensor_type==SENSOR_TYPE_TEMP_HUM_BARO and len(svalues)==5:
|
||||
@@ -221,8 +226,8 @@ class ControllerEmu:
|
||||
return svalues
|
||||
elif sensor_type==SENSOR_TYPE_TEMP_HUM and len(svalues)==3:
|
||||
return [svalues[0], svalues[1]]
|
||||
elif sensor_type==SENSOR_TYPE_TEMP_BARO and len(svalues)==5:
|
||||
return [svalues[0], svalues[3]]
|
||||
elif sensor_type==SENSOR_TYPE_TEMP_BARO and len(svalues)==4:
|
||||
return [svalues[0], svalues[1]]
|
||||
elif sensor_type==SENSOR_TYPE_TRIPLE and len(svalues)==3:
|
||||
return svalues
|
||||
elif sensor_type==SENSOR_TYPE_TEMP_HUM_BARO and len(svalues)==5:
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ Configure node 0, controller 1 as domoticz mqtt:
|
||||
"""
|
||||
return(docs)
|
||||
|
||||
parser = argparse.ArgumentParser(description='ESPEasy testing framework', epilog=get_epilog(), formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser = argparse.ArgumentParser(description='ESPEasy testing CLI', epilog=get_epilog(), formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
|
||||
# parser.add_argument('--url', default='http://localhost:8080/rpc', help='url of rpc server to connect to. default: %(default)s')
|
||||
# parser.add_argument('--user', default=None, help='user to login.')
|
||||
|
||||
@@ -8,5 +8,4 @@ import config
|
||||
|
||||
colorlog.basicConfig(level=logging.DEBUG)
|
||||
|
||||
|
||||
logging.getLogger("requests.packages.urllib3.connectionpool").setLevel(logging.ERROR)
|
||||
|
||||
+15
-2
@@ -13,7 +13,20 @@ import config
|
||||
import shelve
|
||||
import os
|
||||
from espcore import *
|
||||
import argparse
|
||||
import util
|
||||
|
||||
### parse arguments
|
||||
|
||||
parser = argparse.ArgumentParser(description='ESPEasy testing framework', formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument('--debug', action='store_true', help='enable http debugging output')
|
||||
parser.add_argument('--no-resume', action='store_true', help='dont resume testing from where we left off last time')
|
||||
parser.add_argument('--skip-power', action='store_true', help='skip tests that require powercycling.')
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
if args.debug:
|
||||
util.enable_http_debug()
|
||||
|
||||
|
||||
### create node objects and espeasy objects
|
||||
@@ -22,7 +35,7 @@ node=[]
|
||||
espeasy=[]
|
||||
|
||||
for n in config.nodes:
|
||||
node.append(Node(n, "node"+str(len(node))))
|
||||
node.append(Node(n, "node"+str(len(node)), skip_power=args.skip_power))
|
||||
espeasy.append(EspEasy(node[-1]))
|
||||
|
||||
|
||||
@@ -49,7 +62,7 @@ with shelve.open("test.state") as shelve_db:
|
||||
def step(title=""):
|
||||
def step_dec(test):
|
||||
"""add test step. test can resume from every test-step"""
|
||||
if state['module'] and ( state['module'] != test.__module__ or state['name'] != test.__name__ or state['title'] != title):
|
||||
if not args.no_resume and state['module'] and ( state['module'] != test.__module__ or state['name'] != test.__name__ or state['title'] != title):
|
||||
log.debug("Skipping step "+title+": "+test.__module__ + "." + test.__name__ )
|
||||
else:
|
||||
state['module']=None
|
||||
|
||||
+21
-1
@@ -16,13 +16,14 @@ from espcore import *
|
||||
class Node():
|
||||
|
||||
|
||||
def __init__(self, config, id):
|
||||
def __init__(self, config, id, skip_power=False):
|
||||
self.log=logging.getLogger(id)
|
||||
self.log.debug("{type} has ip {ip}".format(id=id, **config))
|
||||
self._config=config
|
||||
self._id=id
|
||||
self._url="http://{ip}/".format(**self._config)
|
||||
self._serial_initialized=False
|
||||
self._skip_power=True
|
||||
|
||||
def serial_needed(self):
|
||||
"""call this at least once if you need serial stuff"""
|
||||
@@ -63,12 +64,21 @@ class Node():
|
||||
|
||||
def powercycle(self):
|
||||
"""powercycle the device"""
|
||||
if self._skip_power:
|
||||
self.log.warning("Skipping power cycle "+self._id)
|
||||
return False
|
||||
|
||||
|
||||
self.poweroff()
|
||||
self.poweron()
|
||||
return True
|
||||
|
||||
def poweroff(self):
|
||||
"""power off device"""
|
||||
|
||||
if self._skip_power:
|
||||
self.log.warning("Skipping power off "+self._id)
|
||||
return False
|
||||
|
||||
#cant yet be done automaticly unfortunatly
|
||||
self.log.info("Please power off node "+self._id)
|
||||
@@ -84,10 +94,15 @@ class Node():
|
||||
del self._serial
|
||||
|
||||
self.log.debug("Detected power off")
|
||||
return True
|
||||
|
||||
def poweron(self):
|
||||
"""power on device"""
|
||||
|
||||
if self._skip_power:
|
||||
self.log.warning("Skipping power on"+self._id)
|
||||
return False
|
||||
|
||||
self.log.info("Please power on node "+self._id)
|
||||
|
||||
done=False
|
||||
@@ -100,6 +115,7 @@ class Node():
|
||||
time.sleep(0.1)
|
||||
|
||||
self.log.debug("Detected power on")
|
||||
return True
|
||||
|
||||
def pingwifi(self, timeout=60):
|
||||
"""waits until espeasy reponds via wifi"""
|
||||
@@ -144,6 +160,7 @@ class Node():
|
||||
def build(self):
|
||||
"""compile binary"""
|
||||
|
||||
self.log.debug("Building...")
|
||||
subprocess.check_call(self._config['build_cmd'].format(**self._config), shell=True, cwd='..')
|
||||
|
||||
|
||||
@@ -152,6 +169,7 @@ class Node():
|
||||
|
||||
self.serial_needed()
|
||||
|
||||
self.log.debug("Flashing...")
|
||||
subprocess.check_call(self._config['flash_cmd'].format(**self._config), shell=True, cwd='..')
|
||||
|
||||
time.sleep(1)
|
||||
@@ -162,6 +180,7 @@ class Node():
|
||||
def serial(self):
|
||||
"""open serial terminal to esp"""
|
||||
self.serial_needed()
|
||||
self.log.debug("Opening serial terminal")
|
||||
subprocess.check_call("platformio serialports monitor --baud 115200 --port {port} --echo".format(**self._config), shell=True, cwd='..')
|
||||
# print("JA")
|
||||
# term=serial.tools.miniterm.Miniterm(self._serial)
|
||||
@@ -173,6 +192,7 @@ class Node():
|
||||
def erase(self):
|
||||
"""erase flash via serial"""
|
||||
self.serial_needed()
|
||||
self.log.debug("Erasing...")
|
||||
subprocess.check_call("esptool.py --port {port} -b 1500000 erase_flash".format(**self._config), shell=True, cwd='..')
|
||||
|
||||
|
||||
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from esptest import *
|
||||
|
||||
# hardware requirements:
|
||||
# - all nodes
|
||||
|
||||
# tests:
|
||||
# - builds, flashes and resets to factory defaults
|
||||
|
||||
# prevent confusing messages during build
|
||||
controller.log_enabled=False
|
||||
|
||||
# @step()
|
||||
# def clean():
|
||||
# subprocess.check_call("cd ..;platformio run --target clean -s", shell=True)
|
||||
|
||||
@step()
|
||||
def patch():
|
||||
subprocess.check_call("cd ../patches; ./check_puya_patch", shell=True)
|
||||
|
||||
|
||||
@step()
|
||||
def build():
|
||||
for n in node:
|
||||
n.build()
|
||||
|
||||
|
||||
@step()
|
||||
def erase():
|
||||
for n in node:
|
||||
n.erase()
|
||||
|
||||
@step()
|
||||
def flash():
|
||||
for n in node:
|
||||
n.flashserial()
|
||||
|
||||
@step()
|
||||
def wificonfig():
|
||||
for n in node:
|
||||
n.wificonfig()
|
||||
|
||||
controller.log_enabled=True
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
completed()
|
||||
+3
-1
@@ -65,7 +65,9 @@ def test():
|
||||
@step()
|
||||
def powercycle():
|
||||
"""test result on poweron"""
|
||||
node[0].powercycle()
|
||||
if not node[0].powercycle():
|
||||
return
|
||||
|
||||
results=controller.recv_domoticz_mqtt(SENSOR_TYPE_SINGLE,2001)
|
||||
test_in_range(results[0], -5,40)
|
||||
results=controller.recv_domoticz_mqtt(SENSOR_TYPE_SINGLE,2002)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
#runs all test modules
|
||||
|
||||
import glob
|
||||
import importlib
|
||||
import os
|
||||
|
||||
Reference in New Issue
Block a user