Compare commits

..

85 Commits

Author SHA1 Message Date
Jm Casler
a56851fefa Trying again with .47 2021-11-23 12:41:51 -08:00
Jm Casler
0fc88c542f Reverting version to 47
I can't get git push root to work. Don't know why.
2021-11-23 12:39:48 -08:00
Jm Casler
16307cb67d 47c 2021-11-23 12:17:26 -08:00
Jm Casler
9f05bd11cc Update to .47b 2021-11-23 12:14:28 -08:00
Jm Casler
4b6ee2dc88 Update version to 1.2.47 2021-11-23 11:48:53 -08:00
Jm Casler
dbabc24d7a Merge pull request #921 from thebentern/ignore-phone-messages-screen
Ignore messages originating from phone on screen
2021-11-23 09:52:11 -08:00
Jm Casler
d3e1bbf355 Merge branch 'master' into ignore-phone-messages-screen 2021-11-22 19:46:55 -08:00
Jm Casler
2fa2497ed8 Merge pull request #918 from osmanovv/llcc68
[LLCC68] module support
2021-11-22 19:46:31 -08:00
Jm Casler
a5727052bc Merge branch 'master' into llcc68 2021-11-22 19:25:52 -08:00
Jm Casler
083518c127 Merge branch 'master' into ignore-phone-messages-screen 2021-11-22 19:23:42 -08:00
Jm Casler
bc7fc1285d Merge pull request #923 from mc-hamster/StoreAndForward
Store and forward - First public release!!
2021-11-22 18:11:21 -08:00
Jm Casler
9ddcc5d2ed S&F Pre-Alpha. Functional. No crashes (I hope). 2021-11-22 17:47:42 -08:00
Jm Casler
a3ce728e26 Move SerialPlugin to plugins/esp32 2021-11-22 13:18:49 -08:00
Jm Casler
cf0d9a2c86 Merge pull request #91 from mc-hamster/master
Move Serial Plugin to new location
2021-11-22 13:17:01 -08:00
Jm Casler
ec7953ccf0 Rename src/plugins/SerialPlugin.h to src/plugins/esp32/SerialPlugin.h 2021-11-22 13:15:50 -08:00
Jm Casler
44a4bde626 Rename src/plugins/SerialPlugin.cpp to src/plugins/esp32/SerialPlugin.cpp 2021-11-22 13:15:36 -08:00
Ben Meadors
5db0a57599 Ignore messages originating from phone on screen 2021-11-21 18:44:08 -06:00
Jm Casler
834f2f4160 Updates to S&F (this one doesn't work) 2021-11-21 14:43:47 -08:00
Jm Casler
4367f05b24 Applied code formatters. 2021-11-20 21:57:21 -08:00
Jm Casler
97aec5f125 Remove comments from SF 2021-11-20 21:35:13 -08:00
Jm Casler
5191fd6475 Initial demonstrable build 2021-11-20 21:21:11 -08:00
Jm Casler
32017e53f5 Formatting applied 2021-11-18 09:36:39 -08:00
Jm Casler
37aab8a42b Able to store and forward, but packets are corrupt 2021-11-18 09:35:05 -08:00
Vladislav Osmanov
bfa0307231 LLCC68 doesn't support SF12 neither the "Long Slow" channel 2021-11-18 14:46:04 +03:00
Vladislav Osmanov
8eb0d685ac [LLCC68] module support 2021-11-17 13:28:08 +03:00
Jm Casler
43e543eabc Merge pull request #89 from meshtastic/master
Update meshtastic from head
2021-11-14 18:34:38 -08:00
Sacha Weatherstone
eee6ef018c Merge pull request #917 from thebentern/add-always-powered-preference
Added is_always_powered preference
2021-11-12 01:08:39 +11:00
Sacha Weatherstone
f7da9ac071 Merge branch 'master' into add-always-powered-preference 2021-11-11 13:50:13 +11:00
Sacha Weatherstone
928198ff03 Merge pull request #915 from jacky4566/master
Add firmware update links
2021-11-11 13:49:39 +11:00
Jm Casler
0cd82507d9 Merge pull request #88 from meshtastic/master
update from head
2021-11-10 16:47:42 -08:00
Ben Meadors
2d939d26ee Renamed to is_always_powered for convention 2021-11-06 12:11:22 -05:00
Ben Meadors
d6c7ea921a Added always_powered preference 2021-11-06 10:03:10 -05:00
Jackson Wiebe
19f43689ca Merge pull request #1 from jacky4566/patch-1
Patch 1
2021-11-05 20:50:22 -06:00
Jackson Wiebe
e78e82ef42 Update README.md 2021-11-05 20:49:40 -06:00
Jackson Wiebe
fdfc600b3d Add links for update procedure
Had to reach out to discord for update instructions when they are located on the website. Adding link here for clarity.
2021-11-05 20:47:33 -06:00
Sacha Weatherstone
334e14ea4d Merge pull request #914 from a-f-G-U-C/admin-set-team
set Team field via Admin plugin
2021-11-03 06:53:11 +11:00
a-f-G-U-C
8115ee0c97 set Team field via Admin plugin 2021-11-02 13:27:56 +00:00
Sacha Weatherstone
e21cc9d479 Merge pull request #913 from Solarvortx/master
Small fix for using 62.5Khz bandwidth setting.
2021-11-02 17:29:34 +11:00
Solanaceae
d5e4ceebcc Small fix for using 62.5Khz bandwidth setting. 2021-11-01 02:31:53 -07:00
Sacha Weatherstone
51646f28ec Merge pull request #908 from a-f-G-U-C/owner-update
fix owner name update issue (off #900)
2021-10-30 13:42:25 +11:00
Sacha Weatherstone
2c5ba29648 Merge branch 'master' into owner-update 2021-10-30 10:13:10 +11:00
Sacha Weatherstone
41bed5c14d Merge pull request #902 from a-f-G-U-C/ublox-filter-dop-fix
implement fix2D and max DOP user settings
2021-10-30 10:12:36 +11:00
a-f-G-U-C
5ef9414a25 fix owner name update issue (off #900) 2021-10-29 13:28:48 +00:00
a-f-G-U-C
2ebed9cb6c update protobufs to 61bc1d0 2021-10-29 13:13:26 +00:00
a-f-G-U-C
7a1409c42a Merge branch 'master' into ublox-filter-dop-fix 2021-10-28 13:03:01 +00:00
Sacha Weatherstone
093e3e55b9 Merge pull request #906 from a-f-G-U-C/fix900-part2
further fixes for issue 900
2021-10-28 23:34:18 +11:00
a-f-G-U-C
a911515dec clean up old data during fix transition 2021-10-28 11:58:08 +00:00
a-f-G-U-C
7ecb36dbe7 Merge branch 'meshtastic:master' into ublox-filter-dop-fix 2021-10-28 11:56:11 +00:00
a-f-G-U-C
d4ea18851d update for RX_SRC_USER 2021-10-28 11:33:32 +00:00
a-f-G-U-C
646d1caf66 updatePosition process time-only message 2021-10-28 11:31:19 +00:00
a-f-G-U-C
74c138620a revert sanity check from PR 898 2021-10-28 11:25:45 +00:00
Jm Casler
3981d2e1f6 Fix for #874 - RangeTest Plugin header fields swapped for elevation and SNR (#904)
Fix for #874 - RangeTest Plugin header fields swapped for elevation and SNR
2021-10-28 15:46:20 +11:00
Jm
6ccaa64ae8 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-10-27 20:05:51 -07:00
Jm
5dd9610d36 Fix for #874 - RangeTest Plugin header fields swapped for elevation and SNR
Fix for #874 - RangeTest Plugin header fields swapped for elevation and SNR
2021-10-27 20:05:33 -07:00
Jm Casler
d4ed7b2f73 Merge pull request #87 from meshtastic/master
Update my fork from head
2021-10-27 19:59:30 -07:00
Jm
9702dffa12 Update proto 2021-10-27 19:57:37 -07:00
Kevin Hester
e2992cd3b9 Merge branch 'master' into ublox-filter-dop-fix 2021-10-28 10:41:03 +08:00
Kevin Hester
4e5ac1ac07 Merge pull request #901 from a-f-G-U-C/rx-src-user
add the user as distinct message source - fixing #900
2021-10-28 10:40:51 +08:00
a-f-G-U-C
e89b3bd1ec implement fix2d and max DOP user settings 2021-10-27 13:30:39 +00:00
a-f-G-U-C
da9dd62a33 add the user as distinct message source 2021-10-27 13:16:51 +00:00
Sacha Weatherstone
e6b4ee8084 Merge pull request #893 from joarSv/feature/environmental-ds18b20-sensor-support
Add support for DS18B20 temperature sensor
2021-10-27 13:54:57 +11:00
Sacha Weatherstone
0133a1b293 Merge branch 'master' into feature/environmental-ds18b20-sensor-support 2021-10-27 10:23:40 +11:00
Sacha Weatherstone
543283c0f6 Merge pull request #898 from a-f-G-U-C/inpos-selfrej-nicelog
better sanity check, logging of inbound positions
2021-10-27 10:21:20 +11:00
Sacha Weatherstone
6d778cdda4 Merge branch 'master' into inpos-selfrej-nicelog 2021-10-27 06:42:13 +11:00
Sacha Weatherstone
3ffcecee6c Merge pull request #897 from a-f-G-U-C/nodedb-updatepos-atomic
implement RX_SRC_LOCAL, Position in updatePosition
2021-10-27 06:38:33 +11:00
a-f-G-U-C
24b2fd2657 better sanity check, logging of inbound positions 2021-10-26 12:41:44 +00:00
a-f-G-U-C
b74c2da530 implement RX_SRC_LOCAL, Position in updatePosition 2021-10-26 12:22:34 +00:00
Kevin Hester
26415cf8e0 Merge branch 'master' into feature/environmental-ds18b20-sensor-support 2021-10-26 02:36:50 +08:00
Sacha Weatherstone
2f7b58abaf Merge pull request #896 from a-f-G-U-C/gps-use-pos-struct
use Position struct for GPS data
2021-10-25 07:41:09 +11:00
a-f-G-U-C
ef1d52ca04 update log message, sanity check 2021-10-24 13:02:31 +00:00
a-f-G-U-C
f69c8dddad update MeshService to use Position struct 2021-10-24 12:48:48 +00:00
a-f-G-U-C
70b80e600d use Position struct for GPS data 2021-10-24 12:38:35 +00:00
Joar Svensson
90d95d8e98 Merge branch 'master' into feature/environmental-ds18b20-sensor-support 2021-10-24 11:36:10 +02:00
a-f-G-U-C
7d267e8027 update GPSStatus to use Position struct atomically (#885) 2021-10-24 11:36:18 +11:00
a-f-G-U-C
0d884d159a implement position packet optional fields (#892) 2021-10-24 11:10:36 +11:00
a-f-G-U-C
3893810b76 fix a positional timestamp reading bug (#886)
* fix a positional timestamp reading bug

* lying about fixType is no longer required
2021-10-24 10:31:44 +11:00
Joar Svensson
fff4735a15 Add support for DS18B20 temperature sensor 2021-10-23 21:10:25 +02:00
a-f-G-U-C
a914ee133c fix a rare GPS data corruption condition (#890)
* fix a rare GPS data corruption condition

* don't use hasLock to determine validity
2021-10-23 14:21:59 +11:00
a-f-G-U-C
d0fb363422 discriminate local/remote node position updates (#889) 2021-10-23 12:58:56 +11:00
a-f-G-U-C
992bbe76d7 add backward compatible position flag defaults (#884) 2021-10-23 12:12:53 +11:00
syund
6fc3c9c868 Inline pow_neg - removes build warning (#891) 2021-10-23 11:40:43 +11:00
Kevin Hester
4fb844bddd Merge pull request #869 from geeksville/dev
new release
2021-10-16 01:45:51 +08:00
Jm
1a8b128640 small change, moving it to the laptop 2021-04-16 19:44:59 -07:00
Jm Casler
a83dcbadb9 Merge pull request #84 from meshtastic/master
update from head
2021-04-16 19:33:42 -07:00
Jm Casler
a5e3f271ea Merge pull request #83 from meshtastic/master
update from head
2021-04-09 20:36:35 -07:00
41 changed files with 782 additions and 407 deletions

View File

@@ -2,4 +2,10 @@
[![Open in Visual Studio Code](https://open.vscode.dev/badges/open-in-vscode.svg)](https://open.vscode.dev/meshtastic/Meshtastic-device) [![Open in Visual Studio Code](https://open.vscode.dev/badges/open-in-vscode.svg)](https://open.vscode.dev/meshtastic/Meshtastic-device)
## This repository contains the device firmware used in the [Meshtastic](https://meshtastic.org) project. ## This repository contains the device firmware used in the [Meshtastic](https://meshtastic.org) project.
Update Instructions
[For ESP32 devices click here](https://meshtastic.org/docs/getting-started/flashing-esp32)
[For nRF52 devices click here](https://meshtastic.org/docs/getting-started/flashing-nrf52)
For developer information and specific building instructions, please see the [developer doccumentation](https://meshtastic.org/docs/developers) For developer information and specific building instructions, please see the [developer doccumentation](https://meshtastic.org/docs/developers)

BIN
bin/.promote-release.sh.swp Normal file

Binary file not shown.

1
bin/regen-protos.bat Normal file
View File

@@ -0,0 +1 @@
cd proto && ..\nanopb-0.4.4\generator-bin\protoc.exe --nanopb_out=-v:..\src\mesh\generated -I=..\proto *.proto

View File

@@ -111,6 +111,8 @@ lib_deps =
https://github.com/meshtastic/esp32_https_server.git https://github.com/meshtastic/esp32_https_server.git
adafruit/DHT sensor library@^1.4.1 adafruit/DHT sensor library@^1.4.1
adafruit/Adafruit Unified Sensor@^1.1.4 adafruit/Adafruit Unified Sensor@^1.1.4
paulstoffregen/OneWire@^2.3.5
robtillaart/DS18B20@^0.1.11
# Hmm - this doesn't work yet # Hmm - this doesn't work yet
# board_build.ldscript = linker/esp32.extram.bss.ld # board_build.ldscript = linker/esp32.extram.bss.ld
lib_ignore = segger_rtt lib_ignore = segger_rtt

View File

@@ -19,28 +19,39 @@ class GPSStatus : public Status
bool hasLock = false; // default to false, until we complete our first read bool hasLock = false; // default to false, until we complete our first read
bool isConnected = false; // Do we have a GPS we are talking to bool isConnected = false; // Do we have a GPS we are talking to
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
int32_t altitude = 0; Position p = Position_init_default;
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs
// scaling before use)
uint32_t heading = 0;
uint32_t numSatellites = 0;
public: public:
GPSStatus() { statusType = STATUS_TYPE_GPS; } GPSStatus() { statusType = STATUS_TYPE_GPS; }
// proposed for deprecation
GPSStatus(bool hasLock, bool isConnected, int32_t latitude, int32_t longitude, int32_t altitude, uint32_t dop, GPSStatus(bool hasLock, bool isConnected, int32_t latitude, int32_t longitude, int32_t altitude, uint32_t dop,
uint32_t heading, uint32_t numSatellites) uint32_t heading, uint32_t numSatellites)
: Status() : Status()
{ {
this->hasLock = hasLock; this->hasLock = hasLock;
this->isConnected = isConnected; this->isConnected = isConnected;
this->latitude = latitude;
this->longitude = longitude; this->p.latitude_i = latitude;
this->altitude = altitude; this->p.longitude_i = longitude;
this->dop = dop; this->p.altitude = altitude;
this->heading = heading; this->p.PDOP = dop;
this->numSatellites = numSatellites; this->p.ground_track = heading;
this->p.sats_in_view = numSatellites;
} }
// preferred method
GPSStatus(bool hasLock, bool isConnected, Position pos)
: Status()
{
this->hasLock = hasLock;
this->isConnected = isConnected;
// all-in-one struct copy
this->p = pos;
}
GPSStatus(const GPSStatus &); GPSStatus(const GPSStatus &);
GPSStatus &operator=(const GPSStatus &); GPSStatus &operator=(const GPSStatus &);
@@ -56,7 +67,7 @@ class GPSStatus : public Status
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
return node->position.latitude_i; return node->position.latitude_i;
} else { } else {
return latitude; return p.latitude_i;
} }
} }
@@ -66,7 +77,7 @@ class GPSStatus : public Status
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
return node->position.longitude_i; return node->position.longitude_i;
} else { } else {
return longitude; return p.longitude_i;
} }
} }
@@ -76,44 +87,59 @@ class GPSStatus : public Status
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
return node->position.altitude; return node->position.altitude;
} else { } else {
return altitude; return p.altitude;
} }
} }
uint32_t getDOP() const { return p.PDOP; }
uint32_t getDOP() const { return dop; } uint32_t getHeading() const { return p.ground_track; }
uint32_t getHeading() const { return heading; } uint32_t getNumSatellites() const { return p.sats_in_view; }
uint32_t getNumSatellites() const { return numSatellites; }
bool matches(const GPSStatus *newStatus) const bool matches(const GPSStatus *newStatus) const
{ {
return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected || newStatus->latitude != latitude || #if GPS_EXTRAVERBOSE
newStatus->longitude != longitude || newStatus->altitude != altitude || newStatus->dop != dop || DEBUG_MSG("GPSStatus.match() new pos@%x to old pos@%x\n",
newStatus->heading != heading || newStatus->numSatellites != numSatellites); newStatus->p.pos_timestamp, p.pos_timestamp);
#endif
return (newStatus->hasLock != hasLock ||
newStatus->isConnected != isConnected ||
newStatus->p.latitude_i != p.latitude_i ||
newStatus->p.longitude_i != p.longitude_i ||
newStatus->p.altitude != p.altitude ||
newStatus->p.altitude_hae != p.altitude_hae ||
newStatus->p.PDOP != p.PDOP ||
newStatus->p.ground_track != p.ground_track ||
newStatus->p.sats_in_view != p.sats_in_view);
} }
int updateStatus(const GPSStatus *newStatus) int updateStatus(const GPSStatus *newStatus)
{ {
// Only update the status if values have actually changed // Only update the status if values have actually changed
bool isDirty; bool isDirty = matches(newStatus);
{
isDirty = matches(newStatus); if (isDirty && p.pos_timestamp &&
initialized = true; (newStatus->p.pos_timestamp == p.pos_timestamp)) {
hasLock = newStatus->hasLock; // We can NEVER be in two locations at the same time! (also PR #886)
isConnected = newStatus->isConnected; DEBUG_MSG("BUG!! positional timestamp unchanged from prev solution\n");
latitude = newStatus->latitude;
longitude = newStatus->longitude;
altitude = newStatus->altitude;
dop = newStatus->dop;
heading = newStatus->heading;
numSatellites = newStatus->numSatellites;
} }
initialized = true;
hasLock = newStatus->hasLock;
isConnected = newStatus->isConnected;
p = newStatus->p;
if (isDirty) { if (isDirty) {
if (hasLock) if (hasLock) {
DEBUG_MSG("New GPS pos lat=%f, lon=%f, alt=%d, pdop=%f, heading=%f, sats=%d\n", latitude * 1e-7, longitude * 1e-7, // In debug logs, identify position by @timestamp:stage (stage 3 = notify)
altitude, dop * 1e-2, heading * 1e-5, numSatellites); DEBUG_MSG("New GPS pos@%x:3 lat=%f, lon=%f, alt=%d, pdop=%.2f, track=%.2f, sats=%d\n",
else p.pos_timestamp,
p.latitude_i * 1e-7, p.longitude_i * 1e-7,
p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5,
p.sats_in_view);
} else
DEBUG_MSG("No GPS lock\n"); DEBUG_MSG("No GPS lock\n");
onNewStatus.notifyObservers(this); onNewStatus.notifyObservers(this);
} }

View File

@@ -11,6 +11,11 @@
/// Should we behave as if we have AC power now? /// Should we behave as if we have AC power now?
static bool isPowered() static bool isPowered()
{ {
// Completely circumvents the battery / power sensing logic and assumes constant power source
if (radioConfig.preferences.is_always_powered) {
return true;
}
bool isRouter = radioConfig.preferences.is_router; bool isRouter = radioConfig.preferences.is_router;
// If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON // If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON

View File

@@ -256,6 +256,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// supported modules list // supported modules list
#define USE_SX1262 #define USE_SX1262
#define USE_SX1268 #define USE_SX1268
#define USE_LLCC68
// common pinouts for SX126X modules // common pinouts for SX126X modules
#define SX126X_CS 18 // NSS for SX126X #define SX126X_CS 18 // NSS for SX126X

View File

@@ -202,11 +202,13 @@ void GPS::publishUpdate()
if (shouldPublish) { if (shouldPublish) {
shouldPublish = false; shouldPublish = false;
DEBUG_MSG("publishing GPS lock=%d\n", hasLock()); // In debug logs, identify position by @timestamp:stage (stage 2 = publish)
DEBUG_MSG("publishing pos@%x:2, hasVal=%d, GPSlock=%d\n",
p.pos_timestamp, hasValidLocation, hasLock());
// Notify any status instances that are observing us // Notify any status instances that are observing us
const meshtastic::GPSStatus status = const meshtastic::GPSStatus status =
meshtastic::GPSStatus(hasLock(), isConnected(), latitude, longitude, altitude, dop, heading, numSatellites); meshtastic::GPSStatus(hasValidLocation, isConnected(), p);
newStatus.notifyObservers(&status); newStatus.notifyObservers(&status);
} }
} }
@@ -244,6 +246,7 @@ int32_t GPS::runOnce()
bool gotLoc = lookForLocation(); bool gotLoc = lookForLocation();
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
DEBUG_MSG("hasValidLocation RISING EDGE\n");
hasValidLocation = true; hasValidLocation = true;
shouldPublish = true; shouldPublish = true;
} }
@@ -260,6 +263,10 @@ int32_t GPS::runOnce()
if (tooLong) { if (tooLong) {
// we didn't get a location during this ack window, therefore declare loss of lock // we didn't get a location during this ack window, therefore declare loss of lock
if (hasValidLocation) {
DEBUG_MSG("hasValidLocation FALLING EDGE (last read: %d)\n", gotLoc);
}
p = Position_init_default;
hasValidLocation = false; hasValidLocation = false;
} }

View File

@@ -17,6 +17,10 @@ class GPS : private concurrency::OSThread
private: private:
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastWhileActiveMsec = 0; uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastWhileActiveMsec = 0;
/**
* hasValidLocation - indicates that the position variables contain a complete
* GPS location, valid and fresh (< gps_update_interval + gps_attempt_time)
*/
bool hasValidLocation = false; // default to false, until we complete our first read bool hasValidLocation = false; // default to false, until we complete our first read
bool isAwake = false; // true if we want a location right now bool isAwake = false; // true if we want a location right now
@@ -39,14 +43,7 @@ class GPS : private concurrency::OSThread
/** If !0 we will attempt to connect to the GPS over I2C */ /** If !0 we will attempt to connect to the GPS over I2C */
static uint8_t i2cAddress; static uint8_t i2cAddress;
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double Position p = Position_init_default;
int32_t altitude = 0;
uint32_t dop = 0; // Diminution of position; PDOP where possible (UBlox), HDOP otherwise (TinyGPS) in 10^2 units (needs
// scaling before use)
uint32_t heading = 0; // Heading of motion, in degrees * 10^-5
int32_t geoidal_height = 0; // geoidal separation, in meters!
time_t pos_timestamp = 0; // positional timestamp from GPS solution
GPS() : concurrency::OSThread("GPS") {} GPS() : concurrency::OSThread("GPS") {}

View File

@@ -14,7 +14,7 @@
// Helper functions // Helper functions
// Raises a number to an exponent, handling negative exponents. // Raises a number to an exponent, handling negative exponents.
static double pow_neg(double base, double exponent) { static inline double pow_neg(double base, double exponent) {
if (exponent == 0) { if (exponent == 0) {
return 1; return 1;
} else if (exponent > 0) { } else if (exponent > 0) {

View File

@@ -95,6 +95,17 @@ bool NMEAGPS::lookForLocation()
if (! hasLock()) if (! hasLock())
return false; return false;
#ifdef GPS_EXTRAVERBOSE
DEBUG_MSG("AGE: LOC=%d FIX=%d DATE=%d TIME=%d\n",
reader.location.age(),
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
gsafixtype.age(),
#else
0,
#endif
reader.date.age(), reader.time.age());
#endif // GPS_EXTRAVERBOSE
// check if a complete GPS solution set is available for reading // check if a complete GPS solution set is available for reading
// tinyGPSDatum::age() also includes isValid() test // tinyGPSDatum::age() also includes isValid() test
// FIXME // FIXME
@@ -105,7 +116,7 @@ bool NMEAGPS::lookForLocation()
(reader.time.age() < GPS_SOL_EXPIRY_MS) && (reader.time.age() < GPS_SOL_EXPIRY_MS) &&
(reader.date.age() < GPS_SOL_EXPIRY_MS))) (reader.date.age() < GPS_SOL_EXPIRY_MS)))
{ {
// DEBUG_MSG("SOME data is TOO OLD\n"); DEBUG_MSG("SOME data is TOO OLD\n");
return false; return false;
} }
@@ -113,7 +124,7 @@ bool NMEAGPS::lookForLocation()
if (! reader.location.isUpdated()) if (! reader.location.isUpdated())
return false; return false;
// Start reading the data // We know the solution is fresh and valid, so just read the data
auto loc = reader.location.value(); auto loc = reader.location.value();
// Some GPSes (Air530) seem to send a zero longitude when the current fix is bogus // Some GPSes (Air530) seem to send a zero longitude when the current fix is bogus
@@ -123,27 +134,34 @@ bool NMEAGPS::lookForLocation()
return false; return false;
} }
p.location_source = Position_LocSource_LOCSRC_GPS_INTERNAL;
// Dilution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it // Dilution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS #ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
dop = TinyGPSPlus::parseDecimal(gsapdop.value()); p.HDOP = reader.hdop.value();
p.PDOP = TinyGPSPlus::parseDecimal(gsapdop.value());
DEBUG_MSG("PDOP=%d, HDOP=%d\n", dop, reader.hdop.value());
#else #else
// FIXME! naive PDOP emulation (assumes VDOP==HDOP) // FIXME! naive PDOP emulation (assumes VDOP==HDOP)
// correct formula is PDOP = SQRT(HDOP^2 + VDOP^2) // correct formula is PDOP = SQRT(HDOP^2 + VDOP^2)
dop = 1.41 * reader.hdop.value(); p.HDOP = reader.hdop.value();
p.PDOP = 1.41 * reader.hdop.value();
#endif #endif
// Discard incomplete or erroneous readings // Discard incomplete or erroneous readings
if (dop == 0) if (reader.hdop.value() == 0)
return false; return false;
latitude = toDegInt(loc.lat); p.latitude_i = toDegInt(loc.lat);
longitude = toDegInt(loc.lng); p.longitude_i = toDegInt(loc.lng);
geoidal_height = reader.geoidHeight.meters(); p.alt_geoid_sep = reader.geoidHeight.meters();
#ifdef GPS_ALTITUDE_HAE p.altitude_hae = reader.altitude.meters() + p.alt_geoid_sep;
altitude = reader.altitude.meters() + geoidal_height; p.altitude = reader.altitude.meters();
#else
altitude = reader.altitude.meters(); p.fix_quality = fixQual;
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
p.fix_type = fixType;
#endif #endif
// positional timestamp // positional timestamp
@@ -155,16 +173,16 @@ bool NMEAGPS::lookForLocation()
t.tm_mon = reader.date.month() - 1; t.tm_mon = reader.date.month() - 1;
t.tm_year = reader.date.year() - 1900; t.tm_year = reader.date.year() - 1900;
t.tm_isdst = false; t.tm_isdst = false;
pos_timestamp = mktime(&t); p.pos_timestamp = mktime(&t);
// Nice to have, if available // Nice to have, if available
if (reader.satellites.isUpdated()) { if (reader.satellites.isUpdated()) {
setNumSatellites(reader.satellites.value()); p.sats_in_view = reader.satellites.value();
} }
if (reader.course.isUpdated() && reader.course.isValid()) { if (reader.course.isUpdated() && reader.course.isValid()) {
if (reader.course.value() < 36000) { // sanity check if (reader.course.value() < 36000) { // sanity check
heading = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5 p.ground_track = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
} else { } else {
DEBUG_MSG("BOGUS course.value() REJECTED: %d\n", DEBUG_MSG("BOGUS course.value() REJECTED: %d\n",
reader.course.value()); reader.course.value());

View File

@@ -10,6 +10,8 @@
#define PDOP_INVALID 9999 #define PDOP_INVALID 9999
// #define UBX_MODE_NMEA
extern RadioConfig radioConfig; extern RadioConfig radioConfig;
UBloxGPS::UBloxGPS() {} UBloxGPS::UBloxGPS() {}
@@ -48,12 +50,22 @@ bool UBloxGPS::setupGPS()
delay(500); delay(500);
if (isConnected()) { if (isConnected()) {
#ifdef UBX_MODE_NMEA
DEBUG_MSG("Connected to UBLOX GPS, downgrading to NMEA mode\n");
DEBUG_MSG("- GPS errors below are related and safe to ignore\n");
#else
DEBUG_MSG("Connected to UBLOX GPS successfully\n"); DEBUG_MSG("Connected to UBLOX GPS successfully\n");
#endif
if (!setUBXMode()) if (!setUBXMode())
RECORD_CRITICALERROR(CriticalErrorCode_UBloxInitFailed); // Don't halt the boot if saving the config fails, but do report the bug RECORD_CRITICALERROR(CriticalErrorCode_UBloxInitFailed); // Don't halt the boot if saving the config fails, but do report the bug
#ifdef UBX_MODE_NMEA
return false;
#else
return true; return true;
#endif
} else { } else {
return false; return false;
} }
@@ -61,6 +73,17 @@ bool UBloxGPS::setupGPS()
bool UBloxGPS::setUBXMode() bool UBloxGPS::setUBXMode()
{ {
#ifdef UBX_MODE_NMEA
if (_serial_gps) {
ublox.setUART1Output(COM_TYPE_NMEA, 1000);
}
if (i2cAddress) {
ublox.setI2COutput(COM_TYPE_NMEA, 1000);
}
return false; // pretend initialization failed to force NMEA mode
#endif
if (_serial_gps) { if (_serial_gps) {
if (!ublox.setUART1Output(COM_TYPE_UBX, 1000)) // Use native API if (!ublox.setUART1Output(COM_TYPE_UBX, 1000)) // Use native API
return false; return false;
@@ -119,7 +142,6 @@ bool UBloxGPS::factoryReset()
void UBloxGPS::whileActive() void UBloxGPS::whileActive()
{ {
ublox.flushPVT(); // reset ALL freshness flags first ublox.flushPVT(); // reset ALL freshness flags first
ublox.getT(maxWait()); // ask for new time data - hopefully ready when we come back ublox.getT(maxWait()); // ask for new time data - hopefully ready when we come back
// Ask for a new position fix - hopefully it will have results ready by next time // Ask for a new position fix - hopefully it will have results ready by next time
@@ -170,28 +192,32 @@ bool UBloxGPS::lookForLocation()
{ {
bool foundLocation = false; bool foundLocation = false;
// catch fixType changes here, instead of whileActive()
if (ublox.moduleQueried.fixType) {
fixType = ublox.getFixType();
}
// check if GPS has an acceptable lock
if (! hasLock()) {
return false;
}
// check if a complete GPS solution set is available for reading // check if a complete GPS solution set is available for reading
// (some of these, like lat/lon are redundant and can be removed) // (some of these, like lat/lon are redundant and can be removed)
if ( ! (ublox.moduleQueried.latitude && if ( ! (ublox.moduleQueried.fixType &&
ublox.moduleQueried.latitude &&
ublox.moduleQueried.longitude && ublox.moduleQueried.longitude &&
ublox.moduleQueried.altitude && ublox.moduleQueried.altitude &&
ublox.moduleQueried.pDOP && ublox.moduleQueried.pDOP &&
ublox.moduleQueried.gpsiTOW)) ublox.moduleQueried.SIV &&
ublox.moduleQueried.gpsDay))
{ {
// Not ready? No problem! We'll try again later. // Not ready? No problem! We'll try again later.
return false; return false;
} }
fixType = ublox.getFixType();
#ifdef UBLOX_EXTRAVERBOSE
DEBUG_MSG("FixType=%d\n", fixType);
#endif
// check if GPS has an acceptable lock
if (! hasLock()) {
ublox.flushPVT(); // reset ALL freshness flags
return false;
}
// read lat/lon/alt/dop data into temporary variables to avoid // read lat/lon/alt/dop data into temporary variables to avoid
// overwriting global variables with potentially invalid data // overwriting global variables with potentially invalid data
int32_t tmp_dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it int32_t tmp_dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
@@ -199,6 +225,10 @@ bool UBloxGPS::lookForLocation()
int32_t tmp_lon = ublox.getLongitude(0); int32_t tmp_lon = ublox.getLongitude(0);
int32_t tmp_alt_msl = ublox.getAltitudeMSL(0); int32_t tmp_alt_msl = ublox.getAltitudeMSL(0);
int32_t tmp_alt_hae = ublox.getAltitude(0); int32_t tmp_alt_hae = ublox.getAltitude(0);
int32_t max_dop = PDOP_INVALID;
if (radioConfig.preferences.gps_max_dop)
max_dop = radioConfig.preferences.gps_max_dop * 100; // scaling
// Note: heading is only currently implmented in the ublox for the 8m chipset - therefore // Note: heading is only currently implmented in the ublox for the 8m chipset - therefore
// don't read it here - it will generate an ignored getPVT command on the 6ms // don't read it here - it will generate an ignored getPVT command on the 6ms
// heading = ublox.getHeading(0); // heading = ublox.getHeading(0);
@@ -215,41 +245,55 @@ bool UBloxGPS::lookForLocation()
time_t tmp_ts = mktime(&t); time_t tmp_ts = mktime(&t);
// SIV number is nice-to-have if it's available // FIXME - can opportunistically attempt to set RTC from GPS timestamp?
if (ublox.moduleQueried.SIV) {
uint16_t gSIV = ublox.getSIV(0);
setNumSatellites(gSIV);
}
// bogus lat lon is reported as 0 or 0 (can be bogus just for one) // bogus lat lon is reported as 0 or 0 (can be bogus just for one)
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg! // Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
// FIXME - NULL ISLAND is a real location on Earth! // FIXME - NULL ISLAND is a real location on Earth!
foundLocation = (tmp_lat != 0) && (tmp_lon != 0) && foundLocation = (tmp_lat != 0) && (tmp_lon != 0) &&
(tmp_lat <= 900000000) && (tmp_lat >= -900000000) && (tmp_lat <= 900000000) && (tmp_lat >= -900000000) &&
(tmp_dop < PDOP_INVALID); (tmp_dop < max_dop);
// only if entire dataset is valid, update globals from temp vars // only if entire dataset is valid, update globals from temp vars
if (foundLocation) { if (foundLocation) {
longitude = tmp_lon; p.location_source = Position_LocSource_LOCSRC_GPS_INTERNAL;
latitude = tmp_lat; p.longitude_i = tmp_lon;
#ifdef GPS_ALTITUDE_HAE p.latitude_i = tmp_lat;
altitude = tmp_alt_hae / 1000; if (fixType > 2) {
#else // if fix is 2d, ignore altitude data
altitude = tmp_alt_msl / 1000; p.altitude = tmp_alt_msl / 1000;
p.altitude_hae = tmp_alt_hae / 1000;
p.alt_geoid_sep = (tmp_alt_hae - tmp_alt_msl) / 1000;
} else {
#ifdef GPS_EXTRAVERBOSE
DEBUG_MSG("no altitude data (fixType=%d)\n", fixType);
#endif #endif
geoidal_height = (tmp_alt_hae - tmp_alt_msl) / 1000; // clean up old values in case it's a 3d-2d fix transition
pos_timestamp = tmp_ts; p.altitude = p.altitude_hae = p.alt_geoid_sep = 0;
dop = tmp_dop; }
p.pos_timestamp = tmp_ts;
p.PDOP = tmp_dop;
p.fix_type = fixType;
p.sats_in_view = ublox.getSIV(0);
// In debug logs, identify position by @timestamp:stage (stage 1 = birth)
DEBUG_MSG("lookForLocation() new pos@%x:1\n", tmp_ts);
} else { } else {
DEBUG_MSG("Invalid location discarded\n"); // INVALID solution - should never happen
DEBUG_MSG("Invalid location lat/lon/hae/dop %d/%d/%d/%d - discarded\n",
tmp_lat, tmp_lon, tmp_alt_hae, tmp_dop);
} }
ublox.flushPVT(); // reset ALL freshness flags at the end
return foundLocation; return foundLocation;
} }
bool UBloxGPS::hasLock() bool UBloxGPS::hasLock()
{ {
return (fixType >= 3 && fixType <= 4); if (radioConfig.preferences.gps_accept_2d)
return (fixType >= 2 && fixType <= 4);
else
return (fixType >= 3 && fixType <= 4);
} }
bool UBloxGPS::whileIdle() bool UBloxGPS::whileIdle()

View File

@@ -226,6 +226,13 @@ static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *sta
display->drawString(0 + x, FONT_HEIGHT_MEDIUM + y, "For help, please post on\nmeshtastic.discourse.group"); display->drawString(0 + x, FONT_HEIGHT_MEDIUM + y, "For help, please post on\nmeshtastic.discourse.group");
} }
// Ignore messages orginating from phone (from the current node 0x0) unless range test or store and forward plugin are enabled
static bool shouldDrawMessage(const MeshPacket *packet) {
return packet->from != 0 &&
!radioConfig.preferences.range_test_plugin_enabled &&
!radioConfig.preferences.store_forward_plugin_enabled;
}
/// Draw the last text message we received /// Draw the last text message we received
static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
@@ -925,8 +932,8 @@ void Screen::setFrames()
if (myNodeInfo.error_code) if (myNodeInfo.error_code)
normalFrames[numframes++] = drawCriticalFaultFrame; normalFrames[numframes++] = drawCriticalFaultFrame;
// If we have a text message - show it next // If we have a text message - show it next, unless it's a phone message and we aren't using any special plugins
if (devicestate.has_rx_text_message) if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message))
normalFrames[numframes++] = drawTextMessageFrame; normalFrames[numframes++] = drawTextMessageFrame;
// then all the nodes // then all the nodes
@@ -1357,8 +1364,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
return 0; return 0;
} }
int Screen::handleTextMessage(const MeshPacket *arg) int Screen::handleTextMessage(const MeshPacket *packet) {
{
if (showingNormalScreen) { if (showingNormalScreen) {
setFrames(); // Regen the list of screens (will show new text message) setFrames(); // Regen the list of screens (will show new text message)
} }

View File

@@ -40,6 +40,7 @@
#include "RF95Interface.h" #include "RF95Interface.h"
#include "SX1262Interface.h" #include "SX1262Interface.h"
#include "SX1268Interface.h" #include "SX1268Interface.h"
#include "LLCC68Interface.h"
#ifdef NRF52_SERIES #ifdef NRF52_SERIES
#include "variant.h" #include "variant.h"
@@ -539,6 +540,19 @@ void setup()
} }
#endif #endif
#if defined(USE_LLCC68)
if (!rIf) {
rIf = new LLCC68Interface(SX126X_CS, SX126X_DIO1, SX126X_RESET, SX126X_BUSY, SPI);
if (!rIf->init()) {
DEBUG_MSG("Warning: Failed to find LLCC68 radio\n");
delete rIf;
rIf = NULL;
} else {
DEBUG_MSG("LLCC68 Radio init succeeded, using LLCC68 radio\n");
}
}
#endif
#ifdef USE_SIM_RADIO #ifdef USE_SIM_RADIO
if (!rIf) { if (!rIf) {
rIf = new SimRadio; rIf = new SimRadio;

View File

@@ -3,4 +3,5 @@
// We need this declaration for proper linking in derived classes // We need this declaration for proper linking in derived classes
template class SX126xInterface<SX1262>; template class SX126xInterface<SX1262>;
template class SX126xInterface<SX1268>; template class SX126xInterface<SX1268>;
template class SX126xInterface<LLCC68>;

View File

@@ -0,0 +1,9 @@
#include "configuration.h"
#include "LLCC68Interface.h"
#include "error.h"
LLCC68Interface::LLCC68Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
SPIClass &spi)
: SX126xInterface(cs, irq, rst, busy, spi)
{
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "SX126xInterface.h"
/**
* Our adapter for LLCC68 radios
* https://www.semtech.com/products/wireless-rf/lora-core/llcc68
* ⚠️⚠️⚠️
* Be aware that LLCC68 does not support Spreading Factor 12 (SF12) and will not work on the default "Long Slow" channel.
* You must change the channel if you get `Critical Error #3` with this module.
* ⚠️⚠️⚠️
*/
class LLCC68Interface : public SX126xInterface<LLCC68>
{
public:
LLCC68Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi);
};

View File

@@ -112,7 +112,11 @@ bool MeshService::reloadConfig()
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh /// The owner User record just got updated, update our node DB and broadcast the info into the mesh
void MeshService::reloadOwner() void MeshService::reloadOwner()
{ {
// DEBUG_MSG("reloadOwner()\n");
// update our local data directly
nodeDB.updateUser(nodeDB.getNodeNum(), owner);
assert(nodeInfoPlugin); assert(nodeInfoPlugin);
// update everyone else
if (nodeInfoPlugin) if (nodeInfoPlugin)
nodeInfoPlugin->sendOurNodeInfo(); nodeInfoPlugin->sendOurNodeInfo();
nodeDB.saveToDisk(); nodeDB.saveToDisk();
@@ -140,7 +144,7 @@ void MeshService::handleToRadio(MeshPacket &p)
// Send the packet into the mesh // Send the packet into the mesh
sendToMesh(packetPool.allocCopy(p)); sendToMesh(packetPool.allocCopy(p), RX_SRC_USER);
bool loopback = false; // if true send any packet the phone sends back itself (for testing) bool loopback = false; // if true send any packet the phone sends back itself (for testing)
if (loopback) { if (loopback) {
@@ -157,12 +161,12 @@ bool MeshService::cancelSending(PacketId id)
return router->cancelSending(nodeDB.getNodeNum(), id); return router->cancelSending(nodeDB.getNodeNum(), id);
} }
void MeshService::sendToMesh(MeshPacket *p) void MeshService::sendToMesh(MeshPacket *p, RxSource src)
{ {
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...) nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it // Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
router->sendLocal(p); router->sendLocal(p, src);
} }
void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies) void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
@@ -209,34 +213,39 @@ NodeInfo *MeshService::refreshMyNodeInfo()
return node; return node;
} }
int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
{ {
// Update our local node info with our position (even if we don't decide to update anyone else) // Update our local node info with our position (even if we don't decide to update anyone else)
NodeInfo *node = refreshMyNodeInfo(); NodeInfo *node = refreshMyNodeInfo();
Position pos = node->position; Position pos = Position_init_default;
if (gps->hasLock()) { if (newStatus->getHasLock()) {
if (gps->altitude != 0) // load data from GPS object, will add timestamp + battery further down
pos.altitude = gps->altitude; pos = gps->p;
pos.latitude_i = gps->latitude;
pos.longitude_i = gps->longitude;
} else { } else {
// The GPS has lost lock, if we are fixed position we should just keep using // The GPS has lost lock, if we are fixed position we should just keep using
// the old position // the old position
#if GPS_EXTRAVERBOSE
DEBUG_MSG("onGPSchanged() - lost validLocation\n");
#endif
if (radioConfig.preferences.fixed_position) { if (radioConfig.preferences.fixed_position) {
DEBUG_MSG("WARNING: Using fixed position\n"); DEBUG_MSG("WARNING: Using fixed position\n");
} else { pos = node->position;
// throw away old position
pos.latitude_i = 0;
pos.longitude_i = 0;
pos.altitude = 0;
} }
} }
DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.time, pos.latitude_i, pos.battery_level); // Finally add a fresh timestamp and battery level reading
// I KNOW this is redundant with refreshMyNodeInfo() above, but these are
// inexpensive nonblocking calls and can be refactored in due course
pos.time = getValidTime(RTCQualityGPS);
pos.battery_level = powerStatus->getBatteryChargePercent();
// In debug logs, identify position by @timestamp:stage (stage 4 = nodeDB)
DEBUG_MSG("onGPSChanged() pos@%x:4, time=%u, lat=%d, bat=%d\n",
pos.pos_timestamp, pos.time, pos.latitude_i, pos.battery_level);
// Update our current position in the local DB // Update our current position in the local DB
nodeDB.updatePosition(nodeDB.getNodeNum(), pos); nodeDB.updatePosition(nodeDB.getNodeNum(), pos, RX_SRC_LOCAL);
return 0; return 0;
} }

View File

@@ -75,7 +75,7 @@ class MeshService
/// Send a packet into the mesh - note p must have been allocated from packetPool. We will return it to that pool after /// Send a packet into the mesh - note p must have been allocated from packetPool. We will return it to that pool after
/// sending. This is the ONLY function you should use for sending messages into the mesh, because it also updates the nodedb /// sending. This is the ONLY function you should use for sending messages into the mesh, because it also updates the nodedb
/// cache /// cache
void sendToMesh(MeshPacket *p); void sendToMesh(MeshPacket *p, RxSource src = RX_SRC_LOCAL);
/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */ /** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
bool cancelSending(PacketId id); bool cancelSending(PacketId id);

View File

@@ -20,7 +20,8 @@ typedef uint32_t PacketId; // A packet sequence number
*/ */
enum RxSource { enum RxSource {
RX_SRC_LOCAL, // message was generated locally RX_SRC_LOCAL, // message was generated locally
RX_SRC_RADIO // message was received from radio mesh RX_SRC_RADIO, // message was received from radio mesh
RX_SRC_USER // message was received from end-user device
}; };
/** /**

View File

@@ -125,6 +125,11 @@ void NodeDB::installDefaultRadioConfig()
memset(&radioConfig, 0, sizeof(radioConfig)); memset(&radioConfig, 0, sizeof(radioConfig));
radioConfig.has_preferences = true; radioConfig.has_preferences = true;
resetRadioConfig(); resetRadioConfig();
// for backward compat, default position flags are BAT+ALT+MSL (0x23 = 35)
radioConfig.preferences.position_flags = (PositionFlags_POS_BATTERY |
PositionFlags_POS_ALTITUDE | PositionFlags_POS_ALT_MSL);
} }
void NodeDB::installDefaultChannels() void NodeDB::installDefaultChannels()
@@ -442,25 +447,42 @@ size_t NodeDB::getNumOnlineNodes()
/** Update position info for this node based on received position data /** Update position info for this node based on received position data
*/ */
void NodeDB::updatePosition(uint32_t nodeId, const Position &p) void NodeDB::updatePosition(uint32_t nodeId, const Position &p, RxSource src)
{ {
NodeInfo *info = getOrCreateNode(nodeId); NodeInfo *info = getOrCreateNode(nodeId);
DEBUG_MSG("DB update position node=0x%x time=%u, latI=%d, lonI=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i); if (src == RX_SRC_LOCAL) {
// Local packet, fully authoritative
DEBUG_MSG("updatePosition LOCAL pos@%x:5, time=%u, latI=%d, lonI=%d\n",
p.pos_timestamp, p.time, p.latitude_i, p.longitude_i);
info->position = p;
// Be careful to only update fields that have been set by the sender } else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.pos_timestamp &&
// A lot of position reports don't have time populated. In that case, be careful to not blow away the time we !p.location_source) {
// recorded based on the packet rxTime // FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO
if (p.time) // (stop-gap fix for issue #900)
DEBUG_MSG("updatePosition SPECIAL time setting time=%u\n", p.time);
info->position.time = p.time; info->position.time = p.time;
if (p.battery_level)
info->position.battery_level = p.battery_level; } else {
if (p.latitude_i || p.longitude_i) { // Be careful to only update fields that have been set by the REMOTE sender
info->position.latitude_i = p.latitude_i; // A lot of position reports don't have time populated. In that case, be careful to not blow away the time we
info->position.longitude_i = p.longitude_i; // recorded based on the packet rxTime
//
// FIXME perhaps handle RX_SRC_USER separately?
DEBUG_MSG("updatePosition REMOTE node=0x%x time=%u, latI=%d, lonI=%d\n",
nodeId, p.time, p.latitude_i, p.longitude_i);
// First, back up fields that we want to protect from overwrite
uint32_t tmp_time = info->position.time;
// Next, update atomically
info->position = p;
// Last, restore any fields that may have been overwritten
if (! info->position.time)
info->position.time = tmp_time;
} }
if (p.altitude)
info->position.altitude = p.altitude;
info->has_position = true; info->has_position = true;
updateGUIforNode = info; updateGUIforNode = info;
notifyObservers(true); // Force an update whether or not our node counts have changed notifyObservers(true); // Force an update whether or not our node counts have changed

View File

@@ -59,7 +59,7 @@ class NodeDB
/** Update position info for this node based on received position data /** Update position info for this node based on received position data
*/ */
void updatePosition(uint32_t nodeId, const Position &p); void updatePosition(uint32_t nodeId, const Position &p, RxSource src = RX_SRC_RADIO);
/** Update user info for this node based on received user data /** Update user info for this node based on received user data
*/ */

View File

@@ -307,6 +307,8 @@ void RadioInterface::applyModemConfig()
if (bw == 31) // This parameter is not an integer if (bw == 31) // This parameter is not an integer
bw = 31.25; bw = 31.25;
if (bw == 62) // Fix for 62.5Khz bandwidth
bw = 62.5;
} }
power = channelSettings.tx_power; power = channelSettings.tx_power;

View File

@@ -145,7 +145,7 @@ void Router::setReceivedMessage()
runASAP = true; runASAP = true;
} }
ErrorCode Router::sendLocal(MeshPacket *p) ErrorCode Router::sendLocal(MeshPacket *p, RxSource src)
{ {
// No need to deliver externally if the destination is the local node // No need to deliver externally if the destination is the local node
if (p->to == nodeDB.getNodeNum()) { if (p->to == nodeDB.getNodeNum()) {
@@ -161,7 +161,7 @@ ErrorCode Router::sendLocal(MeshPacket *p)
// If we are sending a broadcast, we also treat it as if we just received it ourself // If we are sending a broadcast, we also treat it as if we just received it ourself
// this allows local apps (and PCs) to see broadcasts sourced locally // this allows local apps (and PCs) to see broadcasts sourced locally
if (p->to == NODENUM_BROADCAST) { if (p->to == NODENUM_BROADCAST) {
handleReceived(p, RX_SRC_LOCAL); handleReceived(p, src);
} }
return send(p); return send(p);
@@ -334,9 +334,11 @@ void Router::handleReceived(MeshPacket *p, RxSource src)
if (decoded) { if (decoded) {
// parsing was successful, queue for our recipient // parsing was successful, queue for our recipient
if (src == RX_SRC_LOCAL) if (src == RX_SRC_LOCAL)
printPacket("handleReceived(local)", p); printPacket("handleReceived(LOCAL)", p);
else if (src == RX_SRC_USER)
printPacket("handleReceived(USER)", p);
else else
printPacket("handleReceived(remote)", p); printPacket("handleReceived(REMOTE)", p);
} else { } else {
printPacket("packet decoding failed (no PSK?)", p); printPacket("packet decoding failed (no PSK?)", p);
} }

View File

@@ -45,7 +45,7 @@ class Router : protected concurrency::OSThread
* *
* NOTE: This method will free the provided packet (even if we return an error code) * NOTE: This method will free the provided packet (even if we return an error code)
*/ */
ErrorCode sendLocal(MeshPacket *p); ErrorCode sendLocal(MeshPacket *p, RxSource src = RX_SRC_RADIO);
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */ /** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
bool cancelSending(NodeNum from, PacketId id); bool cancelSending(NodeNum from, PacketId id);

View File

@@ -79,7 +79,7 @@ extern const pb_msgdesc_t AdminMessage_msg;
#define AdminMessage_fields &AdminMessage_msg #define AdminMessage_fields &AdminMessage_msg
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define AdminMessage_size 407 #define AdminMessage_size 420
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -125,7 +125,7 @@ extern const pb_msgdesc_t ChannelFile_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define LegacyRadioConfig_size 4 #define LegacyRadioConfig_size 4
#define LegacyRadioConfig_LegacyPreferences_size 2 #define LegacyRadioConfig_LegacyPreferences_size 2
#define DeviceState_size 10054 #define DeviceState_size 9000
#define ChannelFile_size 832 #define ChannelFile_size 832
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -51,3 +51,4 @@ PB_BIND(ToRadio_PeerInfo, ToRadio_PeerInfo, AUTO)

View File

@@ -33,6 +33,24 @@ typedef enum _HardwareModel {
HardwareModel_DIY_V1 = 39 HardwareModel_DIY_V1 = 39
} HardwareModel; } HardwareModel;
typedef enum _Team {
Team_CLEAR = 0,
Team_CYAN = 1,
Team_WHITE = 2,
Team_YELLOW = 3,
Team_ORANGE = 4,
Team_MAGENTA = 5,
Team_RED = 6,
Team_MAROON = 7,
Team_PURPLE = 8,
Team_DARK_BLUE = 9,
Team_BLUE = 10,
Team_TEAL = 11,
Team_GREEN = 12,
Team_DARK_GREEN = 13,
Team_BROWN = 14
} Team;
typedef enum _Constants { typedef enum _Constants {
Constants_Unused = 0, Constants_Unused = 0,
Constants_DATA_PAYLOAD_LEN = 237 Constants_DATA_PAYLOAD_LEN = 237
@@ -158,11 +176,6 @@ typedef struct _Position {
uint32_t fix_type; uint32_t fix_type;
uint32_t sats_in_view; uint32_t sats_in_view;
uint32_t sensor_id; uint32_t sensor_id;
uint32_t heading;
int32_t roll;
int32_t pitch;
uint32_t air_speed;
uint32_t ground_distance_cm;
uint32_t pos_next_update; uint32_t pos_next_update;
uint32_t pos_seq_number; uint32_t pos_seq_number;
} Position; } Position;
@@ -184,6 +197,7 @@ typedef struct _User {
pb_byte_t macaddr[6]; pb_byte_t macaddr[6];
HardwareModel hw_model; HardwareModel hw_model;
bool is_licensed; bool is_licensed;
Team team;
} User; } User;
typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t; typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t;
@@ -253,6 +267,10 @@ typedef struct _ToRadio {
#define _HardwareModel_MAX HardwareModel_DIY_V1 #define _HardwareModel_MAX HardwareModel_DIY_V1
#define _HardwareModel_ARRAYSIZE ((HardwareModel)(HardwareModel_DIY_V1+1)) #define _HardwareModel_ARRAYSIZE ((HardwareModel)(HardwareModel_DIY_V1+1))
#define _Team_MIN Team_CLEAR
#define _Team_MAX Team_BROWN
#define _Team_ARRAYSIZE ((Team)(Team_BROWN+1))
#define _Constants_MIN Constants_Unused #define _Constants_MIN Constants_Unused
#define _Constants_MAX Constants_DATA_PAYLOAD_LEN #define _Constants_MAX Constants_DATA_PAYLOAD_LEN
#define _Constants_ARRAYSIZE ((Constants)(Constants_DATA_PAYLOAD_LEN+1)) #define _Constants_ARRAYSIZE ((Constants)(Constants_DATA_PAYLOAD_LEN+1))
@@ -287,8 +305,8 @@ extern "C" {
#endif #endif
/* Initializer values for message structs */ /* Initializer values for message structs */
#define Position_init_default {0, 0, 0, 0, 0, _Position_LocSource_MIN, _Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define Position_init_default {0, 0, 0, 0, 0, _Position_LocSource_MIN, _Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define User_init_default {"", "", "", {0}, _HardwareModel_MIN, 0} #define User_init_default {"", "", "", {0}, _HardwareModel_MIN, 0, _Team_MIN}
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_default {0, {RouteDiscovery_init_default}} #define Routing_init_default {0, {RouteDiscovery_init_default}}
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0} #define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
@@ -299,8 +317,8 @@ extern "C" {
#define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}} #define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}}
#define ToRadio_init_default {0, {MeshPacket_init_default}} #define ToRadio_init_default {0, {MeshPacket_init_default}}
#define ToRadio_PeerInfo_init_default {0, 0} #define ToRadio_PeerInfo_init_default {0, 0}
#define Position_init_zero {0, 0, 0, 0, 0, _Position_LocSource_MIN, _Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define Position_init_zero {0, 0, 0, 0, 0, _Position_LocSource_MIN, _Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define User_init_zero {"", "", "", {0}, _HardwareModel_MIN, 0} #define User_init_zero {"", "", "", {0}, _HardwareModel_MIN, 0, _Team_MIN}
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_zero {0, {RouteDiscovery_init_zero}} #define Routing_init_zero {0, {RouteDiscovery_init_zero}}
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0} #define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
@@ -357,11 +375,6 @@ extern "C" {
#define Position_fix_type_tag 23 #define Position_fix_type_tag 23
#define Position_sats_in_view_tag 24 #define Position_sats_in_view_tag 24
#define Position_sensor_id_tag 25 #define Position_sensor_id_tag 25
#define Position_heading_tag 30
#define Position_roll_tag 31
#define Position_pitch_tag 32
#define Position_air_speed_tag 33
#define Position_ground_distance_cm_tag 34
#define Position_pos_next_update_tag 40 #define Position_pos_next_update_tag 40
#define Position_pos_seq_number_tag 41 #define Position_pos_seq_number_tag 41
#define RouteDiscovery_route_tag 2 #define RouteDiscovery_route_tag 2
@@ -373,6 +386,7 @@ extern "C" {
#define User_macaddr_tag 4 #define User_macaddr_tag 4
#define User_hw_model_tag 6 #define User_hw_model_tag 6
#define User_is_licensed_tag 7 #define User_is_licensed_tag 7
#define User_team_tag 8
#define MeshPacket_from_tag 1 #define MeshPacket_from_tag 1
#define MeshPacket_to_tag 2 #define MeshPacket_to_tag 2
#define MeshPacket_channel_tag 3 #define MeshPacket_channel_tag 3
@@ -428,11 +442,6 @@ X(a, STATIC, SINGULAR, UINT32, fix_quality, 22) \
X(a, STATIC, SINGULAR, UINT32, fix_type, 23) \ X(a, STATIC, SINGULAR, UINT32, fix_type, 23) \
X(a, STATIC, SINGULAR, UINT32, sats_in_view, 24) \ X(a, STATIC, SINGULAR, UINT32, sats_in_view, 24) \
X(a, STATIC, SINGULAR, UINT32, sensor_id, 25) \ X(a, STATIC, SINGULAR, UINT32, sensor_id, 25) \
X(a, STATIC, SINGULAR, UINT32, heading, 30) \
X(a, STATIC, SINGULAR, SINT32, roll, 31) \
X(a, STATIC, SINGULAR, SINT32, pitch, 32) \
X(a, STATIC, SINGULAR, UINT32, air_speed, 33) \
X(a, STATIC, SINGULAR, UINT32, ground_distance_cm, 34) \
X(a, STATIC, SINGULAR, UINT32, pos_next_update, 40) \ X(a, STATIC, SINGULAR, UINT32, pos_next_update, 40) \
X(a, STATIC, SINGULAR, UINT32, pos_seq_number, 41) X(a, STATIC, SINGULAR, UINT32, pos_seq_number, 41)
#define Position_CALLBACK NULL #define Position_CALLBACK NULL
@@ -444,7 +453,8 @@ X(a, STATIC, SINGULAR, STRING, long_name, 2) \
X(a, STATIC, SINGULAR, STRING, short_name, 3) \ X(a, STATIC, SINGULAR, STRING, short_name, 3) \
X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4) \ X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4) \
X(a, STATIC, SINGULAR, UENUM, hw_model, 6) \ X(a, STATIC, SINGULAR, UENUM, hw_model, 6) \
X(a, STATIC, SINGULAR, BOOL, is_licensed, 7) X(a, STATIC, SINGULAR, BOOL, is_licensed, 7) \
X(a, STATIC, SINGULAR, UENUM, team, 8)
#define User_CALLBACK NULL #define User_CALLBACK NULL
#define User_DEFAULT NULL #define User_DEFAULT NULL
@@ -584,13 +594,13 @@ extern const pb_msgdesc_t ToRadio_PeerInfo_msg;
#define ToRadio_PeerInfo_fields &ToRadio_PeerInfo_msg #define ToRadio_PeerInfo_fields &ToRadio_PeerInfo_msg
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define Position_size 188 #define Position_size 153
#define User_size 76 #define User_size 78
#define RouteDiscovery_size 40 #define RouteDiscovery_size 40
#define Routing_size 42 #define Routing_size 42
#define Data_size 260 #define Data_size 260
#define MeshPacket_size 309 #define MeshPacket_size 309
#define NodeInfo_size 285 #define NodeInfo_size 252
#define MyNodeInfo_size 101 #define MyNodeInfo_size 101
#define LogRecord_size 81 #define LogRecord_size 81
#define FromRadio_size 318 #define FromRadio_size 318

View File

@@ -80,7 +80,8 @@ typedef enum _PositionFlags {
} PositionFlags; } PositionFlags;
typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType { typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType {
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0 RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0,
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20 = 1
} RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType; } RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType;
/* Struct definitions */ /* Struct definitions */
@@ -112,6 +113,8 @@ typedef struct _RadioConfig_UserPreferences {
char mqtt_server[32]; char mqtt_server[32];
bool mqtt_disabled; bool mqtt_disabled;
GpsCoordinateFormat gps_format; GpsCoordinateFormat gps_format;
bool gps_accept_2d;
uint32_t gps_max_dop;
bool factory_reset; bool factory_reset;
bool debug_log_enabled; bool debug_log_enabled;
pb_size_t ignore_incoming_count; pb_size_t ignore_incoming_count;
@@ -142,6 +145,7 @@ typedef struct _RadioConfig_UserPreferences {
uint32_t environmental_measurement_plugin_sensor_pin; uint32_t environmental_measurement_plugin_sensor_pin;
bool store_forward_plugin_enabled; bool store_forward_plugin_enabled;
uint32_t position_flags; uint32_t position_flags;
bool is_always_powered;
} RadioConfig_UserPreferences; } RadioConfig_UserPreferences;
typedef struct _RadioConfig { typedef struct _RadioConfig {
@@ -176,8 +180,8 @@ typedef struct _RadioConfig {
#define _PositionFlags_ARRAYSIZE ((PositionFlags)(PositionFlags_POS_TIMESTAMP+1)) #define _PositionFlags_ARRAYSIZE ((PositionFlags)(PositionFlags_POS_TIMESTAMP+1))
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 #define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MAX RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 #define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MAX RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_ARRAYSIZE ((RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType)(RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11+1)) #define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_ARRAYSIZE ((RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType)(RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20+1))
#ifdef __cplusplus #ifdef __cplusplus
@@ -186,9 +190,9 @@ extern "C" {
/* Initializer values for message structs */ /* Initializer values for message structs */
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default} #define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0} #define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0, 0}
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero} #define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0} #define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0, 0}
/* Field tags (for use in manual encoding/decoding) */ /* Field tags (for use in manual encoding/decoding) */
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1 #define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
@@ -218,6 +222,8 @@ extern "C" {
#define RadioConfig_UserPreferences_mqtt_server_tag 42 #define RadioConfig_UserPreferences_mqtt_server_tag 42
#define RadioConfig_UserPreferences_mqtt_disabled_tag 43 #define RadioConfig_UserPreferences_mqtt_disabled_tag 43
#define RadioConfig_UserPreferences_gps_format_tag 44 #define RadioConfig_UserPreferences_gps_format_tag 44
#define RadioConfig_UserPreferences_gps_accept_2d_tag 45
#define RadioConfig_UserPreferences_gps_max_dop_tag 46
#define RadioConfig_UserPreferences_factory_reset_tag 100 #define RadioConfig_UserPreferences_factory_reset_tag 100
#define RadioConfig_UserPreferences_debug_log_enabled_tag 101 #define RadioConfig_UserPreferences_debug_log_enabled_tag 101
#define RadioConfig_UserPreferences_ignore_incoming_tag 103 #define RadioConfig_UserPreferences_ignore_incoming_tag 103
@@ -247,6 +253,7 @@ extern "C" {
#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_pin_tag 147 #define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_pin_tag 147
#define RadioConfig_UserPreferences_store_forward_plugin_enabled_tag 148 #define RadioConfig_UserPreferences_store_forward_plugin_enabled_tag 148
#define RadioConfig_UserPreferences_position_flags_tag 150 #define RadioConfig_UserPreferences_position_flags_tag 150
#define RadioConfig_UserPreferences_is_always_powered_tag 151
#define RadioConfig_preferences_tag 1 #define RadioConfig_preferences_tag 1
/* Struct field encoding specification for nanopb */ /* Struct field encoding specification for nanopb */
@@ -284,6 +291,8 @@ X(a, STATIC, SINGULAR, FLOAT, frequency_offset, 41) \
X(a, STATIC, SINGULAR, STRING, mqtt_server, 42) \ X(a, STATIC, SINGULAR, STRING, mqtt_server, 42) \
X(a, STATIC, SINGULAR, BOOL, mqtt_disabled, 43) \ X(a, STATIC, SINGULAR, BOOL, mqtt_disabled, 43) \
X(a, STATIC, SINGULAR, UENUM, gps_format, 44) \ X(a, STATIC, SINGULAR, UENUM, gps_format, 44) \
X(a, STATIC, SINGULAR, BOOL, gps_accept_2d, 45) \
X(a, STATIC, SINGULAR, UINT32, gps_max_dop, 46) \
X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \ X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \
X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \ X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \ X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
@@ -312,7 +321,8 @@ X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_display_fare
X(a, STATIC, SINGULAR, UENUM, environmental_measurement_plugin_sensor_type, 146) \ X(a, STATIC, SINGULAR, UENUM, environmental_measurement_plugin_sensor_type, 146) \
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_sensor_pin, 147) \ X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_sensor_pin, 147) \
X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_enabled, 148) \ X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_enabled, 148) \
X(a, STATIC, SINGULAR, UINT32, position_flags, 150) X(a, STATIC, SINGULAR, UINT32, position_flags, 150) \
X(a, STATIC, SINGULAR, BOOL, is_always_powered, 151)
#define RadioConfig_UserPreferences_CALLBACK NULL #define RadioConfig_UserPreferences_CALLBACK NULL
#define RadioConfig_UserPreferences_DEFAULT NULL #define RadioConfig_UserPreferences_DEFAULT NULL
@@ -324,8 +334,8 @@ extern const pb_msgdesc_t RadioConfig_UserPreferences_msg;
#define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg #define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define RadioConfig_size 404 #define RadioConfig_size 417
#define RadioConfig_UserPreferences_size 401 #define RadioConfig_UserPreferences_size 414
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -137,10 +137,15 @@ void AdminPlugin::handleSetOwner(const User &o)
strcpy(owner.id, o.id); strcpy(owner.id, o.id);
} }
if (owner.is_licensed != o.is_licensed) { if (owner.is_licensed != o.is_licensed) {
changed = true; changed = 1;
owner.is_licensed = o.is_licensed; owner.is_licensed = o.is_licensed;
} }
if ((!changed || o.team) && (owner.team != o.team)) {
changed = 1;
owner.team = o.team;
}
if (changed) // If nothing really changed, don't broadcast on the network or write to flash if (changed) // If nothing really changed, don't broadcast on the network or write to flash
service.reloadOwner(); service.reloadOwner();
} }

