mirror of https://github.com/arendst/Tasmota.git
Merge branch 'development' into release
This commit is contained in:
commit
5c9e090ce2
|
@ -119,6 +119,9 @@
|
|||
| USE_MP3_PLAYER | - | - | - | - | x | - | - |
|
||||
| USE_AZ7798 | - | - | - | - | - | - | - |
|
||||
| USE_PN532_HSU | - | - | - | - | x | - | - |
|
||||
| USE_RDM6300 | - | - | - | - | x | - | - |
|
||||
| USE_IBEACON | - | - | - | - | x | - | - |
|
||||
| USE_GPS | - | - | - | - | - | - | - |
|
||||
| USE_ZIGBEE | - | - | - | - | - | - | - |
|
||||
| | | | | | | | |
|
||||
| USE_IR_REMOTE | - | - | x | x | x | x | x |
|
||||
|
|
|
@ -54,5 +54,6 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
|||
- Change Smoother ``Fade`` using 100Hz instead of 20Hz animation (#7179)
|
||||
- Change number of rule ``Var``s and ``Mem``s from 5 to 16 (#4933)
|
||||
- Add support for max 150 characters in most command parameter strings (#3686, #4754)
|
||||
- Add support for GPS as NTP server by Christian Baars and Adrian Scillato
|
||||
- Add Zigbee coalesce sensor attributes into a single message
|
||||
- Add Deepsleep start delay based on Teleperiod if ``Teleperiod`` differs from 10 or 300
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* File: NTPPacket.cpp
|
||||
* Description:
|
||||
* NTP packet representation.
|
||||
* Author: Mooneer Salem <mooneer@gmail.com>
|
||||
* License: New BSD License
|
||||
*/
|
||||
|
||||
#include "NTPPacket.h"
|
||||
|
||||
void NtpPacket::swapEndian()
|
||||
{
|
||||
reverseBytes_(&rootDelay);
|
||||
reverseBytes_(&rootDispersion);
|
||||
reverseBytes_(&referenceTimestampSeconds);
|
||||
reverseBytes_(&referenceTimestampFraction);
|
||||
reverseBytes_(&originTimestampSeconds);
|
||||
reverseBytes_(&originTimestampFraction);
|
||||
reverseBytes_(&receiveTimestampSeconds);
|
||||
reverseBytes_(&receiveTimestampFraction);
|
||||
reverseBytes_(&transmitTimestampSeconds);
|
||||
reverseBytes_(&transmitTimestampFraction);
|
||||
}
|
||||
|
||||
void NtpPacket::reverseBytes_(uint32_t *number)
|
||||
{
|
||||
char buf[4];
|
||||
char *numberAsChar = (char*)number;
|
||||
|
||||
buf[0] = numberAsChar[3];
|
||||
buf[1] = numberAsChar[2];
|
||||
buf[2] = numberAsChar[1];
|
||||
buf[3] = numberAsChar[0];
|
||||
|
||||
numberAsChar[0] = buf[0];
|
||||
numberAsChar[1] = buf[1];
|
||||
numberAsChar[2] = buf[2];
|
||||
numberAsChar[3] = buf[3];
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* File: NTPPacket.h
|
||||
* Description:
|
||||
* NTP packet representation.
|
||||
* Author: Mooneer Salem <mooneer@gmail.com>
|
||||
* License: New BSD License
|
||||
*/
|
||||
|
||||
#ifndef NTP_PACKET_H
|
||||
#define NTP_PACKET_H
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
|
||||
/*
|
||||
* Contains the data in a typical NTP packet.
|
||||
*/
|
||||
struct NtpPacket
|
||||
{
|
||||
static const int PACKET_SIZE = 48;
|
||||
|
||||
unsigned char leapVersionMode;
|
||||
|
||||
unsigned int leapIndicator() const { return leapVersionMode >> 6; }
|
||||
void leapIndicator(unsigned int newValue) { leapVersionMode = (0x3F & leapVersionMode) | ((newValue & 0x03) << 6); }
|
||||
|
||||
unsigned int versionNumber() const { return (leapVersionMode >> 3) & 0x07; }
|
||||
void versionNumber(unsigned int newValue) { leapVersionMode = (0xC7 & leapVersionMode) | ((newValue & 0x07) << 3); }
|
||||
|
||||
unsigned int mode() const { return (leapVersionMode & 0x07); }
|
||||
void mode(unsigned int newValue) { leapVersionMode = (leapVersionMode & 0xF8) | (newValue & 0x07); }
|
||||
|
||||
char stratum;
|
||||
char poll;
|
||||
char precision;
|
||||
uint32_t rootDelay;
|
||||
uint32_t rootDispersion;
|
||||
char referenceId[4];
|
||||
uint32_t referenceTimestampSeconds;
|
||||
uint32_t referenceTimestampFraction;
|
||||
uint32_t originTimestampSeconds;
|
||||
uint32_t originTimestampFraction;
|
||||
uint32_t receiveTimestampSeconds;
|
||||
uint32_t receiveTimestampFraction;
|
||||
uint32_t transmitTimestampSeconds;
|
||||
uint32_t transmitTimestampFraction;
|
||||
|
||||
/*
|
||||
* Rearranges bytes in 32 bit values from big-endian (NTP protocol)
|
||||
* to little-endian (Arduino/PC), or vice versa. Must be called before
|
||||
* modifying the structure or sending the packet.
|
||||
*/
|
||||
void swapEndian();
|
||||
|
||||
/*
|
||||
* Returns packet as a char array for transmission via network.
|
||||
* WARNING: modifying the return value is unsafe.
|
||||
*/
|
||||
const char *packet() { return (const char*)this; }
|
||||
|
||||
/*
|
||||
* Copies packet buffer to packet object.
|
||||
*/
|
||||
void populatePacket(const char *buffer)
|
||||
{
|
||||
memcpy(this, buffer, PACKET_SIZE);
|
||||
}
|
||||
private:
|
||||
/*
|
||||
* Reverses bytes in a number.
|
||||
*/
|
||||
void reverseBytes_(uint32_t *number);
|
||||
};
|
||||
|
||||
#endif // NTP_PACKET_H
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* File: NTPServer.cpp
|
||||
* Description:
|
||||
* NTP server implementation.
|
||||
* Author: Mooneer Salem <mooneer@gmail.com>
|
||||
* License: New BSD License
|
||||
*/
|
||||
|
||||
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
#include "NTPPacket.h"
|
||||
#include "NTPServer.h"
|
||||
|
||||
#define NTP_PORT 123
|
||||
#define NTP_TIMESTAMP_DIFF (2208988800) // 1900 to 1970 in seconds
|
||||
|
||||
bool NtpServer::beginListening()
|
||||
{
|
||||
if (timeServerPort_.begin(NTP_PORT)){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NtpServer::processOneRequest(uint32_t utc, uint32_t millisecs)
|
||||
{
|
||||
// We need the time we've received the packet in our response.
|
||||
uint32_t recvSecs = utc + NTP_TIMESTAMP_DIFF;
|
||||
double recvFractDouble = (double)millisecs/0.00023283064365386963; // millisec/((10^6)/(2^32))
|
||||
uint32_t recvFract = (double)recvFractDouble; //TODO: really handle this!!!
|
||||
bool processed = false;
|
||||
|
||||
int packetDataSize = timeServerPort_.parsePacket();
|
||||
if (packetDataSize && packetDataSize >= NtpPacket::PACKET_SIZE)
|
||||
{
|
||||
// Received what is probably an NTP packet. Read it in and verify
|
||||
// that it's legit.
|
||||
NtpPacket packet;
|
||||
timeServerPort_.read((char*)&packet, NtpPacket::PACKET_SIZE);
|
||||
// TODO: verify packet.
|
||||
|
||||
// Populate response.
|
||||
packet.swapEndian();
|
||||
packet.leapIndicator(0);
|
||||
packet.versionNumber(4);
|
||||
packet.mode(4);
|
||||
packet.stratum = 2; // I guess stratum 1 is too optimistic
|
||||
packet.poll = 10; // 6-10 per RFC 5905.
|
||||
packet.precision = -21; // ~0.5 microsecond precision.
|
||||
packet.rootDelay = 0; //60 * (0xFFFF / 1000); // ~60 milliseconds, TBD
|
||||
packet.rootDispersion = 0; //10 * (0xFFFF / 1000); // ~10 millisecond dispersion, TBD
|
||||
packet.referenceId[0] = 'G';
|
||||
packet.referenceId[1] = 'P';
|
||||
packet.referenceId[2] = 'S';
|
||||
packet.referenceId[3] = 0;
|
||||
packet.referenceTimestampSeconds = utc;
|
||||
packet.referenceTimestampFraction = recvFract;
|
||||
packet.originTimestampSeconds = packet.transmitTimestampSeconds;
|
||||
packet.originTimestampFraction = packet.transmitTimestampFraction;
|
||||
packet.receiveTimestampSeconds = recvSecs;
|
||||
packet.receiveTimestampFraction = recvFract;
|
||||
|
||||
// ...and the transmit time.
|
||||
// timeSource_.now(&packet.transmitTimestampSeconds, &packet.transmitTimestampFraction);
|
||||
|
||||
// Now transmit the response to the client.
|
||||
packet.swapEndian();
|
||||
timeServerPort_.beginPacket(timeServerPort_.remoteIP(), timeServerPort_.remotePort());
|
||||
for (int count = 0; count < NtpPacket::PACKET_SIZE; count++)
|
||||
{
|
||||
timeServerPort_.write(packet.packet()[count]);
|
||||
}
|
||||
timeServerPort_.endPacket();
|
||||
processed = true;
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* File: NTPServer.h
|
||||
* Description:
|
||||
* NTP server implementation.
|
||||
* Author: Mooneer Salem <mooneer@gmail.com>
|
||||
* License: New BSD License
|
||||
*/
|
||||
|
||||
#ifndef NTP_SERVER_H
|
||||
#define NTP_SERVER_H
|
||||
|
||||
class NtpServer
|
||||
{
|
||||
public:
|
||||
NtpServer(WiFiUDP Port)
|
||||
{
|
||||
timeServerPort_=Port;
|
||||
}
|
||||
|
||||
/*
|
||||
* Begins listening for NTP requests.
|
||||
*/
|
||||
bool beginListening(void);
|
||||
|
||||
|
||||
/*
|
||||
* Processes a single NTP request.
|
||||
*/
|
||||
bool processOneRequest(uint32_t utc, uint32_t millisecs);
|
||||
|
||||
private:
|
||||
WiFiUDP timeServerPort_;
|
||||
};
|
||||
|
||||
#endif // NTP_SERVER_H
|
|
@ -6,12 +6,17 @@
|
|||
|
||||
- Release
|
||||
|
||||
### 8.0.0.2 20191223
|
||||
|
||||
- Changed Settings variable namings
|
||||
|
||||
### 8.0.0.1 20191221
|
||||
|
||||
- Change Settings text handling allowing variable length text within a total text pool of 699 characters
|
||||
- Change Smoother ``Fade`` using 100Hz instead of 20Hz animation (#7179)
|
||||
- Change number of rule ``Var``s and ``Mem``s from 5 to 16 (#4933)
|
||||
- Add support for max 150 characters in most command parameter strings (#3686, #4754)
|
||||
- Add support for GPS as NTP server by Christian Baars and Adrian Scillato
|
||||
- Add Zigbee coalesce sensor attributes into a single message
|
||||
- Add Deepsleep start delay based on Teleperiod if ``Teleperiod`` differs from 10 or 300
|
||||
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -627,9 +627,11 @@
|
|||
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
|
||||
#define D_SENSOR_DEEPSLEEP "DeepSleep"
|
||||
#define D_SENSOR_EXS_ENABLE "EXS Enable"
|
||||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "А"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "А"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "安"
|
||||
|
|
|
@ -630,6 +630,8 @@
|
|||
#define D_SENSOR_SLAVE_TX "Slave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Slave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Slave RST"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "安"
|
||||
|
|
|
@ -471,6 +471,8 @@
|
|||
// #define USE_PN532_DATA_RAW // Allow DATA block to be used by non-alpha-numberic data (+ 80 bytes code, 48 bytes ram)
|
||||
//#define USE_RDM6300 // Add support for RDM6300 125kHz RFID Reader (+0k8)
|
||||
//#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
|
||||
//#define USE_GPS // Add support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM)
|
||||
// #define USE_FLOG // Add support for GPS logging in OTA's Flash (Experimental) (+ 2.9kb flash, +8 bytes RAM)
|
||||
|
||||
// -- Power monitoring sensors --------------------
|
||||
#define USE_ENERGY_MARGIN_DETECTION // Add support for Energy Margin detection (+1k6 code)
|
||||
|
|
|
@ -493,7 +493,9 @@ void GetFeatures(void)
|
|||
feature5 |= 0x00100000;
|
||||
#endif
|
||||
// feature5 |= 0x00200000;
|
||||
// feature5 |= 0x00400000;
|
||||
#ifdef USE_GPS
|
||||
feature5 |= 0x00400000;
|
||||
#endif
|
||||
// feature5 |= 0x00800000;
|
||||
|
||||
// feature5 |= 0x01000000;
|
||||
|
|
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
support_flash_log.ino - log to flash support for Sonoff-Tasmota
|
||||
|
||||
Copyright (C) 2019 Theo Arends & Christian Baars
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
--------------------------------------------------------------------------------------------
|
||||
Version Date Action Description
|
||||
--------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
---
|
||||
1.0.0.0 20190923 started - further development by Christian Baars - https://github.com/Staars/Sonoff-Tasmota
|
||||
forked - from arendst/tasmota - https://github.com/arendst/Sonoff-Tasmota
|
||||
base - code base from arendst and - written from scratch
|
||||
|
||||
*/
|
||||
|
||||
/********************************************************************************************\
|
||||
| * Generic helper class to log arbitrary data to the OTA-partition
|
||||
| * Working principle: Add preferrable small chunks of data to the sector buffer, which will
|
||||
| * be written to FLASH when full automatically. The next sector will be
|
||||
| * erased and is the anchor point for downloading and state configuration
|
||||
| * after reboot.
|
||||
\*********************************************************************************************/
|
||||
|
||||
#ifdef USE_FLOG
|
||||
|
||||
class FLOG
|
||||
|
||||
#define MAGIC_WORD_FL 0x464c //F, L
|
||||
|
||||
{
|
||||
|
||||
struct header_t{
|
||||
uint16_t magic_word; // FL
|
||||
uint16_t padding; // leave something for the future
|
||||
uint32_t physical_start_sector:10; //first used sector of the current FLOG
|
||||
uint32_t number:10; // number of this sector, starting with 0 for the first sector
|
||||
uint32_t buf_pointer:12; //internal pointer to the next free position in the buffer = first empty byte when reading
|
||||
}; // should be 4-byte-aligned
|
||||
|
||||
private:
|
||||
void _readSector(uint8_t one_sector);
|
||||
void _eraseSector(uint8_t one_sector);
|
||||
void _writeSector(uint8_t one_sector);
|
||||
void _clearBuffer(void);
|
||||
void _searchSaves(void);
|
||||
void _findFirstErasedSector(void);
|
||||
void _showBuffer(void);
|
||||
void _initBuffer(void);
|
||||
void _saveBufferToSector(void);
|
||||
header_t _saved_header;
|
||||
|
||||
public:
|
||||
uint32_t size; // size of OTA-partition
|
||||
uint32_t start; // start position of OTA-partition in bytes
|
||||
uint32_t end; // end position of OTA-partition in bytes
|
||||
uint16_t num_sectors; // calculated number of sectors with a size of 4096 bytes
|
||||
|
||||
uint16_t first_erased_sector; // this will be our new start
|
||||
uint16_t current_sector; // always point to next sector, where data from the buffer will be written to
|
||||
|
||||
uint16_t bytes_left; // byte per buffer (of sector size 4096 bytes - 8 byte header size)
|
||||
uint16_t sectors_left; // number of saved sectors for download
|
||||
|
||||
uint8_t mode = 0; // 0 - write once on all sectors, then stop, 1 - write infinitely through the sectors
|
||||
bool found_saved_data = false; // possible saved data has been found
|
||||
bool ready = false; // the FLOG is initialized
|
||||
bool running_download = false; // a download operation is running
|
||||
bool recording = false; // ready for recording
|
||||
|
||||
union sector_t{
|
||||
uint32_t dword_buffer[FLASH_SECTOR_SIZE/4];
|
||||
uint8_t byte_buffer[FLASH_SECTOR_SIZE];
|
||||
header_t header; // should be 4-byte-aligned
|
||||
} sector; // the global buffer of 4096 bytes, used for reading and writing
|
||||
|
||||
void init(void);
|
||||
void addToBuffer(uint8_t src[], uint32_t size);
|
||||
void startRecording(bool append);
|
||||
void stopRecording(void);
|
||||
|
||||
typedef void (*CallbackNoArgs) (); // simple typedef for a callback
|
||||
typedef void (*CallbackWithArgs) (uint8_t *_record); // typedef for a callback with one argument
|
||||
|
||||
void startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter);
|
||||
};
|
||||
|
||||
extern "C" uint32_t _SPIFFS_start; // we make shure later, that only one of the two is really used ...
|
||||
extern "C" uint32_t _FS_start; // ... depending on core-sdk-version
|
||||
|
||||
/**
|
||||
* @brief Will examine the start and end of the OTA-partition. Then the sector size will be computed, saved data should be found and the initial state will be configured.
|
||||
*/
|
||||
void FLOG::init(void)
|
||||
{
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: init ..."));
|
||||
size = ESP.getSketchSize();
|
||||
// round one sector up
|
||||
start = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1));
|
||||
#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2)
|
||||
end = (uint32_t)&_SPIFFS_start - 0x40200000;
|
||||
#else // Core > 2.5.2 and STAGE
|
||||
end = (uint32_t)&_FS_start - 0x40200000;
|
||||
#endif
|
||||
num_sectors = (end - start)/FLASH_SECTOR_SIZE;
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: size: 0x%lx, start: 0x%lx, end: 0x%lx, num_sectors(dec): %lu"), size, start, end, num_sectors );
|
||||
_findFirstErasedSector();
|
||||
if(first_erased_sector == 0xffff){
|
||||
_eraseSector(0);
|
||||
first_erased_sector = 0; // start with sector 0, could be first run or after crash
|
||||
}
|
||||
_searchSaves();
|
||||
_initBuffer();
|
||||
ready = true;
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
| *
|
||||
| * private helper functions
|
||||
| *
|
||||
\*********************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Read a sector into the global buffer
|
||||
*
|
||||
* @param one_sector as an uint8_t
|
||||
*/
|
||||
void FLOG::_readSector(uint8_t one_sector){
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: read sector number: %u" ), one_sector);
|
||||
ESP.flashRead(start+(one_sector * FLASH_SECTOR_SIZE),(uint32_t *)§or.dword_buffer, FLASH_SECTOR_SIZE);
|
||||
}
|
||||
/**
|
||||
* @brief Erase the given sector og the OTA-partition
|
||||
*
|
||||
* @param one_sector as an uint8_t
|
||||
*/
|
||||
void FLOG::_eraseSector(uint8_t one_sector){ // Erase sector of FLOG/OTA
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: erasing sector number: %u" ), one_sector);
|
||||
ESP.flashEraseSector((start/FLASH_SECTOR_SIZE)+one_sector);
|
||||
}
|
||||
/**
|
||||
* @brief Write the global buffer to the given sector
|
||||
*
|
||||
* @param one_sector as an uint8_t
|
||||
*/
|
||||
void FLOG::_writeSector(uint8_t one_sector){ // Write sector of FLOG/OTA
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to sector number: %u" ), one_sector);
|
||||
ESP.flashWrite(start+(one_sector * FLASH_SECTOR_SIZE),(uint32_t *)§or.dword_buffer, FLASH_SECTOR_SIZE);
|
||||
}
|
||||
/**
|
||||
* @brief Clear the global buffer, but leave the header intact
|
||||
*
|
||||
*/
|
||||
void FLOG::_clearBuffer(){ //not the header
|
||||
for (uint32_t i = sizeof(sector.header)/4; i<(sizeof(sector.dword_buffer)/4); i++){
|
||||
sector.dword_buffer[i] = 0;
|
||||
}
|
||||
sector.header.buf_pointer = sizeof(sector.header);
|
||||
// _showBuffer();
|
||||
}
|
||||
/**
|
||||
* @brief Write global buffer to FLASH and set the current sector to the next valid position, maybe to 0
|
||||
*
|
||||
*/
|
||||
void FLOG::_saveBufferToSector(){ // save buffer to already erased(!) sector, erase next sector, clear buffer, increment number
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to current sector: %u" ),current_sector);
|
||||
_writeSector(current_sector);
|
||||
if(current_sector == num_sectors){ // 1 MB means ~110 sectors in OTA-partition, if we reach this, start a again
|
||||
current_sector = 0;
|
||||
}
|
||||
else{
|
||||
current_sector++;
|
||||
}
|
||||
_eraseSector(current_sector); // we always erase the next sector, to find out were we are after restart
|
||||
_clearBuffer();
|
||||
sector.header.number++;
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: new sector header number: %u" ),sector.header.number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Typically after restart find the first erased sector as a starting point for further operations
|
||||
*
|
||||
*/
|
||||
void FLOG::_findFirstErasedSector(){
|
||||
for (uint32_t i = 0; i<num_sectors; i++){
|
||||
bool success = true;
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: read sector: %u"), i);
|
||||
_readSector(i);
|
||||
for (uint32_t j = 0; j<(sizeof(sector.dword_buffer)/4); j++){
|
||||
if(sector.dword_buffer[j]!=0xffffffff){
|
||||
// DEBUG_SENSOR_LOG(PSTR("FLOG: buffer_dword: %u"), sector.dword_buffer[j]);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if(success){
|
||||
first_erased_sector = i; // save this for the whole next write operation
|
||||
sector.header.physical_start_sector = i; // save to header for every sector
|
||||
current_sector = i; // this is our actual sector to write to
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: first erased sector: %u, now init ..."), first_erased_sector);
|
||||
return;
|
||||
}
|
||||
}
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: no erased sector found"));
|
||||
first_erased_sector = 0xffff; // this will not happen unless we have 256 MByte FLASH
|
||||
}
|
||||
/**
|
||||
* @brief Look at the sector before the first erased sector to check, if there could be saved data
|
||||
*
|
||||
*/
|
||||
void FLOG::_searchSaves(void){
|
||||
//check if old Data is present
|
||||
found_saved_data = false;
|
||||
uint32_t s;
|
||||
if(first_erased_sector==0){
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: sector 0 was erased before, examine sector: %u"), num_sectors);
|
||||
s = num_sectors; //count back to the highest possible sector
|
||||
}
|
||||
else{
|
||||
s = first_erased_sector-1;
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: examine sector: %u"), s);
|
||||
}
|
||||
_readSector(s); //read the sector before the first erased sector
|
||||
if(sector.header.magic_word!=MAGIC_WORD_FL){
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: wrong magic number, no saved data found"));
|
||||
return;
|
||||
}
|
||||
sectors_left = sector.header.number + 1; // this might be wrong, but this less important
|
||||
_saved_header = sector.header; // back this up for appending mode
|
||||
s = sector.header.physical_start_sector;
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: will check pysical start sector: %u"), s);
|
||||
_readSector(s); //read the physical_start_sector
|
||||
_showBuffer();
|
||||
if(sector.header.magic_word!=MAGIC_WORD_FL){ //F, L
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: wrong magic number, no saved data found"));
|
||||
sectors_left = 0;
|
||||
return;
|
||||
}
|
||||
if(sector.header.number==0){ //physical_start_sector should have number 0
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: possible saved data found"));
|
||||
found_saved_data = true; // TODO: this is only a very rough check and should be completed later
|
||||
}
|
||||
else{
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: number: %u should be 0"), sector.header.number);
|
||||
sectors_left = 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Start with a new buffer to be able to start a write session
|
||||
*
|
||||
*/
|
||||
void FLOG::_initBuffer(void){
|
||||
if(!found_saved_data){ // we must re-init this, because the buffer is in an undefined state
|
||||
sector.header.physical_start_sector = (uint16_t)first_erased_sector;
|
||||
}
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: init header"));
|
||||
sector.header.magic_word = MAGIC_WORD_FL; //F, L
|
||||
sector.header.number = 0;
|
||||
sector.header.buf_pointer = (uint16_t)sizeof(sector.header);
|
||||
current_sector = first_erased_sector;
|
||||
ready = true;
|
||||
_clearBuffer();
|
||||
}
|
||||
/**
|
||||
* @brief - a pure debug function
|
||||
*
|
||||
*/
|
||||
void FLOG::_showBuffer(void){
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: Header: %c %c"), sector.byte_buffer[0],sector.byte_buffer[1]);
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: V_Start_sector: %u, sector number: %u, pointer: %u "), sector.header.physical_start_sector, sector.header.number, sector.header.buf_pointer);
|
||||
uint32_t j = 0;
|
||||
for (uint32_t i = sector.header.buf_pointer-16; i<(sizeof(sector.byte_buffer)); i+=8){
|
||||
// DEBUG_SENSOR_LOG(PSTR("FLOG: buffer: %c %c %c %c %c %c %c %c "), sector.byte_buffer[i], sector.byte_buffer[i+1], sector.byte_buffer[i+2], sector.byte_buffer[i+3], sector.byte_buffer[i+4], sector.byte_buffer[i+5], sector.byte_buffer[i+6], sector.byte_buffer[i+7]);
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: buffer: %u %u %u %u %u %u %u %u "), sector.byte_buffer[i], sector.byte_buffer[i+1], sector.byte_buffer[i+2], sector.byte_buffer[i+3], sector.byte_buffer[i+4], sector.byte_buffer[i+5], sector.byte_buffer[i+6], sector.byte_buffer[i+7]);
|
||||
j++;
|
||||
if(j>3){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief pass a data entry/record as uint8_t array with its size
|
||||
*
|
||||
* @param src uint8_t array
|
||||
* @param size uint32_t size of the array
|
||||
*/
|
||||
void FLOG::addToBuffer(uint8_t src[], uint32_t size){
|
||||
if(mode == 0){
|
||||
if(sector.header.number == num_sectors && !ready){
|
||||
return; // we ignore additional calls and are done, TODO: maybe use meaningful return values
|
||||
}
|
||||
}
|
||||
if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){
|
||||
// DEBUG_SENSOR_LOG(PSTR("FLOG: enough space left in buffer: %u"), FLASH_SECTOR_SIZE - sector.header.buf_pointer - sizeof(sector.header));
|
||||
// DEBUG_SENSOR_LOG(PSTR("FLOG: current buf_pointer: %u, size of added: %u"), sector.header.buf_pointer, size);
|
||||
|
||||
memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size);
|
||||
sector.header.buf_pointer+=size; // this is the next free spot
|
||||
// DEBUG_SENSOR_LOG(PSTR("FLOG: current buf_pointer: %u"), sector.header.buf_pointer);
|
||||
}
|
||||
else{
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: save buffer to flash sector: %u"), current_sector);
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: current buf_pointer: %u"), sector.header.buf_pointer);
|
||||
_saveBufferToSector();
|
||||
sectors_left++;
|
||||
// but now save the data to the fresh buffer
|
||||
if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){
|
||||
memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size);
|
||||
sector.header.buf_pointer+=size; // this is the next free spot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief shows that it is ready to accept recording
|
||||
*
|
||||
* @param append - if true append to current log, else start a new log
|
||||
*/
|
||||
void FLOG::startRecording(bool append){
|
||||
if(recording){
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: already recording"));
|
||||
return;
|
||||
}
|
||||
recording = true;
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: start recording"));
|
||||
_initBuffer();
|
||||
if(!found_saved_data) {
|
||||
append = false; // nothing to append to, we silently start a new log
|
||||
}
|
||||
if(append){
|
||||
sector.header.number = _saved_header.number+1; // continue with the next number
|
||||
sector.header.physical_start_sector = _saved_header.physical_start_sector; // keep the old start sector
|
||||
}
|
||||
else{ //new log, old data is lost
|
||||
sector.header.physical_start_sector = (uint16_t)first_erased_sector;
|
||||
found_saved_data = false;
|
||||
sectors_left = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop recording including saving current buffer to FLASH
|
||||
*
|
||||
*/
|
||||
void FLOG::stopRecording(void){
|
||||
_saveBufferToSector();
|
||||
_findFirstErasedSector();
|
||||
_searchSaves();
|
||||
_initBuffer();
|
||||
recording = false;
|
||||
found_saved_data = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Will start a downloads, needs the correct implementation of 3 callback functions
|
||||
*
|
||||
* @param size: size of the data entry/record in bytes, i.e. sizeof(myStruct)
|
||||
* @param sendHeader: should implement at least something like:
|
||||
* @example WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); // This is very likely unknown!!
|
||||
* WebServer->sendHeader(F("Content-Disposition"), F("attachment; filename=myfile.txt"));
|
||||
* @param sendRecord: will receive the memory address as "uint8_t* addr" and should consume the current entry/record
|
||||
* @example myStruct_t *entry = (myStruct_t*)addr;
|
||||
* Then make useful Strings and send it, i.e.: WebServer->sendContent_P(myString);
|
||||
* @param sendFooter: finish the download, should implement at least:
|
||||
* @example WebServer->sendContent("");
|
||||
*/
|
||||
void FLOG::startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter){
|
||||
|
||||
_readSector(sector.header.physical_start_sector);
|
||||
uint32_t next_sector = sector.header.physical_start_sector;
|
||||
bytes_left = sector.header.buf_pointer - sizeof(sector.header);
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: create file for download, will process %u bytes"), bytes_left);
|
||||
running_download = true;
|
||||
// Callback 1: Create the header incl. file name, content length (probably unknown!!) and additional header stuff
|
||||
sendHeader();
|
||||
|
||||
while(sectors_left){
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: next sector: %u"), next_sector);
|
||||
//initially we have the first sector already loaded, so we do it at the bottom
|
||||
uint32_t k = sizeof(sector.header);
|
||||
while(bytes_left){
|
||||
// DEBUG_SENSOR_LOG(PSTR("FLOG: DL %u %u"), Flog->sector.byte_buffer[k],Flog->sector.byte_buffer[k+1]);
|
||||
uint8_t *_record_start = (uint8_t*)§or.byte_buffer[k]; // this is basically the start address of the current record/entry of the Log
|
||||
// Callback 2: send the pointer for consuming the next record/entry and doing something useful to create a file
|
||||
sendRecord(_record_start);
|
||||
if(k%128 == 0){ // give control to the system every x iteration, TODO: This will fail, when record/entry-size is not 8
|
||||
// DEBUG_SENSOR_LOG(PSTR("FLOG: now loop(), %u bytes left"), Flog->bytes_left);
|
||||
OsWatchLoop();
|
||||
delay(sleep);
|
||||
}
|
||||
k+=size;
|
||||
if(bytes_left>7){
|
||||
bytes_left-=size;
|
||||
}
|
||||
else{
|
||||
bytes_left = 0;
|
||||
DEBUG_SENSOR_LOG(PSTR("FLOG: Flog->bytes_left not dividable by 8 ??????"));
|
||||
}
|
||||
}
|
||||
next_sector++;
|
||||
if(next_sector>num_sectors){
|
||||
next_sector = 0;
|
||||
}
|
||||
sectors_left--;
|
||||
_readSector(next_sector);
|
||||
bytes_left = sector.header.buf_pointer - sizeof(sector.header);
|
||||
OsWatchLoop();
|
||||
delay(sleep);
|
||||
}
|
||||
running_download = false;
|
||||
// Callback 3: create a footer or simply finish the download with an empty payload
|
||||
sendFooter();
|
||||
// refresh settings for another download
|
||||
_searchSaves();
|
||||
_initBuffer();
|
||||
}
|
||||
|
||||
#endif // USE_FLOG
|
|
@ -180,6 +180,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
|||
#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem)
|
||||
#define USE_RDM6300 // Add support for RDM6300 125kHz RFID Reader (+0k8)
|
||||
#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
|
||||
//#define USE_GPS // Add support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM)
|
||||
|
||||
#define USE_ENERGY_SENSOR // Add energy sensors (-14k code)
|
||||
#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code)
|
||||
|
@ -374,6 +375,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
|||
#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem)
|
||||
#undef USE_RDM6300 // Disable support for RDM6300 125kHz RFID Reader (+0k8)
|
||||
#undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
|
||||
#undef USE_GPS // Disable support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM)
|
||||
|
||||
//#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor
|
||||
#undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI
|
||||
|
@ -462,6 +464,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
|||
#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem)
|
||||
#undef USE_RDM6300 // Disable support for RDM6300 125kHz RFID Reader (+0k8)
|
||||
#undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
|
||||
#undef USE_GPS // Disable support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM)
|
||||
|
||||
//#undef USE_ENERGY_SENSOR // Disable energy sensors
|
||||
#undef USE_PZEM004T // Disable PZEM004T energy sensor
|
||||
|
@ -571,6 +574,7 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack
|
|||
#undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem)
|
||||
#undef USE_RDM6300 // Disable support for RDM6300 125kHz RFID Reader (+0k8)
|
||||
#undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
|
||||
#undef USE_GPS // Disable support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM)
|
||||
|
||||
#undef USE_ENERGY_SENSOR // Disable energy sensors
|
||||
#undef USE_PZEM004T // Disable PZEM004T energy sensor
|
||||
|
|
|
@ -214,6 +214,8 @@ enum UserSelectablePins {
|
|||
GPIO_TASMOTASLAVE_RST_INV, // Slave Reset Inverted
|
||||
GPIO_HPMA_RX, // Honeywell HPMA115S0 Serial interface
|
||||
GPIO_HPMA_TX, // Honeywell HPMA115S0 Serial interface
|
||||
GPIO_GPS_RX, // GPS serial interface
|
||||
GPIO_GPS_TX, // GPS serial interface
|
||||
GPIO_SENSOR_END };
|
||||
|
||||
// Programmer selectable GPIO functionality
|
||||
|
@ -294,6 +296,7 @@ const char kSensorNames[] PROGMEM =
|
|||
D_SENSOR_DEEPSLEEP "|" D_SENSOR_EXS_ENABLE "|"
|
||||
D_SENSOR_SLAVE_TX "|" D_SENSOR_SLAVE_RX "|" D_SENSOR_SLAVE_RESET "|" D_SENSOR_SLAVE_RESET "i|"
|
||||
D_SENSOR_HPMA_RX "|" D_SENSOR_HPMA_TX "|"
|
||||
D_SENSOR_GPS_RX "|" D_SENSOR_GPS_TX
|
||||
;
|
||||
|
||||
const char kSensorNamesFixed[] PROGMEM =
|
||||
|
@ -671,6 +674,7 @@ const uint8_t kGpioNiceList[] PROGMEM = {
|
|||
#endif // USE_SOLAX_X1
|
||||
#endif // USE_ENERGY_SENSOR
|
||||
|
||||
// Serial
|
||||
#ifdef USE_SERIAL_BRIDGE
|
||||
GPIO_SBR_TX, // Serial Bridge Serial interface
|
||||
GPIO_SBR_RX, // Serial Bridge Serial interface
|
||||
|
@ -725,6 +729,11 @@ const uint8_t kGpioNiceList[] PROGMEM = {
|
|||
GPIO_IBEACON_RX,
|
||||
GPIO_IBEACON_TX,
|
||||
#endif
|
||||
#ifdef USE_GPS
|
||||
GPIO_GPS_RX, // GPS serial interface
|
||||
GPIO_GPS_TX, // GPS serial interface
|
||||
#endif
|
||||
|
||||
#ifdef USE_MGC3130
|
||||
GPIO_MGC3130_XFER,
|
||||
GPIO_MGC3130_RESET,
|
||||
|
@ -754,8 +763,9 @@ const uint8_t kGpioNiceList[] PROGMEM = {
|
|||
GPIO_A4988_MS3, // A4988 microstep pin3
|
||||
#endif
|
||||
#ifdef USE_DEEPSLEEP
|
||||
GPIO_DEEPSLEEP
|
||||
GPIO_DEEPSLEEP,
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
const uint8_t kModuleNiceList[] PROGMEM = {
|
||||
|
|
|
@ -0,0 +1,860 @@
|
|||
/*
|
||||
xsns_60_GPS.ino - GPS UBLOX support for Tasmota
|
||||
|
||||
Copyright (C) 2019 Theo Arends, Christian Baars and Adrian Scillato
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_GPS
|
||||
/*********************************************************************************************\
|
||||
--------------------------------------------------------------------------------------------
|
||||
Version Date Action Description
|
||||
--------------------------------------------------------------------------------------------
|
||||
|
||||
0.9.1.0 20191216 integrate - Added pin specifications from Tasmota WEB UI. Minor tweaks.
|
||||
---
|
||||
0.9.0.0 20190817 started - further development by Christian Baars - https://github.com/Staars/Sonoff-Tasmota
|
||||
forked - from arendst/tasmota - https://github.com/arendst/Sonoff-Tasmota
|
||||
base - code base from arendst and - https://www.youtube.com/watch?v=TwhCX0c8Xe0
|
||||
|
||||
## GPS-driver for the Ublox-series 6-8
|
||||
Driver is tested on a NEO-6m and a Beitian-220. Series 7 should work too. This adds only about 6kb to the program size, because the efficient UBX-protocol is used. These modules are quite cheap, starting at about 3.50€ for the NEO-6m.
|
||||
|
||||
## Features:
|
||||
- get position and time data
|
||||
- sets system time automatically and Settings.latitude and Settings.longitude via command
|
||||
- can log postion data with timestamp to flash with a small memory footprint of only 12 Bytes per record
|
||||
- constructs a GPX-file for download of this data
|
||||
- Web-UI
|
||||
- simplified NTP-server
|
||||
- command interface
|
||||
|
||||
## Usage:
|
||||
The serial pins are GPX_RX and GPS_TX, no further installation steps needed. To get more debug information compile it with option "DEBUG_TASMOTA_SENSOR".
|
||||
|
||||
|
||||
## Commands:
|
||||
|
||||
+ sensor60 0
|
||||
write to all available sectors, then restart and overwrite the older ones
|
||||
|
||||
+ sensor60 1
|
||||
write to all available sectors, then restart and overwrite the older ones
|
||||
|
||||
+ sensor60 2
|
||||
filter out horizontal drift noise
|
||||
|
||||
+ sensor60 3
|
||||
turn off noise filter
|
||||
|
||||
+ sensor60 4
|
||||
start recording, new data will be appended
|
||||
|
||||
+ sensor60 5
|
||||
start new recording, old data will lost
|
||||
|
||||
+ sensor60 6
|
||||
stop recording, download link will be visible in Web-UI
|
||||
|
||||
+ sensor60 7
|
||||
send mqtt on new postion + TELE -> consider to set TELE to a very high value
|
||||
|
||||
+ sensor60 8
|
||||
only TELE message
|
||||
|
||||
+ sensor60 9
|
||||
start NTP-server
|
||||
|
||||
+ sensor60 10
|
||||
deactivate NTP-server
|
||||
|
||||
+ sensor60 11
|
||||
force update of Tasmota-system-UTC with every new GPS-time-message
|
||||
|
||||
+ sensor60 12
|
||||
do not update of Tasmota-system-UTC with every new GPS-time-message
|
||||
|
||||
+ sensor60 13
|
||||
set latitude and longitude in settings
|
||||
|
||||
|
||||
|
||||
## Rules examples for SSD1306 32x128
|
||||
|
||||
|
||||
rule1 on tele-GPS#lat do DisplayText [s1p21c1l01f1]LAT: %value% endon on tele-GPS#lon do DisplayText [s1p21c1l2]LON: %value% endon on switch1#state==3 do sensor60 4 endon on switch1#state==2 do sensor60 6 endon
|
||||
|
||||
rule2 on tele-GPS#int>9 do DisplayText [f0c9l4]I%value% endon on tele-GPS#int<10 do DisplayText [f0c9l4]I0%value% endon on tele-GPS#fil==1 do DisplayText [f0c18l4]F endon on tele-GPS#fil==0 do DisplayText [f0c18l4]N endon
|
||||
|
||||
rule3 on tele-FLOG#sec do DisplayText [f0c1l4]SAV:%value% endon on tele-FLOG#rec==1 do DisplayText [f0c1l4]REC: endon on tele-FLOG#mode do DisplayText [f0c14l4]M%value% endon
|
||||
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XSNS_60 60
|
||||
|
||||
#include "NTPServer.h"
|
||||
#include "NTPPacket.h"
|
||||
|
||||
/*********************************************************************************************\
|
||||
* constants
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define D_CMND_UBX "UBX"
|
||||
|
||||
const char S_JSON_UBX_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_UBX "%s\":%d}";
|
||||
|
||||
const char kUBXTypes[] PROGMEM = "UBX";
|
||||
|
||||
#define UBX_LAT_LON_THRESHOLD 1000 // filter out some noise of local drift
|
||||
|
||||
/********************************************************************************************\
|
||||
| *globals
|
||||
\*********************************************************************************************/
|
||||
|
||||
const char UBLOX_INIT[] PROGMEM = {
|
||||
// Disable NMEA
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x24, // GxGGA off
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x2B, // GxGLL off
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x32, // GxGSA off
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x39, // GxGSV off
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x40, // GxRMC off
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x05,0x47, // GxVTG off
|
||||
|
||||
// Disable UBX
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xDC, //NAV-PVT off
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xB9, //NAV-POSLLH off
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xC0, //NAV-STATUS off
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x92, //NAV-TIMEUTC off
|
||||
|
||||
// Enable UBX
|
||||
// 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x01,0x00,0x00,0x00,0x00,0x18,0xE1, //NAV-PVT on
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x13,0xBE, //NAV-POSLLH on
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x14,0xC5, //NAV-STATUS on
|
||||
0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x01,0x00,0x00,0x00,0x00,0x32,0x97, //NAV-TIMEUTC on
|
||||
|
||||
// Rate - we will not reset it for the moment after restart
|
||||
// 0xB5,0x62,0x06,0x08,0x06,0x00,0x64,0x00,0x01,0x00,0x01,0x00,0x7A,0x12, //(10Hz)
|
||||
// 0xB5,0x62,0x06,0x08,0x06,0x00,0xC8,0x00,0x01,0x00,0x01,0x00,0xDE,0x6A, //(5Hz)
|
||||
// 0xB5,0x62,0x06,0x08,0x06,0x00,0xE8,0x03,0x01,0x00,0x01,0x00,0x01,0x39 //(1Hz)
|
||||
// 0xB5,0x62,0x06,0x08,0x06,0x00,0xD0,0x07,0x01,0x00,0x01,0x00,0xED,0xBD //(0.5Hz)
|
||||
};
|
||||
|
||||
char UBX_name[4];
|
||||
|
||||
struct UBX_t {
|
||||
const char UBX_HEADER[2] = { 0xB5, 0x62 }; // TODO: Check if we really save space here inside the struct
|
||||
const char NAV_POSLLH_HEADER[2] = { 0x01, 0x02 };
|
||||
const char NAV_STATUS_HEADER[2] = { 0x01, 0x03 };
|
||||
const char NAV_TIME_HEADER[2] = { 0x01, 0x21 };
|
||||
|
||||
struct entry_t {
|
||||
int32_t lat; //raw sensor value
|
||||
int32_t lon; //raw sensor value
|
||||
uint32_t time; //local time from system (maybe provided by the sensor)
|
||||
};
|
||||
|
||||
union {
|
||||
entry_t values;
|
||||
uint8_t bytes[sizeof(entry_t)];
|
||||
} rec_buffer;
|
||||
|
||||
struct POLL_MSG {
|
||||
uint8_t cls;
|
||||
uint8_t id;
|
||||
uint16_t zero;
|
||||
};
|
||||
|
||||
struct NAV_POSLLH {
|
||||
uint8_t cls;
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
uint32_t iTOW;
|
||||
int32_t lon;
|
||||
int32_t lat;
|
||||
int32_t height;
|
||||
int32_t hMSL;
|
||||
uint32_t hAcc;
|
||||
uint32_t vAcc;
|
||||
};
|
||||
|
||||
struct NAV_STATUS {
|
||||
uint8_t cls;
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
uint32_t iTOW;
|
||||
uint8_t gpsFix;
|
||||
uint8_t flags; //bit 0 - gpsfix valid
|
||||
uint8_t fixStat;
|
||||
uint8_t flags2;
|
||||
uint32_t ttff;
|
||||
uint32_t msss;
|
||||
};
|
||||
|
||||
struct NAV_TIME_UTC {
|
||||
uint8_t cls;
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
uint32_t iTOW;
|
||||
uint32_t tAcc;
|
||||
int32_t nano; // Nanoseconds of second, range -1e9 .. 1e9 (UTC)
|
||||
uint16_t year;
|
||||
uint8_t month;
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint8_t min;
|
||||
uint8_t sec;
|
||||
struct {
|
||||
uint8_t UTC:1;
|
||||
uint8_t WKN:1; // week number
|
||||
uint8_t TOW:1; // time of week
|
||||
uint8_t padding:5;
|
||||
} valid;
|
||||
};
|
||||
|
||||
struct CFG_RATE {
|
||||
uint8_t cls; //0x06
|
||||
uint8_t id; //0x08
|
||||
uint16_t len; // 6 bytes
|
||||
uint16_t measRate; // in every ms -> 1 Hz = 1000 ms; 10 Hz = 100 ms -> x = 1000 ms / Hz
|
||||
uint16_t navRate; // x measurements for 1 navigation event
|
||||
uint16_t timeRef; // align to time system: 0= UTC, 1 = GPS, 2 = GLONASS, ...
|
||||
char CK[2]; // checksum
|
||||
};
|
||||
|
||||
struct {
|
||||
uint32_t last_iTOW;
|
||||
int32_t last_lat;
|
||||
int32_t last_lon;
|
||||
int32_t last_height;
|
||||
uint32_t last_hAcc;
|
||||
uint32_t last_vAcc;
|
||||
uint8_t gpsFix;
|
||||
uint8_t non_empty_loops; // in case of an unintended reset of the GPS, the serial interface will get flooded with NMEA
|
||||
uint16_t log_interval; // in tenth of seconds
|
||||
} state;
|
||||
|
||||
struct {
|
||||
uint32_t filter_noise:1;
|
||||
uint32_t send_when_new:1; // no teleinterval
|
||||
uint32_t send_UI_only:1;
|
||||
uint32_t runningNTP:1;
|
||||
uint32_t forceUTCupdate:1;
|
||||
// TODO: more to come
|
||||
} mode;
|
||||
|
||||
union {
|
||||
NAV_POSLLH navPosllh;
|
||||
NAV_STATUS navStatus;
|
||||
NAV_TIME_UTC navTime;
|
||||
POLL_MSG pollMsg;
|
||||
CFG_RATE cfgRate;
|
||||
} Message;
|
||||
|
||||
} UBX;
|
||||
|
||||
enum UBXMsgType {
|
||||
MT_NONE,
|
||||
MT_NAV_POSLLH,
|
||||
MT_NAV_STATUS,
|
||||
MT_NAV_TIME,
|
||||
MT_POLL
|
||||
};
|
||||
|
||||
#ifdef USE_FLOG
|
||||
FLOG *Flog = nullptr;
|
||||
#endif //USE_FLOG
|
||||
TasmotaSerial *UBXSerial;
|
||||
|
||||
NtpServer timeServer(PortUdp);
|
||||
|
||||
/*********************************************************************************************\
|
||||
* helper function
|
||||
\*********************************************************************************************/
|
||||
|
||||
void UBXcalcChecksum(char* CK, size_t msgSize)
|
||||
{
|
||||
memset(CK, 0, 2);
|
||||
for (int i = 0; i < msgSize; i++) {
|
||||
CK[0] += ((char*)(&UBX.Message))[i];
|
||||
CK[1] += CK[0];
|
||||
}
|
||||
}
|
||||
|
||||
bool UBXcompareMsgHeader(const char* msgHeader)
|
||||
{
|
||||
char* ptr = (char*)(&UBX.Message);
|
||||
return ptr[0] == msgHeader[0] && ptr[1] == msgHeader[1];
|
||||
}
|
||||
|
||||
void UBXinitCFG(void)
|
||||
{
|
||||
for (uint32_t i = 0; i < sizeof(UBLOX_INIT); i++) {
|
||||
UBXSerial->write( pgm_read_byte(UBLOX_INIT+i) );
|
||||
}
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: turn off NMEA"));
|
||||
}
|
||||
|
||||
void UBXTriggerTele(void)
|
||||
{
|
||||
mqtt_data[0] = '\0';
|
||||
if (MqttShowSensor()) {
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
|
||||
#ifdef USE_RULES
|
||||
RulesTeleperiod(); // Allow rule based HA messages
|
||||
#endif // USE_RULES
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void UBXDetect(void)
|
||||
{
|
||||
if ((pin[GPIO_GPS_RX] < 99) && (pin[GPIO_GPS_TX] < 99)) {
|
||||
UBXSerial = new TasmotaSerial(pin[GPIO_GPS_RX], pin[GPIO_GPS_TX], 1, 0, 96); // 64 byte buffer is NOT enough
|
||||
if (UBXSerial->begin(9600)) {
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: started serial"));
|
||||
if (UBXSerial->hardwareSerial()) {
|
||||
ClaimSerial();
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: claim HW"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UBXinitCFG(); // turn of NMEA, only use "our" UBX-messages
|
||||
#ifdef USE_FLOG
|
||||
if (!Flog) {
|
||||
Flog = new FLOG; // init Flash Log
|
||||
Flog->init();
|
||||
}
|
||||
#endif // USE_FLOG
|
||||
|
||||
UBX.state.log_interval = 10; // 1 second
|
||||
UBX.mode.send_UI_only = true; // send UI data ...
|
||||
UBXTriggerTele(); // ... once at after start
|
||||
}
|
||||
|
||||
uint32_t UBXprocessGPS()
|
||||
{
|
||||
static uint32_t fpos = 0;
|
||||
static char checksum[2];
|
||||
static uint8_t currentMsgType = MT_NONE;
|
||||
static size_t payloadSize = sizeof(UBX.Message);
|
||||
|
||||
// DEBUG_SENSOR_LOG(PSTR("UBX: check for serial data"));
|
||||
uint32_t data_bytes = 0;
|
||||
while ( UBXSerial->available() ) {
|
||||
data_bytes++;
|
||||
byte c = UBXSerial->read();
|
||||
if ( fpos < 2 ) {
|
||||
// For the first two bytes we are simply looking for a match with the UBX header bytes (0xB5,0x62)
|
||||
if ( c == UBX.UBX_HEADER[fpos] ) {
|
||||
fpos++;
|
||||
} else {
|
||||
fpos = 0; // Reset to beginning state.
|
||||
}
|
||||
} else {
|
||||
// If we come here then fpos >= 2, which means we have found a match with the UBX_HEADER
|
||||
// and we are now reading in the bytes that make up the payload.
|
||||
|
||||
// Place the incoming byte into the ubxMessage struct. The position is fpos-2 because
|
||||
// the struct does not include the initial two-byte header (UBX_HEADER).
|
||||
if ( (fpos-2) < payloadSize ) {
|
||||
((char*)(&UBX.Message))[fpos-2] = c;
|
||||
}
|
||||
fpos++;
|
||||
|
||||
if ( fpos == 4 ) {
|
||||
// We have just received the second byte of the message type header,
|
||||
// so now we can check to see what kind of message it is.
|
||||
if ( UBXcompareMsgHeader(UBX.NAV_POSLLH_HEADER) ) {
|
||||
currentMsgType = MT_NAV_POSLLH;
|
||||
payloadSize = sizeof(UBX_t::NAV_POSLLH);
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_POSLLH"));
|
||||
}
|
||||
else if ( UBXcompareMsgHeader(UBX.NAV_STATUS_HEADER) ) {
|
||||
currentMsgType = MT_NAV_STATUS;
|
||||
payloadSize = sizeof(UBX_t::NAV_STATUS);
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_STATUS"));
|
||||
}
|
||||
else if ( UBXcompareMsgHeader(UBX.NAV_TIME_HEADER) ) {
|
||||
currentMsgType = MT_NAV_TIME;
|
||||
payloadSize = sizeof(UBX_t::NAV_TIME_UTC);
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_TIME_UTC"));
|
||||
}
|
||||
else {
|
||||
// unknown message type, bail
|
||||
fpos = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( fpos == (payloadSize+2) ) {
|
||||
// All payload bytes have now been received, so we can calculate the
|
||||
// expected checksum value to compare with the next two incoming bytes.
|
||||
UBXcalcChecksum(checksum, payloadSize);
|
||||
}
|
||||
else if ( fpos == (payloadSize+3) ) {
|
||||
// First byte after the payload, ie. first byte of the checksum.
|
||||
// Does it match the first byte of the checksum we calculated?
|
||||
if ( c != checksum[0] ) {
|
||||
// Checksum doesn't match, reset to beginning state and try again.
|
||||
fpos = 0;
|
||||
}
|
||||
}
|
||||
else if ( fpos == (payloadSize+4) ) {
|
||||
// Second byte after the payload, ie. second byte of the checksum.
|
||||
// Does it match the second byte of the checksum we calculated?
|
||||
fpos = 0; // We will reset the state regardless of whether the checksum matches.
|
||||
if ( c == checksum[1] ) {
|
||||
// Checksum matches, we have a valid message.
|
||||
return currentMsgType;
|
||||
}
|
||||
}
|
||||
else if ( fpos > (payloadSize+4) ) {
|
||||
// We have now read more bytes than both the expected payload and checksum
|
||||
// together, so something went wrong. Reset to beginning state and try again.
|
||||
fpos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// DEBUG_SENSOR_LOG(PSTR("UBX: got none or unknown Message"));
|
||||
if (data_bytes!=0) {
|
||||
UBX.state.non_empty_loops++;
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: got %u bytes, non-empty-loop: %u"), data_bytes, UBX.state.non_empty_loops);
|
||||
} else {
|
||||
UBX.state.non_empty_loops = 0; // now a hidden GPS-device reset is unlikely
|
||||
}
|
||||
return MT_NONE;
|
||||
}
|
||||
|
||||
/********************************************************************************************\
|
||||
| * callback functions for the download
|
||||
\*********************************************************************************************/
|
||||
|
||||
#ifdef USE_FLOG
|
||||
void UBXsendHeader(void)
|
||||
{
|
||||
WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN);
|
||||
WebServer->sendHeader(F("Content-Disposition"), F("attachment; filename=TASMOTA.gpx"));
|
||||
WSSend(200, CT_STREAM, F(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\r\n"
|
||||
"<GPX version=\"1.1\" creator=\"TASMOTA\" xmlns=\"http://www.topografix.com/GPX/1/1\" \r\n"
|
||||
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n"
|
||||
"xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\r\n"
|
||||
"<trk>\r\n<trkseg>\r\n"));
|
||||
}
|
||||
|
||||
void UBXsendRecord(uint8_t *buf)
|
||||
{
|
||||
char record[100];
|
||||
char stime[32];
|
||||
UBX_t::entry_t *entry = (UBX_t::entry_t*)buf;
|
||||
snprintf_P(stime, sizeof(stime), GetDT(entry->time).c_str());
|
||||
char lat[12];
|
||||
char lon[12];
|
||||
dtostrfd((double)entry->lat/10000000.0f,7,lat);
|
||||
dtostrfd((double)entry->lon/10000000.0f,7,lon);
|
||||
snprintf_P(record, sizeof(record),PSTR("<trkpt\n\t lat=\"%s\" lon=\"%s\">\n\t<time>%s</time>\n</trkpt>\n"),lat ,lon, stime);
|
||||
// DEBUG_SENSOR_LOG(PSTR("FLOG: DL %u %u"), Flog->sector.dword_buffer[k+j],Flog->sector.dword_buffer[k+j+1]);
|
||||
WebServer->sendContent_P(record);
|
||||
}
|
||||
|
||||
void UBXsendFooter(void)
|
||||
{
|
||||
WebServer->sendContent(F("</trkseg>\n</trk>\n</gpx>"));
|
||||
WebServer->sendContent("");
|
||||
Rtc.user_time_entry = false; // we have blocked the main loop and want a new valid time
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void UBXsendFile(void)
|
||||
{
|
||||
if (!HttpCheckPriviledgedAccess()) { return; }
|
||||
Flog->startDownload(sizeof(UBX.rec_buffer),UBXsendHeader,UBXsendRecord,UBXsendFooter);
|
||||
}
|
||||
#endif //USE_FLOG
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void UBXSetRate(uint16_t interval)
|
||||
{
|
||||
UBX.Message.cfgRate.cls = 0x06;
|
||||
UBX.Message.cfgRate.id = 0x08;
|
||||
UBX.Message.cfgRate.len = 6;
|
||||
uint32_t measRate = (1000*(uint32_t)interval); //seconds to milliseconds
|
||||
if (measRate > 0xffff) {
|
||||
measRate = 0xffff; // max. 65535 ms interval
|
||||
}
|
||||
UBX.Message.cfgRate.measRate = (uint16_t)measRate;
|
||||
UBX.Message.cfgRate.navRate = 1;
|
||||
UBX.Message.cfgRate.timeRef = 1;
|
||||
UBXcalcChecksum(UBX.Message.cfgRate.CK, sizeof(UBX.Message.cfgRate)-sizeof(UBX.Message.cfgRate.CK));
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: requested interval: %u seconds measRate: %u ms"), interval, UBX.Message.cfgRate.measRate);
|
||||
UBXSerial->write(UBX.UBX_HEADER[0]);
|
||||
UBXSerial->write(UBX.UBX_HEADER[1]);
|
||||
for (uint32_t i =0; i<sizeof(UBX.Message.cfgRate); i++) {
|
||||
UBXSerial->write(((uint8_t*)(&UBX.Message.cfgRate))[i]);
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: cfgRate byte %u: %x"), i, ((uint8_t*)(&UBX.Message.cfgRate))[i]);
|
||||
}
|
||||
UBX.state.log_interval = 10*interval;
|
||||
}
|
||||
|
||||
void UBXSelectMode(uint16_t mode)
|
||||
{
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: set mode to %u"),mode);
|
||||
switch(mode){
|
||||
#ifdef USE_FLOG
|
||||
case 0:
|
||||
Flog->mode = 0; // write once to all available sectors, then stop
|
||||
break;
|
||||
case 1:
|
||||
Flog->mode = 1; // write to all available sectors, then restart and overwrite the older ones
|
||||
break;
|
||||
case 2:
|
||||
UBX.mode.filter_noise = true; // filter out horizontal drift noise, TODO: find useful values
|
||||
break;
|
||||
case 3:
|
||||
UBX.mode.filter_noise = false;
|
||||
break;
|
||||
case 4:
|
||||
Flog->startRecording(true);
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - appending"));
|
||||
break;
|
||||
case 5:
|
||||
Flog->startRecording(false);
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - new log"));
|
||||
break;
|
||||
case 6:
|
||||
if(Flog->recording == true){
|
||||
Flog->stopRecording();
|
||||
}
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: stop recording"));
|
||||
break;
|
||||
#endif //USE_FLOG
|
||||
case 7:
|
||||
UBX.mode.send_when_new = 1; // send mqtt on new postion + TELE -> consider to set TELE to a very high value
|
||||
break;
|
||||
case 8:
|
||||
UBX.mode.send_when_new = 0; // only TELE
|
||||
break;
|
||||
case 9:
|
||||
if (timeServer.beginListening()) {
|
||||
UBX.mode.runningNTP = true;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
UBX.mode.runningNTP = false;
|
||||
break;
|
||||
case 11:
|
||||
UBX.mode.forceUTCupdate = true;
|
||||
break;
|
||||
case 12:
|
||||
UBX.mode.forceUTCupdate = false;
|
||||
break;
|
||||
case 13:
|
||||
Settings.latitude = UBX.state.last_lat;
|
||||
Settings.longitude = UBX.state.last_lon;
|
||||
break;
|
||||
default:
|
||||
if (mode>1000 && mode <1066) {
|
||||
// UBXSetRate(mode-1000); // min. 1001 = 0.001 Hz, but will be converted to 1/65535 anyway ~0.015 Hz, max. 2000 = 1.000 Hz
|
||||
UBXSetRate(mode-1000); // set interval between measurements in seconds from 1 to 65
|
||||
}
|
||||
break;
|
||||
}
|
||||
UBX.mode.send_UI_only = true;
|
||||
UBXTriggerTele();
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
bool UBXHandlePOSLLH()
|
||||
{
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: iTOW: %u"),UBX.Message.navPosllh.iTOW);
|
||||
if (UBX.state.gpsFix>1) {
|
||||
if (UBX.mode.filter_noise) {
|
||||
if ((UBX.Message.navPosllh.lat-UBX.rec_buffer.values.lat<abs(UBX_LAT_LON_THRESHOLD))||(UBX.Message.navPosllh.lon-UBX.rec_buffer.values.lon<abs(UBX_LAT_LON_THRESHOLD))) {
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: Diff lat: %u lon: %u "),UBX.Message.navPosllh.lat-UBX.rec_buffer.values.lat, UBX.Message.navPosllh.lon-UBX.rec_buffer.values.lon);
|
||||
return false; //no new position
|
||||
}
|
||||
}
|
||||
UBX.rec_buffer.values.lat = UBX.Message.navPosllh.lat;
|
||||
UBX.rec_buffer.values.lon = UBX.Message.navPosllh.lon;
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: lat/lon: %i / %i"), UBX.rec_buffer.values.lat, UBX.rec_buffer.values.lon);
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: hAcc: %d"), UBX.Message.navPosllh.hAcc);
|
||||
UBX.state.last_iTOW = UBX.Message.navPosllh.iTOW;
|
||||
UBX.state.last_height = UBX.Message.navPosllh.height;
|
||||
UBX.state.last_vAcc = UBX.Message.navPosllh.vAcc;
|
||||
UBX.state.last_hAcc = UBX.Message.navPosllh.hAcc;
|
||||
if (UBX.mode.send_when_new) {
|
||||
UBXTriggerTele();
|
||||
}
|
||||
return true; // new position
|
||||
} else {
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: no valid position data"));
|
||||
}
|
||||
return false; // no GPS-fix
|
||||
}
|
||||
|
||||
void UBXHandleSTATUS()
|
||||
{
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: gpsFix: %u, valid: %u"), UBX.Message.navStatus.gpsFix, (UBX.Message.navStatus.flags)&1);
|
||||
if ((UBX.Message.navStatus.flags)&1) {
|
||||
UBX.state.gpsFix = UBX.Message.navStatus.gpsFix; //only store fixed status if flag is valid
|
||||
} else {
|
||||
UBX.state.gpsFix = 0; // without valid flag, everything is "no fix"
|
||||
}
|
||||
}
|
||||
|
||||
void UBXHandleTIME()
|
||||
{
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: UTC-Time: %u-%u-%u %u:%u:%u"), UBX.Message.navTime.year, UBX.Message.navTime.month ,UBX.Message.navTime.day,UBX.Message.navTime.hour,UBX.Message.navTime.min,UBX.Message.navTime.sec);
|
||||
if (UBX.Message.navTime.valid.UTC) {
|
||||
DEBUG_SENSOR_LOG(PSTR("UBX: UTC-Time is valid"));
|
||||
if (Rtc.user_time_entry == false || UBX.mode.forceUTCupdate) {
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: UTC-Time is valid, set system time"));
|
||||
TIME_T gpsTime;
|
||||
gpsTime.year = UBX.Message.navTime.year - 1970;
|
||||
gpsTime.month = UBX.Message.navTime.month;
|
||||
gpsTime.day_of_month = UBX.Message.navTime.day;
|
||||
gpsTime.hour = UBX.Message.navTime.hour;
|
||||
gpsTime.minute = UBX.Message.navTime.min;
|
||||
gpsTime.second = UBX.Message.navTime.sec;
|
||||
Rtc.utc_time = MakeTime(gpsTime);
|
||||
Rtc.user_time_entry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UBXHandleOther(void)
|
||||
{
|
||||
if (UBX.state.non_empty_loops>6) { // we expect only 4-5 non-empty loops in a row, could change with other sensor speed (Hz)
|
||||
UBXinitCFG(); // this should only happen with lots of NMEA-messages, but it is only a guess!!
|
||||
AddLog_P(LOG_LEVEL_ERROR, PSTR("UBX: possible device-reset, will re-init"));
|
||||
UBXSerial->flush();
|
||||
UBX.state.non_empty_loops = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void UBXTimeServer()
|
||||
{
|
||||
if(UBX.mode.runningNTP){
|
||||
timeServer.processOneRequest(Rtc.utc_time, UBX.state.last_iTOW%1000);
|
||||
}
|
||||
}
|
||||
|
||||
void UBXLoop(void)
|
||||
{
|
||||
static uint16_t counter; //count up every 100 msec
|
||||
static bool new_position;
|
||||
|
||||
uint32_t msgType = UBXprocessGPS();
|
||||
|
||||
switch(msgType){
|
||||
case MT_NAV_POSLLH:
|
||||
new_position = UBXHandlePOSLLH();
|
||||
break;
|
||||
case MT_NAV_STATUS:
|
||||
UBXHandleSTATUS();
|
||||
break;
|
||||
case MT_NAV_TIME:
|
||||
UBXHandleTIME();
|
||||
break;
|
||||
default:
|
||||
UBXHandleOther();
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef USE_FLOG
|
||||
if (counter>UBX.state.log_interval) {
|
||||
if (Flog->recording && new_position) {
|
||||
UBX.rec_buffer.values.time = Rtc.local_time;
|
||||
Flog->addToBuffer(UBX.rec_buffer.bytes, sizeof(UBX.rec_buffer.bytes));
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
#endif // USE_FLOG
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
// normaly in i18n.h
|
||||
|
||||
#ifdef USE_WEBSERVER
|
||||
// {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
|
||||
#ifdef USE_FLOG
|
||||
#ifdef DEBUG_TASMOTA_SENSOR
|
||||
const char HTTP_SNS_FLOGVER[] PROGMEM = "{s}<hr>{m}<hr>{e}{s} FLOG with %u sectors: {m}%u bytes{e}"
|
||||
"{s} FLOG next sector for REC: {m} %u {e}"
|
||||
"{s} %u sector(s) with data at sector: {m} %u {e}";
|
||||
const char HTTP_SNS_FLOGREC[] PROGMEM = "{s} RECORDING (bytes in buffer) {m}%u{e}";
|
||||
#endif // DEBUG_TASMOTA_SENSOR
|
||||
|
||||
const char HTTP_SNS_FLOG[] PROGMEM = "{s}<hr>{m}<hr>{e}{s} Flash-Log {m} %s{e}";
|
||||
const char kFLOG_STATE0[] PROGMEM = "ready";
|
||||
const char kFLOG_STATE1[] PROGMEM = "recording";
|
||||
const char * kFLOG_STATE[] ={kFLOG_STATE0, kFLOG_STATE1};
|
||||
|
||||
const char HTTP_BTN_FLOG_DL[] PROGMEM = "<button><a href='/UBX'>Download GPX-File</a></button>";
|
||||
|
||||
#endif //USE_FLOG
|
||||
const char HTTP_SNS_NTPSERVER[] PROGMEM = "{s} NTP server {m}active{e}";
|
||||
|
||||
const char HTTP_SNS_GPS[] PROGMEM = "{s} GPS latitude {m}%s{e}"
|
||||
"{s} GPS longitude {m}%s{e}"
|
||||
"{s} GPS height {m}%s m{e}"
|
||||
"{s} GPS hor. Accuracy {m}%s m{e}"
|
||||
"{s} GPS vert. Accuracy {m}%s m{e}"
|
||||
"{s} GPS sat-fix status {m}%s{e}";
|
||||
|
||||
const char kGPSFix0[] PROGMEM = "no fix";
|
||||
const char kGPSFix1[] PROGMEM = "dead reckoning only";
|
||||
const char kGPSFix2[] PROGMEM = "2D-fix";
|
||||
const char kGPSFix3[] PROGMEM = "3D-fix";
|
||||
const char kGPSFix4[] PROGMEM = "GPS + dead reckoning combined";
|
||||
const char kGPSFix5[] PROGMEM = "Time only fix";
|
||||
const char * kGPSFix[] PROGMEM ={kGPSFix0, kGPSFix1, kGPSFix2, kGPSFix3, kGPSFix4, kGPSFix5};
|
||||
|
||||
// const char UBX_GOOGLE_MAPS[] ="<iframe width='100%%' src='https://maps.google.com/maps?width=&height=&hl=en&q=%s %s+(Tasmota)&ie=UTF8&t=&z=10&iwloc=B&output=embed' frameborder='0' scrolling='no' marginheight='0' marginwidth='0'></iframe>";
|
||||
|
||||
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void UBXShow(bool json)
|
||||
{
|
||||
char lat[12];
|
||||
char lon[12];
|
||||
char height[12];
|
||||
char hAcc[12];
|
||||
char vAcc[12];
|
||||
dtostrfd((double)UBX.rec_buffer.values.lat/10000000.0f,7,lat);
|
||||
dtostrfd((double)UBX.rec_buffer.values.lon/10000000.0f,7,lon);
|
||||
dtostrfd((double)UBX.state.last_height/1000.0f,3,height);
|
||||
dtostrfd((double)UBX.state.last_vAcc/1000.0f,3,hAcc);
|
||||
dtostrfd((double)UBX.state.last_hAcc/1000.0f,3,vAcc);
|
||||
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",\"GPS\":{"));
|
||||
if (UBX.mode.send_UI_only) {
|
||||
uint32_t i = UBX.state.log_interval / 10;
|
||||
ResponseAppend_P(PSTR("\"fil\":%u,\"int\":%u}"), UBX.mode.filter_noise, i);
|
||||
} else {
|
||||
ResponseAppend_P(PSTR("\"lat\":%s,\"lon\":%s,\"height\":%s,\"hAcc\":%s,\"vAcc\":%s}"), lat, lon, height, hAcc, vAcc);
|
||||
}
|
||||
#ifdef USE_FLOG
|
||||
ResponseAppend_P(PSTR(",\"FLOG\":{\"rec\":%u,\"mode\":%u,\"sec\":%u}"), Flog->recording, Flog->mode, Flog->sectors_left);
|
||||
#endif //USE_FLOG
|
||||
UBX.mode.send_UI_only = false;
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_GPS, lat, lon, height, hAcc, vAcc, kGPSFix[UBX.state.gpsFix]);
|
||||
//WSContentSend_P(UBX_GOOGLE_MAPS, lat, lon);
|
||||
#ifdef DEBUG_TASMOTA_SENSOR
|
||||
#ifdef USE_FLOG
|
||||
WSContentSend_PD(HTTP_SNS_FLOGVER, Flog->num_sectors, Flog->size, Flog->current_sector, Flog->sectors_left, Flog->sector.header.physical_start_sector);
|
||||
if (Flog->recording) {
|
||||
WSContentSend_PD(HTTP_SNS_FLOGREC, Flog->sector.header.buf_pointer - 8);
|
||||
}
|
||||
#endif //USE_FLOG
|
||||
#endif // DEBUG_TASMOTA_SENSOR
|
||||
#ifdef USE_FLOG
|
||||
if (Flog->ready) {
|
||||
WSContentSend_P(HTTP_SNS_FLOG,kFLOG_STATE[Flog->recording]);
|
||||
}
|
||||
if (!Flog->recording && Flog->found_saved_data) {
|
||||
WSContentSend_P(HTTP_BTN_FLOG_DL);
|
||||
}
|
||||
#endif //USE_FLOG
|
||||
if (UBX.mode.runningNTP) {
|
||||
WSContentSend_P(HTTP_SNS_NTPSERVER);
|
||||
}
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* check the UBX commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool UBXCmd(void)
|
||||
{
|
||||
bool serviced = true;
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
UBXSelectMode(XdrvMailbox.payload);
|
||||
Response_P(S_JSON_UBX_COMMAND_NVALUE, XdrvMailbox.command, XdrvMailbox.payload);
|
||||
}
|
||||
return serviced;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns60(uint8_t function)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (true) {
|
||||
switch (function) {
|
||||
case FUNC_INIT:
|
||||
UBXDetect();
|
||||
break;
|
||||
case FUNC_COMMAND_SENSOR:
|
||||
if (XSNS_60 == XdrvMailbox.index) {
|
||||
result = UBXCmd();
|
||||
}
|
||||
break;
|
||||
case FUNC_EVERY_50_MSECOND:
|
||||
UBXTimeServer();
|
||||
break;
|
||||
case FUNC_EVERY_100_MSECOND:
|
||||
#ifdef USE_FLOG
|
||||
if (!Flog->running_download)
|
||||
#endif //USE_FLOG
|
||||
{
|
||||
UBXLoop();
|
||||
}
|
||||
break;
|
||||
#ifdef USE_FLOG
|
||||
case FUNC_WEB_ADD_HANDLER:
|
||||
WebServer->on("/UBX", UBXsendFile);
|
||||
break;
|
||||
#endif //USE_FLOG
|
||||
case FUNC_JSON_APPEND:
|
||||
UBXShow(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
#ifdef USE_FLOG
|
||||
if (!Flog->running_download)
|
||||
#endif //USE_FLOG
|
||||
{
|
||||
UBXShow(0);
|
||||
}
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_GPS
|
|
@ -131,7 +131,8 @@ a_setoption = [[
|
|||
"GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1)",
|
||||
"Enable incrementing bootcount when deepsleep is enabled",
|
||||
"Do not power off if slider moved to far left",
|
||||
"","",
|
||||
"Bypass Compatibility check",
|
||||
"",
|
||||
"Enable shutter support",
|
||||
"Invert PCF8574 ports"
|
||||
],[
|
||||
|
@ -187,7 +188,7 @@ a_features = [[
|
|||
"USE_SHUTTER","USE_PCF8574","USE_DDSU666","USE_DEEPSLEEP",
|
||||
"USE_SONOFF_SC","USE_SONOFF_RF","USE_SONOFF_L1","USE_EXS_DIMMER",
|
||||
"USE_ARDUINO_SLAVE","USE_HIH6","USE_HPMA","USE_TSL2591",
|
||||
"USE_DHT12","","","",
|
||||
"USE_DHT12","","USE_GPS","",
|
||||
"","","","",
|
||||
"","","",""
|
||||
]]
|
||||
|
|
Loading…
Reference in New Issue