mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-20 00:37:36 +00:00
Compare commits
85 Commits
v1.2.46.dc
...
v1.2.47
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a56851fefa | ||
|
|
0fc88c542f | ||
|
|
16307cb67d | ||
|
|
9f05bd11cc | ||
|
|
4b6ee2dc88 | ||
|
|
dbabc24d7a | ||
|
|
d3e1bbf355 | ||
|
|
2fa2497ed8 | ||
|
|
a5727052bc | ||
|
|
083518c127 | ||
|
|
bc7fc1285d | ||
|
|
9ddcc5d2ed | ||
|
|
a3ce728e26 | ||
|
|
cf0d9a2c86 | ||
|
|
ec7953ccf0 | ||
|
|
44a4bde626 | ||
|
|
5db0a57599 | ||
|
|
834f2f4160 | ||
|
|
4367f05b24 | ||
|
|
97aec5f125 | ||
|
|
5191fd6475 | ||
|
|
32017e53f5 | ||
|
|
37aab8a42b | ||
|
|
bfa0307231 | ||
|
|
8eb0d685ac | ||
|
|
43e543eabc | ||
|
|
eee6ef018c | ||
|
|
f7da9ac071 | ||
|
|
928198ff03 | ||
|
|
0cd82507d9 | ||
|
|
2d939d26ee | ||
|
|
d6c7ea921a | ||
|
|
19f43689ca | ||
|
|
e78e82ef42 | ||
|
|
fdfc600b3d | ||
|
|
334e14ea4d | ||
|
|
8115ee0c97 | ||
|
|
e21cc9d479 | ||
|
|
d5e4ceebcc | ||
|
|
51646f28ec | ||
|
|
2c5ba29648 | ||
|
|
41bed5c14d | ||
|
|
5ef9414a25 | ||
|
|
2ebed9cb6c | ||
|
|
7a1409c42a | ||
|
|
093e3e55b9 | ||
|
|
a911515dec | ||
|
|
7ecb36dbe7 | ||
|
|
d4ea18851d | ||
|
|
646d1caf66 | ||
|
|
74c138620a | ||
|
|
3981d2e1f6 | ||
|
|
6ccaa64ae8 | ||
|
|
5dd9610d36 | ||
|
|
d4ed7b2f73 | ||
|
|
9702dffa12 | ||
|
|
e2992cd3b9 | ||
|
|
4e5ac1ac07 | ||
|
|
e89b3bd1ec | ||
|
|
da9dd62a33 | ||
|
|
e6b4ee8084 | ||
|
|
0133a1b293 | ||
|
|
543283c0f6 | ||
|
|
6d778cdda4 | ||
|
|
3ffcecee6c | ||
|
|
24b2fd2657 | ||
|
|
b74c2da530 | ||
|
|
26415cf8e0 | ||
|
|
2f7b58abaf | ||
|
|
ef1d52ca04 | ||
|
|
f69c8dddad | ||
|
|
70b80e600d | ||
|
|
90d95d8e98 | ||
|
|
7d267e8027 | ||
|
|
0d884d159a | ||
|
|
3893810b76 | ||
|
|
fff4735a15 | ||
|
|
a914ee133c | ||
|
|
d0fb363422 | ||
|
|
992bbe76d7 | ||
|
|
6fc3c9c868 | ||
|
|
4fb844bddd | ||
|
|
1a8b128640 | ||
|
|
a83dcbadb9 | ||
|
|
a5e3f271ea |
@@ -2,4 +2,10 @@
|
|||||||
[](https://open.vscode.dev/meshtastic/Meshtastic-device)
|
[](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
BIN
bin/.promote-release.sh.swp
Normal file
Binary file not shown.
1
bin/regen-protos.bat
Normal file
1
bin/regen-protos.bat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
cd proto && ..\nanopb-0.4.4\generator-bin\protoc.exe --nanopb_out=-v:..\src\mesh\generated -I=..\proto *.proto
|
||||||
@@ -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
|
||||||
|
|||||||
102
src/GPSStatus.h
102
src/GPSStatus.h
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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") {}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/main.cpp
14
src/main.cpp
@@ -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;
|
||||||
|
|||||||
@@ -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>;
|
||||||
9
src/mesh/LLCC68Interface.cpp
Normal file
9
src/mesh/LLCC68Interface.cpp
Normal 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
17
src/mesh/LLCC68Interface.h
Normal file
17
src/mesh/LLCC68Interface.h
Normal 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);
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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" */
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -51,3 +51,4 @@ PB_BIND(ToRadio_PeerInfo, ToRadio_PeerInfo, AUTO)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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" */
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
@@ -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");
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
*/
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
[VERSION]
|
[VERSION]
|
||||||
major = 1
|
major = 1
|
||||||
minor = 2
|
minor = 2
|
||||||
build = 46
|
build = 47
|
||||||
|
|||||||
Reference in New Issue
Block a user