View File

@@ -5,12 +5,11 @@
#include "plugins/RemoteHardwarePlugin.h" #include "plugins/RemoteHardwarePlugin.h"
#include "plugins/ReplyPlugin.h" #include "plugins/ReplyPlugin.h"
#include "plugins/TextMessagePlugin.h" #include "plugins/TextMessagePlugin.h"
#include "plugins/SerialPlugin.h"
#include "plugins/TextMessagePlugin.h" #include "plugins/TextMessagePlugin.h"
#include "plugins/RoutingPlugin.h" #include "plugins/RoutingPlugin.h"
#include "plugins/AdminPlugin.h" #include "plugins/AdminPlugin.h"
#ifndef NO_ESP32 #ifndef NO_ESP32
#include "plugins/SerialPlugin.h" #include "plugins/esp32/SerialPlugin.h"
#include "plugins/esp32/EnvironmentalMeasurementPlugin.h" #include "plugins/esp32/EnvironmentalMeasurementPlugin.h"
#include "plugins/esp32/RangeTestPlugin.h" #include "plugins/esp32/RangeTestPlugin.h"
#include "plugins/esp32/StoreForwardPlugin.h" #include "plugins/esp32/StoreForwardPlugin.h"

View File

@@ -18,6 +18,36 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, Position *pptr
{ {
auto p = *pptr; auto p = *pptr;
// If inbound message is a replay (or spoof!) of our own messages, we shouldn't process
// (why use second-hand sources for our own data?)
// FIXME this can in fact happen with packets sent from EUD (src=RX_SRC_USER)
// to set fixed location, EUD-GPS location or just the time (see also issue #900)
if (nodeDB.getNodeNum() == getFrom(&mp)) {
DEBUG_MSG("Incoming update from MYSELF\n");
// DEBUG_MSG("Ignored an incoming update from MYSELF\n");
// return false;
}
// Log packet size and list of fields
DEBUG_MSG("POSITION node=%08x l=%d %s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
getFrom(&mp),
mp.decoded.payload.size,
p.latitude_i ? "LAT ":"",
p.longitude_i ? "LON ":"",
p.altitude ? "MSL ":"",
p.altitude_hae ? "HAE ":"",
p.alt_geoid_sep ? "GEO ":"",
p.PDOP ? "PDOP ":"",
p.HDOP ? "HDOP ":"",
p.VDOP ? "VDOP ":"",
p.sats_in_view ? "SIV ":"",
p.fix_quality ? "FXQ ":"",
p.fix_type ? "FXT ":"",
p.pos_timestamp ? "PTS ":"",
p.time ? "TIME ":"",
p.battery_level ? "BAT ":"");
if (p.time) { if (p.time) {
struct timeval tv; struct timeval tv;
uint32_t secs = p.time; uint32_t secs = p.time;
@@ -38,7 +68,44 @@ MeshPacket *PositionPlugin::allocReply()
NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position
assert(node->has_position); assert(node->has_position);
Position p = node->position; // configuration of POSITION packet
// consider making this a function argument?
uint32_t pos_flags = radioConfig.preferences.position_flags;
// Populate a Position struct with ONLY the requested fields
Position p = Position_init_default; // Start with an empty structure
// lat/lon are unconditionally included - IF AVAILABLE!
p.latitude_i = node->position.latitude_i;
p.longitude_i = node->position.longitude_i;
p.time = node->position.time;
if (pos_flags & PositionFlags_POS_BATTERY)
p.battery_level = node->position.battery_level;
if (pos_flags & PositionFlags_POS_ALTITUDE) {
if (pos_flags & PositionFlags_POS_ALT_MSL)
p.altitude = node->position.altitude;
else
p.altitude_hae = node->position.altitude_hae;
if (pos_flags & PositionFlags_POS_GEO_SEP)
p.alt_geoid_sep = node->position.alt_geoid_sep;
}
if (pos_flags & PositionFlags_POS_DOP) {
if (pos_flags & PositionFlags_POS_HVDOP) {
p.HDOP = node->position.HDOP;
p.VDOP = node->position.VDOP;
} else
p.PDOP = node->position.PDOP;
}
if (pos_flags & PositionFlags_POS_SATINVIEW)
p.sats_in_view = node->position.sats_in_view;
if (pos_flags & PositionFlags_POS_TIMESTAMP)
p.pos_timestamp = node->position.pos_timestamp;
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other // Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless // nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
@@ -69,17 +136,20 @@ void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
int32_t PositionPlugin::runOnce() int32_t PositionPlugin::runOnce()
{ {
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
// We limit our GPS broadcasts to a max rate // We limit our GPS broadcasts to a max rate
uint32_t now = millis(); uint32_t now = millis();
if (lastGpsSend == 0 || now - lastGpsSend >= getPref_position_broadcast_secs() * 1000) { if (lastGpsSend == 0 || now - lastGpsSend >= getPref_position_broadcast_secs() * 1000) {
lastGpsSend = now; lastGpsSend = now;
// If we changed channels, ask everyone else for their latest info // If we changed channels, ask everyone else for their latest info
bool requestReplies = currentGeneration != radioGeneration; bool requestReplies = currentGeneration != radioGeneration;
currentGeneration = radioGeneration; currentGeneration = radioGeneration;
DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies); DEBUG_MSG("Sending pos@%x:6 to mesh (wantReplies=%d)\n",
node->position.pos_timestamp, requestReplies);
sendOurPosition(NODENUM_BROADCAST, requestReplies); sendOurPosition(NODENUM_BROADCAST, requestReplies);
} }

View File

@@ -1,20 +1,23 @@
#include "EnvironmentalMeasurementPlugin.h" #include "EnvironmentalMeasurementPlugin.h"
#include "../mesh/generated/environmental_measurement.pb.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "RTC.h" #include "RTC.h"
#include "Router.h" #include "Router.h"
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
#include "../mesh/generated/environmental_measurement.pb.h"
#include <DHT.h> #include <DHT.h>
#include <DS18B20.h>
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
#include <OLEDDisplayUi.h> #include <OLEDDisplayUi.h>
#include <OneWire.h>
#define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 // Some sensors (the DHT11) have a minimum required duration between read attempts #define DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
#define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
#define DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
#ifdef HAS_EINK #ifdef HAS_EINK
// The screen is bigger so use bigger fonts // The screen is bigger so use bigger fonts
#define FONT_SMALL ArialMT_Plain_16 #define FONT_SMALL ArialMT_Plain_16
@@ -31,123 +34,136 @@
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL) #define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM) #define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
int32_t EnvironmentalMeasurementPlugin::runOnce()
int32_t EnvironmentalMeasurementPlugin::runOnce() { {
#ifndef NO_ESP32 // this only works on ESP32 devices #ifndef NO_ESP32 // this only works on ESP32 devices
/* /*
Uncomment the preferences below if you want to use the plugin Uncomment the preferences below if you want to use the plugin
without having to configure it from the PythonAPI or WebUI. without having to configure it from the PythonAPI or WebUI.
*/ */
/*radioConfig.preferences.environmental_measurement_plugin_measurement_enabled = 1; /*radioConfig.preferences.environmental_measurement_plugin_measurement_enabled = 1;
radioConfig.preferences.environmental_measurement_plugin_screen_enabled = 1; radioConfig.preferences.environmental_measurement_plugin_screen_enabled = 1;
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold = 5; radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold = 5;
radioConfig.preferences.environmental_measurement_plugin_update_interval = 30; radioConfig.preferences.environmental_measurement_plugin_update_interval = 600;
radioConfig.preferences.environmental_measurement_plugin_recovery_interval = 60; radioConfig.preferences.environmental_measurement_plugin_recovery_interval = 60;
radioConfig.preferences.environmental_measurement_plugin_display_farenheit = true; radioConfig.preferences.environmental_measurement_plugin_display_farenheit = false;
radioConfig.preferences.environmental_measurement_plugin_sensor_pin = 13; radioConfig.preferences.environmental_measurement_plugin_sensor_pin = 13;
radioConfig.preferences.environmental_measurement_plugin_sensor_type = RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType::RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11;*/ radioConfig.preferences.environmental_measurement_plugin_sensor_type =
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType::
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20;
*/
if (! (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){ if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled ||
radioConfig.preferences.environmental_measurement_plugin_screen_enabled)) {
// If this plugin is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it // If this plugin is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
return (INT32_MAX); return (INT32_MAX);
} }
if (firstTime) { if (firstTime) {
// This is the first time the OSThread library has called this function, so do some setup // This is the first time the OSThread library has called this function, so do some setup
firstTime = 0; firstTime = 0;
if (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled) if (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled) {
{
DEBUG_MSG("EnvironmentalMeasurement: Initializing\n"); DEBUG_MSG("EnvironmentalMeasurement: Initializing\n");
// it's possible to have this plugin enabled, only for displaying values on the screen. // it's possible to have this plugin enabled, only for displaying values on the screen.
// therefore, we should only enable the sensor loop if measurement is also enabled // therefore, we should only enable the sensor loop if measurement is also enabled
switch(radioConfig.preferences.environmental_measurement_plugin_sensor_type) { switch (radioConfig.preferences.environmental_measurement_plugin_sensor_type) {
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11: case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11:
dht = new DHT(radioConfig.preferences.environmental_measurement_plugin_sensor_pin,DHT11); dht = new DHT(radioConfig.preferences.environmental_measurement_plugin_sensor_pin, DHT11);
this->dht->begin(); this->dht->begin();
this->dht->read(); this->dht->read();
DEBUG_MSG("EnvironmentalMeasurement: Opened DHT11 on pin: %d\n",radioConfig.preferences.environmental_measurement_plugin_sensor_pin); DEBUG_MSG("EnvironmentalMeasurement: Opened DHT11 on pin: %d\n",
radioConfig.preferences.environmental_measurement_plugin_sensor_pin);
return (DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20:
oneWire = new OneWire(radioConfig.preferences.environmental_measurement_plugin_sensor_pin);
ds18b20 = new DS18B20(oneWire);
this->ds18b20->begin();
this->ds18b20->setResolution(12);
this->ds18b20->requestTemperatures();
DEBUG_MSG("EnvironmentalMeasurement: Opened DS18B20 on pin: %d\n",
radioConfig.preferences.environmental_measurement_plugin_sensor_pin);
return (DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
default:
DEBUG_MSG("EnvironmentalMeasurement: Invalid sensor type selected; Disabling plugin");
return (INT32_MAX);
break; break;
default:
DEBUG_MSG("EnvironmentalMeasurement: Invalid sensor type selected; Disabling plugin");
return (INT32_MAX);
break;
} }
// begin reading measurements from the sensor
// DHT have a max read-rate of 1HZ, so we should wait at least 1 second
// after initializing the sensor before we try to read from it.
// returning the interval here means that the next time OSThread
// calls our plugin, we'll run the other branch of this if statement
// and actually do a "sendOurEnvironmentalMeasurement()"
return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
} }
return (INT32_MAX); return (INT32_MAX);
} } else {
else { if (!radioConfig.preferences.environmental_measurement_plugin_measurement_enabled) {
if (!radioConfig.preferences.environmental_measurement_plugin_measurement_enabled)
{
// if we somehow got to a second run of this plugin with measurement disabled, then just wait forever // if we somehow got to a second run of this plugin with measurement disabled, then just wait forever
// I can't imagine we'd ever get here though. // I can't imagine we'd ever get here though.
return (INT32_MAX); return (INT32_MAX);
} }
// this is not the first time OSThread library has called this function // this is not the first time OSThread library has called this function
// so just do what we intend to do on the interval // so just do what we intend to do on the interval
if(sensor_read_error_count > radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold) if (sensor_read_error_count > radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold) {
{ if (radioConfig.preferences.environmental_measurement_plugin_recovery_interval > 0) {
if (radioConfig.preferences.environmental_measurement_plugin_recovery_interval > 0 ) { DEBUG_MSG("EnvironmentalMeasurement: TEMPORARILY DISABLED; The "
DEBUG_MSG( "environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Will retry reads in "
"EnvironmentalMeasurement: TEMPORARILY DISABLED; The environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Will retry reads in %d seconds\n", "%d seconds\n",
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold, radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold,
radioConfig.preferences.environmental_measurement_plugin_recovery_interval); radioConfig.preferences.environmental_measurement_plugin_recovery_interval);
sensor_read_error_count = 0; sensor_read_error_count = 0;
return(radioConfig.preferences.environmental_measurement_plugin_recovery_interval*1000); return (radioConfig.preferences.environmental_measurement_plugin_recovery_interval * 1000);
} }
DEBUG_MSG( DEBUG_MSG("EnvironmentalMeasurement: DISABLED; The environmental_measurement_plugin_read_error_count_threshold has "
"EnvironmentalMeasurement: DISABLED; The environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Reads will not be retried until after device reset\n", "been exceed: %d. Reads will not be retried until after device reset\n",
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold); radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold);
return(INT32_MAX); return (INT32_MAX);
} else if (sensor_read_error_count > 0) {
DEBUG_MSG("EnvironmentalMeasurement: There have been %d sensor read failures. Will retry %d more times\n",
sensor_read_error_count, sensor_read_error_count, sensor_read_error_count,
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold -
sensor_read_error_count);
} }
else if (sensor_read_error_count > 0){ if (!sendOurEnvironmentalMeasurement()) {
DEBUG_MSG("EnvironmentalMeasurement: There have been %d sensor read failures. Will retry %d more times\n", // if we failed to read the sensor, then try again
sensor_read_error_count,
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold-sensor_read_error_count);
}
if (!sendOurEnvironmentalMeasurement() ){
// if we failed to read the sensor, then try again
// as soon as we can according to the maximum polling frequency // as soon as we can according to the maximum polling frequency
return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); // return (DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
switch (radioConfig.preferences.environmental_measurement_plugin_sensor_type) {
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11:
return (DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20:
return (DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
default:
return (DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
}
} }
} }
// The return of runOnce is an int32 representing the desired number of // The return of runOnce is an int32 representing the desired number of
// miliseconds until the function should be called again by the // miliseconds until the function should be called again by the
// OSThread library. Multiply the preference value by 1000 to convert seconds to miliseconds // OSThread library. Multiply the preference value by 1000 to convert seconds to miliseconds
return(radioConfig.preferences.environmental_measurement_plugin_update_interval * 1000); return (radioConfig.preferences.environmental_measurement_plugin_update_interval * 1000);
#endif #endif
} }
bool EnvironmentalMeasurementPlugin::wantUIFrame() { bool EnvironmentalMeasurementPlugin::wantUIFrame()
{
return radioConfig.preferences.environmental_measurement_plugin_screen_enabled; return radioConfig.preferences.environmental_measurement_plugin_screen_enabled;
} }
String GetSenderName(const MeshPacket &mp) { String GetSenderName(const MeshPacket &mp)
{
String sender; String sender;
auto node = nodeDB.getNode(getFrom(&mp)); auto node = nodeDB.getNode(getFrom(&mp));
if (node){ if (node) {
sender = node->user.short_name; sender = node->user.short_name;
} } else {
else {
sender = "UNK"; sender = "UNK";
} }
return sender; return sender;
} }
uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp) { uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp)
{
uint32_t now = getTime(); uint32_t now = getTime();
uint32_t last_seen = mp->rx_time; uint32_t last_seen = mp->rx_time;
@@ -156,15 +172,13 @@ uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp) {
delta = 0; delta = 0;
return delta; return delta;
} }
float EnvironmentalMeasurementPlugin::CelsiusToFarenheit(float c)
float EnvironmentalMeasurementPlugin::CelsiusToFarenheit(float c) { {
return (c*9)/5 + 32; return (c * 9) / 5 + 32;
} }
void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
@@ -173,21 +187,16 @@ void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplay
if (lastMeasurementPacket == nullptr) { if (lastMeasurementPacket == nullptr) {
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement"); display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement");
//DEBUG_MSG("EnvironmentalMeasurement: No previous measurement; not drawing frame\n");
return; return;
} }
EnvironmentalMeasurement lastMeasurement; EnvironmentalMeasurement lastMeasurement;
uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket); uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket);
String lastSender = GetSenderName(*lastMeasurementPacket); String lastSender = GetSenderName(*lastMeasurementPacket);
auto &p = lastMeasurementPacket->decoded; auto &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, EnvironmentalMeasurement_fields, &lastMeasurement)) {
p.payload.size,
EnvironmentalMeasurement_fields,
&lastMeasurement)) {
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error"); display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error");
DEBUG_MSG("EnvironmentalMeasurement: unable to decode last packet"); DEBUG_MSG("EnvironmentalMeasurement: unable to decode last packet");
@@ -195,20 +204,23 @@ void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplay
} }
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
String last_temp = String(lastMeasurement.temperature,0) +"°C"; String last_temp = String(lastMeasurement.temperature, 0) + "°C";
if (radioConfig.preferences.environmental_measurement_plugin_display_farenheit){ if (radioConfig.preferences.environmental_measurement_plugin_display_farenheit) {
last_temp = String(CelsiusToFarenheit(lastMeasurement.temperature),0) +"°F";; last_temp = String(CelsiusToFarenheit(lastMeasurement.temperature), 0) + "°F";
;
} }
display->drawString(x, y += fontHeight(FONT_MEDIUM), lastSender+": "+last_temp +"/"+ String(lastMeasurement.relative_humidity,0) + "%("+String(agoSecs)+"s)"); display->drawString(x, y += fontHeight(FONT_MEDIUM),
lastSender + ": " + last_temp + "/" + String(lastMeasurement.relative_humidity, 0) + "%(" +
String(agoSecs) + "s)");
} }
bool EnvironmentalMeasurementPlugin::handleReceivedProtobuf(const MeshPacket &mp, EnvironmentalMeasurement *p) bool EnvironmentalMeasurementPlugin::handleReceivedProtobuf(const MeshPacket &mp, EnvironmentalMeasurement *p)
{ {
if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){ if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled ||
// If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume radioConfig.preferences.environmental_measurement_plugin_screen_enabled)) {
return false; // If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume
return false;
} }
String sender = GetSenderName(mp); String sender = GetSenderName(mp);
@@ -230,13 +242,32 @@ bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum des
DEBUG_MSG("-----------------------------------------\n"); DEBUG_MSG("-----------------------------------------\n");
DEBUG_MSG("EnvironmentalMeasurement: Read data\n"); DEBUG_MSG("EnvironmentalMeasurement: Read data\n");
if (!this->dht->read(true)){
sensor_read_error_count++; switch (radioConfig.preferences.environmental_measurement_plugin_sensor_type) {
DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n"); case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11:
if (!this->dht->read(true)) {
sensor_read_error_count++;
DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n");
return false;
}
m.relative_humidity = this->dht->readHumidity();
m.temperature = this->dht->readTemperature();
break;
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20:
if (this->ds18b20->isConversionComplete()) {
m.temperature = this->ds18b20->getTempC();
m.relative_humidity = 0; // This sensor is temperature only
this->ds18b20->requestTemperatures();
break;
} else {
sensor_read_error_count++;
DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n");
return false;
}
default:
DEBUG_MSG("EnvironmentalMeasurement: Invalid sensor type selected; Disabling plugin");
return false; return false;
} }
m.relative_humidity = this->dht->readHumidity();
m.temperature = this->dht->readTemperature();
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity); DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity);
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature); DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature);
@@ -250,4 +281,3 @@ bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum des
service.sendToMesh(p); service.sendToMesh(p);
return true; return true;
} }

