mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
[GPS] Add distance counter + distance from ref point
Also updated float form fields to allow upto 6 decimals as those were only used for GPS coordinates.
This commit is contained in:
@@ -21,8 +21,16 @@ 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 (header.valuecount === 7) {
|
||||
return decode(bytes, [header, latLng, latLng, altitude, uint16_1e2, hdop, uint8, uint8],
|
||||
['header', 'latitude', 'longitude', 'altitude', 'speed', 'hdop', 'max_snr', 'sat_tracked']);
|
||||
} else if (header.valuecount === 8) {
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
|
||||
+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
|
||||
|
||||
@@ -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
|
||||
@@ -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