mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Compare commits
25
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a7f662e66 | ||
|
|
7bf6aa06c7 | ||
|
|
6aca592049 | ||
|
|
1e02509a80 | ||
|
|
d0cdf7de42 | ||
|
|
c15379887a | ||
|
|
354e8af0c1 | ||
|
|
aa226174b9 | ||
|
|
cd26623b2a | ||
|
|
0100d26080 | ||
|
|
29abb5dbba | ||
|
|
0e5b73be42 | ||
|
|
2571783139 | ||
|
|
a664199224 | ||
|
|
2b0b11033a | ||
|
|
69178038d6 | ||
|
|
b44553c34b | ||
|
|
e07f93fce6 | ||
|
|
d6ce926b95 | ||
|
|
5d27748232 | ||
|
|
f141c9dbf1 | ||
|
|
a5f4f04f25 | ||
|
|
befa29b1b0 | ||
|
|
bd9018b01e | ||
|
|
28dc6b3694 |
Vendored
+22
@@ -1,3 +1,25 @@
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20201016 (since mega-20201009)
|
||||
-------------------------------------------------
|
||||
|
||||
Release date: vr 16 okt 2020 23:26:50 CEST
|
||||
|
||||
TD-er (7):
|
||||
[LoRaTTN] Add encoder/decoder for unit of measure for AcuDC243
|
||||
[WiFi] Fix WiFi (re)connect issues caused by new WiFi timers
|
||||
[Timing Stats] Add duty cycle column + LoRa TTN on air timings
|
||||
[Syslog] Fix syslog name does not include unit number (#3288)
|
||||
[Docs] Document LoRaWAN TTN dynamic send interval setting
|
||||
[GPS] Add distance counter + distance from ref point
|
||||
[LoRa TTN] Fix decoder issue in GPS data
|
||||
|
||||
Ton Huisman (1):
|
||||
[Checks] Check all 4 variable names
|
||||
|
||||
uwekaditz (1):
|
||||
[P092] Exception solved
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
Changes in release mega-20201009 (since mega-20200929)
|
||||
-------------------------------------------------
|
||||
|
||||
@@ -153,5 +153,39 @@ Decoding Data
|
||||
Controller Settings
|
||||
-------------------
|
||||
|
||||
Since there are legal limitations on the amount of time you are allowed to send,
|
||||
it is even more important to understand what effect the Controller Queue parameters may have on the reliability of data transmission.
|
||||
|
||||
You are allowed to send only 1% of the time.
|
||||
Meaning if your message takes 100 msec to send, you can only send a message every 10 seconds.
|
||||
|
||||
As a rule of thumb, the time needed to send a message of N bytes doubles for every step up in the Spreading Factor.
|
||||
|
||||
For example, a message sent at SF7 may take 100 msec to send.
|
||||
The same message sent at SF8, will take 200 msec.
|
||||
At SF9 takes 400 msec, etc. The slowest is SF12.
|
||||
|
||||
The RN2483 module does keep track of the used air time, per channel.
|
||||
Meaning it is possible to send a burst of upto 8 messages (since we have 8 channels) after which we have to wait for a free channel to send out a new one.
|
||||
|
||||
As with any other ESPEasy controller, there is a queue mechanism to manage the messages ready to be sent and also allow for a number of retries.
|
||||
|
||||
This number of retries is even more important on this LoRaWAN TTN controller.
|
||||
If sending a message fails due to no free channels, the minimum send interval will be dynamically increased, based on the air time of the message to be sent.
|
||||
The dynamic adjustment is 10x the expected air time. So by setting the number of retries to 10, it is almost guaranteed the message will eventually be sent.
|
||||
10 retries with 10x the expected air time equals a maximum of 100x the expected air time, which eventually will be as low as 1% of the time sending.
|
||||
|
||||
N.B. This expected air time is dependant on the set Spread Factor and the length of the message.
|
||||
|
||||
In practice the messages will be sent in bursts, and thus the extra wait time is often 2 - 3x the expected air time of the message.
|
||||
So on setups with a large variation in message sizes, it makes sense to send the large ones at the start of a message burst.
|
||||
|
||||
Setting the number of retries high (e.g. 10x), may be useful to make sure a sequence of messages in a burst will all get sent.
|
||||
But it may also lead to a large number of messages to be lost as the queue is full.
|
||||
So it depends on the use case what will be the best strategy here.
|
||||
|
||||
At least the Minimum Send Interval can be kept low (e.g. the default 100 msec) to allow for quickly sending out a burst of upto 8 messages.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -242,6 +242,11 @@ String rn2xx3::peekLastError() const
|
||||
return _rn2xx3_handler.peekLastError();
|
||||
}
|
||||
|
||||
float rn2xx3::getLoRaAirTime(uint8_t pl) const
|
||||
{
|
||||
return _rn2xx3_handler.getLoRaAirTime(pl);
|
||||
}
|
||||
|
||||
String rn2xx3::getLastError()
|
||||
{
|
||||
return _rn2xx3_handler.getLastError();
|
||||
|
||||
@@ -221,6 +221,42 @@ public:
|
||||
* This can be overwritten by the network when using OTAA.
|
||||
* So to force a datarate, call this function after initOTAA().
|
||||
*/
|
||||
/*
|
||||
EU / CN
|
||||
Frequencies:
|
||||
|
||||
EU 863-870 MHz (LoRaWAN Specification (2015), Page 35, Table 14)
|
||||
CN 779-787 MHz (LoRaWAN Specification (2015), Page 44, Table 25)
|
||||
EU 433 MHz (LoRaWAN Specification (2015), Page 48, Table 31)
|
||||
DataRate Modulation SF BW bit/s
|
||||
0 LoRa 12 125 250
|
||||
1 LoRa 11 125 440
|
||||
2 LoRa 10 125 980
|
||||
3 LoRa 9 125 1'760
|
||||
4 LoRa 8 125 3'125
|
||||
5 LoRa 7 125 5'470
|
||||
6 LoRa 7 250 11'000
|
||||
7 FSK 50 kbps 50'000
|
||||
Data rates 8-15 are reserved.
|
||||
|
||||
US
|
||||
Frequencies:
|
||||
|
||||
US 902-928 MHz (LoRaWAN Specification (2015), Page 40, Table 18)
|
||||
DataRate Modulation SF BW bit/s
|
||||
0 LoRa 10 125 980
|
||||
1 LoRa 9 125 1'760
|
||||
2 LoRa 8 125 3'125
|
||||
3 LoRa 7 125 5'470
|
||||
4 LoRa 8 500 12'500
|
||||
8 LoRa 12 500 980
|
||||
9 LoRa 11 500 1'760
|
||||
10 LoRa 10 500 3'900
|
||||
11 LoRa 9 500 7'000
|
||||
12 LoRa 8 500 12'500
|
||||
13 LoRa 7 500 21'900
|
||||
Data rates 5-7 and 14-15 are reserved.
|
||||
*/
|
||||
bool setDR(int dr);
|
||||
|
||||
/*
|
||||
@@ -287,6 +323,11 @@ public:
|
||||
|
||||
String peekLastError() const;
|
||||
|
||||
// Compute the air time for a packet in msec.
|
||||
// Formula used from https://www.loratools.nl/#/airtime
|
||||
// @param pl Payload length in bytes
|
||||
float getLoRaAirTime(uint8_t pl) const;
|
||||
|
||||
bool hasJoined() const {
|
||||
return _rn2xx3_handler.Status.Joined;
|
||||
}
|
||||
|
||||
@@ -441,6 +441,7 @@ bool rn2xx3_handler::setSF(uint8_t sf)
|
||||
if (dr >= 0)
|
||||
{
|
||||
_sf = sf;
|
||||
_dr = dr;
|
||||
return setDR(dr);
|
||||
}
|
||||
}
|
||||
@@ -590,10 +591,10 @@ bool rn2xx3_handler::setFrequencyPlan(RN2xx3_datatypes::Freq_plan fp)
|
||||
default:
|
||||
{
|
||||
// set default channels 868.1, 868.3 and 868.5?
|
||||
returnValue = false; // well we didn't do anything, so yes, false
|
||||
break;
|
||||
return false; // well we didn't do anything, so yes, false
|
||||
}
|
||||
}
|
||||
_fp = fp;
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
@@ -697,6 +698,56 @@ bool rn2xx3_handler::getRxDelayValues(uint32_t& rxdelay1,
|
||||
return _rxdelay1 != 0 && _rxdelay2 != 0;
|
||||
}
|
||||
|
||||
float rn2xx3_handler::getLoRaAirTime(uint8_t pl) const
|
||||
{
|
||||
uint8_t sf = _sf; // Spreading factor 7 - 12
|
||||
uint16_t bw = 125; // Bandwidth 125 kHz default for LoRaWAN. 250 kHz also supported.
|
||||
uint8_t cr = 1; // Code Rate 4 / (CR + 4) = 4/5. 4/5 default for LoRaWAN
|
||||
uint8_t n_preamble = 8; // Preamble length Default for frame = 8, beacon = 10
|
||||
bool header = true; // Explicit header Default on for LoRaWAN
|
||||
bool crc = true; // CRC Default on for LoRaWAN
|
||||
|
||||
if (sf > 12) {
|
||||
sf = 12;
|
||||
} else if (sf < 7) {
|
||||
sf = 7;
|
||||
}
|
||||
|
||||
if (cr > 4) {
|
||||
cr = 4;
|
||||
} else if (cr < 1) {
|
||||
cr = 1;
|
||||
}
|
||||
|
||||
// Symbols in frame
|
||||
int payload_length = 8;
|
||||
{
|
||||
int beta_offset = 28;
|
||||
|
||||
if (crc) { beta_offset += 16; }
|
||||
|
||||
if (!header) { beta_offset -= 20; }
|
||||
float beta_f = 8.0f * pl - 4.0f * sf + beta_offset;
|
||||
bool lowDataRateOptimization = (bw == 125 && sf >= 11);
|
||||
|
||||
if (lowDataRateOptimization) {
|
||||
beta_f = beta_f / (4.0f * (sf - 2));
|
||||
} else {
|
||||
beta_f = beta_f / (4.0f * sf);
|
||||
}
|
||||
int beta = static_cast<int>(beta_f + 1.0f); // ceil
|
||||
|
||||
if (beta > 0) {
|
||||
payload_length += (beta * (cr + 4));
|
||||
}
|
||||
}
|
||||
|
||||
// t_symbol and t_air in msec
|
||||
float t_symbol = static_cast<float>(1 << sf) / bw;
|
||||
float t_air = ((n_preamble + 4.25f) + payload_length) * t_symbol;
|
||||
return t_air;
|
||||
}
|
||||
|
||||
void rn2xx3_handler::set_state(rn2xx3_handler::RN_state state) {
|
||||
const bool was_processing_cmd = _processing_cmd != Active_cmd::none;
|
||||
|
||||
|
||||
@@ -189,6 +189,12 @@ public:
|
||||
bool getRxDelayValues(uint32_t& rxdelay1,
|
||||
uint32_t& rxdelay2);
|
||||
|
||||
// Compute the air time for a packet in msec.
|
||||
// Formula used from https://www.loratools.nl/#/airtime
|
||||
// @param pl Payload length in bytes
|
||||
float getLoRaAirTime(uint8_t pl) const;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
RN2xx3_datatypes::Model configureModuleType();
|
||||
@@ -259,6 +265,7 @@ private:
|
||||
RN2xx3_datatypes::Model _moduleType = RN2xx3_datatypes::Model::RN_NA;
|
||||
RN2xx3_datatypes::Freq_plan _fp = RN2xx3_datatypes::Freq_plan::TTN_EU;
|
||||
uint8_t _sf = 7;
|
||||
uint8_t _dr = 5;
|
||||
|
||||
bool _otaa = true; // Switch between OTAA or ABP activation (default OTAA)
|
||||
bool _asyncMode = false; // When set, the user must call async_loop() frequently
|
||||
|
||||
@@ -544,13 +544,9 @@ function Converter(decoded, port) {
|
||||
|
||||
case 82:
|
||||
converted.name = "GPS";
|
||||
// The GPS plugin must be set first to output like this.
|
||||
// GPS data is already decoded in packed_decoder.js
|
||||
// HDOP is needed by TTN mapper to weigh the quality of the data.
|
||||
// When using TTN mapper, make sure to output these values.
|
||||
// converted.longitude = converted.val_1;
|
||||
// converted.latitude = converted.val_2;
|
||||
// converted.altitude = converted.val_3;
|
||||
// converted.hdop = converted.val_4;
|
||||
break;
|
||||
|
||||
case 83:
|
||||
@@ -568,12 +564,16 @@ function Converter(decoded, port) {
|
||||
|
||||
case 85:
|
||||
converted.name = "AcuDC243";
|
||||
// This plugin can output any value, just using the default settings here.
|
||||
// TODO TD-er: Must add binary representation of all values
|
||||
converted.volt = converted.val_1;
|
||||
converted.current = converted.val_2;
|
||||
converted.watt = converted.val_3;
|
||||
converted.wh_total = converted.val_4;
|
||||
// This plugin can output any value, so show string representation
|
||||
// of the unit of measure
|
||||
converted.unit1 = getAcuDC243Unit(converted.unit1);
|
||||
converted.unit2 = getAcuDC243Unit(converted.unit2);
|
||||
converted.unit3 = getAcuDC243Unit(converted.unit3);
|
||||
converted.unit4 = getAcuDC243Unit(converted.unit4);
|
||||
converted.v1 = converted.val_1;
|
||||
converted.v2 = converted.val_2;
|
||||
converted.v3 = converted.val_3;
|
||||
converted.v4 = converted.val_4;
|
||||
break;
|
||||
|
||||
case 86:
|
||||
@@ -668,4 +668,28 @@ function Converter(decoded, port) {
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
function getAcuDC243Unit(unit_id) {
|
||||
switch (unit_id) {
|
||||
case 0: // P085_QUERY_V 0
|
||||
return "V";
|
||||
case 1: // P085_QUERY_A 1
|
||||
return "A";
|
||||
case 2: // P085_QUERY_W 2
|
||||
return "W";
|
||||
case 3: // P085_QUERY_Wh_imp 3
|
||||
return "Wh imp";
|
||||
case 4: // P085_QUERY_Wh_exp 4
|
||||
return "Wh exp";
|
||||
case 5: // P085_QUERY_Wh_tot 5
|
||||
return "Wh total";
|
||||
case 6: // P085_QUERY_Wh_net 6
|
||||
return "Wh net";
|
||||
case 7: // P085_QUERY_h_tot 7
|
||||
return "hours tot";
|
||||
case 8: // P085_QUERY_h_load 8
|
||||
return "hours load";
|
||||
}
|
||||
return "unknown" + unit_id;
|
||||
}
|
||||
@@ -21,8 +21,22 @@ function Decoder(bytes, port) {
|
||||
|
||||
case 82:
|
||||
// GPS
|
||||
return decode(bytes, [header, latLng, latLng, altitude, uint16_1e2, hdop, uint8, uint8],
|
||||
['header', 'latitude', 'longitude', 'altitude', 'speed', 'hdop', 'max_snr', 'sat_tracked']);
|
||||
if (bytes.length === 18) {
|
||||
return decode(bytes, [header, latLng, latLng, altitude, uint16_1e2, hdop, uint8, uint8],
|
||||
['header', 'latitude', 'longitude', 'altitude', 'speed', 'hdop', 'max_snr', 'sat_tracked']);
|
||||
} else if (bytes.length === 21) {
|
||||
return decode(bytes, [header, latLng, latLng, altitude, uint16_1e2, hdop, uint8, uint8, uint24_1e2],
|
||||
['header', 'latitude', 'longitude', 'altitude', 'speed', 'hdop', 'max_snr', 'sat_tracked', 'distance_total_km']);
|
||||
} else {
|
||||
return decode(bytes, [header, latLng, latLng, altitude, uint16_1e2, hdop, uint8, uint8, uint24_1e2, uint24_1e1],
|
||||
['header', 'latitude', 'longitude', 'altitude', 'speed', 'hdop', 'max_snr', 'sat_tracked', 'distance_total_km', 'distance_ref']);
|
||||
}
|
||||
|
||||
case 85:
|
||||
// AcuDC243
|
||||
return decode(bytes, [header, uint8, int32_1e4, uint8, int32_1e4, uint8, int32_1e4, uint8, int32_1e4],
|
||||
['header', 'unit1', 'val_1', 'unit2', 'val_2', 'unit3', 'val_3', 'unit4', 'val_4']);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -161,17 +161,18 @@ String NetworkGetHostname() {
|
||||
// ********************************************************************************
|
||||
// Determine Wifi AP name to set. (also used for mDNS)
|
||||
// ********************************************************************************
|
||||
String NetworkGetHostNameFromSettings()
|
||||
String NetworkGetHostNameFromSettings(bool force_add_unitnr)
|
||||
{
|
||||
if (force_add_unitnr) return Settings.getHostname(true);
|
||||
return Settings.getHostname();
|
||||
}
|
||||
|
||||
String NetworkCreateRFCCompliantHostname() {
|
||||
return createRFCCompliantHostname(NetworkGetHostNameFromSettings());
|
||||
String NetworkCreateRFCCompliantHostname(bool force_add_unitnr) {
|
||||
return createRFCCompliantHostname(NetworkGetHostNameFromSettings(force_add_unitnr));
|
||||
}
|
||||
|
||||
// Create hostname with - instead of spaces
|
||||
String createRFCCompliantHostname(String oldString) {
|
||||
String createRFCCompliantHostname(const String& oldString) {
|
||||
String result(oldString);
|
||||
|
||||
result.replace(" ", "-");
|
||||
|
||||
@@ -11,10 +11,10 @@ IPAddress NetworkGatewayIP();
|
||||
IPAddress NetworkDnsIP (uint8_t dns_no);
|
||||
uint8_t * NetworkMacAddressAsBytes(uint8_t* mac);
|
||||
String NetworkMacAddress();
|
||||
String NetworkGetHostNameFromSettings();
|
||||
String NetworkGetHostNameFromSettings(bool force_add_unitnr = false);
|
||||
String NetworkGetHostname();
|
||||
String NetworkCreateRFCCompliantHostname();
|
||||
String createRFCCompliantHostname(String oldString);
|
||||
String NetworkCreateRFCCompliantHostname(bool force_add_unitnr = false);
|
||||
String createRFCCompliantHostname(const String& oldString);
|
||||
String WifiSoftAPmacAddress();
|
||||
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ void WiFiEvent(system_event_id_t event, system_event_info_t info) {
|
||||
ssid_copy[32] = 0; // Potentially add 0-termination if none present earlier
|
||||
last_ssid = (const char*) ssid_copy;
|
||||
lastConnectMoment.setNow();
|
||||
wifi_considered_stable = false;
|
||||
processedConnect = false;
|
||||
break;
|
||||
}
|
||||
@@ -82,7 +81,7 @@ void WiFiEvent(system_event_id_t event, system_event_info_t info) {
|
||||
WiFi.persistent(false);
|
||||
WiFi.disconnect(true);
|
||||
|
||||
if (last_wifi_connect_attempt_moment.isSet() && (lastConnectMoment > last_wifi_connect_attempt_moment)) {
|
||||
if (last_wifi_connect_attempt_moment.isSet() && (!lastConnectMoment.isSet())) {
|
||||
// There was an unsuccessful connection attempt
|
||||
lastConnectedDuration_us = last_wifi_connect_attempt_moment.timeDiff(lastDisconnectMoment);
|
||||
} else {
|
||||
@@ -169,7 +168,6 @@ void WiFiEvent(system_event_id_t event, system_event_info_t info) {
|
||||
|
||||
void onConnected(const WiFiEventStationModeConnected& event) {
|
||||
lastConnectMoment.setNow();
|
||||
wifi_considered_stable = false;
|
||||
processedConnect = false;
|
||||
channel_changed = RTC.lastWiFiChannel != event.channel;
|
||||
RTC.lastWiFiChannel = event.channel;
|
||||
@@ -187,7 +185,7 @@ void onConnected(const WiFiEventStationModeConnected& event) {
|
||||
void onDisconnect(const WiFiEventStationModeDisconnected& event) {
|
||||
lastDisconnectMoment.setNow();
|
||||
|
||||
if (lastConnectMoment > last_wifi_connect_attempt_moment) {
|
||||
if (last_wifi_connect_attempt_moment.isSet() && !lastConnectMoment.isSet()) {
|
||||
// There was an unsuccessful connection attempt
|
||||
lastConnectedDuration_us = last_wifi_connect_attempt_moment.timeDiff(lastDisconnectMoment);
|
||||
} else {
|
||||
|
||||
+15
-4
@@ -213,7 +213,11 @@ void WiFiConnectRelaxed() {
|
||||
log += wifi_connect_attempt + 1;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
wifiStatus = ESPEASY_WIFI_DISCONNECTED;
|
||||
lastDisconnectMoment.clear();
|
||||
lastConnectMoment.clear();
|
||||
lastGetIPmoment.clear();
|
||||
wifi_considered_stable = false;
|
||||
last_wifi_connect_attempt_moment.setNow();
|
||||
wifiConnectInProgress = true;
|
||||
|
||||
@@ -258,7 +262,9 @@ bool prepareWiFi() {
|
||||
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
|
||||
#endif // if defined(ESP32)
|
||||
|
||||
if (RTC.lastWiFiChannel == 0 && wifi_connect_attempt <= 1) {
|
||||
const bool canSkipScan = RTC.lastWiFiChannel != 0 && wifi_connect_attempt <= 1;
|
||||
|
||||
if (!canSkipScan) {
|
||||
WifiScan(false, true);
|
||||
}
|
||||
setConnectionSpeed();
|
||||
@@ -322,7 +328,7 @@ void resetWiFi() {
|
||||
// Don't reset WiFi too often
|
||||
return;
|
||||
}
|
||||
lastDisconnectMoment.setNow();
|
||||
lastDisconnectMoment.clear();
|
||||
lastWiFiResetMoment.setNow();
|
||||
wifiStatus = ESPEASY_WIFI_DISCONNECTED;
|
||||
lastConnectMoment.clear();
|
||||
@@ -645,7 +651,12 @@ bool wifiConnectTimeoutReached() {
|
||||
// For the first attempt, do not wait to start connecting.
|
||||
if (wifi_connect_attempt == 0) { return true; }
|
||||
|
||||
if (lastDisconnectMoment.isSet() && (last_wifi_connect_attempt_moment > lastDisconnectMoment)) {
|
||||
if (!last_wifi_connect_attempt_moment.isSet()) {
|
||||
// No attempt made
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lastDisconnectMoment.isSet()) {
|
||||
// Connection attempt was already ended.
|
||||
return true;
|
||||
}
|
||||
@@ -656,7 +667,7 @@ bool wifiConnectTimeoutReached() {
|
||||
}
|
||||
|
||||
// wait until it connects + add some device specific random offset to prevent
|
||||
// all nodes overloading the accesspoint when turning on at the same time.
|
||||
// all nodes overloading the access point when turning on at the same time.
|
||||
#if defined(ESP8266)
|
||||
const unsigned int randomOffset_in_msec = (wifi_connect_attempt == 1) ? 0 : 1000 * ((ESP.getChipId() & 0xF));
|
||||
#endif // if defined(ESP8266)
|
||||
|
||||
@@ -128,7 +128,7 @@ void handle_unprocessedWiFiEvents()
|
||||
if (wifi_connect_attempt > 0) {
|
||||
// We only want to clear this counter if the connection is currently stable.
|
||||
if (bitRead(wifiStatus, ESPEASY_WIFI_SERVICES_INITIALIZED)) {
|
||||
if (lastConnectMoment.timeoutReached(WIFI_CONNECTION_CONSIDERED_STABLE)) {
|
||||
if (lastConnectMoment.isSet() && lastConnectMoment.timeoutReached(WIFI_CONNECTION_CONSIDERED_STABLE)) {
|
||||
// Connection considered stable
|
||||
wifi_connect_attempt = 0;
|
||||
wifi_considered_stable = true;
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ void syslog(byte logLevel, const char *message)
|
||||
// Using Settings.Name as the Hostname (Hostname must NOT content space)
|
||||
{
|
||||
String header;
|
||||
String hostname = Settings.Name;
|
||||
String hostname = NetworkCreateRFCCompliantHostname(true);
|
||||
hostname.trim();
|
||||
hostname.replace(' ', '_');
|
||||
header.reserve(16 + hostname.length());
|
||||
|
||||
@@ -349,12 +349,13 @@ void addFloatNumberBox(const String& id, float value, float min, float max)
|
||||
html += id;
|
||||
html += '\'';
|
||||
html += F(" min=");
|
||||
html += min;
|
||||
html += String(min, 6);
|
||||
html += F(" max=");
|
||||
html += max;
|
||||
html += F(" step=0.01");
|
||||
html += F(" style='width:5em;' value=");
|
||||
html += value;
|
||||
html += String(max, 6);
|
||||
html += F(" step=0.000001");
|
||||
|
||||
html += F(" style='width:7em;' value=");
|
||||
html += String(value, 6);
|
||||
html += '>';
|
||||
|
||||
addHtml(html);
|
||||
|
||||
@@ -18,6 +18,7 @@ void handle_timingstats() {
|
||||
html_table_header(F("Function"));
|
||||
html_table_header(F("#calls"));
|
||||
html_table_header(F("call/sec"));
|
||||
html_table_header(F("duty (%)"));
|
||||
html_table_header(F("min (ms)"));
|
||||
html_table_header(F("Avg (ms)"));
|
||||
html_table_header(F("max (ms)"));
|
||||
@@ -35,6 +36,8 @@ void handle_timingstats() {
|
||||
addRowLabel(F("Time span"));
|
||||
addHtml(String(timespan));
|
||||
addHtml(F(" sec"));
|
||||
addRowLabel(F("*"));
|
||||
addHtml(F("Duty cycle based on average < 1 msec is highly unreliable"));
|
||||
html_end_table();
|
||||
|
||||
sendHeadandTail_stdtemplate(_TAIL);
|
||||
@@ -56,17 +59,34 @@ void format_using_threshhold(unsigned long value) {
|
||||
|
||||
void stream_html_timing_stats(const TimingStats& stats, long timeSinceLastReset) {
|
||||
unsigned long minVal, maxVal;
|
||||
unsigned int c = stats.getMinMax(minVal, maxVal);
|
||||
const unsigned int c = stats.getMinMax(minVal, maxVal);
|
||||
|
||||
html_TD();
|
||||
addHtml(String(c));
|
||||
html_TD();
|
||||
float call_per_sec = static_cast<float>(c) / static_cast<float>(timeSinceLastReset) * 1000.0f;
|
||||
const float call_per_sec = static_cast<float>(c) / static_cast<float>(timeSinceLastReset) * 1000.0f;
|
||||
const float avg = stats.getAvg();
|
||||
addHtml(String(call_per_sec, 2));
|
||||
html_TD();
|
||||
{
|
||||
const float duty = (call_per_sec * avg / 10000.0f);
|
||||
String duty_str = String(duty, 2);
|
||||
if (avg < 1000) {
|
||||
// Unreliable as average is below 1 msec
|
||||
duty_str += '*';
|
||||
html_I(duty_str);
|
||||
} else if (duty > 10.0f) {
|
||||
// Over 10% of the time
|
||||
html_B(duty_str);
|
||||
} else {
|
||||
addHtml(duty_str);
|
||||
}
|
||||
}
|
||||
|
||||
html_TD();
|
||||
format_using_threshhold(minVal);
|
||||
html_TD();
|
||||
format_using_threshhold(stats.getAvg());
|
||||
format_using_threshhold(avg);
|
||||
html_TD();
|
||||
format_using_threshhold(maxVal);
|
||||
}
|
||||
|
||||
+62
-6
@@ -105,6 +105,10 @@ struct C018_data_struct {
|
||||
return res;
|
||||
}
|
||||
|
||||
bool command_finished() const {
|
||||
return myLora->command_finished();
|
||||
}
|
||||
|
||||
bool txUncnfBytes(const byte *data, uint8_t size, uint8_t port) {
|
||||
bool res = myLora->txBytes(data, size, port) != RN2xx3_datatypes::TX_return_type::TX_FAIL;
|
||||
|
||||
@@ -268,9 +272,17 @@ struct C018_data_struct {
|
||||
return sampleSetCounter;
|
||||
}
|
||||
|
||||
float getLoRaAirTime(uint8_t pl) const {
|
||||
if (isInitialized()) {
|
||||
return myLora->getLoRaAirTime(pl + 13); // We have a LoRaWAN header of 13 bytes.
|
||||
}
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
void async_loop() {
|
||||
if (isInitialized()) {
|
||||
rn2xx3_handler::RN_state state = myLora->async_loop();
|
||||
|
||||
if (rn2xx3_handler::RN_state::must_perform_init == state) {
|
||||
if (myLora->get_busy_count() > 10) {
|
||||
if (_resetPin != -1) {
|
||||
@@ -281,7 +293,8 @@ struct C018_data_struct {
|
||||
delay(200);
|
||||
}
|
||||
autobaud_success = false;
|
||||
// triggerAutobaud();
|
||||
|
||||
// triggerAutobaud();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -749,10 +762,10 @@ bool C018_init(struct EventStruct *event) {
|
||||
|
||||
LoadControllerSettings(event->ControllerIndex, ControllerSettings);
|
||||
C018_DelayHandler->configureControllerSettings(ControllerSettings);
|
||||
AppEUI = getControllerUser(event->ControllerIndex, ControllerSettings);
|
||||
AppKey = getControllerPass(event->ControllerIndex, ControllerSettings);
|
||||
AppEUI = getControllerUser(event->ControllerIndex, ControllerSettings);
|
||||
AppKey = getControllerPass(event->ControllerIndex, ControllerSettings);
|
||||
SampleSetInitiator = ControllerSettings.SampleSetInitiator;
|
||||
Port = ControllerSettings.Port;
|
||||
Port = ControllerSettings.Port;
|
||||
}
|
||||
|
||||
std::shared_ptr<C018_ConfigStruct> customConfig(new C018_ConfigStruct);
|
||||
@@ -808,8 +821,36 @@ bool do_process_c018_delay_queue(int controller_number, const C018_queue_element
|
||||
// *INDENT-ON*
|
||||
|
||||
bool do_process_c018_delay_queue(int controller_number, const C018_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
|
||||
bool success = C018_data.txHexBytes(element.packed, ControllerSettings.Port);
|
||||
String error = C018_data.getLastError(); // Clear the error string.
|
||||
uint8_t pl = (element.packed.length() / 2);
|
||||
float airtime_ms = C018_data.getLoRaAirTime(pl);
|
||||
bool mustSetDelay = false;
|
||||
bool success = false;
|
||||
|
||||
if (!C018_data.command_finished()) {
|
||||
mustSetDelay = true;
|
||||
} else {
|
||||
success = C018_data.txHexBytes(element.packed, ControllerSettings.Port);
|
||||
|
||||
if (success) {
|
||||
if (airtime_ms > 0.0) {
|
||||
ADD_TIMER_STAT(C018_AIR_TIME, static_cast<unsigned long>(airtime_ms * 1000));
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("LoRaWAN : Payload Length: ");
|
||||
log += pl + 13; // We have a LoRaWAN header of 13 bytes.
|
||||
log += F(" Air Time: ");
|
||||
log += String(airtime_ms, 3);
|
||||
log += F(" ms");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
String error = C018_data.getLastError(); // Clear the error string.
|
||||
|
||||
if (error.indexOf(F("no_free_ch")) != -1) {
|
||||
mustSetDelay = true;
|
||||
}
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("C018 : Sent: ");
|
||||
@@ -823,6 +864,21 @@ bool do_process_c018_delay_queue(int controller_number, const C018_queue_element
|
||||
log += error;
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
|
||||
if (mustSetDelay) {
|
||||
// Module is still sending, delay for 10x expected air time, which is equivalent of 10% air time duty cycle.
|
||||
// This can be retried a few times, so at most 10 retries like these are needed to get below 1% air time again.
|
||||
// Very likely only 2 - 3 of these delays are needed, as we have 8 channels to send from and messages are likely sent in bursts.
|
||||
C018_DelayHandler->setAdditionalDelay(10 * airtime_ms);
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("LoRaWAN : Unable to send. Delay for ");
|
||||
log += 10 * airtime_ms;
|
||||
log += F(" ms");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
+131
-270
@@ -1,3 +1,5 @@
|
||||
#include "_Plugin_Helper.h"
|
||||
|
||||
#ifdef USES_P082
|
||||
|
||||
// #######################################################################################################
|
||||
@@ -10,215 +12,45 @@
|
||||
//
|
||||
//
|
||||
|
||||
#include <ESPeasySerial.h>
|
||||
#include <TinyGPS++.h>
|
||||
#include "_Plugin_Helper.h"
|
||||
#include "ESPEasy_packed_raw_data.h"
|
||||
# include "ESPEasy_packed_raw_data.h"
|
||||
|
||||
#include "src/Globals/ESPEasy_time.h"
|
||||
#include "src/Helpers/ESPEasy_time_calc.h"
|
||||
# include "src/Globals/ESPEasy_time.h"
|
||||
# include "src/Helpers/ESPEasy_time_calc.h"
|
||||
|
||||
#define PLUGIN_082
|
||||
#define PLUGIN_ID_082 82
|
||||
#define PLUGIN_NAME_082 "Position - GPS [TESTING]"
|
||||
#define PLUGIN_VALUENAME1_082 "Longitude"
|
||||
#define PLUGIN_VALUENAME2_082 "Latitude"
|
||||
#define PLUGIN_VALUENAME3_082 "Altitude"
|
||||
#define PLUGIN_VALUENAME4_082 "Speed"
|
||||
# include "src/PluginStructs/P082_data_struct.h"
|
||||
|
||||
# define PLUGIN_082
|
||||
# define PLUGIN_ID_082 82
|
||||
# define PLUGIN_NAME_082 "Position - GPS [TESTING]"
|
||||
# define PLUGIN_VALUENAME1_082 "Longitude"
|
||||
# define PLUGIN_VALUENAME2_082 "Latitude"
|
||||
# define PLUGIN_VALUENAME3_082 "Altitude"
|
||||
# define PLUGIN_VALUENAME4_082 "Speed"
|
||||
|
||||
|
||||
#define P082_TIMEOUT PCONFIG(0)
|
||||
#define P082_TIMEOUT_LABEL PCONFIG_LABEL(0)
|
||||
#define P082_BAUDRATE PCONFIG(1)
|
||||
#define P082_BAUDRATE_LABEL PCONFIG_LABEL(1)
|
||||
#define P082_DISTANCE PCONFIG(2)
|
||||
#define P082_DISTANCE_LABEL PCONFIG_LABEL(2)
|
||||
#define P082_QUERY1 PCONFIG(3)
|
||||
#define P082_QUERY2 PCONFIG(4)
|
||||
#define P082_QUERY3 PCONFIG(5)
|
||||
#define P082_QUERY4 PCONFIG(6)
|
||||
# define P082_TIMEOUT PCONFIG(0)
|
||||
# define P082_TIMEOUT_LABEL PCONFIG_LABEL(0)
|
||||
# define P082_BAUDRATE PCONFIG(1)
|
||||
# define P082_BAUDRATE_LABEL PCONFIG_LABEL(1)
|
||||
# define P082_DISTANCE PCONFIG(2)
|
||||
# define P082_DISTANCE_LABEL PCONFIG_LABEL(2)
|
||||
# define P082_QUERY1 PCONFIG(3)
|
||||
# define P082_QUERY2 PCONFIG(4)
|
||||
# define P082_QUERY3 PCONFIG(5)
|
||||
# define P082_QUERY4 PCONFIG(6)
|
||||
# define P082_LONG_REF PCONFIG_FLOAT(0)
|
||||
# define P082_LAT_REF PCONFIG_FLOAT(1)
|
||||
|
||||
#define P082_NR_OUTPUT_VALUES VARS_PER_TASK
|
||||
#define P082_QUERY1_CONFIG_POS 3
|
||||
# define P082_NR_OUTPUT_VALUES VARS_PER_TASK
|
||||
# define P082_QUERY1_CONFIG_POS 3
|
||||
|
||||
|
||||
#define P082_QUERY_LONG 0
|
||||
#define P082_QUERY_LAT 1
|
||||
#define P082_QUERY_ALT 2
|
||||
#define P082_QUERY_SPD 3
|
||||
#define P082_QUERY_SATVIS 4
|
||||
#define P082_QUERY_SATUSE 5
|
||||
#define P082_QUERY_HDOP 6
|
||||
#define P082_QUERY_FIXQ 7
|
||||
#define P082_QUERY_DB_MAX 8
|
||||
#define P082_QUERY_CHKSUM_FAIL 9
|
||||
#define P082_NR_OUTPUT_OPTIONS 10
|
||||
# define P082_DISTANCE_DFLT 0 // Disable update per distance travelled.
|
||||
# define P082_QUERY1_DFLT P082_query::P082_QUERY_LONG
|
||||
# define P082_QUERY2_DFLT P082_query::P082_QUERY_LAT
|
||||
# define P082_QUERY3_DFLT P082_query::P082_QUERY_ALT
|
||||
# define P082_QUERY4_DFLT P082_query::P082_QUERY_SPD
|
||||
|
||||
#define P082_TIMESTAMP_AGE 1500
|
||||
#define P082_DEFAULT_FIX_TIMEOUT 2500 // TTL of fix status in ms since last update
|
||||
#define P082_DISTANCE_DFLT 0 // Disable update per distance travelled.
|
||||
#define P082_QUERY1_DFLT P082_QUERY_LONG
|
||||
#define P082_QUERY2_DFLT P082_QUERY_LAT
|
||||
#define P082_QUERY3_DFLT P082_QUERY_ALT
|
||||
#define P082_QUERY4_DFLT P082_QUERY_SPD
|
||||
|
||||
|
||||
#define P082_SEND_GPS_TO_LOG
|
||||
|
||||
struct P082_data_struct : public PluginTaskData_base {
|
||||
P082_data_struct() : gps(nullptr), P082_easySerial(nullptr) {}
|
||||
|
||||
~P082_data_struct() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (gps != nullptr) {
|
||||
delete gps;
|
||||
gps = nullptr;
|
||||
}
|
||||
|
||||
if (P082_easySerial != nullptr) {
|
||||
delete P082_easySerial;
|
||||
P082_easySerial = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool init(ESPEasySerialPort port, const int16_t serial_rx, const int16_t serial_tx) {
|
||||
if (serial_rx < 0) {
|
||||
return false;
|
||||
}
|
||||
reset();
|
||||
gps = new (std::nothrow) TinyGPSPlus();
|
||||
P082_easySerial = new (std::nothrow) ESPeasySerial(port, serial_rx, serial_tx);
|
||||
if (P082_easySerial != nullptr) {
|
||||
P082_easySerial->begin(9600);
|
||||
}
|
||||
return isInitialized();
|
||||
}
|
||||
|
||||
bool isInitialized() const {
|
||||
return gps != nullptr && P082_easySerial != nullptr;
|
||||
}
|
||||
|
||||
bool loop() {
|
||||
if (!isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
bool completeSentence = false;
|
||||
|
||||
if (P082_easySerial != nullptr) {
|
||||
int available = P082_easySerial->available();
|
||||
unsigned long startLoop = millis();
|
||||
while (available > 0 && timePassedSince(startLoop) < 10) {
|
||||
--available;
|
||||
char c = P082_easySerial->read();
|
||||
#ifdef P082_SEND_GPS_TO_LOG
|
||||
currentSentence += c;
|
||||
#endif // ifdef P082_SEND_GPS_TO_LOG
|
||||
|
||||
if (gps->encode(c)) {
|
||||
// Full sentence received
|
||||
#ifdef P082_SEND_GPS_TO_LOG
|
||||
lastSentence = currentSentence;
|
||||
currentSentence = "";
|
||||
#endif // ifdef P082_SEND_GPS_TO_LOG
|
||||
completeSentence = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return completeSentence;
|
||||
}
|
||||
|
||||
bool hasFix(unsigned int maxAge_msec) {
|
||||
if (!isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
return gps->location.isValid() && gps->location.age() < maxAge_msec;
|
||||
}
|
||||
|
||||
bool storeCurPos(unsigned int maxAge_msec) {
|
||||
if (!hasFix(maxAge_msec)) {
|
||||
return false;
|
||||
}
|
||||
last_lat = gps->location.lat();
|
||||
last_lng = gps->location.lng();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the distance in meters compared to last stored position.
|
||||
// @retval -1 when no fix.
|
||||
double distanceSinceLast(unsigned int maxAge_msec) {
|
||||
if (!hasFix(maxAge_msec)) {
|
||||
return -1.0;
|
||||
}
|
||||
if ((last_lat < 0.0001 && last_lat > -0.0001) || (last_lng < 0.0001 && last_lng > -0.0001)) {
|
||||
return -1.0;
|
||||
}
|
||||
return gps->distanceBetween(last_lat, last_lng, gps->location.lat(), gps->location.lng());
|
||||
}
|
||||
|
||||
// Return the GPS time stamp, which is in UTC.
|
||||
// @param age is the time in msec since the last update of the time +
|
||||
// additional centiseconds given by the GPS.
|
||||
bool getDateTime(struct tm& dateTime, uint32_t& age, bool& pps_sync) {
|
||||
if (!isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pps_time != 0) {
|
||||
age = timePassedSince(pps_time);
|
||||
pps_time = 0;
|
||||
pps_sync = true;
|
||||
|
||||
if ((age > 1000) || (gps->time.age() > age)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
age = gps->time.age();
|
||||
pps_sync = false;
|
||||
}
|
||||
|
||||
if (age > P082_TIMESTAMP_AGE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gps->date.age() > P082_TIMESTAMP_AGE) {
|
||||
return false;
|
||||
}
|
||||
if (!gps->date.isValid() || !gps->time.isValid()) {
|
||||
return false;
|
||||
}
|
||||
dateTime.tm_year = gps->date.year() - 1970;
|
||||
dateTime.tm_mon = gps->date.month();
|
||||
dateTime.tm_mday = gps->date.day();
|
||||
|
||||
dateTime.tm_hour = gps->time.hour();
|
||||
dateTime.tm_min = gps->time.minute();
|
||||
dateTime.tm_sec = gps->time.second();
|
||||
|
||||
// FIXME TD-er: Must the offset in centisecond be added when pps_sync active?
|
||||
if (!pps_sync) {
|
||||
age += (gps->time.centisecond() * 10);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TinyGPSPlus *gps = nullptr;
|
||||
ESPeasySerial *P082_easySerial = nullptr;
|
||||
|
||||
double last_lat = 0.0;
|
||||
double last_lng = 0.0;
|
||||
|
||||
unsigned long pps_time = 0;
|
||||
unsigned long last_measurement = 0;
|
||||
#ifdef P082_SEND_GPS_TO_LOG
|
||||
String lastSentence;
|
||||
String currentSentence;
|
||||
#endif // ifdef P082_SEND_GPS_TO_LOG
|
||||
|
||||
float cache[P082_NR_OUTPUT_OPTIONS] = {0};
|
||||
};
|
||||
|
||||
// Must use volatile declared variable (which will end up in iRAM)
|
||||
volatile unsigned long P082_pps_time = 0;
|
||||
@@ -252,15 +84,15 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
for (byte i = 0; i < VARS_PER_TASK; ++i) {
|
||||
if (i < P082_NR_OUTPUT_VALUES) {
|
||||
const byte pconfigIndex = i + P082_QUERY1_CONFIG_POS;
|
||||
byte choice = PCONFIG(pconfigIndex);
|
||||
P082_query choice = static_cast<P082_query>(PCONFIG(pconfigIndex));
|
||||
safe_strncpy(
|
||||
ExtraTaskSettings.TaskDeviceValueNames[i],
|
||||
Plugin_082_valuename(choice, false),
|
||||
sizeof(ExtraTaskSettings.TaskDeviceValueNames[i]));
|
||||
|
||||
switch (choice) {
|
||||
case P082_QUERY_LONG:
|
||||
case P082_QUERY_LAT:
|
||||
case P082_query::P082_QUERY_LONG:
|
||||
case P082_query::P082_QUERY_LAT:
|
||||
ExtraTaskSettings.TaskDeviceValueDecimals[i] = 6;
|
||||
break;
|
||||
default:
|
||||
@@ -301,10 +133,10 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
{
|
||||
P082_TIMEOUT = P082_DEFAULT_FIX_TIMEOUT;
|
||||
P082_DISTANCE = P082_DISTANCE_DFLT;
|
||||
P082_QUERY1 = P082_QUERY1_DFLT;
|
||||
P082_QUERY2 = P082_QUERY2_DFLT;
|
||||
P082_QUERY3 = P082_QUERY3_DFLT;
|
||||
P082_QUERY4 = P082_QUERY4_DFLT;
|
||||
P082_QUERY1 = static_cast<byte>(P082_QUERY1_DFLT);
|
||||
P082_QUERY2 = static_cast<byte>(P082_QUERY2_DFLT);
|
||||
P082_QUERY3 = static_cast<byte>(P082_QUERY3_DFLT);
|
||||
P082_QUERY4 = static_cast<byte>(P082_QUERY4_DFLT);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
@@ -318,19 +150,20 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
}
|
||||
|
||||
case PLUGIN_WEBFORM_LOAD: {
|
||||
|
||||
/*
|
||||
P082_data_struct *P082_data =
|
||||
static_cast<P082_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
if (nullptr != P082_data && P082_data->isInitialized()) {
|
||||
String detectedString = F("Detected: ");
|
||||
detectedString += String(P082_data->P082_easySerial->baudRate());
|
||||
detectedString += String(P082_data->easySerial->baudRate());
|
||||
addUnit(detectedString);
|
||||
*/
|
||||
|
||||
addFormNumericBox(F("Fix Timeout"), P082_TIMEOUT_LABEL, P082_TIMEOUT, 100, 10000);
|
||||
addUnit(F("ms"));
|
||||
|
||||
addFormSubHeader(F("Current Sensor Data"));
|
||||
|
||||
P082_html_show_stats(event);
|
||||
|
||||
// Settings to add:
|
||||
@@ -348,18 +181,25 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
// fixQuality, fixMode
|
||||
// statistics (chars processed, failed checksum)
|
||||
|
||||
{
|
||||
addFormSubHeader(F("Reference Point"));
|
||||
|
||||
addFormFloatNumberBox(F("Latitude"), F("lat_ref"), P082_LAT_REF, -90.0f, 90.0f);
|
||||
addFormFloatNumberBox(F("Longitude"), F("lng_ref"), P082_LONG_REF, -180.0f, 180.0f);
|
||||
}
|
||||
|
||||
{
|
||||
// In a separate scope to free memory of String array as soon as possible
|
||||
sensorTypeHelper_webformLoad_header();
|
||||
String options[P082_NR_OUTPUT_OPTIONS];
|
||||
String options[static_cast<byte>(P082_query::P082_NR_OUTPUT_OPTIONS)];
|
||||
|
||||
for (int i = 0; i < P082_NR_OUTPUT_OPTIONS; ++i) {
|
||||
options[i] = Plugin_082_valuename(i, true);
|
||||
for (byte i = 0; i < static_cast<byte>(P082_query::P082_NR_OUTPUT_OPTIONS); ++i) {
|
||||
options[i] = Plugin_082_valuename(static_cast<P082_query>(i), true);
|
||||
}
|
||||
|
||||
for (byte i = 0; i < P082_NR_OUTPUT_VALUES; ++i) {
|
||||
const byte pconfigIndex = i + P082_QUERY1_CONFIG_POS;
|
||||
sensorTypeHelper_loadOutputSelector(event, pconfigIndex, i, P082_NR_OUTPUT_OPTIONS, options);
|
||||
sensorTypeHelper_loadOutputSelector(event, pconfigIndex, i, static_cast<int>(P082_query::P082_NR_OUTPUT_OPTIONS), options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,10 +215,13 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
P082_TIMEOUT = getFormItemInt(P082_TIMEOUT_LABEL);
|
||||
P082_DISTANCE = getFormItemInt(P082_DISTANCE_LABEL);
|
||||
|
||||
P082_LONG_REF = getFormItemFloat(F("lng_ref"));
|
||||
P082_LAT_REF = getFormItemFloat(F("lat_ref"));
|
||||
|
||||
// Save output selector parameters.
|
||||
for (byte i = 0; i < P082_NR_OUTPUT_VALUES; ++i) {
|
||||
for (int i = 0; i < P082_NR_OUTPUT_VALUES; ++i) {
|
||||
const byte pconfigIndex = i + P082_QUERY1_CONFIG_POS;
|
||||
const byte choice = PCONFIG(pconfigIndex);
|
||||
const P082_query choice = static_cast<P082_query>(PCONFIG(pconfigIndex));
|
||||
sensorTypeHelper_saveOutputSelector(event, pconfigIndex, i, Plugin_082_valuename(choice, false));
|
||||
}
|
||||
|
||||
@@ -391,9 +234,9 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
P082_TIMEOUT = P082_DEFAULT_FIX_TIMEOUT;
|
||||
}
|
||||
const ESPEasySerialPort port = static_cast<ESPEasySerialPort>(CONFIG_PORT);
|
||||
const int16_t serial_rx = CONFIG_PIN1;
|
||||
const int16_t serial_tx = CONFIG_PIN2;
|
||||
const int16_t pps_pin = CONFIG_PIN3;
|
||||
const int16_t serial_rx = CONFIG_PIN1;
|
||||
const int16_t serial_tx = CONFIG_PIN2;
|
||||
const int16_t pps_pin = CONFIG_PIN3;
|
||||
initPluginTaskData(event->TaskIndex, new (std::nothrow) P082_data_struct());
|
||||
P082_data_struct *P082_data =
|
||||
static_cast<P082_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
@@ -431,9 +274,9 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
static_cast<P082_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
|
||||
if ((nullptr != P082_data) && P082_data->loop()) {
|
||||
#ifdef P082_SEND_GPS_TO_LOG
|
||||
addLog(LOG_LEVEL_DEBUG, P082_data->lastSentence);
|
||||
#endif // ifdef P082_SEND_GPS_TO_LOG
|
||||
# ifdef P082_SEND_GPS_TO_LOG
|
||||
addLog(LOG_LEVEL_DEBUG, P082_data->_lastSentence);
|
||||
# endif // ifdef P082_SEND_GPS_TO_LOG
|
||||
Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 10);
|
||||
delay(0); // Processing a full sentence may take a while, run some
|
||||
// background tasks.
|
||||
@@ -460,8 +303,15 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
|
||||
if (curFixStatus) {
|
||||
if (P082_data->gps->location.isUpdated()) {
|
||||
P082_setOutputValue(event, P082_QUERY_LONG, P082_data->gps->location.lng());
|
||||
P082_setOutputValue(event, P082_QUERY_LAT, P082_data->gps->location.lat());
|
||||
const float lng = P082_data->gps->location.lng();
|
||||
const float lat = P082_data->gps->location.lat();
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_LONG), lng);
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_LAT), lat);
|
||||
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_DISTANCE), P082_data->_distance);
|
||||
const float dist_ref = P082_data->gps->distanceBetween(P082_LAT_REF, P082_LONG_REF, lat, lng);
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_DIST_REF), dist_ref);
|
||||
|
||||
|
||||
if (P082_DISTANCE > 0) {
|
||||
distance = P082_data->distanceSinceLast(P082_TIMEOUT);
|
||||
@@ -472,24 +322,25 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
|
||||
if (P082_data->gps->altitude.isUpdated()) {
|
||||
// ToDo make unit selectable
|
||||
P082_setOutputValue(event, P082_QUERY_ALT, P082_data->gps->altitude.meters());
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_ALT), P082_data->gps->altitude.meters());
|
||||
success = true;
|
||||
addLog(LOG_LEVEL_DEBUG, F("GPS: Altitude update."));
|
||||
}
|
||||
|
||||
if (P082_data->gps->speed.isUpdated()) {
|
||||
// ToDo make unit selectable
|
||||
P082_setOutputValue(event, P082_QUERY_SPD, P082_data->gps->speed.mps());
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_SPD), P082_data->gps->speed.mps());
|
||||
addLog(LOG_LEVEL_DEBUG, F("GPS: Speed update."));
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
P082_setOutputValue(event, P082_QUERY_SATVIS, P082_data->gps->satellitesStats.nrSatsVisible());
|
||||
P082_setOutputValue(event, P082_QUERY_SATUSE, P082_data->gps->satellitesStats.nrSatsTracked());
|
||||
P082_setOutputValue(event, P082_QUERY_HDOP, P082_data->gps->hdop.value() / 100.0f);
|
||||
P082_setOutputValue(event, P082_QUERY_FIXQ, P082_data->gps->location.Quality());
|
||||
P082_setOutputValue(event, P082_QUERY_DB_MAX, P082_data->gps->satellitesStats.getBestSNR());
|
||||
P082_setOutputValue(event, P082_QUERY_CHKSUM_FAIL, P082_data->gps->failedChecksum());
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_SATVIS), P082_data->gps->satellitesStats.nrSatsVisible());
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_SATUSE), P082_data->gps->satellitesStats.nrSatsTracked());
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_HDOP), P082_data->gps->hdop.value() / 100.0f);
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_FIXQ), P082_data->gps->location.Quality());
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_DB_MAX), P082_data->gps->satellitesStats.getBestSNR());
|
||||
P082_setOutputValue(event, static_cast<byte>(P082_query::P082_QUERY_CHKSUM_FAIL), P082_data->gps->failedChecksum());
|
||||
|
||||
|
||||
if (curFixStatus) {
|
||||
P082_setSystemTime(event);
|
||||
@@ -502,7 +353,7 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
|
||||
if (P082_DISTANCE > 0) {
|
||||
// Check travelled distance.
|
||||
if (distance > static_cast<double>(P082_DISTANCE) || distance < 0.0) {
|
||||
if ((distance > static_cast<double>(P082_DISTANCE)) || (distance < 0.0)) {
|
||||
if (P082_data->storeCurPos(P082_TIMEOUT)) {
|
||||
distance_passed = true;
|
||||
|
||||
@@ -523,21 +374,21 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
}
|
||||
}
|
||||
|
||||
if (P082_data->last_measurement == 0) {
|
||||
if (P082_data->_last_measurement == 0) {
|
||||
interval_passed = true;
|
||||
} else if (timeOutReached(P082_data->last_measurement + (Settings.TaskDeviceTimer[event->TaskIndex] * 1000))) {
|
||||
} else if (timeOutReached(P082_data->_last_measurement + (Settings.TaskDeviceTimer[event->TaskIndex] * 1000))) {
|
||||
interval_passed = true;
|
||||
}
|
||||
success = (distance_passed || interval_passed);
|
||||
|
||||
if (success) {
|
||||
P082_data->last_measurement = millis();
|
||||
P082_data->_last_measurement = millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef USES_PACKED_RAW_DATA
|
||||
# ifdef USES_PACKED_RAW_DATA
|
||||
case PLUGIN_GET_PACKED_RAW_DATA:
|
||||
{
|
||||
P082_data_struct *P082_data =
|
||||
@@ -545,28 +396,37 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
|
||||
|
||||
if ((nullptr != P082_data) && P082_data->isInitialized()) {
|
||||
// Matching JS code:
|
||||
// return decode(bytes, [header, latLng, latLng, altitude, uint16_1e2, hdop, uint8, uint8],
|
||||
// ['header', 'latitude', 'longitude', 'altitude', 'speed', 'hdop', 'max_snr', 'sat_tracked']);
|
||||
// return decode(bytes, [header, latLng, latLng, altitude, uint16_1e2, hdop, uint8, uint8, uint24, uint24_1e1],
|
||||
// ['header', 'latitude', 'longitude', 'altitude', 'speed', 'hdop', 'max_snr', 'sat_tracked', 'distance_total', 'distance_ref']);
|
||||
// altitude type: return +(int16(bytes) / 4 - 1000).toFixed(1);
|
||||
string += LoRa_addFloat(P082_data->cache[P082_QUERY_LAT], PackedData_latLng);
|
||||
string += LoRa_addFloat(P082_data->cache[P082_QUERY_LONG], PackedData_latLng);
|
||||
string += LoRa_addFloat(P082_data->cache[P082_QUERY_ALT], PackedData_altitude);
|
||||
string += LoRa_addFloat(P082_data->cache[P082_QUERY_SPD], PackedData_uint16_1e2);
|
||||
string += LoRa_addFloat(P082_data->cache[P082_QUERY_HDOP], PackedData_hdop);
|
||||
string += LoRa_addFloat(P082_data->cache[P082_QUERY_DB_MAX], PackedData_uint8);
|
||||
string += LoRa_addFloat(P082_data->cache[P082_QUERY_SATUSE], PackedData_uint8);
|
||||
event->Par1 = 7; // valuecount 7
|
||||
|
||||
string += LoRa_addFloat(P082_data->_cache[static_cast<byte>(P082_query::P082_QUERY_LAT)], PackedData_latLng);
|
||||
string += LoRa_addFloat(P082_data->_cache[static_cast<byte>(P082_query::P082_QUERY_LONG)], PackedData_latLng);
|
||||
string += LoRa_addFloat(P082_data->_cache[static_cast<byte>(P082_query::P082_QUERY_ALT)], PackedData_altitude);
|
||||
string += LoRa_addFloat(P082_data->_cache[static_cast<byte>(P082_query::P082_QUERY_SPD)], PackedData_uint16_1e2);
|
||||
string += LoRa_addFloat(P082_data->_cache[static_cast<byte>(P082_query::P082_QUERY_HDOP)], PackedData_hdop);
|
||||
string += LoRa_addFloat(P082_data->_cache[static_cast<byte>(P082_query::P082_QUERY_DB_MAX)], PackedData_uint8);
|
||||
string += LoRa_addFloat(P082_data->_cache[static_cast<byte>(P082_query::P082_QUERY_SATUSE)], PackedData_uint8);
|
||||
string += LoRa_addFloat(P082_data->_cache[static_cast<byte>(P082_query::P082_QUERY_DISTANCE)] / 1000, PackedData_uint24_1e2); // Max 167772.16 km
|
||||
event->Par1 = 8;
|
||||
if (P082_referencePointSet(event)) {
|
||||
string += LoRa_addFloat(P082_data->_cache[static_cast<byte>(P082_query::P082_QUERY_DIST_REF)], PackedData_uint24_1e1); // Max 1677.7216 km
|
||||
event->Par1 = 9;
|
||||
}
|
||||
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif // USES_PACKED_RAW_DATA
|
||||
|
||||
# endif // USES_PACKED_RAW_DATA
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool P082_referencePointSet(struct EventStruct *event) {
|
||||
return ! ((P082_LONG_REF < 0.1) && (P082_LONG_REF > -0.1)
|
||||
&& (P082_LAT_REF < 0.1) && (P082_LAT_REF > -0.1) );
|
||||
}
|
||||
|
||||
void P082_setOutputValue(struct EventStruct *event, byte outputType, float value) {
|
||||
P082_data_struct *P082_data =
|
||||
static_cast<P082_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
@@ -574,8 +434,10 @@ void P082_setOutputValue(struct EventStruct *event, byte outputType, float value
|
||||
if ((nullptr == P082_data) || !P082_data->isInitialized()) {
|
||||
return;
|
||||
}
|
||||
if (outputType < P082_NR_OUTPUT_OPTIONS)
|
||||
P082_data->cache[outputType] = value;
|
||||
|
||||
if (outputType < static_cast<byte>(P082_query::P082_NR_OUTPUT_OPTIONS)) {
|
||||
P082_data->_cache[outputType] = value;
|
||||
}
|
||||
|
||||
for (byte i = 0; i < P082_NR_OUTPUT_VALUES; ++i) {
|
||||
const byte pconfigIndex = i + P082_QUERY1_CONFIG_POS;
|
||||
@@ -595,6 +457,7 @@ void P082_logStats(struct EventStruct *event) {
|
||||
return;
|
||||
}
|
||||
String log;
|
||||
|
||||
log.reserve(128);
|
||||
log = F("GPS:");
|
||||
log += F(" Fix: ");
|
||||
@@ -654,9 +517,9 @@ void P082_html_show_satStats(struct EventStruct *event, bool tracked, bool onlyG
|
||||
addHtml(String(id));
|
||||
|
||||
if (tracked) {
|
||||
addHtml( " (");
|
||||
addHtml(" (");
|
||||
addHtml(String(snr));
|
||||
addHtml( ")");
|
||||
addHtml(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -705,7 +568,7 @@ void P082_html_show_stats(struct EventStruct *event) {
|
||||
|
||||
addRowLabel(F("Best SNR"));
|
||||
addHtml(String(P082_data->gps->satellitesStats.getBestSNR()));
|
||||
addHtml( F(" dBHz"));
|
||||
addHtml(F(" dBHz"));
|
||||
|
||||
// Satellites tracked or in view.
|
||||
P082_html_show_satStats(event, true, true);
|
||||
@@ -728,8 +591,19 @@ void P082_html_show_stats(struct EventStruct *event) {
|
||||
addHtml(F("-"));
|
||||
}
|
||||
|
||||
addRowLabel(F("Distance Travelled"));
|
||||
addHtml(String(static_cast<int>(P082_data->_cache[static_cast<byte>(P082_query::P082_QUERY_DISTANCE)])));
|
||||
addUnit(F("m"));
|
||||
|
||||
if (P082_referencePointSet(event)) {
|
||||
addRowLabel(F("Distance from Ref. Point"));
|
||||
addHtml(String(static_cast<int>(P082_data->_cache[static_cast<byte>(P082_query::P082_QUERY_DIST_REF)])));
|
||||
addUnit(F("m"));
|
||||
}
|
||||
|
||||
addRowLabel(F("Checksum (pass/fail/invalid)"));
|
||||
String chksumStats;
|
||||
|
||||
chksumStats = P082_data->gps->passedChecksum();
|
||||
chksumStats += '/';
|
||||
chksumStats += P082_data->gps->failedChecksum();
|
||||
@@ -755,7 +629,8 @@ void P082_setSystemTime(struct EventStruct *event) {
|
||||
struct tm dateTime;
|
||||
uint32_t age;
|
||||
bool pps_sync;
|
||||
P082_data->pps_time = P082_pps_time; // Must copy the interrupt gathered time first.
|
||||
|
||||
P082_data->_pps_time = P082_pps_time; // Must copy the interrupt gathered time first.
|
||||
|
||||
if (P082_data->getDateTime(dateTime, age, pps_sync)) {
|
||||
// Use floating point precision to use the time since last update from GPS
|
||||
@@ -772,19 +647,5 @@ void Plugin_082_interrupt() {
|
||||
P082_pps_time = millis();
|
||||
}
|
||||
|
||||
String Plugin_082_valuename(byte value_nr, bool displayString) {
|
||||
switch (value_nr) {
|
||||
case P082_QUERY_LONG: return displayString ? F("Longitude") : F("long");
|
||||
case P082_QUERY_LAT: return displayString ? F("Latitude") : F("lat");
|
||||
case P082_QUERY_ALT: return displayString ? F("Altitude") : F("alt");
|
||||
case P082_QUERY_SPD: return displayString ? F("Speed (m/s)") : F("spd");
|
||||
case P082_QUERY_SATVIS: return displayString ? F("Satellites Visible") : F("sat_vis");
|
||||
case P082_QUERY_SATUSE: return displayString ? F("Satellites Tracked") : F("sat_tr");
|
||||
case P082_QUERY_HDOP: return displayString ? F("HDOP") : F("hdop");
|
||||
case P082_QUERY_FIXQ: return displayString ? F("Fix Quality") : F("fix_qual");
|
||||
case P082_QUERY_DB_MAX: return displayString ? F("Max SNR in dBHz") : F("snr_max");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
#endif // USES_P082
|
||||
|
||||
@@ -360,6 +360,31 @@ boolean Plugin_085(byte function, struct EventStruct *event, String& string) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef USES_PACKED_RAW_DATA
|
||||
case PLUGIN_GET_PACKED_RAW_DATA:
|
||||
{
|
||||
P085_data_struct *P085_data =
|
||||
static_cast<P085_data_struct *>(getPluginTaskData(event->TaskIndex));
|
||||
|
||||
if ((nullptr != P085_data) && P085_data->isInitialized()) {
|
||||
// Matching JS code:
|
||||
// return decode(bytes, [header, uint8, int32_1e4, uint8, int32_1e4, uint8, int32_1e4, uint8, int32_1e4],
|
||||
// ['header', 'unit1', 'val_1', 'unit2', 'val_2', 'unit3', 'val_3', 'unit4', 'val_4']);
|
||||
for (byte i = 0; i < VARS_PER_TASK; ++i) {
|
||||
const byte pconfigIndex = i + P085_QUERY1_CONFIG_POS;
|
||||
const byte choice = PCONFIG(pconfigIndex);
|
||||
string += LoRa_addInt(choice, PackedData_uint8);
|
||||
string += LoRa_addFloat(UserVar[event->BaseVarIndex + i], PackedData_int32_1e4);
|
||||
}
|
||||
event->Par1 = 8; // valuecount
|
||||
|
||||
success = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif // USES_PACKED_RAW_DATA
|
||||
|
||||
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
+34
-5
@@ -21,6 +21,12 @@
|
||||
For following devices just a pull up resistor is needed if the device is used stand alone:
|
||||
UVR1611, UVR61-3 and ESR21
|
||||
|
||||
@uwekaditz 2020-10-05 Exception solved
|
||||
CHG: Interrupt will be only enabled after network is connected
|
||||
CHG: usecPassedSince() not longer used in ISR_PinChanged(), caused the exception ???
|
||||
CHG: in case no DLbus is connected, the next reading attempt happens only after the next PLUGIN_092_READ was called
|
||||
(reduces load and log messages)
|
||||
|
||||
@uwekaditz 2020-03-01 Memory usage optimized
|
||||
CHG: Moved arrays into the class DLBus
|
||||
CHG: Moved arrays to PLUGIN_092_DEBUG
|
||||
@@ -435,12 +441,12 @@ boolean Plugin_092(uint8_t function, struct EventStruct *event, String& string)
|
||||
}
|
||||
P092_init = true;
|
||||
P092_ReceivedOK = false;
|
||||
addLog(LOG_LEVEL_INFO, F("P092_init: attachInterrupt"));
|
||||
addLog(LOG_LEVEL_INFO, F("P092_init: Set pin"));
|
||||
DLbus_Data->ISR_DLB_Pin = CONFIG_PIN1;
|
||||
pinMode(CONFIG_PIN1, INPUT_PULLUP);
|
||||
|
||||
// on a CHANGE on the data pin P092_Pin_changed is called
|
||||
DLbus_Data->attachDLBusInterrupt();
|
||||
// // on a CHANGE on the data pin P092_Pin_changed is called
|
||||
// DLbus_Data->attachDLBusInterrupt();
|
||||
UserVar[event->BaseVarIndex] = NAN;
|
||||
}
|
||||
success = true;
|
||||
@@ -461,9 +467,16 @@ boolean Plugin_092(uint8_t function, struct EventStruct *event, String& string)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DLbus_Data->IsISRset) {
|
||||
// on a CHANGE on the data pin P092_Pin_changed is called
|
||||
DLbus_Data->attachDLBusInterrupt();
|
||||
addLog(LOG_LEVEL_INFO, F("P092 ISR set"));
|
||||
}
|
||||
|
||||
if (DLbus_Data->ISR_Receiving) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Plugin_092_SetIndices(PCONFIG(0));
|
||||
|
||||
if (DLbus_Data->ISR_AllBitsReceived) {
|
||||
@@ -488,8 +501,9 @@ boolean Plugin_092(uint8_t function, struct EventStruct *event, String& string)
|
||||
success = P092_ReceivedOK;
|
||||
}
|
||||
|
||||
if ((P092_ReceivedOK == false) ||
|
||||
(timePassedSince(P092_LastReceived) > (static_cast<long>(Settings.TaskDeviceTimer[event->TaskIndex] * 1000 / 2)))) {
|
||||
if ((DLbus_Data->IsNoData == false) &&
|
||||
((P092_ReceivedOK == false) ||
|
||||
(timePassedSince(P092_LastReceived) > (static_cast<long>(Settings.TaskDeviceTimer[event->TaskIndex] * 1000 / 2))))) {
|
||||
Plugin_092_StartReceiving();
|
||||
success = true;
|
||||
}
|
||||
@@ -525,10 +539,24 @@ boolean Plugin_092(uint8_t function, struct EventStruct *event, String& string)
|
||||
addLog(LOG_LEVEL_ERROR, log);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DLbus_Data->IsISRset) {
|
||||
// on a CHANGE on the data pin P092_Pin_changed is called
|
||||
addLog(LOG_LEVEL_ERROR, F("## P092_read: Error DL-Bus: ISR not set"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DLbus_Data->IsNoData) {
|
||||
// start new receiving attempt
|
||||
DLbus_Data->IsNoData = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
success = P092_ReceivedOK;
|
||||
|
||||
if (P092_ReceivedOK == false) {
|
||||
addLog(LOG_LEVEL_INFO, F("P092_read: Still receiving DL-Bus bits!"));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
sP092_ReadData P092_ReadData;
|
||||
@@ -723,6 +751,7 @@ void Plugin_092_StartReceiving(void) {
|
||||
if (DLbus_Data->ISR_PulseCount == 0) {
|
||||
// nothing received
|
||||
DLbus_Data->ISR_Receiving = false;
|
||||
DLbus_Data->IsNoData = true; // stop receiving until next PLUGIN_092_READ
|
||||
addLog(LOG_LEVEL_ERROR, F("## StartReceiving: Error: Nothing received! No DL bus connected!"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +160,13 @@ struct ControllerDelayHandlerStruct {
|
||||
return nextTime;
|
||||
}
|
||||
|
||||
// Set the "lastSend" to "now" + some additional delay.
|
||||
// This will cause the next schedule time to be delayed to
|
||||
// msecFromNow + minTimeBetweenMessages
|
||||
void setAdditionalDelay(unsigned long msecFromNow) {
|
||||
lastSend = millis() + msecFromNow;
|
||||
}
|
||||
|
||||
size_t getQueueMemorySize() const {
|
||||
size_t totalSize = 0;
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ bool ExtraTaskSettingsStruct::checkInvalidCharInNames(const char *name) const {
|
||||
bool ExtraTaskSettingsStruct::checkInvalidCharInNames() const {
|
||||
if (!checkInvalidCharInNames(&TaskDeviceName[0])) { return false; }
|
||||
|
||||
for (int i = 0; i < (VARS_PER_TASK - 1); ++i) {
|
||||
for (int i = 0; i < VARS_PER_TASK; ++i) {
|
||||
if (!checkInvalidCharInNames(&TaskDeviceValueNames[i][0])) { return false; }
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -228,6 +228,7 @@ String getMiscStatsName(int stat) {
|
||||
case PARSE_SYSVAR: return F("parseSystemVariables()");
|
||||
case PARSE_SYSVAR_NOCHANGE: return F("parseSystemVariables() No change");
|
||||
case HANDLE_SERVING_WEBPAGE: return F("handle webpage");
|
||||
case C018_AIR_TIME: return F("C018 LoRa TTN - Air Time");
|
||||
case C001_DELAY_QUEUE:
|
||||
case C002_DELAY_QUEUE:
|
||||
case C003_DELAY_QUEUE:
|
||||
|
||||
@@ -54,26 +54,33 @@
|
||||
# define C018_DELAY_QUEUE 33
|
||||
# define C019_DELAY_QUEUE 34
|
||||
# define C020_DELAY_QUEUE 35
|
||||
# define TRY_CONNECT_HOST_TCP 36
|
||||
# define TRY_CONNECT_HOST_UDP 37
|
||||
# define HOST_BY_NAME_STATS 38
|
||||
# define CONNECT_CLIENT_STATS 39
|
||||
# define LOAD_CUSTOM_TASK_STATS 40
|
||||
# define WIFI_ISCONNECTED_STATS 41
|
||||
# define WIFI_NOTCONNECTED_STATS 42
|
||||
# define LOAD_TASK_SETTINGS 43
|
||||
# define TRY_OPEN_FILE 44
|
||||
# define FS_GC_SUCCESS 45
|
||||
# define FS_GC_FAIL 46
|
||||
# define PARSE_SYSVAR 47
|
||||
# define PARSE_SYSVAR_NOCHANGE 48
|
||||
# define PARSE_TEMPLATE_PADDED 49
|
||||
# define RULES_PROCESSING 50
|
||||
# define GRAT_ARP_STATS 51
|
||||
# define BACKGROUND_TASKS 52
|
||||
# define HANDLE_SCHEDULER_IDLE 53
|
||||
# define HANDLE_SCHEDULER_TASK 54
|
||||
# define HANDLE_SERVING_WEBPAGE 55
|
||||
# define C021_DELAY_QUEUE 36
|
||||
# define C022_DELAY_QUEUE 37
|
||||
# define C023_DELAY_QUEUE 38
|
||||
# define C024_DELAY_QUEUE 39
|
||||
# define C025_DELAY_QUEUE 40
|
||||
# define C018_AIR_TIME 41
|
||||
# define TRY_CONNECT_HOST_TCP 42
|
||||
# define TRY_CONNECT_HOST_UDP 43
|
||||
# define HOST_BY_NAME_STATS 44
|
||||
# define CONNECT_CLIENT_STATS 45
|
||||
# define LOAD_CUSTOM_TASK_STATS 46
|
||||
# define WIFI_ISCONNECTED_STATS 47
|
||||
# define WIFI_NOTCONNECTED_STATS 48
|
||||
# define LOAD_TASK_SETTINGS 49
|
||||
# define TRY_OPEN_FILE 50
|
||||
# define FS_GC_SUCCESS 51
|
||||
# define FS_GC_FAIL 52
|
||||
# define PARSE_SYSVAR 53
|
||||
# define PARSE_SYSVAR_NOCHANGE 54
|
||||
# define PARSE_TEMPLATE_PADDED 55
|
||||
# define RULES_PROCESSING 56
|
||||
# define GRAT_ARP_STATS 57
|
||||
# define BACKGROUND_TASKS 58
|
||||
# define HANDLE_SCHEDULER_IDLE 59
|
||||
# define HANDLE_SCHEDULER_TASK 60
|
||||
# define HANDLE_SERVING_WEBPAGE 61
|
||||
|
||||
|
||||
class TimingStats {
|
||||
public:
|
||||
@@ -109,7 +116,7 @@ extern std::map<int, TimingStats> controllerStats;
|
||||
extern std::map<int, TimingStats> miscStats;
|
||||
extern unsigned long timingstats_last_reset;
|
||||
|
||||
# define START_TIMER const unsigned statisticsTimerStart(micros());
|
||||
# define START_TIMER const unsigned long statisticsTimerStart(micros());
|
||||
# define STOP_TIMER_TASK(T, F) \
|
||||
if (mustLogFunction(F)) pluginStats[(T) * 256 + (F)].add(usecPassedSince(statisticsTimerStart));
|
||||
# define STOP_TIMER_CONTROLLER(T, F) \
|
||||
@@ -118,12 +125,16 @@ extern unsigned long timingstats_last_reset;
|
||||
// #define STOP_TIMER_LOADFILE miscStats[LOADFILE_STATS].add(usecPassedSince(statisticsTimerStart));
|
||||
# define STOP_TIMER(L) miscStats[L].add(usecPassedSince(statisticsTimerStart));
|
||||
|
||||
// Add a timer statistic value in usec.
|
||||
# define ADD_TIMER_STAT(L, T) miscStats[L].add(T);
|
||||
|
||||
#else // ifdef USES_TIMING_STATS
|
||||
|
||||
# define START_TIMER
|
||||
# define STOP_TIMER_TASK(T, F) ;
|
||||
# define STOP_TIMER_CONTROLLER(T, F) ;
|
||||
# define STOP_TIMER(L) ;
|
||||
# define ADD_TIMER_STAT(L, T) ;
|
||||
|
||||
|
||||
// FIXME TD-er: This class is used as a parameter in functions defined in .ino files.
|
||||
|
||||
@@ -10,6 +10,10 @@ public:
|
||||
|
||||
LongTermTimer() {}
|
||||
|
||||
explicit LongTermTimer(bool usenow) : _timer_usec(0ull) {
|
||||
if (usenow) setNow();
|
||||
}
|
||||
|
||||
//explicit LongTermTimer(uint64_t start_time) : _timer_usec(start_time) {}
|
||||
|
||||
inline bool operator<(const LongTermTimer& rhs) const
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
#include "P082_data_struct.h"
|
||||
|
||||
|
||||
#ifdef USES_P082
|
||||
|
||||
String Plugin_082_valuename(P082_query value_nr, bool displayString) {
|
||||
switch (value_nr) {
|
||||
case P082_query::P082_QUERY_LONG: return displayString ? F("Longitude") : F("long");
|
||||
case P082_query::P082_QUERY_LAT: return displayString ? F("Latitude") : F("lat");
|
||||
case P082_query::P082_QUERY_ALT: return displayString ? F("Altitude") : F("alt");
|
||||
case P082_query::P082_QUERY_SPD: return displayString ? F("Speed (m/s)") : F("spd");
|
||||
case P082_query::P082_QUERY_SATVIS: return displayString ? F("Satellites Visible") : F("sat_vis");
|
||||
case P082_query::P082_QUERY_SATUSE: return displayString ? F("Satellites Tracked") : F("sat_tr");
|
||||
case P082_query::P082_QUERY_HDOP: return displayString ? F("HDOP") : F("hdop");
|
||||
case P082_query::P082_QUERY_FIXQ: return displayString ? F("Fix Quality") : F("fix_qual");
|
||||
case P082_query::P082_QUERY_DB_MAX: return displayString ? F("Max SNR in dBHz") : F("snr_max");
|
||||
case P082_query::P082_QUERY_CHKSUM_FAIL: return displayString ? F("Checksum Fail") : F("chksum_fail");
|
||||
case P082_query::P082_QUERY_DISTANCE: return displayString ? F("Distance (ODO)") : F("dist");
|
||||
case P082_query::P082_QUERY_DIST_REF: return displayString ? F("Distance from Reference Point") : F("dist_ref");
|
||||
case P082_query::P082_NR_OUTPUT_OPTIONS: break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
P082_data_struct::P082_data_struct() : gps(nullptr), easySerial(nullptr) {}
|
||||
|
||||
P082_data_struct::~P082_data_struct() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void P082_data_struct::reset() {
|
||||
if (gps != nullptr) {
|
||||
delete gps;
|
||||
gps = nullptr;
|
||||
}
|
||||
|
||||
if (easySerial != nullptr) {
|
||||
delete easySerial;
|
||||
easySerial = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool P082_data_struct::init(ESPEasySerialPort port, const int16_t serial_rx, const int16_t serial_tx) {
|
||||
if (serial_rx < 0) {
|
||||
return false;
|
||||
}
|
||||
reset();
|
||||
gps = new (std::nothrow) TinyGPSPlus();
|
||||
easySerial = new (std::nothrow) ESPeasySerial(port, serial_rx, serial_tx);
|
||||
|
||||
if (easySerial != nullptr) {
|
||||
easySerial->begin(9600);
|
||||
}
|
||||
return isInitialized();
|
||||
}
|
||||
|
||||
bool P082_data_struct::isInitialized() const {
|
||||
return gps != nullptr && easySerial != nullptr;
|
||||
}
|
||||
|
||||
bool P082_data_struct::loop() {
|
||||
if (!isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
bool completeSentence = false;
|
||||
|
||||
if (easySerial != nullptr) {
|
||||
int available = easySerial->available();
|
||||
unsigned long startLoop = millis();
|
||||
|
||||
while (available > 0 && timePassedSince(startLoop) < 10) {
|
||||
--available;
|
||||
char c = easySerial->read();
|
||||
# ifdef P082_SEND_GPS_TO_LOG
|
||||
_currentSentence += c;
|
||||
# endif // ifdef P082_SEND_GPS_TO_LOG
|
||||
|
||||
if (gps->encode(c)) {
|
||||
// Full sentence received
|
||||
# ifdef P082_SEND_GPS_TO_LOG
|
||||
_lastSentence = _currentSentence;
|
||||
_currentSentence = "";
|
||||
# endif // ifdef P082_SEND_GPS_TO_LOG
|
||||
completeSentence = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return completeSentence;
|
||||
}
|
||||
|
||||
bool P082_data_struct::hasFix(unsigned int maxAge_msec) {
|
||||
if (!isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
return gps->location.isValid() && gps->location.age() < maxAge_msec;
|
||||
}
|
||||
|
||||
bool P082_data_struct::storeCurPos(unsigned int maxAge_msec) {
|
||||
if (!hasFix(maxAge_msec)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_distance += distanceSinceLast(maxAge_msec);
|
||||
_last_lat = gps->location.lat();
|
||||
_last_lng = gps->location.lng();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the distance in meters compared to last stored position.
|
||||
// @retval -1 when no fix.
|
||||
double P082_data_struct::distanceSinceLast(unsigned int maxAge_msec) {
|
||||
if (!hasFix(maxAge_msec)) {
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
if (((_last_lat < 0.0001) && (_last_lat > -0.0001)) || ((_last_lng < 0.0001) && (_last_lng > -0.0001))) {
|
||||
return -1.0;
|
||||
}
|
||||
return gps->distanceBetween(_last_lat, _last_lng, gps->location.lat(), gps->location.lng());
|
||||
}
|
||||
|
||||
// Return the GPS time stamp, which is in UTC.
|
||||
// @param age is the time in msec since the last update of the time +
|
||||
// additional centiseconds given by the GPS.
|
||||
bool P082_data_struct::getDateTime(struct tm& dateTime, uint32_t& age, bool& pps_sync) {
|
||||
if (!isInitialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_pps_time != 0) {
|
||||
age = timePassedSince(_pps_time);
|
||||
_pps_time = 0;
|
||||
pps_sync = true;
|
||||
|
||||
if ((age > 1000) || (gps->time.age() > age)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
age = gps->time.age();
|
||||
pps_sync = false;
|
||||
}
|
||||
|
||||
if (age > P082_TIMESTAMP_AGE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gps->date.age() > P082_TIMESTAMP_AGE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gps->date.isValid() || !gps->time.isValid()) {
|
||||
return false;
|
||||
}
|
||||
dateTime.tm_year = gps->date.year() - 1970;
|
||||
dateTime.tm_mon = gps->date.month();
|
||||
dateTime.tm_mday = gps->date.day();
|
||||
|
||||
dateTime.tm_hour = gps->time.hour();
|
||||
dateTime.tm_min = gps->time.minute();
|
||||
dateTime.tm_sec = gps->time.second();
|
||||
|
||||
// FIXME TD-er: Must the offset in centisecond be added when pps_sync active?
|
||||
if (!pps_sync) {
|
||||
age += (gps->time.centisecond() * 10);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // ifdef USES_P082
|
||||
@@ -0,0 +1,91 @@
|
||||
#ifndef PLUGINSTRUCTS_P082_DATA_STRUCT_H
|
||||
#define PLUGINSTRUCTS_P082_DATA_STRUCT_H
|
||||
|
||||
#include "../../_Plugin_Helper.h"
|
||||
#include "../../ESPEasy_common.h"
|
||||
|
||||
#ifdef USES_P082
|
||||
|
||||
# include <TinyGPS++.h>
|
||||
# include <ESPeasySerial.h>
|
||||
|
||||
|
||||
# define P082_SEND_GPS_TO_LOG
|
||||
|
||||
# define P082_TIMESTAMP_AGE 1500
|
||||
# define P082_DEFAULT_FIX_TIMEOUT 2500 // TTL of fix status in ms since last update
|
||||
|
||||
|
||||
enum class P082_query : byte {
|
||||
P082_QUERY_LONG = 0,
|
||||
P082_QUERY_LAT = 1,
|
||||
P082_QUERY_ALT = 2,
|
||||
P082_QUERY_SPD = 3,
|
||||
P082_QUERY_SATVIS = 4,
|
||||
P082_QUERY_SATUSE = 5,
|
||||
P082_QUERY_HDOP = 6,
|
||||
P082_QUERY_FIXQ = 7,
|
||||
P082_QUERY_DB_MAX = 8,
|
||||
P082_QUERY_CHKSUM_FAIL = 9,
|
||||
P082_QUERY_DISTANCE = 10,
|
||||
P082_QUERY_DIST_REF = 11,
|
||||
P082_NR_OUTPUT_OPTIONS
|
||||
};
|
||||
|
||||
String Plugin_082_valuename(P082_query value_nr, bool displayString);
|
||||
|
||||
|
||||
|
||||
struct P082_data_struct : public PluginTaskData_base {
|
||||
P082_data_struct();
|
||||
|
||||
~P082_data_struct();
|
||||
|
||||
void reset();
|
||||
|
||||
bool init(ESPEasySerialPort port,
|
||||
const int16_t serial_rx,
|
||||
const int16_t serial_tx);
|
||||
|
||||
bool isInitialized() const;
|
||||
|
||||
bool loop();
|
||||
|
||||
bool hasFix(unsigned int maxAge_msec);
|
||||
|
||||
bool storeCurPos(unsigned int maxAge_msec);
|
||||
|
||||
// Return the distance in meters compared to last stored position.
|
||||
// @retval -1 when no fix.
|
||||
double distanceSinceLast(unsigned int maxAge_msec);
|
||||
|
||||
// Return the GPS time stamp, which is in UTC.
|
||||
// @param age is the time in msec since the last update of the time +
|
||||
// additional centiseconds given by the GPS.
|
||||
bool getDateTime(struct tm& dateTime,
|
||||
uint32_t & age,
|
||||
bool & pps_sync);
|
||||
|
||||
TinyGPSPlus *gps = nullptr;
|
||||
ESPeasySerial *easySerial = nullptr;
|
||||
|
||||
double _last_lat = 0.0;
|
||||
double _last_lng = 0.0;
|
||||
double _ref_lat = 0.0;
|
||||
double _ref_lng = 0.0;
|
||||
double _distance = 0.0;
|
||||
|
||||
|
||||
|
||||
unsigned long _pps_time = 0;
|
||||
unsigned long _last_measurement = 0;
|
||||
# ifdef P082_SEND_GPS_TO_LOG
|
||||
String _lastSentence;
|
||||
String _currentSentence;
|
||||
# endif // ifdef P082_SEND_GPS_TO_LOG
|
||||
|
||||
float _cache[static_cast<byte>(P082_query::P082_NR_OUTPUT_OPTIONS)] = { 0 };
|
||||
};
|
||||
|
||||
#endif // ifdef USES_P082
|
||||
#endif // ifndef PLUGINSTRUCTS_P082_DATA_STRUCT_H
|
||||
@@ -58,6 +58,8 @@ void DLBus::AddToErrorLog(const String& string)
|
||||
void DLBus::attachDLBusInterrupt(void)
|
||||
{
|
||||
ISR_Receiving = false;
|
||||
IsISRset = true;
|
||||
IsNoData = false;
|
||||
attachInterrupt(digitalPinToInterrupt(ISR_DLB_Pin), ISR, CHANGE);
|
||||
}
|
||||
|
||||
@@ -80,9 +82,12 @@ void ICACHE_RAM_ATTR DLBus::ISR(void)
|
||||
|
||||
void ICACHE_RAM_ATTR DLBus::ISR_PinChanged(void)
|
||||
{
|
||||
long TimeDiff = usecPassedSince(ISR_TimeLastBitChange); // time difference to previous pulse in µs
|
||||
// long TimeDiff = usecPassedSince(ISR_TimeLastBitChange); // time difference to previous pulse in µs
|
||||
uint32_t _now = micros();
|
||||
int32_t TimeDiff = (int32_t)(_now - ISR_TimeLastBitChange);
|
||||
|
||||
ISR_TimeLastBitChange = micros(); // save last pin change time
|
||||
// ISR_TimeLastBitChange = micros(); // save last pin change time
|
||||
ISR_TimeLastBitChange = _now; // save last pin change time
|
||||
|
||||
if (ISR_Receiving) {
|
||||
uint8_t val = digitalRead(ISR_DLB_Pin); // read state
|
||||
|
||||
@@ -42,6 +42,8 @@ public:
|
||||
uint8_t DeviceBytes[2] = { 0 };
|
||||
uint8_t ByteStream[DLbus_MaxDataBits / 8 + 1]; // every bit gets sorted into a bitmap
|
||||
boolean IsLogLevelInfo = false;
|
||||
boolean IsNoData = false;
|
||||
boolean IsISRset = false; // ISR set flag
|
||||
uint8_t LogLevelInfo = 0xff;
|
||||
uint8_t LogLevelError = 0xFF;
|
||||
void attachDLBusInterrupt(void);
|
||||
|
||||
@@ -91,7 +91,7 @@ input.wide, select.wide{
|
||||
}
|
||||
.widenumber{
|
||||
max-width:500px;
|
||||
width:100px
|
||||
width:7em
|
||||
}
|
||||
.container,.container2{
|
||||
font-size:12pt;
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user