View File

@@ -1,15 +1,20 @@
#pragma once #pragma once
#include "ProtobufPlugin.h"
#include "../mesh/generated/environmental_measurement.pb.h" #include "../mesh/generated/environmental_measurement.pb.h"
#include "ProtobufPlugin.h"
#include <DHT.h>
#include <DS18B20.h>
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
#include <OLEDDisplayUi.h> #include <OLEDDisplayUi.h>
#include <DHT.h> #include <OneWire.h>
class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public ProtobufPlugin<EnvironmentalMeasurement> class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public ProtobufPlugin<EnvironmentalMeasurement>
{ {
public: public:
EnvironmentalMeasurementPlugin(): concurrency::OSThread("EnvironmentalMeasurementPlugin"), ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg) { EnvironmentalMeasurementPlugin()
lastMeasurementPacket = nullptr; : concurrency::OSThread("EnvironmentalMeasurementPlugin"),
ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg)
{
lastMeasurementPacket = nullptr;
} }
virtual bool wantUIFrame(); virtual bool wantUIFrame();
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
@@ -24,11 +29,13 @@ class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public Pro
* Send our EnvironmentalMeasurement into the mesh * Send our EnvironmentalMeasurement into the mesh
*/ */
bool sendOurEnvironmentalMeasurement(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); bool sendOurEnvironmentalMeasurement(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
private: private:
float CelsiusToFarenheit(float c); float CelsiusToFarenheit(float c);
bool firstTime = 1; bool firstTime = 1;
DHT* dht; DHT *dht;
OneWire *oneWire;
DS18B20 *ds18b20;
const MeshPacket *lastMeasurementPacket; const MeshPacket *lastMeasurementPacket;
uint32_t sensor_read_error_count = 0; uint32_t sensor_read_error_count = 0;
}; };

