mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-15 14:27:19 +00:00
Compare commits
148 Commits
v1.2.46.dc
...
v1.2.48.37
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
371335e6ab | ||
|
|
8bc4b581d1 | ||
|
|
2311653ca8 | ||
|
|
040e3234fc | ||
|
|
62cf2adaec | ||
|
|
063499ef46 | ||
|
|
b7eaeb8c31 | ||
|
|
6bd495a491 | ||
|
|
f09aa9bec0 | ||
|
|
a7b3b4386f | ||
|
|
6a426d5d71 | ||
|
|
0ca061a457 | ||
|
|
f1ef1eeaff | ||
|
|
1175c981c0 | ||
|
|
1d3387466f | ||
|
|
fcccddc4ad | ||
|
|
be44fa11b1 | ||
|
|
de104a2707 | ||
|
|
9d019c1a99 | ||
|
|
11d954422b | ||
|
|
f4d348173c | ||
|
|
79eb5546a1 | ||
|
|
4f10ab8d04 | ||
|
|
ab0a06e536 | ||
|
|
59ef28dfa7 | ||
|
|
d87106900b | ||
|
|
241325d245 | ||
|
|
0118f3991a | ||
|
|
2a617ba7b2 | ||
|
|
bbf3091889 | ||
|
|
5199b4d3c7 | ||
|
|
99d35b175c | ||
|
|
98d0907f4d | ||
|
|
5f6d97151c | ||
|
|
8cefa82ff1 | ||
|
|
623b846713 | ||
|
|
e791a5aa2f | ||
|
|
82332c9cd0 | ||
|
|
d5506bb33c | ||
|
|
937f67c4ec | ||
|
|
3ed4a1e3e1 | ||
|
|
757de54a4f | ||
|
|
5cf0b6b3b1 | ||
|
|
809179ce0e | ||
|
|
d7315778d6 | ||
|
|
69a2029ade | ||
|
|
7a649e3fc3 | ||
|
|
f019151e3f | ||
|
|
88fd671880 | ||
|
|
d72c091ead | ||
|
|
3ec508169a | ||
|
|
1775770e54 | ||
|
|
f6fde55363 | ||
|
|
e548de3c88 | ||
|
|
f75aac8ebf | ||
|
|
bc311ac9f6 | ||
|
|
d4c6007047 | ||
|
|
c5f210384f | ||
|
|
d685682dd9 | ||
|
|
e31f9b6e5e | ||
|
|
a56851fefa | ||
|
|
0fc88c542f | ||
|
|
16307cb67d | ||
|
|
9f05bd11cc | ||
|
|
4b6ee2dc88 | ||
|
|
a0d829a91e | ||
|
|
dbabc24d7a | ||
|
|
e3ee3c411c | ||
|
|
d3e1bbf355 | ||
|
|
2fa2497ed8 | ||
|
|
a5727052bc | ||
|
|
083518c127 | ||
|
|
c36b233c49 | ||
|
|
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
.gitignore
vendored
2
.gitignore
vendored
@@ -9,7 +9,7 @@ main/credentials.h
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
.idea/workspace.xml
|
||||
.idea
|
||||
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
7
.idea/codeStyles/Project.xml
generated
7
.idea/codeStyles/Project.xml
generated
@@ -1,7 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<clangFormatSettings>
|
||||
<option name="ENABLED" value="true" />
|
||||
</clangFormatSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
@@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
2
.idea/meshtastic-esp32.iml
generated
2
.idea/meshtastic-esp32.iml
generated
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
||||
4
.idea/misc.xml
generated
4
.idea/misc.xml
generated
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/meshtastic-esp32.iml" filepath="$PROJECT_DIR$/.idea/meshtastic-esp32.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/vcs.xml
generated
9
.idea/vcs.xml
generated
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/design" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/proto" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/sdk-nrfxlib" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -2,4 +2,10 @@
|
||||
[](https://open.vscode.dev/meshtastic/Meshtastic-device)
|
||||
## 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)
|
||||
|
||||
BIN
bin/.promote-release.sh.swp
Normal file
BIN
bin/.promote-release.sh.swp
Normal file
Binary file not shown.
@@ -6,6 +6,10 @@ VERSION=`bin/buildinfo.py long`
|
||||
|
||||
# Must have a V prefix to trigger github
|
||||
git tag "v${VERSION}"
|
||||
git push root "v${VERSION}" # push the tag
|
||||
|
||||
# Commented out per https://github.com/meshtastic/Meshtastic-device/issues/947
|
||||
#git push root "v${VERSION}" # push the tag
|
||||
|
||||
git push origin "v${VERSION}" # push the tag
|
||||
|
||||
echo "Tag ${VERSION} pushed to github, github actions should now be building the draft release. If it seems good, click to publish it"
|
||||
|
||||
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
|
||||
2
bin/uf2-convert.bat
Normal file
2
bin/uf2-convert.bat
Normal file
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
if [%1]==[] (echo "Please specify a platformio NRF target (i.e. rak4631) as the first argument.") else (python3 .\bin\uf2conv.py .\.pio\build\%1\firmware.hex -c -o .\.pio\build\%1\firmware.uf2 -f 0xADA52840)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 532 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 5c-3.87 0-7 3.13-7 7h2c0-2.76 2.24-5 5-5s5 2.24 5 5h2c0-3.87-3.13-7-7-7zm1 9.29c.88-.39 1.5-1.26 1.5-2.29 0-1.38-1.12-2.5-2.5-2.5S9.5 10.62 9.5 12c0 1.02.62 1.9 1.5 2.29v3.3L7.59 21 9 22.41l3-3 3 3L16.41 21 13 17.59v-3.3zM12 1C5.93 1 1 5.93 1 12h2c0-4.97 4.03-9 9-9s9 4.03 9 9h2c0-6.07-4.93-11-11-11z"/></svg>
|
||||
|
Before Width: | Height: | Size: 442 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,7 +0,0 @@
|
||||
|
||||
|
||||
* install python
|
||||
* install git (including git-bash)
|
||||
* install platformio
|
||||
* install vscode
|
||||
* install https://sourceforge.net/projects/mingw-w64/ (for windows gcc/g++) - you'll need to add the bin directory to your PATH
|
||||
@@ -69,7 +69,7 @@ debug_tool = jlink
|
||||
|
||||
lib_deps =
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git#35d796226b853b0c0ff818b2f1aa3d35e7296a96 ; ESP8266_SSD1306
|
||||
https://github.com/geeksville/OneButton.git ; OneButton library for non-blocking button debounce
|
||||
https://github.com/meshtastic/OneButton.git#3bcba9492d01e2a8a86f46700ab16f96dd2cf1f5 ; OneButton library for non-blocking button debounce
|
||||
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
||||
https://github.com/meshtastic/arduino-fsm.git
|
||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad
|
||||
@@ -111,6 +111,8 @@ lib_deps =
|
||||
https://github.com/meshtastic/esp32_https_server.git
|
||||
adafruit/DHT sensor library@^1.4.1
|
||||
adafruit/Adafruit Unified Sensor@^1.1.4
|
||||
paulstoffregen/OneWire@^2.3.5
|
||||
robtillaart/DS18B20@^0.1.11
|
||||
# Hmm - this doesn't work yet
|
||||
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||
lib_ignore = segger_rtt
|
||||
|
||||
2
proto
2
proto
Submodule proto updated: e24fa8c6ed...5a556ee4a9
1
src/.gitignore
vendored
1
src/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
main.ino.cpp
|
||||
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 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;
|
||||
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;
|
||||
|
||||
Position p = Position_init_default;
|
||||
|
||||
public:
|
||||
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,
|
||||
uint32_t heading, uint32_t numSatellites)
|
||||
: Status()
|
||||
{
|
||||
this->hasLock = hasLock;
|
||||
this->isConnected = isConnected;
|
||||
this->latitude = latitude;
|
||||
this->longitude = longitude;
|
||||
this->altitude = altitude;
|
||||
this->dop = dop;
|
||||
this->heading = heading;
|
||||
this->numSatellites = numSatellites;
|
||||
|
||||
this->p.latitude_i = latitude;
|
||||
this->p.longitude_i = longitude;
|
||||
this->p.altitude = altitude;
|
||||
this->p.PDOP = dop;
|
||||
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 &operator=(const GPSStatus &);
|
||||
|
||||
@@ -56,7 +67,7 @@ class GPSStatus : public Status
|
||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
return node->position.latitude_i;
|
||||
} else {
|
||||
return latitude;
|
||||
return p.latitude_i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +77,7 @@ class GPSStatus : public Status
|
||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
return node->position.longitude_i;
|
||||
} else {
|
||||
return longitude;
|
||||
return p.longitude_i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,44 +87,59 @@ class GPSStatus : public Status
|
||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
return node->position.altitude;
|
||||
} 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 numSatellites; }
|
||||
uint32_t getNumSatellites() const { return p.sats_in_view; }
|
||||
|
||||
bool matches(const GPSStatus *newStatus) const
|
||||
{
|
||||
return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected || newStatus->latitude != latitude ||
|
||||
newStatus->longitude != longitude || newStatus->altitude != altitude || newStatus->dop != dop ||
|
||||
newStatus->heading != heading || newStatus->numSatellites != numSatellites);
|
||||
#if GPS_EXTRAVERBOSE
|
||||
DEBUG_MSG("GPSStatus.match() new pos@%x to old pos@%x\n",
|
||||
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)
|
||||
{
|
||||
// Only update the status if values have actually changed
|
||||
bool isDirty;
|
||||
{
|
||||
isDirty = matches(newStatus);
|
||||
initialized = true;
|
||||
hasLock = newStatus->hasLock;
|
||||
isConnected = newStatus->isConnected;
|
||||
latitude = newStatus->latitude;
|
||||
longitude = newStatus->longitude;
|
||||
altitude = newStatus->altitude;
|
||||
dop = newStatus->dop;
|
||||
heading = newStatus->heading;
|
||||
numSatellites = newStatus->numSatellites;
|
||||
bool isDirty = matches(newStatus);
|
||||
|
||||
if (isDirty && p.pos_timestamp &&
|
||||
(newStatus->p.pos_timestamp == p.pos_timestamp)) {
|
||||
// We can NEVER be in two locations at the same time! (also PR #886)
|
||||
DEBUG_MSG("BUG!! positional timestamp unchanged from prev solution\n");
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
hasLock = newStatus->hasLock;
|
||||
isConnected = newStatus->isConnected;
|
||||
|
||||
p = newStatus->p;
|
||||
|
||||
if (isDirty) {
|
||||
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,
|
||||
altitude, dop * 1e-2, heading * 1e-5, numSatellites);
|
||||
else
|
||||
if (hasLock) {
|
||||
// In debug logs, identify position by @timestamp:stage (stage 3 = notify)
|
||||
DEBUG_MSG("New GPS pos@%x:3 lat=%f, lon=%f, alt=%d, pdop=%.2f, track=%.2f, sats=%d\n",
|
||||
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");
|
||||
onNewStatus.notifyObservers(this);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
/// Should we behave as if we have AC power now?
|
||||
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;
|
||||
|
||||
// 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
|
||||
#define USE_SX1262
|
||||
#define USE_SX1268
|
||||
#define USE_LLCC68
|
||||
|
||||
// common pinouts for SX126X modules
|
||||
#define SX126X_CS 18 // NSS for SX126X
|
||||
@@ -326,6 +327,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define GPS_RX_PIN 36
|
||||
#define GPS_TX_PIN 37
|
||||
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
|
||||
#define I2C_SDA 4 // I2C pins for this board
|
||||
#define I2C_SCL 15
|
||||
|
||||
|
||||
@@ -202,11 +202,13 @@ void GPS::publishUpdate()
|
||||
if (shouldPublish) {
|
||||
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
|
||||
const meshtastic::GPSStatus status =
|
||||
meshtastic::GPSStatus(hasLock(), isConnected(), latitude, longitude, altitude, dop, heading, numSatellites);
|
||||
meshtastic::GPSStatus(hasValidLocation, isConnected(), p);
|
||||
newStatus.notifyObservers(&status);
|
||||
}
|
||||
}
|
||||
@@ -244,6 +246,7 @@ int32_t GPS::runOnce()
|
||||
|
||||
bool gotLoc = lookForLocation();
|
||||
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
|
||||
DEBUG_MSG("hasValidLocation RISING EDGE\n");
|
||||
hasValidLocation = true;
|
||||
shouldPublish = true;
|
||||
}
|
||||
@@ -260,6 +263,10 @@ int32_t GPS::runOnce()
|
||||
|
||||
if (tooLong) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,10 @@ class GPS : private concurrency::OSThread
|
||||
private:
|
||||
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 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 */
|
||||
static uint8_t i2cAddress;
|
||||
|
||||
int32_t latitude = 0, longitude = 0; // as an int mult by 1e-7 to get value as double
|
||||
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
|
||||
Position p = Position_init_default;
|
||||
|
||||
GPS() : concurrency::OSThread("GPS") {}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
// Helper functions
|
||||
// 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) {
|
||||
return 1;
|
||||
} else if (exponent > 0) {
|
||||
|
||||
@@ -95,6 +95,17 @@ bool NMEAGPS::lookForLocation()
|
||||
if (! hasLock())
|
||||
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
|
||||
// tinyGPSDatum::age() also includes isValid() test
|
||||
// FIXME
|
||||
@@ -105,7 +116,7 @@ bool NMEAGPS::lookForLocation()
|
||||
(reader.time.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;
|
||||
}
|
||||
|
||||
@@ -113,7 +124,7 @@ bool NMEAGPS::lookForLocation()
|
||||
if (! reader.location.isUpdated())
|
||||
return false;
|
||||
|
||||
// Start reading the data
|
||||
// We know the solution is fresh and valid, so just read the data
|
||||
auto loc = reader.location.value();
|
||||
|
||||
// Some GPSes (Air530) seem to send a zero longitude when the current fix is bogus
|
||||
@@ -123,27 +134,34 @@ bool NMEAGPS::lookForLocation()
|
||||
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
|
||||
#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
|
||||
// FIXME! naive PDOP emulation (assumes VDOP==HDOP)
|
||||
// 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
|
||||
|
||||
// Discard incomplete or erroneous readings
|
||||
if (dop == 0)
|
||||
if (reader.hdop.value() == 0)
|
||||
return false;
|
||||
|
||||
latitude = toDegInt(loc.lat);
|
||||
longitude = toDegInt(loc.lng);
|
||||
p.latitude_i = toDegInt(loc.lat);
|
||||
p.longitude_i = toDegInt(loc.lng);
|
||||
|
||||
geoidal_height = reader.geoidHeight.meters();
|
||||
#ifdef GPS_ALTITUDE_HAE
|
||||
altitude = reader.altitude.meters() + geoidal_height;
|
||||
#else
|
||||
altitude = reader.altitude.meters();
|
||||
p.alt_geoid_sep = reader.geoidHeight.meters();
|
||||
p.altitude_hae = reader.altitude.meters() + p.alt_geoid_sep;
|
||||
p.altitude = reader.altitude.meters();
|
||||
|
||||
p.fix_quality = fixQual;
|
||||
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
|
||||
p.fix_type = fixType;
|
||||
#endif
|
||||
|
||||
// positional timestamp
|
||||
@@ -155,16 +173,16 @@ bool NMEAGPS::lookForLocation()
|
||||
t.tm_mon = reader.date.month() - 1;
|
||||
t.tm_year = reader.date.year() - 1900;
|
||||
t.tm_isdst = false;
|
||||
pos_timestamp = mktime(&t);
|
||||
p.pos_timestamp = mktime(&t);
|
||||
|
||||
// Nice to have, if available
|
||||
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.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 {
|
||||
DEBUG_MSG("BOGUS course.value() REJECTED: %d\n",
|
||||
reader.course.value());
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#define PDOP_INVALID 9999
|
||||
|
||||
// #define UBX_MODE_NMEA
|
||||
|
||||
extern RadioConfig radioConfig;
|
||||
|
||||
UBloxGPS::UBloxGPS() {}
|
||||
@@ -48,12 +50,22 @@ bool UBloxGPS::setupGPS()
|
||||
delay(500);
|
||||
|
||||
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");
|
||||
#endif
|
||||
|
||||
if (!setUBXMode())
|
||||
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;
|
||||
#endif
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -61,6 +73,17 @@ bool UBloxGPS::setupGPS()
|
||||
|
||||
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 (!ublox.setUART1Output(COM_TYPE_UBX, 1000)) // Use native API
|
||||
return false;
|
||||
@@ -119,7 +142,6 @@ bool UBloxGPS::factoryReset()
|
||||
void UBloxGPS::whileActive()
|
||||
{
|
||||
ublox.flushPVT(); // reset ALL freshness flags first
|
||||
|
||||
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
|
||||
@@ -170,28 +192,32 @@ bool UBloxGPS::lookForLocation()
|
||||
{
|
||||
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
|
||||
// (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.altitude &&
|
||||
ublox.moduleQueried.pDOP &&
|
||||
ublox.moduleQueried.gpsiTOW))
|
||||
ublox.moduleQueried.SIV &&
|
||||
ublox.moduleQueried.gpsDay))
|
||||
{
|
||||
// Not ready? No problem! We'll try again later.
|
||||
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
|
||||
// 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
|
||||
@@ -199,6 +225,10 @@ bool UBloxGPS::lookForLocation()
|
||||
int32_t tmp_lon = ublox.getLongitude(0);
|
||||
int32_t tmp_alt_msl = ublox.getAltitudeMSL(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
|
||||
// don't read it here - it will generate an ignored getPVT command on the 6ms
|
||||
// heading = ublox.getHeading(0);
|
||||
@@ -215,41 +245,55 @@ bool UBloxGPS::lookForLocation()
|
||||
|
||||
time_t tmp_ts = mktime(&t);
|
||||
|
||||
// SIV number is nice-to-have if it's available
|
||||
if (ublox.moduleQueried.SIV) {
|
||||
uint16_t gSIV = ublox.getSIV(0);
|
||||
setNumSatellites(gSIV);
|
||||
}
|
||||
// FIXME - can opportunistically attempt to set RTC from GPS timestamp?
|
||||
|
||||
// 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!
|
||||
// FIXME - NULL ISLAND is a real location on Earth!
|
||||
foundLocation = (tmp_lat != 0) && (tmp_lon != 0) &&
|
||||
(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
|
||||
if (foundLocation) {
|
||||
longitude = tmp_lon;
|
||||
latitude = tmp_lat;
|
||||
#ifdef GPS_ALTITUDE_HAE
|
||||
altitude = tmp_alt_hae / 1000;
|
||||
#else
|
||||
altitude = tmp_alt_msl / 1000;
|
||||
p.location_source = Position_LocSource_LOCSRC_GPS_INTERNAL;
|
||||
p.longitude_i = tmp_lon;
|
||||
p.latitude_i = tmp_lat;
|
||||
if (fixType > 2) {
|
||||
// if fix is 2d, ignore altitude data
|
||||
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
|
||||
geoidal_height = (tmp_alt_hae - tmp_alt_msl) / 1000;
|
||||
pos_timestamp = tmp_ts;
|
||||
dop = tmp_dop;
|
||||
// clean up old values in case it's a 3d-2d fix transition
|
||||
p.altitude = p.altitude_hae = p.alt_geoid_sep = 0;
|
||||
}
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -147,6 +147,17 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
drawIconScreen(region, display, state, x, y);
|
||||
}
|
||||
|
||||
// Used on boot when a certificate is being created
|
||||
static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(64 + x, y, "Creating SSL certificate");
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait...");
|
||||
}
|
||||
|
||||
#ifdef HAS_EINK
|
||||
/// Used on eink displays while in deep sleep
|
||||
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
@@ -226,6 +237,13 @@ static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *sta
|
||||
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
|
||||
static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -787,6 +805,8 @@ void Screen::forceDisplay()
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t lastScreenTransition;
|
||||
|
||||
int32_t Screen::runOnce()
|
||||
{
|
||||
// If we don't have a screen, don't ever spend any CPU for us.
|
||||
@@ -863,8 +883,13 @@ int32_t Screen::runOnce()
|
||||
// standard screen switching is stopped.
|
||||
if (showingNormalScreen) {
|
||||
// standard screen loop handling here
|
||||
if (radioConfig.preferences.auto_screen_carousel_secs > 0 &&
|
||||
(millis() - lastScreenTransition) > (radioConfig.preferences.auto_screen_carousel_secs * 1000)) {
|
||||
DEBUG_MSG("LastScreenTransition exceeded %ums transitioning to next frame\n", (millis() - lastScreenTransition));
|
||||
handleOnPress();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// DEBUG_MSG("want fps %d, fixed=%d\n", targetFramerate,
|
||||
// ui.getUiState()->frameState); If we are scrolling we need to be called
|
||||
// soon, otherwise just 1 fps (to save CPU) We also ask to be called twice
|
||||
@@ -891,6 +916,16 @@ void Screen::drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiStat
|
||||
screen->debugInfo.drawFrameWiFi(display, state, x, y);
|
||||
}
|
||||
|
||||
/* show a message that the SSL cert is being built
|
||||
* it is expected that this will be used during the boot phase */
|
||||
void Screen::setSSLFrames()
|
||||
{
|
||||
DEBUG_MSG("showing SSL frames\n");
|
||||
static FrameCallback sslFrames[] = {drawSSLScreen};
|
||||
ui.setFrames(sslFrames, 1);
|
||||
ui.update();
|
||||
}
|
||||
|
||||
// restore our regular frame list
|
||||
void Screen::setFrames()
|
||||
{
|
||||
@@ -925,9 +960,10 @@ void Screen::setFrames()
|
||||
if (myNodeInfo.error_code)
|
||||
normalFrames[numframes++] = drawCriticalFaultFrame;
|
||||
|
||||
// If we have a text message - show it next
|
||||
if (devicestate.has_rx_text_message)
|
||||
// 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 && shouldDrawMessage(&devicestate.rx_text_message)) {
|
||||
normalFrames[numframes++] = drawTextMessageFrame;
|
||||
}
|
||||
|
||||
// then all the nodes
|
||||
// We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens
|
||||
@@ -1017,12 +1053,13 @@ void Screen::handlePrint(const char *text)
|
||||
}
|
||||
|
||||
void Screen::handleOnPress()
|
||||
{
|
||||
{
|
||||
// If screen was off, just wake it, otherwise advance to next frame
|
||||
// If we are in a transition, the press must have bounced, drop it.
|
||||
if (ui.getUiState()->frameState == FIXED) {
|
||||
ui.nextFrame();
|
||||
|
||||
DEBUG_MSG("Setting LastScreenTransition\n");
|
||||
lastScreenTransition = millis();
|
||||
setFastFramerate();
|
||||
}
|
||||
}
|
||||
@@ -1357,8 +1394,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Screen::handleTextMessage(const MeshPacket *arg)
|
||||
{
|
||||
int Screen::handleTextMessage(const MeshPacket *packet) {
|
||||
if (showingNormalScreen) {
|
||||
setFrames(); // Regen the list of screens (will show new text message)
|
||||
}
|
||||
|
||||
@@ -220,6 +220,9 @@ class Screen : public concurrency::OSThread
|
||||
/// Used to force (super slow) eink displays to draw critical frames
|
||||
void forceDisplay();
|
||||
|
||||
/// Draws our SSL cert screen during boot (called from WebServer)
|
||||
void setSSLFrames();
|
||||
|
||||
protected:
|
||||
/// Updates the UI.
|
||||
//
|
||||
|
||||
14
src/main.cpp
14
src/main.cpp
@@ -40,6 +40,7 @@
|
||||
#include "RF95Interface.h"
|
||||
#include "SX1262Interface.h"
|
||||
#include "SX1268Interface.h"
|
||||
#include "LLCC68Interface.h"
|
||||
|
||||
#ifdef NRF52_SERIES
|
||||
#include "variant.h"
|
||||
@@ -539,6 +540,19 @@ void setup()
|
||||
}
|
||||
#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
|
||||
if (!rIf) {
|
||||
rIf = new SimRadio;
|
||||
|
||||
@@ -17,7 +17,7 @@ ErrorCode FloodingRouter::send(MeshPacket *p)
|
||||
return Router::send(p);
|
||||
}
|
||||
|
||||
bool FloodingRouter::shouldFilterReceived(const MeshPacket *p)
|
||||
bool FloodingRouter::shouldFilterReceived(MeshPacket *p)
|
||||
{
|
||||
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
|
||||
printPacket("Ignoring incoming msg, because we've already seen it", p);
|
||||
|
||||
@@ -50,7 +50,7 @@ class FloodingRouter : public Router, protected PacketHistory
|
||||
* Called immedately on receiption, before any further processing.
|
||||
* @return true to abandon the packet
|
||||
*/
|
||||
virtual bool shouldFilterReceived(const MeshPacket *p);
|
||||
virtual bool shouldFilterReceived(MeshPacket *p);
|
||||
|
||||
/**
|
||||
* Look for broadcasts we need to rebroadcast
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
|
||||
// We need this declaration for proper linking in derived classes
|
||||
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
|
||||
void MeshService::reloadOwner()
|
||||
{
|
||||
// DEBUG_MSG("reloadOwner()\n");
|
||||
// update our local data directly
|
||||
nodeDB.updateUser(nodeDB.getNodeNum(), owner);
|
||||
assert(nodeInfoPlugin);
|
||||
// update everyone else
|
||||
if (nodeInfoPlugin)
|
||||
nodeInfoPlugin->sendOurNodeInfo();
|
||||
nodeDB.saveToDisk();
|
||||
@@ -140,7 +144,7 @@ void MeshService::handleToRadio(MeshPacket &p)
|
||||
|
||||
// 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)
|
||||
if (loopback) {
|
||||
@@ -157,12 +161,12 @@ bool MeshService::cancelSending(PacketId 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...)
|
||||
|
||||
// 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)
|
||||
@@ -209,34 +213,39 @@ NodeInfo *MeshService::refreshMyNodeInfo()
|
||||
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)
|
||||
NodeInfo *node = refreshMyNodeInfo();
|
||||
Position pos = node->position;
|
||||
Position pos = Position_init_default;
|
||||
|
||||
if (gps->hasLock()) {
|
||||
if (gps->altitude != 0)
|
||||
pos.altitude = gps->altitude;
|
||||
pos.latitude_i = gps->latitude;
|
||||
pos.longitude_i = gps->longitude;
|
||||
if (newStatus->getHasLock()) {
|
||||
// load data from GPS object, will add timestamp + battery further down
|
||||
pos = gps->p;
|
||||
} else {
|
||||
// The GPS has lost lock, if we are fixed position we should just keep using
|
||||
// the old position
|
||||
#if GPS_EXTRAVERBOSE
|
||||
DEBUG_MSG("onGPSchanged() - lost validLocation\n");
|
||||
#endif
|
||||
if (radioConfig.preferences.fixed_position) {
|
||||
DEBUG_MSG("WARNING: Using fixed position\n");
|
||||
} else {
|
||||
// throw away old position
|
||||
pos.latitude_i = 0;
|
||||
pos.longitude_i = 0;
|
||||
pos.altitude = 0;
|
||||
pos = node->position;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
nodeDB.updatePosition(nodeDB.getNodeNum(), pos);
|
||||
nodeDB.updatePosition(nodeDB.getNodeNum(), pos, RX_SRC_LOCAL);
|
||||
|
||||
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
|
||||
/// sending. This is the ONLY function you should use for sending messages into the mesh, because it also updates the nodedb
|
||||
/// 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 */
|
||||
bool cancelSending(PacketId id);
|
||||
|
||||
@@ -20,7 +20,8 @@ typedef uint32_t PacketId; // A packet sequence number
|
||||
*/
|
||||
enum RxSource {
|
||||
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));
|
||||
radioConfig.has_preferences = true;
|
||||
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()
|
||||
@@ -442,25 +447,42 @@ size_t NodeDB::getNumOnlineNodes()
|
||||
|
||||
/** 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);
|
||||
|
||||
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
|
||||
// A lot of position reports don't have time populated. In that case, be careful to not blow away the time we
|
||||
// recorded based on the packet rxTime
|
||||
if (p.time)
|
||||
} else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.pos_timestamp &&
|
||||
!p.location_source) {
|
||||
// FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO
|
||||
// (stop-gap fix for issue #900)
|
||||
DEBUG_MSG("updatePosition SPECIAL time setting time=%u\n", p.time);
|
||||
info->position.time = p.time;
|
||||
if (p.battery_level)
|
||||
info->position.battery_level = p.battery_level;
|
||||
if (p.latitude_i || p.longitude_i) {
|
||||
info->position.latitude_i = p.latitude_i;
|
||||
info->position.longitude_i = p.longitude_i;
|
||||
|
||||
} else {
|
||||
// Be careful to only update fields that have been set by the REMOTE sender
|
||||
// A lot of position reports don't have time populated. In that case, be careful to not blow away the time we
|
||||
// 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;
|
||||
updateGUIforNode = info;
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -307,6 +307,8 @@ void RadioInterface::applyModemConfig()
|
||||
|
||||
if (bw == 31) // This parameter is not an integer
|
||||
bw = 31.25;
|
||||
if (bw == 62) // Fix for 62.5Khz bandwidth
|
||||
bw = 62.5;
|
||||
}
|
||||
|
||||
power = channelSettings.tx_power;
|
||||
|
||||
@@ -87,8 +87,8 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
* for a long time.
|
||||
*/
|
||||
const uint8_t syncWord = 0x2b;
|
||||
|
||||
float currentLimit = 100; // FIXME
|
||||
|
||||
float currentLimit = 100; // 100mA OCP - Should be acceptable for RFM95/SX127x chipset.
|
||||
|
||||
LockingModule module; // The HW interface to the radio
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ ErrorCode ReliableRouter::send(MeshPacket *p)
|
||||
return FloodingRouter::send(p);
|
||||
}
|
||||
|
||||
bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
|
||||
bool ReliableRouter::shouldFilterReceived(MeshPacket *p)
|
||||
{
|
||||
// Note: do not use getFrom() here, because we want to ignore messages sent from phone
|
||||
if (p->to == NODENUM_BROADCAST && p->from == getNodeNum()) {
|
||||
@@ -54,6 +54,17 @@ bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
|
||||
}
|
||||
}
|
||||
|
||||
/* send acks for repeated packets that want acks and are destined for us
|
||||
* this way if an ACK is dropped and a packet is resent we'll ACK the resent packet
|
||||
* make sure wasSeenRecently _doesn't_ update
|
||||
* finding the channel requires decoding the packet. */
|
||||
if (p->want_ack && (p->to == getNodeNum()) && wasSeenRecently(p, false)) {
|
||||
if (perhapsDecode(p)) {
|
||||
sendAckNak(Routing_Error_NONE, getFrom(p), p->id, p->channel);
|
||||
DEBUG_MSG("acking a repeated want_ack packet\n");
|
||||
}
|
||||
}
|
||||
|
||||
return FloodingRouter::shouldFilterReceived(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ class ReliableRouter : public FloodingRouter
|
||||
/**
|
||||
* We hook this method so we can see packets before FloodingRouter says they should be discarded
|
||||
*/
|
||||
virtual bool shouldFilterReceived(const MeshPacket *p);
|
||||
virtual bool shouldFilterReceived(MeshPacket *p);
|
||||
|
||||
/**
|
||||
* Add p to the list of packets to retransmit occasionally. We will free it once we stop retransmitting.
|
||||
|
||||
@@ -145,7 +145,7 @@ void Router::setReceivedMessage()
|
||||
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
|
||||
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
|
||||
// this allows local apps (and PCs) to see broadcasts sourced locally
|
||||
if (p->to == NODENUM_BROADCAST) {
|
||||
handleReceived(p, RX_SRC_LOCAL);
|
||||
handleReceived(p, src);
|
||||
}
|
||||
|
||||
return send(p);
|
||||
@@ -334,9 +334,11 @@ void Router::handleReceived(MeshPacket *p, RxSource src)
|
||||
if (decoded) {
|
||||
// parsing was successful, queue for our recipient
|
||||
if (src == RX_SRC_LOCAL)
|
||||
printPacket("handleReceived(local)", p);
|
||||
printPacket("handleReceived(LOCAL)", p);
|
||||
else if (src == RX_SRC_USER)
|
||||
printPacket("handleReceived(USER)", p);
|
||||
else
|
||||
printPacket("handleReceived(remote)", p);
|
||||
printPacket("handleReceived(REMOTE)", p);
|
||||
} else {
|
||||
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)
|
||||
*/
|
||||
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 */
|
||||
bool cancelSending(NodeNum from, PacketId id);
|
||||
@@ -90,7 +90,7 @@ class Router : protected concurrency::OSThread
|
||||
* Called immedately on receiption, before any further processing.
|
||||
* @return true to abandon the packet
|
||||
*/
|
||||
virtual bool shouldFilterReceived(const MeshPacket *p) { return false; }
|
||||
virtual bool shouldFilterReceived(MeshPacket *p) { return false; }
|
||||
|
||||
/**
|
||||
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to
|
||||
|
||||
@@ -29,6 +29,8 @@ class SX126xInterface : public RadioLibInterface
|
||||
|
||||
protected:
|
||||
|
||||
float currentLimit = 140; // Higher OCP limit for SX126x PA
|
||||
|
||||
/**
|
||||
* Specific module instance
|
||||
*/
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
#ifndef PB_ADMIN_PB_H_INCLUDED
|
||||
#define PB_ADMIN_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
#include "channel.pb.h"
|
||||
#include "mesh.pb.h"
|
||||
#include "radioconfig.pb.h"
|
||||
#include "channel.pb.h"
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
@@ -79,7 +79,7 @@ extern const pb_msgdesc_t AdminMessage_msg;
|
||||
#define AdminMessage_fields &AdminMessage_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define AdminMessage_size 407
|
||||
#define AdminMessage_size 447
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#ifndef PB_DEVICEONLY_PB_H_INCLUDED
|
||||
#define PB_DEVICEONLY_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
#include "mesh.pb.h"
|
||||
#include "channel.pb.h"
|
||||
#include "mesh.pb.h"
|
||||
#include "radioconfig.pb.h"
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
@@ -125,7 +125,7 @@ extern const pb_msgdesc_t ChannelFile_msg;
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define LegacyRadioConfig_size 4
|
||||
#define LegacyRadioConfig_LegacyPreferences_size 2
|
||||
#define DeviceState_size 10054
|
||||
#define DeviceState_size 9594
|
||||
#define ChannelFile_size 832
|
||||
|
||||
#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;
|
||||
|
||||
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 {
|
||||
Constants_Unused = 0,
|
||||
Constants_DATA_PAYLOAD_LEN = 237
|
||||
@@ -158,11 +176,6 @@ typedef struct _Position {
|
||||
uint32_t fix_type;
|
||||
uint32_t sats_in_view;
|
||||
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_seq_number;
|
||||
} Position;
|
||||
@@ -184,6 +197,10 @@ typedef struct _User {
|
||||
pb_byte_t macaddr[6];
|
||||
HardwareModel hw_model;
|
||||
bool is_licensed;
|
||||
Team team;
|
||||
uint32_t tx_power_dbm;
|
||||
uint32_t ant_gain_dbi;
|
||||
uint32_t ant_azimuth;
|
||||
} User;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t;
|
||||
@@ -253,6 +270,10 @@ typedef struct _ToRadio {
|
||||
#define _HardwareModel_MAX HardwareModel_DIY_V1
|
||||
#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_MAX Constants_DATA_PAYLOAD_LEN
|
||||
#define _Constants_ARRAYSIZE ((Constants)(Constants_DATA_PAYLOAD_LEN+1))
|
||||
@@ -287,8 +308,8 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* 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 User_init_default {"", "", "", {0}, _HardwareModel_MIN, 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, _Team_MIN, 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 Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
|
||||
@@ -299,8 +320,8 @@ extern "C" {
|
||||
#define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}}
|
||||
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
||||
#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 User_init_zero {"", "", "", {0}, _HardwareModel_MIN, 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, _Team_MIN, 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 Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
|
||||
@@ -357,11 +378,6 @@ extern "C" {
|
||||
#define Position_fix_type_tag 23
|
||||
#define Position_sats_in_view_tag 24
|
||||
#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_seq_number_tag 41
|
||||
#define RouteDiscovery_route_tag 2
|
||||
@@ -373,6 +389,10 @@ extern "C" {
|
||||
#define User_macaddr_tag 4
|
||||
#define User_hw_model_tag 6
|
||||
#define User_is_licensed_tag 7
|
||||
#define User_team_tag 8
|
||||
#define User_tx_power_dbm_tag 10
|
||||
#define User_ant_gain_dbi_tag 11
|
||||
#define User_ant_azimuth_tag 12
|
||||
#define MeshPacket_from_tag 1
|
||||
#define MeshPacket_to_tag 2
|
||||
#define MeshPacket_channel_tag 3
|
||||
@@ -428,11 +448,6 @@ X(a, STATIC, SINGULAR, UINT32, fix_quality, 22) \
|
||||
X(a, STATIC, SINGULAR, UINT32, fix_type, 23) \
|
||||
X(a, STATIC, SINGULAR, UINT32, sats_in_view, 24) \
|
||||
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_seq_number, 41)
|
||||
#define Position_CALLBACK NULL
|
||||
@@ -444,7 +459,11 @@ X(a, STATIC, SINGULAR, STRING, long_name, 2) \
|
||||
X(a, STATIC, SINGULAR, STRING, short_name, 3) \
|
||||
X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4) \
|
||||
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) \
|
||||
X(a, STATIC, SINGULAR, UINT32, tx_power_dbm, 10) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ant_gain_dbi, 11) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ant_azimuth, 12)
|
||||
#define User_CALLBACK NULL
|
||||
#define User_DEFAULT NULL
|
||||
|
||||
@@ -584,13 +603,13 @@ extern const pb_msgdesc_t ToRadio_PeerInfo_msg;
|
||||
#define ToRadio_PeerInfo_fields &ToRadio_PeerInfo_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define Position_size 188
|
||||
#define User_size 76
|
||||
#define Position_size 153
|
||||
#define User_size 96
|
||||
#define RouteDiscovery_size 40
|
||||
#define Routing_size 42
|
||||
#define Data_size 260
|
||||
#define MeshPacket_size 309
|
||||
#define NodeInfo_size 285
|
||||
#define NodeInfo_size 270
|
||||
#define MyNodeInfo_size 101
|
||||
#define LogRecord_size 81
|
||||
#define FromRadio_size 318
|
||||
|
||||
@@ -24,6 +24,7 @@ typedef enum _PortNum {
|
||||
PortNum_STORE_FORWARD_APP = 65,
|
||||
PortNum_RANGE_TEST_APP = 66,
|
||||
PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 67,
|
||||
PortNum_ZPS_APP = 68,
|
||||
PortNum_PRIVATE_APP = 256,
|
||||
PortNum_ATAK_FORWARDER = 257,
|
||||
PortNum_MAX = 511
|
||||
|
||||
@@ -80,7 +80,8 @@ typedef enum _PositionFlags {
|
||||
} PositionFlags;
|
||||
|
||||
typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType {
|
||||
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0
|
||||
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0,
|
||||
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20 = 1
|
||||
} RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType;
|
||||
|
||||
/* Struct definitions */
|
||||
@@ -100,6 +101,7 @@ typedef struct _RadioConfig_UserPreferences {
|
||||
bool wifi_ap_mode;
|
||||
RegionCode region;
|
||||
ChargeCurrent charge_current;
|
||||
bool position_broadcast_smart;
|
||||
LocationSharing location_share;
|
||||
GpsOperation gps_operation;
|
||||
uint32_t gps_update_interval;
|
||||
@@ -112,6 +114,8 @@ typedef struct _RadioConfig_UserPreferences {
|
||||
char mqtt_server[32];
|
||||
bool mqtt_disabled;
|
||||
GpsCoordinateFormat gps_format;
|
||||
bool gps_accept_2d;
|
||||
uint32_t gps_max_dop;
|
||||
bool factory_reset;
|
||||
bool debug_log_enabled;
|
||||
pb_size_t ignore_incoming_count;
|
||||
@@ -132,6 +136,8 @@ typedef struct _RadioConfig_UserPreferences {
|
||||
uint32_t range_test_plugin_sender;
|
||||
bool range_test_plugin_save;
|
||||
uint32_t store_forward_plugin_records;
|
||||
uint32_t store_forward_plugin_history_return_max;
|
||||
uint32_t store_forward_plugin_history_return_window;
|
||||
bool environmental_measurement_plugin_measurement_enabled;
|
||||
bool environmental_measurement_plugin_screen_enabled;
|
||||
uint32_t environmental_measurement_plugin_read_error_count_threshold;
|
||||
@@ -141,7 +147,10 @@ typedef struct _RadioConfig_UserPreferences {
|
||||
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType environmental_measurement_plugin_sensor_type;
|
||||
uint32_t environmental_measurement_plugin_sensor_pin;
|
||||
bool store_forward_plugin_enabled;
|
||||
bool store_forward_plugin_heartbeat;
|
||||
uint32_t position_flags;
|
||||
bool is_always_powered;
|
||||
uint32_t auto_screen_carousel_secs;
|
||||
} RadioConfig_UserPreferences;
|
||||
|
||||
typedef struct _RadioConfig {
|
||||
@@ -176,8 +185,8 @@ typedef struct _RadioConfig {
|
||||
#define _PositionFlags_ARRAYSIZE ((PositionFlags)(PositionFlags_POS_TIMESTAMP+1))
|
||||
|
||||
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11
|
||||
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MAX RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11
|
||||
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_ARRAYSIZE ((RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType)(RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11+1))
|
||||
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MAX RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20
|
||||
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_ARRAYSIZE ((RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType)(RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20+1))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -186,9 +195,9 @@ extern "C" {
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#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, 0, _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, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0, 0, 0, 0}
|
||||
#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, 0, _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, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
|
||||
@@ -206,6 +215,7 @@ extern "C" {
|
||||
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
|
||||
#define RadioConfig_UserPreferences_region_tag 15
|
||||
#define RadioConfig_UserPreferences_charge_current_tag 16
|
||||
#define RadioConfig_UserPreferences_position_broadcast_smart_tag 17
|
||||
#define RadioConfig_UserPreferences_location_share_tag 32
|
||||
#define RadioConfig_UserPreferences_gps_operation_tag 33
|
||||
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
|
||||
@@ -218,6 +228,8 @@ extern "C" {
|
||||
#define RadioConfig_UserPreferences_mqtt_server_tag 42
|
||||
#define RadioConfig_UserPreferences_mqtt_disabled_tag 43
|
||||
#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_debug_log_enabled_tag 101
|
||||
#define RadioConfig_UserPreferences_ignore_incoming_tag 103
|
||||
@@ -237,6 +249,8 @@ extern "C" {
|
||||
#define RadioConfig_UserPreferences_range_test_plugin_sender_tag 133
|
||||
#define RadioConfig_UserPreferences_range_test_plugin_save_tag 134
|
||||
#define RadioConfig_UserPreferences_store_forward_plugin_records_tag 137
|
||||
#define RadioConfig_UserPreferences_store_forward_plugin_history_return_max_tag 138
|
||||
#define RadioConfig_UserPreferences_store_forward_plugin_history_return_window_tag 139
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_measurement_enabled_tag 140
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_screen_enabled_tag 141
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_read_error_count_threshold_tag 142
|
||||
@@ -246,7 +260,10 @@ extern "C" {
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_type_tag 146
|
||||
#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_heartbeat_tag 149
|
||||
#define RadioConfig_UserPreferences_position_flags_tag 150
|
||||
#define RadioConfig_UserPreferences_is_always_powered_tag 151
|
||||
#define RadioConfig_UserPreferences_auto_screen_carousel_secs_tag 152
|
||||
#define RadioConfig_preferences_tag 1
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
@@ -272,6 +289,7 @@ X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \
|
||||
X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \
|
||||
X(a, STATIC, SINGULAR, UENUM, region, 15) \
|
||||
X(a, STATIC, SINGULAR, UENUM, charge_current, 16) \
|
||||
X(a, STATIC, SINGULAR, BOOL, position_broadcast_smart, 17) \
|
||||
X(a, STATIC, SINGULAR, UENUM, location_share, 32) \
|
||||
X(a, STATIC, SINGULAR, UENUM, gps_operation, 33) \
|
||||
X(a, STATIC, SINGULAR, UINT32, gps_update_interval, 34) \
|
||||
@@ -284,6 +302,8 @@ X(a, STATIC, SINGULAR, FLOAT, frequency_offset, 41) \
|
||||
X(a, STATIC, SINGULAR, STRING, mqtt_server, 42) \
|
||||
X(a, STATIC, SINGULAR, BOOL, mqtt_disabled, 43) \
|
||||
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, debug_log_enabled, 101) \
|
||||
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
|
||||
@@ -303,6 +323,8 @@ X(a, STATIC, SINGULAR, BOOL, range_test_plugin_enabled, 132) \
|
||||
X(a, STATIC, SINGULAR, UINT32, range_test_plugin_sender, 133) \
|
||||
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_save, 134) \
|
||||
X(a, STATIC, SINGULAR, UINT32, store_forward_plugin_records, 137) \
|
||||
X(a, STATIC, SINGULAR, UINT32, store_forward_plugin_history_return_max, 138) \
|
||||
X(a, STATIC, SINGULAR, UINT32, store_forward_plugin_history_return_window, 139) \
|
||||
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_measurement_enabled, 140) \
|
||||
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_screen_enabled, 141) \
|
||||
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_read_error_count_threshold, 142) \
|
||||
@@ -312,7 +334,10 @@ 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, UINT32, environmental_measurement_plugin_sensor_pin, 147) \
|
||||
X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_enabled, 148) \
|
||||
X(a, STATIC, SINGULAR, UINT32, position_flags, 150)
|
||||
X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_heartbeat, 149) \
|
||||
X(a, STATIC, SINGULAR, UINT32, position_flags, 150) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_always_powered, 151) \
|
||||
X(a, STATIC, SINGULAR, UINT32, auto_screen_carousel_secs, 152)
|
||||
#define RadioConfig_UserPreferences_CALLBACK NULL
|
||||
#define RadioConfig_UserPreferences_DEFAULT NULL
|
||||
|
||||
@@ -324,8 +349,8 @@ extern const pb_msgdesc_t RadioConfig_UserPreferences_msg;
|
||||
#define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define RadioConfig_size 404
|
||||
#define RadioConfig_UserPreferences_size 401
|
||||
#define RadioConfig_size 444
|
||||
#define RadioConfig_UserPreferences_size 441
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
19
src/mesh/generated/storeforward.pb.c
Normal file
19
src/mesh/generated/storeforward.pb.c
Normal file
@@ -0,0 +1,19 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#include "storeforward.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
PB_BIND(StoreAndForward, StoreAndForward, AUTO)
|
||||
|
||||
|
||||
PB_BIND(StoreAndForward_Statistics, StoreAndForward_Statistics, AUTO)
|
||||
|
||||
|
||||
PB_BIND(StoreAndForward_History, StoreAndForward_History, AUTO)
|
||||
|
||||
|
||||
|
||||
|
||||
135
src/mesh/generated/storeforward.pb.h
Normal file
135
src/mesh/generated/storeforward.pb.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#ifndef PB_STOREFORWARD_PB_H_INCLUDED
|
||||
#define PB_STOREFORWARD_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Enum definitions */
|
||||
typedef enum _StoreAndForward_RequestResponse {
|
||||
StoreAndForward_RequestResponse_UNSET = 0,
|
||||
StoreAndForward_RequestResponse_ROUTER_ERROR = 1,
|
||||
StoreAndForward_RequestResponse_ROUTER_HEARTBEAT = 2,
|
||||
StoreAndForward_RequestResponse_ROUTER_PING = 3,
|
||||
StoreAndForward_RequestResponse_ROUTER_PONG = 4,
|
||||
StoreAndForward_RequestResponse_ROUTER_BUSY = 5,
|
||||
StoreAndForward_RequestResponse_CLIENT_ERROR = 101,
|
||||
StoreAndForward_RequestResponse_CLIENT_HISTORY = 102,
|
||||
StoreAndForward_RequestResponse_CLIENT_STATS = 103,
|
||||
StoreAndForward_RequestResponse_CLIENT_PING = 104,
|
||||
StoreAndForward_RequestResponse_CLIENT_PONG = 105
|
||||
} StoreAndForward_RequestResponse;
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _StoreAndForward_History {
|
||||
uint32_t HistoryMessages;
|
||||
uint32_t Window;
|
||||
} StoreAndForward_History;
|
||||
|
||||
typedef struct _StoreAndForward_Statistics {
|
||||
uint32_t MessagesTotal;
|
||||
uint32_t MessagesSaved;
|
||||
uint32_t MessagesMax;
|
||||
uint32_t UpTime;
|
||||
uint32_t Requests;
|
||||
uint32_t RequestsHistory;
|
||||
bool Heartbeat;
|
||||
uint32_t ReturnMax;
|
||||
uint32_t ReturnWindow;
|
||||
} StoreAndForward_Statistics;
|
||||
|
||||
typedef struct _StoreAndForward {
|
||||
StoreAndForward_RequestResponse rr;
|
||||
bool has_stats;
|
||||
StoreAndForward_Statistics stats;
|
||||
bool has_history;
|
||||
StoreAndForward_History history;
|
||||
} StoreAndForward;
|
||||
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _StoreAndForward_RequestResponse_MIN StoreAndForward_RequestResponse_UNSET
|
||||
#define _StoreAndForward_RequestResponse_MAX StoreAndForward_RequestResponse_CLIENT_PONG
|
||||
#define _StoreAndForward_RequestResponse_ARRAYSIZE ((StoreAndForward_RequestResponse)(StoreAndForward_RequestResponse_CLIENT_PONG+1))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define StoreAndForward_init_default {_StoreAndForward_RequestResponse_MIN, false, StoreAndForward_Statistics_init_default, false, StoreAndForward_History_init_default}
|
||||
#define StoreAndForward_Statistics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define StoreAndForward_History_init_default {0, 0}
|
||||
#define StoreAndForward_init_zero {_StoreAndForward_RequestResponse_MIN, false, StoreAndForward_Statistics_init_zero, false, StoreAndForward_History_init_zero}
|
||||
#define StoreAndForward_Statistics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define StoreAndForward_History_init_zero {0, 0}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define StoreAndForward_History_HistoryMessages_tag 1
|
||||
#define StoreAndForward_History_Window_tag 2
|
||||
#define StoreAndForward_Statistics_MessagesTotal_tag 1
|
||||
#define StoreAndForward_Statistics_MessagesSaved_tag 2
|
||||
#define StoreAndForward_Statistics_MessagesMax_tag 3
|
||||
#define StoreAndForward_Statistics_UpTime_tag 4
|
||||
#define StoreAndForward_Statistics_Requests_tag 5
|
||||
#define StoreAndForward_Statistics_RequestsHistory_tag 6
|
||||
#define StoreAndForward_Statistics_Heartbeat_tag 7
|
||||
#define StoreAndForward_Statistics_ReturnMax_tag 8
|
||||
#define StoreAndForward_Statistics_ReturnWindow_tag 9
|
||||
#define StoreAndForward_rr_tag 1
|
||||
#define StoreAndForward_stats_tag 2
|
||||
#define StoreAndForward_history_tag 3
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define StoreAndForward_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, rr, 1) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, stats, 2) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, history, 3)
|
||||
#define StoreAndForward_CALLBACK NULL
|
||||
#define StoreAndForward_DEFAULT NULL
|
||||
#define StoreAndForward_stats_MSGTYPE StoreAndForward_Statistics
|
||||
#define StoreAndForward_history_MSGTYPE StoreAndForward_History
|
||||
|
||||
#define StoreAndForward_Statistics_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, MessagesTotal, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, MessagesSaved, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, MessagesMax, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, UpTime, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, Requests, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, RequestsHistory, 6) \
|
||||
X(a, STATIC, SINGULAR, BOOL, Heartbeat, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ReturnMax, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ReturnWindow, 9)
|
||||
#define StoreAndForward_Statistics_CALLBACK NULL
|
||||
#define StoreAndForward_Statistics_DEFAULT NULL
|
||||
|
||||
#define StoreAndForward_History_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, HistoryMessages, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, Window, 2)
|
||||
#define StoreAndForward_History_CALLBACK NULL
|
||||
#define StoreAndForward_History_DEFAULT NULL
|
||||
|
||||
extern const pb_msgdesc_t StoreAndForward_msg;
|
||||
extern const pb_msgdesc_t StoreAndForward_Statistics_msg;
|
||||
extern const pb_msgdesc_t StoreAndForward_History_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define StoreAndForward_fields &StoreAndForward_msg
|
||||
#define StoreAndForward_Statistics_fields &StoreAndForward_Statistics_msg
|
||||
#define StoreAndForward_History_fields &StoreAndForward_History_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define StoreAndForward_size 68
|
||||
#define StoreAndForward_Statistics_size 50
|
||||
#define StoreAndForward_History_size 12
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "main.h"
|
||||
#include "mesh/http/WebServer.h"
|
||||
#include "NodeDB.h"
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
@@ -77,7 +78,7 @@ static void taskCreateCert(void *parameter)
|
||||
{
|
||||
prefs.begin("MeshtasticHTTPS", false);
|
||||
|
||||
// Delete the saved certs
|
||||
// Delete the saved certs (used in debugging)
|
||||
if (0) {
|
||||
DEBUG_MSG("Deleting any saved SSL keys ...\n");
|
||||
// prefs.clear();
|
||||
@@ -166,11 +167,16 @@ void createSSLCert()
|
||||
NULL); /* Task handle. */
|
||||
|
||||
DEBUG_MSG("Waiting for SSL Cert to be generated.\n");
|
||||
int seconds = 0;
|
||||
while (!isCertReady) {
|
||||
DEBUG_MSG(".");
|
||||
delay(1000);
|
||||
yield();
|
||||
esp_task_wdt_reset();
|
||||
seconds++;
|
||||
if ((seconds == 3) && screen) {
|
||||
screen->setSSLFrames();
|
||||
}
|
||||
}
|
||||
DEBUG_MSG("SSL Cert Ready!\n");
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ static int32_t reconnectWiFi()
|
||||
if (*wifiName) {
|
||||
needReconnect = false;
|
||||
|
||||
DEBUG_MSG("... Reconnecting to WiFi access point");
|
||||
DEBUG_MSG("... Reconnecting to WiFi access point\n");
|
||||
WiFi.mode(WIFI_MODE_STA);
|
||||
WiFi.begin(wifiName, wifiPsw);
|
||||
}
|
||||
|
||||
@@ -557,7 +557,7 @@ void setBluetoothEnable(bool on)
|
||||
|
||||
bluetoothOn = on;
|
||||
if (on) {
|
||||
if (!initWifi(0)) // if we are using wifi, don't turn on bluetooth also
|
||||
if (!initWifi(isSoftAPForced())) // if we are using wifi, don't turn on bluetooth also
|
||||
{
|
||||
Serial.printf("Pre BT: %u heap size\n", ESP.getFreeHeap());
|
||||
// ESP_ERROR_CHECK( heap_trace_start(HEAP_TRACE_LEAKS) );
|
||||
|
||||
@@ -137,10 +137,15 @@ void AdminPlugin::handleSetOwner(const User &o)
|
||||
strcpy(owner.id, o.id);
|
||||
}
|
||||
if (owner.is_licensed != o.is_licensed) {
|
||||
changed = true;
|
||||
changed = 1;
|
||||
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
|
||||
service.reloadOwner();
|
||||
}
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
#include "plugins/RemoteHardwarePlugin.h"
|
||||
#include "plugins/ReplyPlugin.h"
|
||||
#include "plugins/TextMessagePlugin.h"
|
||||
#include "plugins/SerialPlugin.h"
|
||||
#include "plugins/TextMessagePlugin.h"
|
||||
#include "plugins/RoutingPlugin.h"
|
||||
#include "plugins/AdminPlugin.h"
|
||||
#ifndef NO_ESP32
|
||||
#include "plugins/SerialPlugin.h"
|
||||
#include "plugins/esp32/SerialPlugin.h"
|
||||
#include "plugins/esp32/EnvironmentalMeasurementPlugin.h"
|
||||
#include "plugins/esp32/RangeTestPlugin.h"
|
||||
#include "plugins/esp32/StoreForwardPlugin.h"
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#include "configuration.h"
|
||||
#include "PositionPlugin.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include "gps/GeoCoord.h"
|
||||
|
||||
PositionPlugin *positionPlugin;
|
||||
|
||||
@@ -18,6 +19,24 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, Position *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) {
|
||||
struct timeval tv;
|
||||
uint32_t secs = p.time;
|
||||
@@ -38,7 +57,44 @@ MeshPacket *PositionPlugin::allocReply()
|
||||
NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a 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
|
||||
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
|
||||
@@ -69,18 +125,58 @@ void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
|
||||
|
||||
int32_t PositionPlugin::runOnce()
|
||||
{
|
||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
|
||||
// radioConfig.preferences.position_broadcast_smart = true;
|
||||
|
||||
// We limit our GPS broadcasts to a max rate
|
||||
uint32_t now = millis();
|
||||
if (lastGpsSend == 0 || now - lastGpsSend >= getPref_position_broadcast_secs() * 1000) {
|
||||
|
||||
lastGpsSend = now;
|
||||
|
||||
lastGpsLatitude = node->position.latitude_i;
|
||||
lastGpsLongitude = node->position.longitude_i;
|
||||
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
bool requestReplies = 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);
|
||||
} else if (radioConfig.preferences.position_broadcast_smart == true) {
|
||||
NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position
|
||||
|
||||
if (node->has_position && (node->position.latitude_i != 0 || node->position.longitude_i != 0)) {
|
||||
float distance = GeoCoord::latLongToMeter(lastGpsLatitude * 1e-7, lastGpsLongitude * 1e-7,
|
||||
node->position.latitude_i * 1e-7, node->position.longitude_i * 1e-7);
|
||||
|
||||
/* Please don't change these values. This accomodates for possible poor positioning
|
||||
in the event the GPS has a poor satelite lock.
|
||||
*/
|
||||
const uint8_t distanceTravel = 150;
|
||||
|
||||
/* Minimum time between position updates.
|
||||
Note: At an average walking speed of 3.5mph, it takes 90 seconds to travel 150 meters.
|
||||
*/
|
||||
const uint8_t timeTravel = 60;
|
||||
|
||||
// If the distance traveled since the last update is greater than 100 meters
|
||||
// and it's been at least 60 seconds since the last update
|
||||
if ((abs(distance) >= distanceTravel) &&
|
||||
(lastGpsSend == 0 || now - timeTravel >= getPref_position_broadcast_secs() * 1000)) {
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
DEBUG_MSG("Sending smart pos@%x:6 to mesh (wantReplies=%d)\n", node->position.pos_timestamp, requestReplies);
|
||||
sendOurPosition(NODENUM_BROADCAST, requestReplies);
|
||||
|
||||
/* Update lastGpsSend to now. This means if the device is stationary, then
|
||||
getPref_position_broadcast_secs will still apply.
|
||||
*/
|
||||
lastGpsSend = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 5000; // to save power only wake for our callback occasionally
|
||||
|
||||
@@ -13,6 +13,10 @@ class PositionPlugin : public ProtobufPlugin<Position>, private concurrency::OST
|
||||
/// We limit our GPS broadcasts to a max rate
|
||||
uint32_t lastGpsSend = 0;
|
||||
|
||||
// Store the latest good lat / long
|
||||
uint32_t lastGpsLatitude = 0;
|
||||
uint32_t lastGpsLongitude = 0;
|
||||
|
||||
/// We force a rebroadcast if the radio settings change
|
||||
uint32_t currentGeneration = 0;
|
||||
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
#include "EnvironmentalMeasurementPlugin.h"
|
||||
#include "../mesh/generated/environmental_measurement.pb.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "../mesh/generated/environmental_measurement.pb.h"
|
||||
#include <DHT.h>
|
||||
#include <DS18B20.h>
|
||||
#include <OLEDDisplay.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 DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
|
||||
|
||||
|
||||
#ifdef HAS_EINK
|
||||
// The screen is bigger so use bigger fonts
|
||||
#define FONT_SMALL ArialMT_Plain_16
|
||||
@@ -31,123 +34,136 @@
|
||||
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
|
||||
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
|
||||
|
||||
|
||||
int32_t EnvironmentalMeasurementPlugin::runOnce() {
|
||||
int32_t EnvironmentalMeasurementPlugin::runOnce()
|
||||
{
|
||||
#ifndef NO_ESP32 // this only works on ESP32 devices
|
||||
|
||||
/*
|
||||
Uncomment the preferences below if you want to use the plugin
|
||||
without having to configure it from the PythonAPI or WebUI.
|
||||
*/
|
||||
|
||||
|
||||
/*radioConfig.preferences.environmental_measurement_plugin_measurement_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_update_interval = 30;
|
||||
radioConfig.preferences.environmental_measurement_plugin_update_interval = 600;
|
||||
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_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
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
if (firstTime) {
|
||||
// This is the first time the OSThread library has called this function, so do some setup
|
||||
|
||||
firstTime = 0;
|
||||
|
||||
if (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled)
|
||||
{
|
||||
|
||||
if (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled) {
|
||||
DEBUG_MSG("EnvironmentalMeasurement: Initializing\n");
|
||||
// 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
|
||||
switch(radioConfig.preferences.environmental_measurement_plugin_sensor_type) {
|
||||
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11:
|
||||
dht = new DHT(radioConfig.preferences.environmental_measurement_plugin_sensor_pin,DHT11);
|
||||
this->dht->begin();
|
||||
this->dht->read();
|
||||
DEBUG_MSG("EnvironmentalMeasurement: Opened DHT11 on pin: %d\n",radioConfig.preferences.environmental_measurement_plugin_sensor_pin);
|
||||
switch (radioConfig.preferences.environmental_measurement_plugin_sensor_type) {
|
||||
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11:
|
||||
dht = new DHT(radioConfig.preferences.environmental_measurement_plugin_sensor_pin, DHT11);
|
||||
this->dht->begin();
|
||||
this->dht->read();
|
||||
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;
|
||||
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);
|
||||
}
|
||||
else {
|
||||
if (!radioConfig.preferences.environmental_measurement_plugin_measurement_enabled)
|
||||
{
|
||||
} else {
|
||||
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
|
||||
// I can't imagine we'd ever get here though.
|
||||
return (INT32_MAX);
|
||||
}
|
||||
// this is not the first time OSThread library has called this function
|
||||
// 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 (radioConfig.preferences.environmental_measurement_plugin_recovery_interval > 0 ) {
|
||||
DEBUG_MSG(
|
||||
"EnvironmentalMeasurement: TEMPORARILY DISABLED; The environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Will retry reads in %d seconds\n",
|
||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold,
|
||||
radioConfig.preferences.environmental_measurement_plugin_recovery_interval);
|
||||
sensor_read_error_count = 0;
|
||||
return(radioConfig.preferences.environmental_measurement_plugin_recovery_interval*1000);
|
||||
if (sensor_read_error_count > radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold) {
|
||||
if (radioConfig.preferences.environmental_measurement_plugin_recovery_interval > 0) {
|
||||
DEBUG_MSG("EnvironmentalMeasurement: TEMPORARILY DISABLED; The "
|
||||
"environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Will retry reads in "
|
||||
"%d seconds\n",
|
||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold,
|
||||
radioConfig.preferences.environmental_measurement_plugin_recovery_interval);
|
||||
sensor_read_error_count = 0;
|
||||
return (radioConfig.preferences.environmental_measurement_plugin_recovery_interval * 1000);
|
||||
}
|
||||
DEBUG_MSG(
|
||||
"EnvironmentalMeasurement: DISABLED; The environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Reads will not be retried until after device reset\n",
|
||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold);
|
||||
return(INT32_MAX);
|
||||
DEBUG_MSG("EnvironmentalMeasurement: DISABLED; The environmental_measurement_plugin_read_error_count_threshold has "
|
||||
"been exceed: %d. Reads will not be retried until after device reset\n",
|
||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold);
|
||||
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){
|
||||
DEBUG_MSG("EnvironmentalMeasurement: There have been %d sensor read failures. Will retry %d more times\n",
|
||||
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
|
||||
if (!sendOurEnvironmentalMeasurement()) {
|
||||
// if we failed to read the sensor, then try again
|
||||
// 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
|
||||
// miliseconds until the function should be called again by the
|
||||
// The return of runOnce is an int32 representing the desired number of
|
||||
// miliseconds until the function should be called again by the
|
||||
// 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
|
||||
}
|
||||
|
||||
bool EnvironmentalMeasurementPlugin::wantUIFrame() {
|
||||
bool EnvironmentalMeasurementPlugin::wantUIFrame()
|
||||
{
|
||||
return radioConfig.preferences.environmental_measurement_plugin_screen_enabled;
|
||||
}
|
||||
|
||||
String GetSenderName(const MeshPacket &mp) {
|
||||
String GetSenderName(const MeshPacket &mp)
|
||||
{
|
||||
String sender;
|
||||
|
||||
auto node = nodeDB.getNode(getFrom(&mp));
|
||||
if (node){
|
||||
if (node) {
|
||||
sender = node->user.short_name;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
sender = "UNK";
|
||||
}
|
||||
return sender;
|
||||
}
|
||||
|
||||
uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp) {
|
||||
uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp)
|
||||
{
|
||||
uint32_t now = getTime();
|
||||
|
||||
uint32_t last_seen = mp->rx_time;
|
||||
@@ -156,15 +172,13 @@ uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp) {
|
||||
delta = 0;
|
||||
|
||||
return delta;
|
||||
|
||||
}
|
||||
|
||||
|
||||
float EnvironmentalMeasurementPlugin::CelsiusToFarenheit(float c) {
|
||||
return (c*9)/5 + 32;
|
||||
float EnvironmentalMeasurementPlugin::CelsiusToFarenheit(float c)
|
||||
{
|
||||
return (c * 9) / 5 + 32;
|
||||
}
|
||||
|
||||
|
||||
void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
@@ -173,21 +187,16 @@ void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplay
|
||||
if (lastMeasurementPacket == nullptr) {
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement");
|
||||
//DEBUG_MSG("EnvironmentalMeasurement: No previous measurement; not drawing frame\n");
|
||||
return;
|
||||
}
|
||||
|
||||
EnvironmentalMeasurement lastMeasurement;
|
||||
|
||||
|
||||
|
||||
uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket);
|
||||
String lastSender = GetSenderName(*lastMeasurementPacket);
|
||||
|
||||
auto &p = lastMeasurementPacket->decoded;
|
||||
if (!pb_decode_from_bytes(p.payload.bytes,
|
||||
p.payload.size,
|
||||
EnvironmentalMeasurement_fields,
|
||||
&lastMeasurement)) {
|
||||
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, EnvironmentalMeasurement_fields, &lastMeasurement)) {
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error");
|
||||
DEBUG_MSG("EnvironmentalMeasurement: unable to decode last packet");
|
||||
@@ -195,20 +204,23 @@ void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplay
|
||||
}
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
String last_temp = String(lastMeasurement.temperature,0) +"°C";
|
||||
if (radioConfig.preferences.environmental_measurement_plugin_display_farenheit){
|
||||
last_temp = String(CelsiusToFarenheit(lastMeasurement.temperature),0) +"°F";;
|
||||
String last_temp = String(lastMeasurement.temperature, 0) + "°C";
|
||||
if (radioConfig.preferences.environmental_measurement_plugin_display_farenheit) {
|
||||
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)
|
||||
{
|
||||
if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){
|
||||
// If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume
|
||||
return false;
|
||||
if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled ||
|
||||
radioConfig.preferences.environmental_measurement_plugin_screen_enabled)) {
|
||||
// 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);
|
||||
@@ -230,13 +242,32 @@ bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum des
|
||||
DEBUG_MSG("-----------------------------------------\n");
|
||||
|
||||
DEBUG_MSG("EnvironmentalMeasurement: Read data\n");
|
||||
if (!this->dht->read(true)){
|
||||
sensor_read_error_count++;
|
||||
DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n");
|
||||
|
||||
switch (radioConfig.preferences.environmental_measurement_plugin_sensor_type) {
|
||||
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;
|
||||
}
|
||||
m.relative_humidity = this->dht->readHumidity();
|
||||
m.temperature = this->dht->readTemperature();
|
||||
|
||||
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity);
|
||||
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature);
|
||||
@@ -250,4 +281,3 @@ bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum des
|
||||
service.sendToMesh(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
#pragma once
|
||||
#include "ProtobufPlugin.h"
|
||||
#include "../mesh/generated/environmental_measurement.pb.h"
|
||||
#include "ProtobufPlugin.h"
|
||||
#include <DHT.h>
|
||||
#include <DS18B20.h>
|
||||
#include <OLEDDisplay.h>
|
||||
#include <OLEDDisplayUi.h>
|
||||
#include <DHT.h>
|
||||
#include <OneWire.h>
|
||||
|
||||
class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public ProtobufPlugin<EnvironmentalMeasurement>
|
||||
{
|
||||
public:
|
||||
EnvironmentalMeasurementPlugin(): concurrency::OSThread("EnvironmentalMeasurementPlugin"), ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg) {
|
||||
lastMeasurementPacket = nullptr;
|
||||
EnvironmentalMeasurementPlugin()
|
||||
: concurrency::OSThread("EnvironmentalMeasurementPlugin"),
|
||||
ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg)
|
||||
{
|
||||
lastMeasurementPacket = nullptr;
|
||||
}
|
||||
virtual bool wantUIFrame();
|
||||
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
|
||||
*/
|
||||
bool sendOurEnvironmentalMeasurement(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||
|
||||
|
||||
private:
|
||||
float CelsiusToFarenheit(float c);
|
||||
bool firstTime = 1;
|
||||
DHT* dht;
|
||||
DHT *dht;
|
||||
OneWire *oneWire;
|
||||
DS18B20 *ds18b20;
|
||||
const MeshPacket *lastMeasurementPacket;
|
||||
uint32_t sensor_read_error_count = 0;
|
||||
};
|
||||
@@ -151,6 +151,7 @@ ProcessMessage RangeTestPluginRadio::handleReceived(const MeshPacket &mp)
|
||||
appendFile(mp);
|
||||
}
|
||||
|
||||
/*
|
||||
DEBUG_MSG("-----------------------------------------\n");
|
||||
DEBUG_MSG("p.payload.bytes \"%s\"\n", p.payload.bytes);
|
||||
DEBUG_MSG("p.payload.size %d\n", p.payload.size);
|
||||
@@ -174,6 +175,7 @@ ProcessMessage RangeTestPluginRadio::handleReceived(const MeshPacket &mp)
|
||||
DEBUG_MSG("gpsStatus->getHasLock() %d\n", gpsStatus->getHasLock());
|
||||
DEBUG_MSG("gpsStatus->getDOP() %d\n", gpsStatus->getDOP());
|
||||
DEBUG_MSG("-----------------------------------------\n");
|
||||
*/
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -237,7 +239,7 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
|
||||
|
||||
// Print the CSV header
|
||||
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");
|
||||
} else {
|
||||
DEBUG_MSG("File write failed\n");
|
||||
|
||||
@@ -50,4 +50,4 @@ class SerialPluginRadio : public SinglePortPlugin
|
||||
virtual ProcessMessage handleReceived(const MeshPacket &mp);
|
||||
};
|
||||
|
||||
extern SerialPluginRadio *serialPluginRadio;
|
||||
extern SerialPluginRadio *serialPluginRadio;
|
||||
@@ -5,14 +5,12 @@
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "mesh/generated/storeforward.pb.h"
|
||||
#include "plugins/PluginDev.h"
|
||||
#include <Arduino.h>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
|
||||
#define STOREFORWARD_MAX_PACKETS 0
|
||||
#define STOREFORWARD_SEND_HISTORY_PERIOD 10 * 60
|
||||
#define STOREFORWARD_SEND_HISTORY_MAX 0
|
||||
|
||||
StoreForwardPlugin *storeForwardPlugin;
|
||||
|
||||
int32_t StoreForwardPlugin::runOnce()
|
||||
@@ -23,22 +21,31 @@ int32_t StoreForwardPlugin::runOnce()
|
||||
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
||||
|
||||
if (radioConfig.preferences.is_router) {
|
||||
// Maybe some cleanup functions?
|
||||
this->historyReport();
|
||||
return (60 * 1000);
|
||||
|
||||
if (this->busy) {
|
||||
// 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 {
|
||||
/*
|
||||
* 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 - Disabled (is_router = false)\n");
|
||||
|
||||
DEBUG_MSG("Store & Forward Plugin - Sending heartbeat\n");
|
||||
|
||||
storeForwardPlugin->sendPayload();
|
||||
|
||||
return (4 * 60 * 1000);
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -51,6 +58,9 @@ int32_t StoreForwardPlugin::runOnce()
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
/*
|
||||
Create our data structure in the PSRAM.
|
||||
*/
|
||||
void StoreForwardPlugin::populatePSRAM()
|
||||
{
|
||||
/*
|
||||
@@ -58,6 +68,8 @@ void StoreForwardPlugin::populatePSRAM()
|
||||
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(" Total heap: %d\n", ESP.getHeapSize());
|
||||
@@ -65,12 +77,14 @@ void StoreForwardPlugin::populatePSRAM()
|
||||
DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize());
|
||||
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 =
|
||||
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->packetHistoryTXQueue =
|
||||
static_cast<PacketHistoryStruct *>(ps_calloc(store_forward_plugin_replay_max_records, sizeof(PacketHistoryStruct)));
|
||||
DEBUG_MSG("After PSRAM initilization:\n");
|
||||
|
||||
DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize());
|
||||
@@ -78,34 +92,13 @@ void StoreForwardPlugin::populatePSRAM()
|
||||
DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize());
|
||||
DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram());
|
||||
DEBUG_MSG("Store and Forward Stats:\n");
|
||||
DEBUG_MSG(" numberOfPackets - %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);
|
||||
}
|
||||
}
|
||||
DEBUG_MSG(" numberOfPackets for packetHistory - %u\n", numberOfPackets);
|
||||
}
|
||||
|
||||
void StoreForwardPlugin::historyReport()
|
||||
{
|
||||
DEBUG_MSG("Iterating through the message history...\n");
|
||||
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 +106,75 @@ void StoreForwardPlugin::historyReport()
|
||||
*/
|
||||
void StoreForwardPlugin::historySend(uint32_t msAgo, uint32_t to)
|
||||
{
|
||||
// Send "Welcome back"
|
||||
this->sendPayloadWelcome(to, false);
|
||||
|
||||
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);
|
||||
}
|
||||
uint32_t packetsSent = 0;
|
||||
|
||||
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)
|
||||
{
|
||||
auto &p = mp;
|
||||
|
||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded);
|
||||
assert(numbytes <= MAX_RHPACKETLEN);
|
||||
// uint32_t packetHistoryTXQueueIndex = 0;
|
||||
|
||||
DEBUG_MSG("MP numbytes %u\n", numbytes);
|
||||
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.decoded;
|
||||
|
||||
// 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].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++;
|
||||
}
|
||||
|
||||
@@ -147,48 +184,42 @@ MeshPacket *StoreForwardPlugin::allocReply()
|
||||
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");
|
||||
MeshPacket *p = allocReply();
|
||||
|
||||
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];
|
||||
snprintf(heartbeatString, sizeof(heartbeatString), "1");
|
||||
|
||||
p->decoded.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply
|
||||
memcpy(p->decoded.payload.bytes, "1", 1);
|
||||
p->decoded.payload.size =
|
||||
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,
|
||||
this->packetHistoryTXQueue[packetHistory_index].payload_size);
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
p->decoded.payload.size = strlen(str); // You must specify how many bytes are in the reply
|
||||
memcpy(p->decoded.payload.bytes, str, strlen(str));
|
||||
|
||||
service.sendToMesh(p);
|
||||
|
||||
// HardwareMessage_init_default
|
||||
}
|
||||
|
||||
ProcessMessage StoreForwardPlugin::handleReceived(const MeshPacket &mp)
|
||||
@@ -196,15 +227,44 @@ ProcessMessage StoreForwardPlugin::handleReceived(const MeshPacket &mp)
|
||||
#ifndef NO_ESP32
|
||||
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
||||
|
||||
DEBUG_MSG("--- S&F Received something\n");
|
||||
|
||||
/*
|
||||
StoreAndForwardMessage sfm = StoreAndForwardMessage_init_default;
|
||||
|
||||
switch (sfm.rr) {
|
||||
}
|
||||
*/
|
||||
auto &p = mp.decoded;
|
||||
|
||||
// The router node should not be sending messages as a client.
|
||||
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) {
|
||||
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, "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
"01234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
"01234567890123456789012345678901234567890123456789012345678901234567890123456");
|
||||
storeForwardPlugin->sendMessage(getFrom(&mp), this->routerMessage);
|
||||
|
||||
} else {
|
||||
storeForwardPlugin->historyAdd(mp);
|
||||
}
|
||||
|
||||
} else if (mp.decoded.portnum == PortNum_STORE_FORWARD_APP) {
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum);
|
||||
@@ -220,22 +280,83 @@ ProcessMessage StoreForwardPlugin::handleReceived(const MeshPacket &mp)
|
||||
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
ProcessMessage StoreForwardPlugin::handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p)
|
||||
{
|
||||
if (!radioConfig.preferences.store_forward_plugin_enabled) {
|
||||
// If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume
|
||||
return ProcessMessage::CONTINUE;
|
||||
}
|
||||
|
||||
//auto sfp = *p;
|
||||
//auto p = *p;
|
||||
|
||||
// Advance states as needed
|
||||
switch (p->rr) {
|
||||
case StoreAndForward_RequestResponse_CLIENT_ERROR:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case StoreAndForward_RequestResponse_CLIENT_HISTORY:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case StoreAndForward_RequestResponse_CLIENT_PING:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case StoreAndForward_RequestResponse_CLIENT_PONG:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case StoreAndForward_RequestResponse_CLIENT_STATS:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case StoreAndForward_RequestResponse_ROUTER_BUSY:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case StoreAndForward_RequestResponse_ROUTER_ERROR:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case StoreAndForward_RequestResponse_ROUTER_HEARTBEAT:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case StoreAndForward_RequestResponse_ROUTER_PING:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case StoreAndForward_RequestResponse_ROUTER_PONG:
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
assert(0); // unexpected state - FIXME, make an error code and reboot
|
||||
}
|
||||
|
||||
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
StoreForwardPlugin::StoreForwardPlugin()
|
||||
: SinglePortPlugin("StoreForwardPlugin", PortNum_STORE_FORWARD_APP), concurrency::OSThread("StoreForwardPlugin")
|
||||
: SinglePortPlugin("StoreForwardPlugin", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("StoreForwardPlugin")
|
||||
{
|
||||
|
||||
#ifndef NO_ESP32
|
||||
|
||||
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) {
|
||||
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) {
|
||||
@@ -248,7 +369,11 @@ StoreForwardPlugin::StoreForwardPlugin()
|
||||
|
||||
// Do the startup here
|
||||
|
||||
// Popupate PSRAM with our data structures.
|
||||
this->populatePSRAM();
|
||||
|
||||
this->packetTimeMax = 2000;
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("Device has less than 1M of PSRAM free. Aborting startup.\n");
|
||||
DEBUG_MSG("Store & Forward Plugin - Aborting Startup.\n");
|
||||
@@ -265,4 +390,4 @@ StoreForwardPlugin::StoreForwardPlugin()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "SinglePortPlugin.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "mesh/generated/storeforward.pb.h"
|
||||
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
@@ -9,19 +11,30 @@
|
||||
struct PacketHistoryStruct {
|
||||
uint32_t time;
|
||||
uint32_t to;
|
||||
uint32_t from;
|
||||
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
|
||||
{
|
||||
bool firstTime = 1;
|
||||
// bool firstTime = 1;
|
||||
bool busy = 0;
|
||||
uint32_t busyTo;
|
||||
char routerMessage[80];
|
||||
|
||||
uint32_t receivedRecord[50][2] = {{0}};
|
||||
|
||||
PacketHistoryStruct *packetHistory;
|
||||
uint32_t packetHistoryCurrent = 0;
|
||||
|
||||
PacketHistoryStruct *packetHistoryTXQueue;
|
||||
uint32_t packetHistoryTXQueue_size;
|
||||
uint32_t packetHistoryTXQueue_index = 0;
|
||||
|
||||
uint32_t packetTimeMax = 0;
|
||||
|
||||
public:
|
||||
StoreForwardPlugin();
|
||||
|
||||
@@ -29,54 +42,37 @@ class StoreForwardPlugin : public SinglePortPlugin, private concurrency::OSThrea
|
||||
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.
|
||||
*/
|
||||
void sawNode(uint32_t whoWeSaw, uint32_t sawSecAgo);
|
||||
void historyAdd(const MeshPacket *mp);
|
||||
void historyAdd(const MeshPacket &mp);
|
||||
void historyReport();
|
||||
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
|
||||
*/
|
||||
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||
void sendPayloadWelcome(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||
void sendPayload(NodeNum dest = NODENUM_BROADCAST, uint32_t packetHistory_index = 0);
|
||||
void sendMessage(NodeNum dest, char *str);
|
||||
virtual MeshPacket *allocReply();
|
||||
/*
|
||||
Override the wantPortnum method.
|
||||
*/
|
||||
virtual bool wantPortnum(PortNum p) { return true; };
|
||||
|
||||
private:
|
||||
// Nothing here
|
||||
void populatePSRAM();
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce();
|
||||
|
||||
/** 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 handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p);
|
||||
|
||||
};
|
||||
|
||||
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;
|
||||
*/
|
||||
extern StoreForwardPlugin *storeForwardPlugin;
|
||||
@@ -1,4 +1,4 @@
|
||||
[VERSION]
|
||||
major = 1
|
||||
minor = 2
|
||||
build = 46
|
||||
build = 48
|
||||
|
||||
Reference in New Issue
Block a user