View File

@@ -237,7 +237,7 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
// Print the CSV header // Print the CSV header
if (fileToWrite.println( if (fileToWrite.println(
"time,from,sender name,sender lat,sender long,rx lat,rx long,rx snr,rx elevation,distance,payload")) { "time,from,sender name,sender lat,sender long,rx lat,rx long,rx elevation,rx snr,distance,payload")) {
DEBUG_MSG("File was written\n"); DEBUG_MSG("File was written\n");
} else { } else {
DEBUG_MSG("File write failed\n"); DEBUG_MSG("File write failed\n");

View File

@@ -50,4 +50,4 @@ class SerialPluginRadio : public SinglePortPlugin
virtual ProcessMessage handleReceived(const MeshPacket &mp); virtual ProcessMessage handleReceived(const MeshPacket &mp);
}; };
extern SerialPluginRadio *serialPluginRadio; extern SerialPluginRadio *serialPluginRadio;

View File

@@ -8,10 +8,8 @@
#include "plugins/PluginDev.h" #include "plugins/PluginDev.h"
#include <Arduino.h> #include <Arduino.h>
#include <map> #include <map>
#include <iterator>
#define STOREFORWARD_MAX_PACKETS 0
#define STOREFORWARD_SEND_HISTORY_PERIOD 10 * 60
#define STOREFORWARD_SEND_HISTORY_MAX 0
StoreForwardPlugin *storeForwardPlugin; StoreForwardPlugin *storeForwardPlugin;
@@ -23,22 +21,31 @@ int32_t StoreForwardPlugin::runOnce()
if (radioConfig.preferences.store_forward_plugin_enabled) { if (radioConfig.preferences.store_forward_plugin_enabled) {
if (radioConfig.preferences.is_router) { if (radioConfig.preferences.is_router) {
// Maybe some cleanup functions?
this->historyReport(); if (this->busy) {
return (60 * 1000); // Send out the message queue.
//DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index);
storeForwardPlugin->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
strcpy(this->routerMessage, "** S&F - Done");
storeForwardPlugin->sendMessage(this->busyTo, this->routerMessage);
//DEBUG_MSG("--- --- --- In busy loop - Done \n");
this->packetHistoryTXQueue_index = 0;
this->busy = false;
} else {
this->packetHistoryTXQueue_index++;
}
}
// TODO: Dynamicly adjust the time this returns in the loop based on the size of the packets being actually transmitted.
return (this->packetTimeMax);
} else { } else {
/* DEBUG_MSG("Store & Forward Plugin - Disabled (is_router = false)\n");
* If the plugin is turned on and is_router is not enabled, then we'll send a heartbeat every
* few minutes.
*
* This behavior is expected to change. It's only here until we come up with something better.
*/
DEBUG_MSG("Store & Forward Plugin - Sending heartbeat\n"); return (INT32_MAX);
storeForwardPlugin->sendPayload();
return (4 * 60 * 1000);
} }
} else { } else {
@@ -51,6 +58,9 @@ int32_t StoreForwardPlugin::runOnce()
return (INT32_MAX); return (INT32_MAX);
} }
/*
Create our data structure in the PSRAM.
*/
void StoreForwardPlugin::populatePSRAM() void StoreForwardPlugin::populatePSRAM()
{ {
/* /*
@@ -58,6 +68,8 @@ void StoreForwardPlugin::populatePSRAM()
https://learn.upesy.com/en/programmation/psram.html#psram-tab https://learn.upesy.com/en/programmation/psram.html#psram-tab
*/ */
uint32_t store_forward_plugin_replay_max_records = 250;
DEBUG_MSG("Before PSRAM initilization:\n"); DEBUG_MSG("Before PSRAM initilization:\n");
DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize()); DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize());
@@ -65,12 +77,13 @@ void StoreForwardPlugin::populatePSRAM()
DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize()); DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize());
DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram()); DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram());
// Use a maximum of half the available PSRAM unless otherwise specified. // Use a maximum of 2/3 the available PSRAM unless otherwise specified.
uint32_t numberOfPackets = uint32_t numberOfPackets =
STOREFORWARD_MAX_PACKETS ? STOREFORWARD_MAX_PACKETS : ((ESP.getPsramSize() / 2) / sizeof(PacketHistoryStruct)); (radioConfig.preferences.store_forward_plugin_records ? radioConfig.preferences.store_forward_plugin_records
: (((ESP.getFreePsram() / 3) * 2) / sizeof(PacketHistoryStruct)));
// this->packetHistory = (PacketHistoryStruct *)ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct));
this->packetHistory = static_cast<PacketHistoryStruct *>(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct))); this->packetHistory = static_cast<PacketHistoryStruct *>(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
this->packetHistoryTXQueue = static_cast<PacketHistoryStruct *>(ps_calloc(store_forward_plugin_replay_max_records, sizeof(PacketHistoryStruct)));
DEBUG_MSG("After PSRAM initilization:\n"); DEBUG_MSG("After PSRAM initilization:\n");
DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize()); DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize());
@@ -78,34 +91,13 @@ void StoreForwardPlugin::populatePSRAM()
DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize()); DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize());
DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram()); DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram());
DEBUG_MSG("Store and Forward Stats:\n"); DEBUG_MSG("Store and Forward Stats:\n");
DEBUG_MSG(" numberOfPackets - %u\n", numberOfPackets); DEBUG_MSG(" numberOfPackets for packetHistory - %u\n", numberOfPackets);
}
// We saw a node.
void StoreForwardPlugin::sawNode(uint32_t whoWeSaw, uint32_t sawSecAgo)
{
if (radioConfig.preferences.is_router) {
// If node has been away for more than 10 minutes, send the node the last 10 minutes of
// messages
if (sawSecAgo > STOREFORWARD_SEND_HISTORY_PERIOD) {
// Node has been away for a while.
storeForwardPlugin->historySend(STOREFORWARD_SEND_HISTORY_PERIOD, whoWeSaw);
}
}
} }
void StoreForwardPlugin::historyReport() void StoreForwardPlugin::historyReport()
{ {
DEBUG_MSG("Iterating through the message history...\n"); DEBUG_MSG("Iterating through the message history...\n");
DEBUG_MSG("Message history contains %u records\n", this->packetHistoryCurrent); DEBUG_MSG("Message history contains %u records\n", this->packetHistoryCurrent);
uint32_t startTimer = millis();
for (int i = 0; i < this->packetHistoryCurrent; i++) {
if (this->packetHistory[i].time) {
// DEBUG_MSG("... time-%u to-0x%08x\n", this->packetHistory[i].time, this->packetHistory[i].to & 0xffffffff);
}
}
DEBUG_MSG("StoreForwardPlugin::historyReport runtime - %u ms\n", millis() - startTimer);
} }
/* /*
@@ -113,31 +105,80 @@ void StoreForwardPlugin::historyReport()
*/ */
void StoreForwardPlugin::historySend(uint32_t msAgo, uint32_t to) void StoreForwardPlugin::historySend(uint32_t msAgo, uint32_t to)
{ {
// Send "Welcome back"
this->sendPayloadWelcome(to, false);
for (int i = 0; i < this->packetHistoryCurrent; i++) { uint32_t packetsSent = 0;
if (this->packetHistory[i].time) {
// DEBUG_MSG("... time-%u to-0x%08x\n", this->packetHistory[i].time, this->packetHistory[i].to & 0xffffffff);
} uint32_t queueSize = storeForwardPlugin->historyQueueCreate(msAgo, to);
if (queueSize) {
snprintf(this->routerMessage, 80, "** S&F - Sending %d message(s)", queueSize);
storeForwardPlugin->sendMessage(to, this->routerMessage);
this->busy = true; // runOnce() will pickup the next steps once busy = true.
this->busyTo = to;
} else {
strcpy(this->routerMessage, "** S&F - No history to send");
storeForwardPlugin->sendMessage(to, this->routerMessage);
} }
} }
void StoreForwardPlugin::historyAdd(const MeshPacket *mp) uint32_t StoreForwardPlugin::historyQueueCreate(uint32_t msAgo, uint32_t to) {
//uint32_t packetHistoryTXQueueIndex = 0;
this->packetHistoryTXQueue_size = 0;
for (int i = 0; i < this->packetHistoryCurrent; i++) {
/*
DEBUG_MSG("SF historyQueueCreate\n");
DEBUG_MSG("SF historyQueueCreate - time %d\n", this->packetHistory[i].time);
DEBUG_MSG("SF historyQueueCreate - millis %d\n", millis());
DEBUG_MSG("SF historyQueueCreate - math %d\n", (millis() - msAgo));
*/
if (this->packetHistory[i].time && (this->packetHistory[i].time < (millis() - msAgo))) {
DEBUG_MSG("SF historyQueueCreate - Time matches - ok\n");
/*
Copy the messages that were received by the router in the last msAgo
to the packetHistoryTXQueue structure.
TODO: The condition (this->packetHistory[i].to & 0xffffffff) == to) is not tested since
I don't have an easy way to target a specific user. Will need to do this soon.
*/
if ((this->packetHistory[i].to & 0xffffffff) == 0xffffffff
||
((this->packetHistory[i].to & 0xffffffff) == to)
) {
this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].time = this->packetHistory[i].time;
this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].to = this->packetHistory[i].to;
this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].from = this->packetHistory[i].from;
this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].payload_size = this->packetHistory[i].payload_size;
memcpy(this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].payload, this->packetHistory[i].payload, Constants_DATA_PAYLOAD_LEN);
this->packetHistoryTXQueue_size++;
DEBUG_MSG("PacketHistoryStruct time=%d\n", this->packetHistory[i].time);
DEBUG_MSG("PacketHistoryStruct msg=%.*s\n", this->packetHistory[i].payload);
//DEBUG_MSG("PacketHistoryStruct msg=%.*s\n", this->packetHistoryTXQueue[packetHistoryTXQueueIndex].payload);
}
}
}
return this->packetHistoryTXQueue_size;
}
void StoreForwardPlugin::historyAdd(const MeshPacket &mp)
{ {
auto &p = mp; auto &p = mp.decoded;
static uint8_t bytes[MAX_RHPACKETLEN];
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded);
assert(numbytes <= MAX_RHPACKETLEN);
DEBUG_MSG("MP numbytes %u\n", numbytes);
// destination, source, bytes
// memcpy(p->encrypted.bytes, bytes, numbytes);
memcpy(this->packetHistory[this->packetHistoryCurrent].bytes, bytes, MAX_RHPACKETLEN);
this->packetHistory[this->packetHistoryCurrent].time = millis(); this->packetHistory[this->packetHistoryCurrent].time = millis();
this->packetHistory[this->packetHistoryCurrent].to = mp->to; this->packetHistory[this->packetHistoryCurrent].to = mp.to;
this->packetHistory[this->packetHistoryCurrent].from = mp.from;
this->packetHistory[this->packetHistoryCurrent].payload_size = p.payload.size;
memcpy(this->packetHistory[this->packetHistoryCurrent].payload, p.payload.bytes, Constants_DATA_PAYLOAD_LEN);
this->packetHistoryCurrent++; this->packetHistoryCurrent++;
} }
@@ -147,46 +188,39 @@ MeshPacket *StoreForwardPlugin::allocReply()
return reply; return reply;
} }
void StoreForwardPlugin::sendPayload(NodeNum dest, bool wantReplies) void StoreForwardPlugin::sendPayload(NodeNum dest, uint32_t packetHistory_index)
{ {
DEBUG_MSG("Sending S&F Payload\n"); DEBUG_MSG("Sending S&F Payload\n");
MeshPacket *p = allocReply(); MeshPacket *p = allocReply();
p->to = dest; p->to = dest;
p->decoded.want_response = wantReplies; p->from = this->packetHistoryTXQueue[packetHistory_index].from;
p->want_ack = true; // Let's assume that if the router received the S&F request that the client is in range.
// TODO: Make this configurable.
p->want_ack = false;
static char heartbeatString[20]; p->decoded.payload.size =
snprintf(heartbeatString, sizeof(heartbeatString), "1"); this->packetHistoryTXQueue[packetHistory_index].payload_size; // You must specify how many bytes are in the reply
memcpy(p->decoded.payload.bytes, this->packetHistoryTXQueue[packetHistory_index].payload,
p->decoded.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply this->packetHistoryTXQueue[packetHistory_index].payload_size);
memcpy(p->decoded.payload.bytes, "1", 1);
service.sendToMesh(p); service.sendToMesh(p);
} }
void StoreForwardPlugin::sendPayloadWelcome(NodeNum dest, bool wantReplies) void StoreForwardPlugin::sendMessage(NodeNum dest, char *str)
{ {
DEBUG_MSG("*********************************\n");
DEBUG_MSG("*********************************\n");
DEBUG_MSG("*********************************\n");
DEBUG_MSG("Sending S&F Welcome Message\n");
DEBUG_MSG("*********************************\n");
DEBUG_MSG("*********************************\n");
DEBUG_MSG("*********************************\n");
MeshPacket *p = allocReply(); MeshPacket *p = allocReply();
p->to = dest; p->to = dest;
p->decoded.want_response = wantReplies;
p->want_ack = true; // Let's assume that if the router received the S&F request that the client is in range.
// TODO: Make this configurable.
p->want_ack = false;
p->decoded.portnum = PortNum_TEXT_MESSAGE_APP; p->decoded.payload.size = strlen(str); // You must specify how many bytes are in the reply
memcpy(p->decoded.payload.bytes, str, strlen(str));
static char heartbeatString[80];
snprintf(heartbeatString, sizeof(heartbeatString), "Welcome back to the mesh. We have not seen you in x minutes!");
p->decoded.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply
memcpy(p->decoded.payload.bytes, heartbeatString, p->decoded.payload.size);
service.sendToMesh(p); service.sendToMesh(p);
} }
@@ -196,15 +230,33 @@ ProcessMessage StoreForwardPlugin::handleReceived(const MeshPacket &mp)
#ifndef NO_ESP32 #ifndef NO_ESP32
if (radioConfig.preferences.store_forward_plugin_enabled) { if (radioConfig.preferences.store_forward_plugin_enabled) {
DEBUG_MSG("--- S&F Received something\n");
auto &p = mp.decoded;
// The router node should not be sending messages as a client.
if (getFrom(&mp) != nodeDB.getNodeNum()) { if (getFrom(&mp) != nodeDB.getNodeNum()) {
printPacket("----- PACKET FROM RADIO -----", &mp);
// uint32_t sawTime = storeForwardPlugin->sawNode(getFrom(&mp) & 0xffffffff);
// DEBUG_MSG("We last saw this node (%u), %u sec ago\n", mp.from & 0xffffffff, (millis() - sawTime) / 1000);
DEBUG_MSG(" -------------- ");
if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) { if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) {
DEBUG_MSG("Packet came from - PortNum_TEXT_MESSAGE_APP\n"); DEBUG_MSG("Packet came from - PortNum_TEXT_MESSAGE_APP\n");
storeForwardPlugin->historyAdd(&mp); if ((p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && (p.payload.bytes[2] == 0x00)) {
DEBUG_MSG("--- --- --- Request to send\n");
// Send the last 60 minutes of messages.
if (this->busy) {
strcpy(this->routerMessage, "** S&F - Busy. Try again shortly.");
storeForwardPlugin->sendMessage(getFrom(&mp), this->routerMessage);
} else {
storeForwardPlugin->historySend(1000 * 60, getFrom(&mp));
}
} else if ((p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && (p.payload.bytes[2] == 'm') && (p.payload.bytes[3] == 0x00)) {
strcpy(this->routerMessage, "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456");
storeForwardPlugin->sendMessage(getFrom(&mp), this->routerMessage);
} else {
storeForwardPlugin->historyAdd(mp);
}
} else { } else {
DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum); DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum);
@@ -221,21 +273,22 @@ ProcessMessage StoreForwardPlugin::handleReceived(const MeshPacket &mp)
} }
StoreForwardPlugin::StoreForwardPlugin() StoreForwardPlugin::StoreForwardPlugin()
: SinglePortPlugin("StoreForwardPlugin", PortNum_STORE_FORWARD_APP), concurrency::OSThread("StoreForwardPlugin") : SinglePortPlugin("StoreForwardPlugin", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("StoreForwardPlugin")
{ {
#ifndef NO_ESP32 #ifndef NO_ESP32
isPromiscuous = true; // Brown chicken brown cow isPromiscuous = true; // Brown chicken brown cow
/*
Uncomment the preferences below if you want to use the plugin
without having to configure it from the PythonAPI or WebUI.
*/
if (StoreForward_Dev) { if (StoreForward_Dev) {
radioConfig.preferences.store_forward_plugin_enabled = 1; /*
radioConfig.preferences.is_router = 1; Uncomment the preferences below if you want to use the plugin
without having to configure it from the PythonAPI or WebUI.
*/
// radioConfig.preferences.store_forward_plugin_enabled = 1;
// radioConfig.preferences.is_router = 1;
// radioConfig.preferences.is_always_powered = 1;
} }
if (radioConfig.preferences.store_forward_plugin_enabled) { if (radioConfig.preferences.store_forward_plugin_enabled) {
@@ -248,7 +301,17 @@ StoreForwardPlugin::StoreForwardPlugin()
// Do the startup here // Do the startup here
// Popupate PSRAM with our data structures.
this->populatePSRAM(); this->populatePSRAM();
// Calculate the packet time.
// this->packetTimeMax = RadioLibInterface::instance->getPacketTime(Constants_DATA_PAYLOAD_LEN);
//RadioLibInterface::instance->getPacketTime(Constants_DATA_PAYLOAD_LEN);
//RadioLibInterface::instance->getPacketTime(Constants_DATA_PAYLOAD_LEN);
//RadioInterface::getPacketTime(500)l
this->packetTimeMax = 2000;
} else { } else {
DEBUG_MSG("Device has less than 1M of PSRAM free. Aborting startup.\n"); DEBUG_MSG("Device has less than 1M of PSRAM free. Aborting startup.\n");
DEBUG_MSG("Store & Forward Plugin - Aborting Startup.\n"); DEBUG_MSG("Store & Forward Plugin - Aborting Startup.\n");
@@ -265,4 +328,4 @@ StoreForwardPlugin::StoreForwardPlugin()
} }
} }
#endif #endif
} }

View File

@@ -9,19 +9,31 @@
struct PacketHistoryStruct { struct PacketHistoryStruct {
uint32_t time; uint32_t time;
uint32_t to; uint32_t to;
uint32_t from;
bool ack; bool ack;
uint8_t bytes[MAX_RHPACKETLEN]; uint8_t payload[Constants_DATA_PAYLOAD_LEN];
pb_size_t payload_size;
}; };
class StoreForwardPlugin : public SinglePortPlugin, private concurrency::OSThread class StoreForwardPlugin : public SinglePortPlugin, private concurrency::OSThread
{ {
bool firstTime = 1; //bool firstTime = 1;
bool busy = 0;
uint32_t busyTo;
char routerMessage[80];
uint32_t receivedRecord[50][2] = {{0}}; uint32_t receivedRecord[50][2] = {{0}};
PacketHistoryStruct *packetHistory; PacketHistoryStruct *packetHistory;
uint32_t packetHistoryCurrent = 0; uint32_t packetHistoryCurrent = 0;
PacketHistoryStruct *packetHistoryTXQueue;
uint32_t packetHistoryTXQueue_size;
uint32_t packetHistoryTXQueue_index = 0;
uint32_t packetTimeMax = 0;
public: public:
StoreForwardPlugin(); StoreForwardPlugin();
@@ -29,54 +41,32 @@ class StoreForwardPlugin : public SinglePortPlugin, private concurrency::OSThrea
Update our local reference of when we last saw that node. Update our local reference of when we last saw that node.
@return 0 if we have never seen that node before otherwise return the last time we saw the node. @return 0 if we have never seen that node before otherwise return the last time we saw the node.
*/ */
void sawNode(uint32_t whoWeSaw, uint32_t sawSecAgo); void historyAdd(const MeshPacket &mp);
void historyAdd(const MeshPacket *mp);
void historyReport(); void historyReport();
void historySend(uint32_t msAgo, uint32_t to); void historySend(uint32_t msAgo, uint32_t to);
void populatePSRAM();
uint32_t historyQueueCreate(uint32_t msAgo, uint32_t to);
/** /**
* Send our payload into the mesh * Send our payload into the mesh
*/ */
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); void sendPayload(NodeNum dest = NODENUM_BROADCAST, uint32_t packetHistory_index = 0);
void sendPayloadWelcome(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); void sendMessage(NodeNum dest, char *str);
virtual MeshPacket *allocReply(); virtual MeshPacket *allocReply();
virtual bool wantPortnum(PortNum p) { return true; }; virtual bool wantPortnum(PortNum p) { return true; };
private: private:
// Nothing here void populatePSRAM();
protected: protected:
virtual int32_t runOnce(); virtual int32_t runOnce();
/** Called to handle a particular incoming message /** Called to handle a particular incoming message
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for it @return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
it
*/ */
virtual ProcessMessage handleReceived(const MeshPacket &mp); virtual ProcessMessage handleReceived(const MeshPacket &mp);
}; };
extern StoreForwardPlugin *storeForwardPlugin; extern StoreForwardPlugin *storeForwardPlugin;
/*
* Radio interface for StoreForwardPlugin
*
*/
/*
class StoreForwardPluginRadio : public SinglePortPlugin
{
// uint32_t lastRxID;
public:
StoreForwardPluginRadio() : SinglePortPlugin("StoreForwardPluginRadio", PortNum_STORE_FORWARD_APP) {}
// StoreForwardPluginRadio() : SinglePortPlugin("StoreForwardPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
void sendPayloadHeartbeat(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
protected:
virtual MeshPacket *allocReply2();
};
extern StoreForwardPluginRadio *storeForwardPluginRadio;
*/

View File

@@ -1,4 +1,4 @@
[VERSION] [VERSION]
major = 1 major = 1
minor = 2 minor = 2
build = 46 build = 47