Compare commits

..

40 Commits

Author SHA1 Message Date
Kevin Hester
bfd147062f Merge pull request #761 from geeksville/dev1.2
Dev1.2
2021-03-27 17:19:50 +08:00
Kevin Hester
890ec7bdb2 doc update 2021-03-27 17:19:15 +08:00
Kevin Hester
76269b397f Merge remote-tracking branch 'root/master' into dev1.2
# Conflicts:
#	src/esp32/main-esp32.cpp
#	src/mesh/generated/deviceonly.pb.h
#	src/mesh/generated/mesh.pb.h
2021-03-27 16:44:42 +08:00
Jm Casler
9fb6b1718f Merge pull request #770 from mc-hamster/master
#669 - Add restart counter
2021-03-27 01:30:28 -07:00
Jm
57c82988e2 #669 - Add restart counter 2021-03-27 01:20:07 -07:00
Kevin Hester
1e3b037fea populate position.time for broadcast positions 2021-03-27 16:17:01 +08:00
Jm Casler
1e7808991d Merge pull request #81 from meshtastic/master
update from master
2021-03-27 01:00:57 -07:00
Jm
4f4cdf4f9e #669 Add restart counter 2021-03-27 01:00:27 -07:00
Kevin Hester
78f2c656d0 fix nrf52 builds 2021-03-27 11:21:43 +08:00
Kevin Hester
37ec969f96 Merge remote-tracking branch 'root/master' into dev1.2
# Conflicts:
#	src/mesh/NodeDB.cpp
2021-03-27 11:10:47 +08:00
Jm Casler
f1a6693bb7 Merge pull request #768 from IZ1IVA/patch-1
Update device-update.sh
2021-03-26 19:54:49 -07:00
Kevin Hester
8ffd5a1d4f add reboot message 2021-03-27 10:19:59 +08:00
IZ1IVA
29eb5e8327 Update device-update.sh
Please have a look at https://github.com/meshtastic/Meshtastic-device/issues/760
2021-03-26 14:49:27 +01:00
Jm Casler
c175c21189 Merge pull request #767 from mc-hamster/master
Fix bad merge
2021-03-25 19:37:15 -07:00
Jm
fc2862bd16 Fix bad merge 2021-03-25 19:36:37 -07:00
Jm Casler
c9f814a9a7 Merge pull request #80 from meshtastic/master
Merge pull request #766 from mc-hamster/master
2021-03-25 19:33:35 -07:00
Jm Casler
92d2d3960b Merge pull request #766 from mc-hamster/master
#758 Report elevation while in range test
2021-03-25 19:32:22 -07:00
Jm Casler
7872cb050d Merge branch 'master' into master 2021-03-25 19:32:13 -07:00
Jm
89029311c1 Update NodeDB.cpp 2021-03-25 19:28:15 -07:00
Jm
f6f586decb Remove my code that doesn't work with channels 2021-03-25 19:27:46 -07:00
Jm
471c06b169 #758 Report elevation while in range test 2021-03-25 19:25:20 -07:00
Jm
040bb1d1e0 Add a "Development Mode" for our plugins 2021-03-25 19:24:36 -07:00
Jm
bbaf5946f0 Update StoreForwardPlugin.cpp 2021-03-25 19:22:46 -07:00
Jm Casler
5286f23c9a Merge pull request #764 from meehow/android-captive-portal
captive portal for Android devices
2021-03-25 19:10:10 -07:00
Kevin Hester
7e9e33d462 fix has_gps reporting to phones 2021-03-26 09:30:33 +08:00
Kevin Hester
04225f7bc2 change! time of last packet rx in node->last_heard instead of node->position.time 2021-03-26 09:30:15 +08:00
Kevin Hester
dd0f1b2704 Merge remote-tracking branch 'root/master' into dev1.2 2021-03-26 08:57:47 +08:00
Kevin Hester
669807524e Merge pull request #763 from IZ1IVA/patch-6
Update device-install.sh
2021-03-26 08:54:03 +08:00
Michał Adamski
97a5405293 captive portal for Android devices 2021-03-25 21:49:06 +01:00
IZ1IVA
f298c7d053 Update device-install.sh
Please have a look at https://github.com/meshtastic/Meshtastic-device/issues/760
2021-03-25 10:43:25 +01:00
Kevin Hester
a59f5344de Merge remote-tracking branch 'root/master' into dev1.2 2021-03-25 09:18:44 +08:00
Kevin Hester
13cfce48fa cleanly disable bluetooth while serial API is in use (and only then) 2021-03-25 08:54:43 +08:00
Kevin Hester
0261c243e0 PhoneAPIs shouldn't register for messages until they have clients 2021-03-25 07:51:54 +08:00
Kevin Hester
ab325d6d2c Merge branch 'dev1.2' of https://github.com/geeksville/Meshtastic-esp32 into dev1.2 2021-03-25 06:15:36 +08:00
Kevin Hester
b20930c111 move streamapi into a thread, saves power and increases responsiveness 2021-03-25 06:15:15 +08:00
Kevin Hester
770788d0a4 Merge pull request #759 from geeksville/dev1.2
update altitude in nodedb for received altitudes (reported by @iz1kga)
2021-03-24 19:25:48 +08:00
Kevin Hester
d02f615cad Merge branch 'master' into dev1.2 2021-03-24 19:25:40 +08:00
Kevin Hester
e17fe7e075 update altitude in nodedb for received altitudes (reported by @iz1kga) 2021-03-24 19:24:33 +08:00
Jm Casler
e193f63687 Update StoreForwardPlugin.cpp 2021-03-21 19:46:02 -07:00
Jm Casler
1eb37dded8 partial work for S&F 2021-03-21 19:45:35 -07:00
36 changed files with 433 additions and 337 deletions

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
PYTHON=${PYTHON:-python} PYTHON=${PYTHON:-python3}
set -e set -e

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
PYTHON=${PYTHON:-python} PYTHON=${PYTHON:-python3}
# Usage info # Usage info
show_help() { show_help() {

View File

@@ -4,11 +4,19 @@ You probably don't care about this section - skip to the next one.
## before next release ## before next release
* DONE have android fill in if local GPS has poor signal
* fix heltec battery scaling
* add reference counting to mesh packets
* allow multiple simultanteous phoneapi connections
* DONE split position.time and last_heard
* DONE update android app to use last_heard
* DONE turn off bluetooth interface ENTIRELY while using serial API (was python client times out on connect sometimes)
* DONE gps assistance from phone not working?
* DONE test latest firmware update with is_router * DONE test latest firmware update with is_router
* DONE firmware OTA updates of is_router true nodes fails? * DONE firmware OTA updates of is_router true nodes fails?
* DONE add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263 * DONE add UI in android app to reset to defaults https://github.com/meshtastic/Meshtastic-Android/issues/263
* DONE TEST THIS! changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752 * DONE TEST THIS! changing channels requires a reboot to take effect https://github.com/meshtastic/Meshtastic-device/issues/752
* DIBE bug report with remote info request timing out * DONE bug report with remote info request timing out
* DONE retest channel changing in android (using sim?) * DONE retest channel changing in android (using sim?)
* DONE move remote admin doc from forum into git * DONE move remote admin doc from forum into git
* DONE check crashlytics * DONE check crashlytics

2
proto

Submodule proto updated: 820fa497df...0ea2328026

View File

@@ -9,6 +9,23 @@
#include "sleep.h" #include "sleep.h"
#include "target_specific.h" #include "target_specific.h"
/// Should we behave as if we have AC power now?
static bool isPowered()
{
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
// We assume routers might be powered all the time, but from a low current (solar) source
bool isLowPower = radioConfig.preferences.is_low_power || isRouter;
/* To determine if we're externally powered, assumptions
1) If we're powered up and there's no battery, we must be getting power externally. (because we'd be dead otherwise)
2) If we detect USB power from the power management chip, we must be getting power externally.
*/
return !isLowPower && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB());
}
static void sdsEnter() static void sdsEnter()
{ {
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw // FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
@@ -119,14 +136,34 @@ static void serialEnter()
{ {
setBluetoothEnable(false); setBluetoothEnable(false);
screen->setOn(true); screen->setOn(true);
screen->print("Using API...\n"); screen->print("Serial connected\n");
}
static void serialExit()
{
screen->print("Serial disconnected\n");
} }
static void powerEnter() static void powerEnter()
{ {
screen->setOn(true); if (!isPowered()) {
setBluetoothEnable(true); // If we got here, we are in the wrong state - we should be in powered, let that state ahndle things
screen->print("Powered...\n"); DEBUG_MSG("Loss of power in Powered\n");
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
} else {
screen->setOn(true);
setBluetoothEnable(true);
screen->print("Powered...\n");
}
}
static void powerIdle()
{
if (!isPowered()) {
// If we got here, we are in the wrong state
DEBUG_MSG("Loss of power in Powered\n");
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
}
} }
static void powerExit() static void powerExit()
@@ -153,6 +190,14 @@ static void onEnter()
} }
} }
static void onIdle()
{
if (isPowered()) {
// If we got here, we are in the wrong state - we should be in powered, let that state ahndle things
powerFSM.trigger(EVENT_POWER_CONNECTED);
}
}
static void screenPress() static void screenPress()
{ {
screen->onPress(); screen->onPress();
@@ -164,26 +209,16 @@ State stateSDS(sdsEnter, NULL, NULL, "SDS");
State stateLS(lsEnter, lsIdle, lsExit, "LS"); State stateLS(lsEnter, lsIdle, lsExit, "LS");
State stateNB(nbEnter, NULL, NULL, "NB"); State stateNB(nbEnter, NULL, NULL, "NB");
State stateDARK(darkEnter, NULL, NULL, "DARK"); State stateDARK(darkEnter, NULL, NULL, "DARK");
State stateSERIAL(serialEnter, NULL, NULL, "SERIAL"); State stateSERIAL(serialEnter, NULL, serialExit, "SERIAL");
State stateBOOT(bootEnter, NULL, NULL, "BOOT"); State stateBOOT(bootEnter, NULL, NULL, "BOOT");
State stateON(onEnter, NULL, NULL, "ON"); State stateON(onEnter, onIdle, NULL, "ON");
State statePOWER(powerEnter, NULL, powerExit, "POWER"); State statePOWER(powerEnter, powerIdle, powerExit, "POWER");
Fsm powerFSM(&stateBOOT); Fsm powerFSM(&stateBOOT);
void PowerFSM_setup() void PowerFSM_setup()
{ {
bool isRouter = radioConfig.preferences.is_router; bool isRouter = radioConfig.preferences.is_router;
bool hasPower = isPowered();
// If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON
// We assume routers might be powered all the time, but from a low current (solar) source
bool isLowPower = radioConfig.preferences.is_low_power || isRouter;
/* To determine if we're externally powered, assumptions
1) If we're powered up and there's no battery, we must be getting power externally. (because we'd be dead otherwise)
2) If we detect USB power from the power management chip, we must be getting power externally.
*/
bool hasPower = !isLowPower && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB());
DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower); DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower);
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout"); powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
@@ -231,22 +266,25 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateON, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text"); // restarts the sleep timer powerFSM.add_transition(&stateON, &stateON, EVENT_RECEIVED_TEXT_MSG, NULL, "Received text"); // restarts the sleep timer
} }
// If we are not in statePOWER but get a serial connection, suppress sleep (and keep the screen on) while connected
powerFSM.add_transition(&stateLS, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API"); powerFSM.add_transition(&stateLS, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
powerFSM.add_transition(&stateNB, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API"); powerFSM.add_transition(&stateNB, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
powerFSM.add_transition(&stateDARK, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API"); powerFSM.add_transition(&stateDARK, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
powerFSM.add_transition(&stateON, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API"); powerFSM.add_transition(&stateON, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
powerFSM.add_transition(&statePOWER, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
if (!isLowPower) { // If we get power connected, go to the power connect state
powerFSM.add_transition(&stateLS, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); powerFSM.add_transition(&stateLS, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
powerFSM.add_transition(&stateNB, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); powerFSM.add_transition(&stateNB, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
powerFSM.add_transition(&stateDARK, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); powerFSM.add_transition(&stateDARK, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
powerFSM.add_transition(&stateON, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect"); powerFSM.add_transition(&stateON, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
}
powerFSM.add_transition(&statePOWER, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected"); powerFSM.add_transition(&statePOWER, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected");
powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected"); // powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_POWER_DISCONNECTED, NULL, "power disconnected");
powerFSM.add_transition(&stateSERIAL, &stateNB, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect"); // the only way to leave state serial is for the client to disconnect (or we timeout and force disconnect them)
// when we leave, go to ON (which might not be the correct state if we have power connected, we will fix that in onEnter)
powerFSM.add_transition(&stateSERIAL, &stateON, EVENT_SERIAL_DISCONNECTED, NULL, "serial disconnect");
powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone"); powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone");

View File

@@ -6,27 +6,29 @@
#define Port Serial #define Port Serial
SerialConsole console; SerialConsole *console;
void consoleInit()
{
new SerialConsole(); // Must be dynamically allocated because we are now inheriting from thread
}
void consolePrintf(const char *format, ...) void consolePrintf(const char *format, ...)
{ {
va_list arg; va_list arg;
va_start(arg, format); va_start(arg, format);
console.vprintf(format, arg); console->vprintf(format, arg);
va_end(arg); va_end(arg);
} }
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port) SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port)
{ {
assert(!console);
console = this;
canWrite = false; // We don't send packets to our port until it has talked to us first canWrite = false; // We don't send packets to our port until it has talked to us first
// setDestination(&noopPrint); for testing, try turning off 'all' debug output and see what leaks // setDestination(&noopPrint); for testing, try turning off 'all' debug output and see what leaks
}
/// Do late init that can't happen at constructor time
void SerialConsole::init()
{
Port.begin(SERIAL_BAUD); Port.begin(SERIAL_BAUD);
StreamAPI::init();
emitRebooted(); emitRebooted();
} }
@@ -34,14 +36,14 @@ void SerialConsole::init()
* we override this to notice when we've received a protobuf over the serial * we override this to notice when we've received a protobuf over the serial
* stream. Then we shunt off debug serial output. * stream. Then we shunt off debug serial output.
*/ */
void SerialConsole::handleToRadio(const uint8_t *buf, size_t len) bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
{ {
// Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets // Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets
if (!radioConfig.preferences.debug_log_enabled) if (!radioConfig.preferences.debug_log_enabled)
setDestination(&noopPrint); setDestination(&noopPrint);
canWrite = true; canWrite = true;
StreamAPI::handleToRadio(buf, len); return StreamAPI::handleToRadio(buf, len);
} }
/// Hookable to find out when connection changes /// Hookable to find out when connection changes

View File

@@ -11,14 +11,11 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
public: public:
SerialConsole(); SerialConsole();
/// Do late init that can't happen at constructor time
virtual void init();
/** /**
* we override this to notice when we've received a protobuf over the serial stream. Then we shunt off * we override this to notice when we've received a protobuf over the serial stream. Then we shunt off
* debug serial output. * debug serial output.
*/ */
virtual void handleToRadio(const uint8_t *buf, size_t len); virtual bool handleToRadio(const uint8_t *buf, size_t len);
virtual size_t write(uint8_t c) virtual size_t write(uint8_t c)
{ {
@@ -34,5 +31,6 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
// A simple wrapper to allow non class aware code write to the console // A simple wrapper to allow non class aware code write to the console
void consolePrintf(const char *format, ...); void consolePrintf(const char *format, ...);
void consoleInit();
extern SerialConsole console; extern SerialConsole *console;

View File

@@ -1,6 +1,5 @@
#pragma once #pragma once
#include "configuration.h"
#include "../freertosinc.h" #include "../freertosinc.h"
namespace concurrency namespace concurrency
@@ -28,4 +27,4 @@ class BinarySemaphoreFreeRTOS
#endif #endif
} } // namespace concurrency

View File

@@ -457,7 +457,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "SerialConsole.h" #include "SerialConsole.h"
#define DEBUG_PORT console // Serial debug port #define DEBUG_PORT (*console) // Serial debug port
// What platforms should use SEGGER? // What platforms should use SEGGER?
#ifdef NRF52_SERIES #ifdef NRF52_SERIES

View File

@@ -16,7 +16,6 @@
int16_t updateResultHandle = -1; int16_t updateResultHandle = -1;
static CRC32 crc; static CRC32 crc;
static uint32_t rebootAtMsec = 0; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
static uint32_t updateExpectedSize, updateActualSize; static uint32_t updateExpectedSize, updateActualSize;
static uint8_t update_result; static uint8_t update_result;
@@ -139,14 +138,6 @@ int update_region_callback(uint16_t conn_handle, uint16_t attr_handle, struct bl
return chr_readwrite8(&update_region, sizeof(update_region), ctxt); return chr_readwrite8(&update_region, sizeof(update_region), ctxt);
} }
void bluetoothRebootCheck()
{
if (rebootAtMsec && millis() > rebootAtMsec) {
DEBUG_MSG("Rebooting for update\n");
ESP.restart();
}
}
/* /*
See bluetooth-api.md See bluetooth-api.md

View File

@@ -4,8 +4,6 @@
void reinitUpdateService(); void reinitUpdateService();
void bluetoothRebootCheck();
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif

View File

@@ -7,9 +7,10 @@
#include "sleep.h" #include "sleep.h"
#include "target_specific.h" #include "target_specific.h"
#include "utils.h" #include "utils.h"
#include <Preferences.h>
#include <driver/rtc_io.h>
#include <nvs.h> #include <nvs.h>
#include <nvs_flash.h> #include <nvs_flash.h>
#include <driver/rtc_io.h>
void getMacAddr(uint8_t *dmac) void getMacAddr(uint8_t *dmac)
{ {
@@ -45,6 +46,18 @@ void esp32Setup()
DEBUG_MSG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d\n", nvs_stats.used_entries, nvs_stats.free_entries, DEBUG_MSG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d\n", nvs_stats.used_entries, nvs_stats.free_entries,
nvs_stats.total_entries); nvs_stats.total_entries);
DEBUG_MSG("Setup Preferences in Flash Storage\n");
// Create object to store our persistant data
Preferences preferences;
preferences.begin("meshtastic", false);
uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0);
rebootCounter++;
preferences.putUInt("rebootCounter", rebootCounter);
preferences.end();
DEBUG_MSG("Number of Device Reboots: %d\n", rebootCounter);
// enableModemSleep(); // enableModemSleep();
// Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any // Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any
@@ -84,7 +97,6 @@ void esp32Loop()
{ {
esp_task_wdt_reset(); // service our app level watchdog esp_task_wdt_reset(); // service our app level watchdog
loopBLE(); loopBLE();
bluetoothRebootCheck();
// for debug printing // for debug printing
// radio.radioIf.canSleep(); // radio.radioIf.canSleep();

View File

@@ -177,6 +177,7 @@ class ButtonThread : public OSThread
OneButton userButtonAlt; OneButton userButtonAlt;
#endif #endif
static bool shutdown_on_long_stop; static bool shutdown_on_long_stop;
public: public:
static uint32_t longPressTime; static uint32_t longPressTime;
@@ -250,15 +251,15 @@ class ButtonThread : public OSThread
power->shutdown(); power->shutdown();
} }
#elif NRF52_SERIES #elif NRF52_SERIES
// Do actual shutdown when button released, otherwise the button release // Do actual shutdown when button released, otherwise the button release
// may wake the board immediatedly. // may wake the board immediatedly.
if (!shutdown_on_long_stop) { if (!shutdown_on_long_stop) {
DEBUG_MSG("Shutdown from long press"); DEBUG_MSG("Shutdown from long press");
playBeep(); playBeep();
ledOff(PIN_LED1); ledOff(PIN_LED1);
ledOff(PIN_LED2); ledOff(PIN_LED2);
shutdown_on_long_stop = true; shutdown_on_long_stop = true;
} }
#endif #endif
} else { } else {
// DEBUG_MSG("Long press %u\n", (millis() - longPressTime)); // DEBUG_MSG("Long press %u\n", (millis() - longPressTime));
@@ -315,9 +316,8 @@ void setup()
SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_TRIM); SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_TRIM);
#endif #endif
// Debug
#ifdef DEBUG_PORT #ifdef DEBUG_PORT
DEBUG_PORT.init(); // Set serial baud rate and init our mesh console consoleInit(); // Set serial baud rate and init our mesh console
#endif #endif
initDeepSleep(); initDeepSleep();
@@ -576,14 +576,24 @@ Periodic axpDebugOutput(axpDebugRead);
axpDebugOutput.setup(); axpDebugOutput.setup();
#endif #endif
uint32_t rebootAtMsec; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
void rebootCheck()
{
if (rebootAtMsec && millis() > rebootAtMsec) {
#ifndef NO_ESP32
DEBUG_MSG("Rebooting for update\n");
ESP.restart();
#else
DEBUG_MSG("FIXME implement reboot for this platform");
#endif
}
}
void loop() void loop()
{ {
// axpDebugOutput.loop(); // axpDebugOutput.loop();
#ifdef DEBUG_PORT
DEBUG_PORT.loop(); // Send/receive protobufs over the serial port
#endif
// heap_caps_check_integrity_all(true); // FIXME - disable this expensive check // heap_caps_check_integrity_all(true); // FIXME - disable this expensive check
#ifndef NO_ESP32 #ifndef NO_ESP32
@@ -592,6 +602,7 @@ void loop()
#ifdef NRF52_SERIES #ifdef NRF52_SERIES
nrf52Loop(); nrf52Loop();
#endif #endif
rebootCheck();
// For debugging // For debugging
// if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving(); // if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();

View File

@@ -20,4 +20,6 @@ extern graphics::Screen *screen;
// Return a human readable string of the form "Meshtastic_ab13" // Return a human readable string of the form "Meshtastic_ab13"
const char *getDeviceName(); const char *getDeviceName();
extern uint32_t rebootAtMsec;
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(); void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop();

View File

@@ -114,7 +114,7 @@ bool MeshService::reloadConfig()
void MeshService::reloadOwner() void MeshService::reloadOwner()
{ {
assert(nodeInfoPlugin); assert(nodeInfoPlugin);
if(nodeInfoPlugin) if (nodeInfoPlugin)
nodeInfoPlugin->sendOurNodeInfo(); nodeInfoPlugin->sendOurNodeInfo();
nodeDB.saveToDisk(); nodeDB.saveToDisk();
} }
@@ -172,13 +172,12 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
assert(node); assert(node);
if (node->has_position) { if (node->has_position) {
if(positionPlugin) { if (positionPlugin) {
DEBUG_MSG("Sending position ping to 0x%x, wantReplies=%d\n", dest, wantReplies); DEBUG_MSG("Sending position ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
positionPlugin->sendOurPosition(dest, wantReplies); positionPlugin->sendOurPosition(dest, wantReplies);
} }
} } else {
else { if (nodeInfoPlugin) {
if(nodeInfoPlugin) {
DEBUG_MSG("Sending nodeinfo ping to 0x%x, wantReplies=%d\n", dest, wantReplies); DEBUG_MSG("Sending nodeinfo ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies); nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
} }
@@ -198,10 +197,13 @@ NodeInfo *MeshService::refreshMyNodeInfo()
Position &position = node->position; Position &position = node->position;
// Update our local node info with our position (even if we don't decide to update anyone else) // Update our local node info with our time (even if we don't decide to update anyone else)
position.time = node->last_heard =
getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid
// For the time in the position field, only set that if we have a real GPS clock
position.time = getValidTime(RTCQualityGPS);
position.battery_level = powerStatus->getBatteryChargePercent(); position.battery_level = powerStatus->getBatteryChargePercent();
updateBatteryLevel(position.battery_level); updateBatteryLevel(position.battery_level);

View File

@@ -84,13 +84,12 @@ class MeshService
NodeInfo *refreshMyNodeInfo(); NodeInfo *refreshMyNodeInfo();
private: private:
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh /// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh
/// returns 0 to allow futher processing /// returns 0 to allow futher processing
int onGPSChanged(const meshtastic::GPSStatus *arg); int onGPSChanged(const meshtastic::GPSStatus *arg);
/// Handle a packet that just arrived from the radio. This method does _ReliableRouternot_ free the provided packet. If it needs /// Handle a packet that just arrived from the radio. This method does _ReliableRouternot_ free the provided packet. If it
/// to keep the packet around it makes a copy /// needs to keep the packet around it makes a copy
int handleFromRadio(const MeshPacket *p); int handleFromRadio(const MeshPacket *p);
friend class RoutingPlugin; friend class RoutingPlugin;
}; };

View File

@@ -23,6 +23,8 @@
#ifndef NO_ESP32 #ifndef NO_ESP32
#include "mesh/http/WiFiAPClient.h" #include "mesh/http/WiFiAPClient.h"
#include "plugins/esp32/StoreForwardPlugin.h"
#include <Preferences.h>
#endif #endif
NodeDB nodeDB; NodeDB nodeDB;
@@ -233,6 +235,15 @@ void NodeDB::init()
myNodeInfo.hw_model_deprecated[0] = '\0'; myNodeInfo.hw_model_deprecated[0] = '\0';
// strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model)); // strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
#ifndef NO_ESP32
Preferences preferences;
preferences.begin("meshtastic", false);
myNodeInfo.reboot_count = preferences.getUInt("rebootCounter", 0);
preferences.end();
DEBUG_MSG("Number of Device Reboots: %d\n", myNodeInfo.reboot_count);
#endif
resetRadioConfig(); // If bogus settings got saved, then fix them resetRadioConfig(); // If bogus settings got saved, then fix them
DEBUG_MSG("region=%d, NODENUM=0x%x, dbsize=%d\n", radioConfig.preferences.region, myNodeInfo.my_node_num, *numNodes); DEBUG_MSG("region=%d, NODENUM=0x%x, dbsize=%d\n", radioConfig.preferences.region, myNodeInfo.my_node_num, *numNodes);
@@ -408,8 +419,7 @@ uint32_t sinceLastSeen(const NodeInfo *n)
{ {
uint32_t now = getTime(); uint32_t now = getTime();
uint32_t last_seen = n->position.time; int delta = (int)(now - n->last_heard);
int delta = (int)(now - last_seen);
if (delta < 0) // our clock must be slightly off still - not set from GPS yet if (delta < 0) // our clock must be slightly off still - not set from GPS yet
delta = 0; delta = 0;
@@ -443,7 +453,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p)
// Be careful to only update fields that have been set by the sender // 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 // 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 // recorded based on the packet rxTime
if (!info->position.time && p.time) if (p.time)
info->position.time = p.time; info->position.time = p.time;
if (p.battery_level) if (p.battery_level)
info->position.battery_level = p.battery_level; info->position.battery_level = p.battery_level;
@@ -451,6 +461,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p)
info->position.latitude_i = p.latitude_i; info->position.latitude_i = p.latitude_i;
info->position.longitude_i = p.longitude_i; info->position.longitude_i = p.longitude_i;
} }
if (p.altitude)
info->position.altitude = p.altitude;
info->has_position = true; info->has_position = true;
updateGUIforNode = info; updateGUIforNode = info;
notifyObservers(true); // Force an update whether or not our node counts have changed notifyObservers(true); // Force an update whether or not our node counts have changed
@@ -491,10 +503,8 @@ void NodeDB::updateFrom(const MeshPacket &mp)
NodeInfo *info = getOrCreateNode(getFrom(&mp)); NodeInfo *info = getOrCreateNode(getFrom(&mp));
if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard
info->has_position = true; // at least the time is valid info->last_heard = mp.rx_time;
info->position.time = mp.rx_time;
}
if (mp.rx_snr) if (mp.rx_snr)
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node. info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.

View File

@@ -1,10 +1,10 @@
#include "PhoneAPI.h" #include "PhoneAPI.h"
#include "Channels.h"
#include "GPS.h" #include "GPS.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "RadioInterface.h" #include "RadioInterface.h"
#include "Channels.h"
#include <assert.h> #include <assert.h>
#if FromRadio_size > MAX_TO_FROM_RADIO_SIZE #if FromRadio_size > MAX_TO_FROM_RADIO_SIZE
@@ -17,66 +17,73 @@
PhoneAPI::PhoneAPI() {} PhoneAPI::PhoneAPI() {}
void PhoneAPI::init() PhoneAPI::~PhoneAPI()
{ {
observe(&service.fromNumChanged);
}
PhoneAPI::~PhoneAPI() {
close(); close();
} }
void PhoneAPI::close() { void PhoneAPI::handleStartConfig()
unobserve(); {
state = STATE_SEND_NOTHING; if (!isConnected()) {
bool oldConnected = isConnected; onConnectionChanged(true);
isConnected = false; observe(&service.fromNumChanged);
if(oldConnected != isConnected) }
onConnectionChanged(isConnected);
// even if we were already connected - restart our state machine
state = STATE_SEND_MY_INFO;
DEBUG_MSG("Reset nodeinfo read pointer\n");
nodeInfoForPhone = NULL; // Don't keep returning old nodeinfos
nodeDB.resetReadPointer(); // FIXME, this read pointer should be moved out of nodeDB and into this class - because
// this will break once we have multiple instances of PhoneAPI running independently
}
void PhoneAPI::close()
{
if (state != STATE_SEND_NOTHING) {
state = STATE_SEND_NOTHING;
unobserve();
releasePhonePacket(); // Don't leak phone packets on shutdown
onConnectionChanged(false);
}
} }
void PhoneAPI::checkConnectionTimeout() void PhoneAPI::checkConnectionTimeout()
{ {
if (isConnected) { if (isConnected()) {
bool newConnected = (millis() - lastContactMsec < getPref_phone_timeout_secs() * 1000L); bool newConnected = (millis() - lastContactMsec < getPref_phone_timeout_secs() * 1000L);
if (!newConnected) { if (!newConnected)
isConnected = false; close();
onConnectionChanged(isConnected);
}
} }
} }
/** /**
* Handle a ToRadio protobuf * Handle a ToRadio protobuf
*/ */
void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
{ {
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // As long as the phone keeps talking to us, don't let the radio go to sleep
lastContactMsec = millis(); lastContactMsec = millis();
if (!isConnected) {
isConnected = true;
onConnectionChanged(isConnected);
}
// return (lastContactMsec != 0) && // return (lastContactMsec != 0) &&
memset(&toRadioScratch, 0, sizeof(toRadioScratch)); memset(&toRadioScratch, 0, sizeof(toRadioScratch));
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) { if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
switch (toRadioScratch.which_payloadVariant) { switch (toRadioScratch.which_payloadVariant) {
case ToRadio_packet_tag: { case ToRadio_packet_tag:
MeshPacket &p = toRadioScratch.packet; return handleToRadioPacket(toRadioScratch.packet);
printPacket("PACKET FROM PHONE", &p);
service.handleToRadio(p);
break;
}
case ToRadio_want_config_id_tag: case ToRadio_want_config_id_tag:
config_nonce = toRadioScratch.want_config_id; config_nonce = toRadioScratch.want_config_id;
DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce); DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce);
state = STATE_SEND_MY_INFO;
DEBUG_MSG("Reset nodeinfo read pointer\n"); handleStartConfig();
nodeInfoForPhone = NULL; // Don't keep returning old nodeinfos break;
nodeDB.resetReadPointer(); // FIXME, this read pointer should be moved out of nodeDB and into this class - because
// this will break once we have multiple instances of PhoneAPI running independently case ToRadio_disconnect_tag:
close();
break; break;
default: default:
@@ -86,6 +93,8 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
} else { } else {
DEBUG_MSG("Error: ignoring malformed toradio\n"); DEBUG_MSG("Error: ignoring malformed toradio\n");
} }
return false;
} }
/** /**
@@ -120,14 +129,12 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
case STATE_SEND_MY_INFO: case STATE_SEND_MY_INFO:
// If the user has specified they don't want our node to share its location, make sure to tell the phone // If the user has specified they don't want our node to share its location, make sure to tell the phone
// app not to send locations on our behalf. // app not to send locations on our behalf.
myNodeInfo.has_gps = (radioConfig.preferences.location_share == LocationSharing_LocDisabled) myNodeInfo.has_gps = gps && gps->isConnected(); // Update with latest GPS connect info
? true
: (gps && gps->isConnected()); // Update with latest GPS connect info
fromRadioScratch.which_payloadVariant = FromRadio_my_info_tag; fromRadioScratch.which_payloadVariant = FromRadio_my_info_tag;
fromRadioScratch.my_info = myNodeInfo; fromRadioScratch.my_info = myNodeInfo;
state = STATE_SEND_NODEINFO; state = STATE_SEND_NODEINFO;
service.refreshMyNodeInfo(); // Update my NodeInfo because the client will be asking for it soon. service.refreshMyNodeInfo(); // Update my NodeInfo because the client will be asking for it soon.
break; break;
case STATE_SEND_NODEINFO: { case STATE_SEND_NODEINFO: {
@@ -135,7 +142,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
nodeInfoForPhone = NULL; // We just consumed a nodeinfo, will need a new one next time nodeInfoForPhone = NULL; // We just consumed a nodeinfo, will need a new one next time
if (info) { if (info) {
DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id, DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->last_heard, info->user.id,
info->user.long_name); info->user.long_name);
fromRadioScratch.which_payloadVariant = FromRadio_node_info_tag; fromRadioScratch.which_payloadVariant = FromRadio_node_info_tag;
fromRadioScratch.node_info = *info; fromRadioScratch.node_info = *info;
@@ -159,16 +166,13 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
case STATE_SEND_PACKETS: case STATE_SEND_PACKETS:
// Do we have a message from the mesh? // Do we have a message from the mesh?
if (packetForPhone) { if (packetForPhone) {
printPacket("phone downloaded packet", packetForPhone); printPacket("phone downloaded packet", packetForPhone);
// Encapsulate as a FromRadio packet // Encapsulate as a FromRadio packet
fromRadioScratch.which_payloadVariant = FromRadio_packet_tag; fromRadioScratch.which_payloadVariant = FromRadio_packet_tag;
fromRadioScratch.packet = *packetForPhone; fromRadioScratch.packet = *packetForPhone;
service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore
packetForPhone = NULL;
} }
releasePhonePacket();
break; break;
default: default:
@@ -187,6 +191,16 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
return 0; return 0;
} }
void PhoneAPI::handleDisconnect() {}
void PhoneAPI::releasePhonePacket()
{
if (packetForPhone) {
service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore
packetForPhone = NULL;
}
}
/** /**
* Return true if we have data available to send to the phone * Return true if we have data available to send to the phone
*/ */
@@ -226,7 +240,13 @@ bool PhoneAPI::available()
/** /**
* Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool * Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool
*/ */
void PhoneAPI::handleToRadioPacket(MeshPacket *p) {} bool PhoneAPI::handleToRadioPacket(MeshPacket &p)
{
printPacket("PACKET FROM PHONE", &p);
service.handleToRadio(p);
return true;
}
/// If the mesh service tells us fromNum has changed, tell the phone /// If the mesh service tells us fromNum has changed, tell the phone
int PhoneAPI::onNotify(uint32_t newValue) int PhoneAPI::onNotify(uint32_t newValue)

View File

@@ -22,6 +22,7 @@ class PhoneAPI
enum State { enum State {
STATE_UNUSED, // (no longer used) old default state - until Android apps are all updated, uses the old BLE API STATE_UNUSED, // (no longer used) old default state - until Android apps are all updated, uses the old BLE API
STATE_SEND_NOTHING, // (Eventual) Initial state, don't send anything until the client starts asking for config STATE_SEND_NOTHING, // (Eventual) Initial state, don't send anything until the client starts asking for config
// (disconnected)
STATE_SEND_MY_INFO, // send our my info record STATE_SEND_MY_INFO, // send our my info record
// STATE_SEND_RADIO, // in 1.2 we now send this as a regular mesh packet // STATE_SEND_RADIO, // in 1.2 we now send this as a regular mesh packet
// STATE_SEND_OWNER, no need to send Owner specially, it is just part of the nodedb // STATE_SEND_OWNER, no need to send Owner specially, it is just part of the nodedb
@@ -58,17 +59,15 @@ class PhoneAPI
/// Destructor - calls close() /// Destructor - calls close()
virtual ~PhoneAPI(); virtual ~PhoneAPI();
/// Do late init that can't happen at constructor time
virtual void init();
// Call this when the client drops the connection, resets the state to STATE_SEND_NOTHING // Call this when the client drops the connection, resets the state to STATE_SEND_NOTHING
// Unregisters our observer. A closed connection **can** be reopened by calling init again. // Unregisters our observer. A closed connection **can** be reopened by calling init again.
virtual void close(); virtual void close();
/** /**
* Handle a ToRadio protobuf * Handle a ToRadio protobuf
* @return true true if a packet was queued for sending (so that caller can yield)
*/ */
virtual void handleToRadio(const uint8_t *buf, size_t len); virtual bool handleToRadio(const uint8_t *buf, size_t len);
/** /**
* Get the next packet we want to send to the phone * Get the next packet we want to send to the phone
@@ -83,17 +82,16 @@ class PhoneAPI
*/ */
bool available(); bool available();
protected: bool isConnected() { return state != STATE_SEND_NOTHING; }
/// Are we currently connected to a client?
bool isConnected = false;
protected:
/// Our fromradio packet while it is being assembled /// Our fromradio packet while it is being assembled
FromRadio fromRadioScratch; FromRadio fromRadioScratch;
/// Hookable to find out when connection changes /// Hookable to find out when connection changes
virtual void onConnectionChanged(bool connected) {} virtual void onConnectionChanged(bool connected) {}
/// If we haven't heard from the other side in a while then say not connected /// If we haven't heard from the other side in a while then say not connected
void checkConnectionTimeout(); void checkConnectionTimeout();
/** /**
@@ -101,11 +99,22 @@ class PhoneAPI
*/ */
virtual void onNowHasData(uint32_t fromRadioNum) {} virtual void onNowHasData(uint32_t fromRadioNum) {}
private:
/** /**
* Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool * Subclasses can use this to find out when a client drops the link
*/ */
void handleToRadioPacket(MeshPacket *p); virtual void handleDisconnect();
private:
void releasePhonePacket();
/// begin a new connection
void handleStartConfig();
/**
* Handle a packet that the phone wants us to send. We can write to it but can not keep a reference to it
* @return true true if a packet was queued for sending
*/
bool handleToRadioPacket(MeshPacket &p);
/// If the mesh service tells us fromNum has changed, tell the phone /// If the mesh service tells us fromNum has changed, tell the phone
virtual int onNotify(uint32_t newValue); virtual int onNotify(uint32_t newValue);

View File

@@ -5,48 +5,62 @@
#define START2 0xc3 #define START2 0xc3
#define HEADER_LEN 4 #define HEADER_LEN 4
void StreamAPI::loop() int32_t StreamAPI::runOnce()
{ {
auto result = readStream();
writeStream(); writeStream();
readStream();
checkConnectionTimeout(); checkConnectionTimeout();
return result;
} }
/** /**
* Read any rx chars from the link and call handleToRadio * Read any rx chars from the link and call handleToRadio
*/ */
void StreamAPI::readStream() int32_t StreamAPI::readStream()
{ {
while (stream->available()) { // Currently we never want to block uint32_t now = millis();
uint8_t c = stream->read(); if (!stream->available()) {
// Nothing available this time, if the computer has talked to us recently, poll often, otherwise let CPU sleep a long time
bool recentRx = (now - lastRxMsec) < 2000;
return recentRx ? 5 : 250;
} else {
while (stream->available()) { // Currently we never want to block
uint8_t c = stream->read();
// Use the read pointer for a little state machine, first look for framing, then length bytes, then payload // Use the read pointer for a little state machine, first look for framing, then length bytes, then payload
size_t ptr = rxPtr++; // assume we will probably advance the rxPtr size_t ptr = rxPtr++; // assume we will probably advance the rxPtr
rxBuf[ptr] = c; // store all bytes (including framing) rxBuf[ptr] = c; // store all bytes (including framing)
if (ptr == 0) { // looking for START1 if (ptr == 0) { // looking for START1
if (c != START1) if (c != START1)
rxPtr = 0; // failed to find framing rxPtr = 0; // failed to find framing
} else if (ptr == 1) { // looking for START2 } else if (ptr == 1) { // looking for START2
if (c != START2) if (c != START2)
rxPtr = 0; // failed to find framing rxPtr = 0; // failed to find framing
} else if (ptr >= HEADER_LEN) { // we have at least read our 4 byte framing } else if (ptr >= HEADER_LEN) { // we have at least read our 4 byte framing
uint32_t len = (rxBuf[2] << 8) + rxBuf[3]; // big endian 16 bit length follows framing uint32_t len = (rxBuf[2] << 8) + rxBuf[3]; // big endian 16 bit length follows framing
if (ptr == HEADER_LEN) { if (ptr == HEADER_LEN) {
// we _just_ finished our 4 byte header, validate length now (note: a length of zero is a valid // we _just_ finished our 4 byte header, validate length now (note: a length of zero is a valid
// protobuf also) // protobuf also)
if (len > MAX_TO_FROM_RADIO_SIZE) if (len > MAX_TO_FROM_RADIO_SIZE)
rxPtr = 0; // length is bogus, restart search for framing rxPtr = 0; // length is bogus, restart search for framing
} }
if (rxPtr != 0 && ptr + 1 == len + HEADER_LEN) { if (rxPtr != 0 && ptr + 1 == len + HEADER_LEN) {
// If we didn't just fail the packet and we now have the right # of bytes, parse it rxPtr = 0; // start over again on the next packet
handleToRadio(rxBuf + HEADER_LEN, len);
rxPtr = 0; // start over again // If we didn't just fail the packet and we now have the right # of bytes, parse it
if (handleToRadio(rxBuf + HEADER_LEN, len))
return 0; // we want to be called again ASAP because we still have more work to do
}
} }
} }
// we had packets available this time, so assume we might have them next time also
lastRxMsec = now;
return 0;
} }
} }
@@ -71,7 +85,7 @@ void StreamAPI::writeStream()
void StreamAPI::emitTxBuffer(size_t len) void StreamAPI::emitTxBuffer(size_t len)
{ {
if (len != 0) { if (len != 0) {
DEBUG_MSG("emit tx %d\n", len); // DEBUG_MSG("emit tx %d\n", len);
txBuf[0] = START1; txBuf[0] = START1;
txBuf[1] = START2; txBuf[1] = START2;
txBuf[2] = (len >> 8) & 0xff; txBuf[2] = (len >> 8) & 0xff;
@@ -93,6 +107,6 @@ void StreamAPI::emitRebooted()
fromRadioScratch.which_payloadVariant = FromRadio_rebooted_tag; fromRadioScratch.which_payloadVariant = FromRadio_rebooted_tag;
fromRadioScratch.rebooted = true; fromRadioScratch.rebooted = true;
DEBUG_MSG("Emitting reboot packet for serial shell\n"); // DEBUG_MSG("Emitting reboot packet for serial shell\n");
emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, FromRadio_size, FromRadio_fields, &fromRadioScratch)); emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, FromRadio_size, FromRadio_fields, &fromRadioScratch));
} }

View File

@@ -2,6 +2,7 @@
#include "PhoneAPI.h" #include "PhoneAPI.h"
#include "Stream.h" #include "Stream.h"
#include "concurrency/OSThread.h"
// A To/FromRadio packet + our 32 bit header // A To/FromRadio packet + our 32 bit header
#define MAX_STREAM_BUF_SIZE (MAX_TO_FROM_RADIO_SIZE + sizeof(uint32_t)) #define MAX_STREAM_BUF_SIZE (MAX_TO_FROM_RADIO_SIZE + sizeof(uint32_t))
@@ -27,7 +28,7 @@ valid utf8 encoding. This makes it a bit easier to start a device outputting reg
after it has received a valid packet from the PC, turn off unencoded debug printing and switch to this packet encoding. after it has received a valid packet from the PC, turn off unencoded debug printing and switch to this packet encoding.
*/ */
class StreamAPI : public PhoneAPI class StreamAPI : public PhoneAPI, protected concurrency::OSThread
{ {
/** /**
* The stream we read/write from * The stream we read/write from
@@ -37,21 +38,23 @@ class StreamAPI : public PhoneAPI
uint8_t rxBuf[MAX_STREAM_BUF_SIZE]; uint8_t rxBuf[MAX_STREAM_BUF_SIZE];
size_t rxPtr = 0; size_t rxPtr = 0;
/// time of last rx, used, to slow down our polling if we haven't heard from anyone
uint32_t lastRxMsec = 0;
public: public:
StreamAPI(Stream *_stream) : stream(_stream) {} StreamAPI(Stream *_stream) : concurrency::OSThread("StreamAPI"), stream(_stream) {}
/** /**
* Currently we require frequent invocation from loop() to check for arrived serial packets and to send new packets to the * Currently we require frequent invocation from loop() to check for arrived serial packets and to send new packets to the
* phone. * phone.
* FIXME, to support better power behavior instead move to a thread and block on serial reads.
*/ */
void loop(); virtual int32_t runOnce();
private: private:
/** /**
* Read any rx chars from the link and call handleToRadio * Read any rx chars from the link and call handleToRadio
*/ */
void readStream(); int32_t readStream();
/** /**
* call getFromRadio() and deliver encapsulated packets to the Stream * call getFromRadio() and deliver encapsulated packets to the Stream
@@ -63,7 +66,7 @@ class StreamAPI : public PhoneAPI
* Send a FromRadio.rebooted = true packet to the phone * Send a FromRadio.rebooted = true packet to the phone
*/ */
void emitRebooted(); void emitRebooted();
/** /**
* Send the current txBuffer over our stream * Send the current txBuffer over our stream
*/ */

View File

@@ -26,6 +26,7 @@ typedef struct _AdminMessage {
bool confirm_set_channel; bool confirm_set_channel;
bool confirm_set_radio; bool confirm_set_radio;
bool exit_simulator; bool exit_simulator;
int32_t reboot_seconds;
}; };
} AdminMessage; } AdminMessage;
@@ -49,6 +50,7 @@ extern "C" {
#define AdminMessage_confirm_set_channel_tag 32 #define AdminMessage_confirm_set_channel_tag 32
#define AdminMessage_confirm_set_radio_tag 33 #define AdminMessage_confirm_set_radio_tag 33
#define AdminMessage_exit_simulator_tag 34 #define AdminMessage_exit_simulator_tag 34
#define AdminMessage_reboot_seconds_tag 35
/* Struct field encoding specification for nanopb */ /* Struct field encoding specification for nanopb */
#define AdminMessage_FIELDLIST(X, a) \ #define AdminMessage_FIELDLIST(X, a) \
@@ -61,7 +63,8 @@ X(a, STATIC, ONEOF, UINT32, (variant,get_channel_request,get_channel_requ
X(a, STATIC, ONEOF, MESSAGE, (variant,get_channel_response,get_channel_response), 7) \ X(a, STATIC, ONEOF, MESSAGE, (variant,get_channel_response,get_channel_response), 7) \
X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_channel,confirm_set_channel), 32) \ X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_channel,confirm_set_channel), 32) \
X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_radio,confirm_set_radio), 33) \ X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_radio,confirm_set_radio), 33) \
X(a, STATIC, ONEOF, BOOL, (variant,exit_simulator,exit_simulator), 34) X(a, STATIC, ONEOF, BOOL, (variant,exit_simulator,exit_simulator), 34) \
X(a, STATIC, ONEOF, INT32, (variant,reboot_seconds,reboot_seconds), 35)
#define AdminMessage_CALLBACK NULL #define AdminMessage_CALLBACK NULL
#define AdminMessage_DEFAULT NULL #define AdminMessage_DEFAULT NULL
#define AdminMessage_variant_set_radio_MSGTYPE RadioConfig #define AdminMessage_variant_set_radio_MSGTYPE RadioConfig

View File

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

View File

@@ -110,6 +110,7 @@ typedef struct _MyNodeInfo {
CriticalErrorCode error_code; CriticalErrorCode error_code;
uint32_t error_address; uint32_t error_address;
uint32_t error_count; uint32_t error_count;
uint32_t reboot_count;
uint32_t message_timeout_msec; uint32_t message_timeout_msec;
uint32_t min_app_version; uint32_t min_app_version;
uint32_t max_channels; uint32_t max_channels;
@@ -161,6 +162,7 @@ typedef struct _NodeInfo {
User user; User user;
bool has_position; bool has_position;
Position position; Position position;
uint32_t last_heard;
float snr; float snr;
} NodeInfo; } NodeInfo;
@@ -191,6 +193,7 @@ typedef struct _ToRadio {
union { union {
MeshPacket packet; MeshPacket packet;
uint32_t want_config_id; uint32_t want_config_id;
bool disconnect;
}; };
} ToRadio; } ToRadio;
@@ -232,8 +235,8 @@ extern "C" {
#define Routing_init_default {0, {RouteDiscovery_init_default}} #define Routing_init_default {0, {RouteDiscovery_init_default}}
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0} #define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0} #define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0} #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0} #define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0}
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN} #define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
#define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}} #define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}}
#define ToRadio_init_default {0, {MeshPacket_init_default}} #define ToRadio_init_default {0, {MeshPacket_init_default}}
@@ -243,8 +246,8 @@ extern "C" {
#define Routing_init_zero {0, {RouteDiscovery_init_zero}} #define Routing_init_zero {0, {RouteDiscovery_init_zero}}
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0} #define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0} #define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0} #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0} #define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0}
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN} #define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
#define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}} #define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}}
#define ToRadio_init_zero {0, {MeshPacket_init_zero}} #define ToRadio_init_zero {0, {MeshPacket_init_zero}}
@@ -269,6 +272,7 @@ extern "C" {
#define MyNodeInfo_error_code_tag 7 #define MyNodeInfo_error_code_tag 7
#define MyNodeInfo_error_address_tag 8 #define MyNodeInfo_error_address_tag 8
#define MyNodeInfo_error_count_tag 9 #define MyNodeInfo_error_count_tag 9
#define MyNodeInfo_reboot_count_tag 10
#define MyNodeInfo_message_timeout_msec_tag 13 #define MyNodeInfo_message_timeout_msec_tag 13
#define MyNodeInfo_min_app_version_tag 14 #define MyNodeInfo_min_app_version_tag 14
#define MyNodeInfo_max_channels_tag 15 #define MyNodeInfo_max_channels_tag 15
@@ -298,6 +302,7 @@ extern "C" {
#define NodeInfo_num_tag 1 #define NodeInfo_num_tag 1
#define NodeInfo_user_tag 2 #define NodeInfo_user_tag 2
#define NodeInfo_position_tag 3 #define NodeInfo_position_tag 3
#define NodeInfo_last_heard_tag 4
#define NodeInfo_snr_tag 7 #define NodeInfo_snr_tag 7
#define Routing_route_request_tag 1 #define Routing_route_request_tag 1
#define Routing_route_reply_tag 2 #define Routing_route_reply_tag 2
@@ -311,6 +316,7 @@ extern "C" {
#define FromRadio_packet_tag 11 #define FromRadio_packet_tag 11
#define ToRadio_packet_tag 2 #define ToRadio_packet_tag 2
#define ToRadio_want_config_id_tag 100 #define ToRadio_want_config_id_tag 100
#define ToRadio_disconnect_tag 104
/* Struct field encoding specification for nanopb */ /* Struct field encoding specification for nanopb */
#define Position_FIELDLIST(X, a) \ #define Position_FIELDLIST(X, a) \
@@ -376,6 +382,7 @@ X(a, STATIC, SINGULAR, INT32, rx_rssi, 13)
X(a, STATIC, SINGULAR, UINT32, num, 1) \ X(a, STATIC, SINGULAR, UINT32, num, 1) \
X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \ X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \
X(a, STATIC, OPTIONAL, MESSAGE, position, 3) \ X(a, STATIC, OPTIONAL, MESSAGE, position, 3) \
X(a, STATIC, SINGULAR, FIXED32, last_heard, 4) \
X(a, STATIC, SINGULAR, FLOAT, snr, 7) X(a, STATIC, SINGULAR, FLOAT, snr, 7)
#define NodeInfo_CALLBACK NULL #define NodeInfo_CALLBACK NULL
#define NodeInfo_DEFAULT NULL #define NodeInfo_DEFAULT NULL
@@ -392,6 +399,7 @@ X(a, STATIC, SINGULAR, STRING, firmware_version, 6) \
X(a, STATIC, SINGULAR, UENUM, error_code, 7) \ X(a, STATIC, SINGULAR, UENUM, error_code, 7) \
X(a, STATIC, SINGULAR, UINT32, error_address, 8) \ X(a, STATIC, SINGULAR, UINT32, error_address, 8) \
X(a, STATIC, SINGULAR, UINT32, error_count, 9) \ X(a, STATIC, SINGULAR, UINT32, error_count, 9) \
X(a, STATIC, SINGULAR, UINT32, reboot_count, 10) \
X(a, STATIC, SINGULAR, UINT32, message_timeout_msec, 13) \ X(a, STATIC, SINGULAR, UINT32, message_timeout_msec, 13) \
X(a, STATIC, SINGULAR, UINT32, min_app_version, 14) \ X(a, STATIC, SINGULAR, UINT32, min_app_version, 14) \
X(a, STATIC, SINGULAR, UINT32, max_channels, 15) X(a, STATIC, SINGULAR, UINT32, max_channels, 15)
@@ -423,7 +431,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 11)
#define ToRadio_FIELDLIST(X, a) \ #define ToRadio_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 2) \ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 2) \
X(a, STATIC, ONEOF, UINT32, (payloadVariant,want_config_id,want_config_id), 100) X(a, STATIC, ONEOF, UINT32, (payloadVariant,want_config_id,want_config_id), 100) \
X(a, STATIC, ONEOF, BOOL, (payloadVariant,disconnect,disconnect), 104)
#define ToRadio_CALLBACK NULL #define ToRadio_CALLBACK NULL
#define ToRadio_DEFAULT NULL #define ToRadio_DEFAULT NULL
#define ToRadio_payloadVariant_packet_MSGTYPE MeshPacket #define ToRadio_payloadVariant_packet_MSGTYPE MeshPacket
@@ -460,8 +469,8 @@ extern const pb_msgdesc_t ToRadio_msg;
#define Routing_size 42 #define Routing_size 42
#define Data_size 260 #define Data_size 260
#define MeshPacket_size 309 #define MeshPacket_size 309
#define NodeInfo_size 126 #define NodeInfo_size 131
#define MyNodeInfo_size 89 #define MyNodeInfo_size 95
#define LogRecord_size 81 #define LogRecord_size 81
#define FromRadio_size 318 #define FromRadio_size 318
#define ToRadio_size 312 #define ToRadio_size 312

View File

@@ -1,5 +1,6 @@
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "RadioLibInterface.h"
#include "airtime.h" #include "airtime.h"
#include "main.h" #include "main.h"
#include "mesh/http/ContentHelper.h" #include "mesh/http/ContentHelper.h"
@@ -10,8 +11,8 @@
#include <HTTPBodyParser.hpp> #include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp> #include <HTTPMultipartBodyParser.hpp>
#include <HTTPURLEncodedBodyParser.hpp> #include <HTTPURLEncodedBodyParser.hpp>
#include <Preferences.h>
#include <SPIFFS.h> #include <SPIFFS.h>
#include "RadioLibInterface.h"
#ifndef NO_ESP32 #ifndef NO_ESP32
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
@@ -77,7 +78,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
ResourceNode *nodeAPIv1ToRadio = new ResourceNode("/api/v1/toradio", "PUT", &handleAPIv1ToRadio); ResourceNode *nodeAPIv1ToRadio = new ResourceNode("/api/v1/toradio", "PUT", &handleAPIv1ToRadio);
ResourceNode *nodeAPIv1FromRadio = new ResourceNode("/api/v1/fromradio", "GET", &handleAPIv1FromRadio); ResourceNode *nodeAPIv1FromRadio = new ResourceNode("/api/v1/fromradio", "GET", &handleAPIv1FromRadio);
ResourceNode *nodeHotspot = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot); ResourceNode *nodeHotspotApple = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
ResourceNode *nodeHotspotAndroid = new ResourceNode("/generate_204", "GET", &handleHotspot);
ResourceNode *nodeFavicon = new ResourceNode("/favicon.ico", "GET", &handleFavicon); ResourceNode *nodeFavicon = new ResourceNode("/favicon.ico", "GET", &handleFavicon);
ResourceNode *nodeRoot = new ResourceNode("/", "GET", &handleRoot); ResourceNode *nodeRoot = new ResourceNode("/", "GET", &handleRoot);
ResourceNode *nodeStaticBrowse = new ResourceNode("/static", "GET", &handleStaticBrowse); ResourceNode *nodeStaticBrowse = new ResourceNode("/static", "GET", &handleStaticBrowse);
@@ -96,7 +98,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
secureServer->registerNode(nodeAPIv1ToRadioOptions); secureServer->registerNode(nodeAPIv1ToRadioOptions);
secureServer->registerNode(nodeAPIv1ToRadio); secureServer->registerNode(nodeAPIv1ToRadio);
secureServer->registerNode(nodeAPIv1FromRadio); secureServer->registerNode(nodeAPIv1FromRadio);
secureServer->registerNode(nodeHotspot); secureServer->registerNode(nodeHotspotApple);
secureServer->registerNode(nodeHotspotAndroid);
secureServer->registerNode(nodeFavicon); secureServer->registerNode(nodeFavicon);
secureServer->registerNode(nodeRoot); secureServer->registerNode(nodeRoot);
secureServer->registerNode(nodeStaticBrowse); secureServer->registerNode(nodeStaticBrowse);
@@ -117,7 +120,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
insecureServer->registerNode(nodeAPIv1ToRadioOptions); insecureServer->registerNode(nodeAPIv1ToRadioOptions);
insecureServer->registerNode(nodeAPIv1ToRadio); insecureServer->registerNode(nodeAPIv1ToRadio);
insecureServer->registerNode(nodeAPIv1FromRadio); insecureServer->registerNode(nodeAPIv1FromRadio);
insecureServer->registerNode(nodeHotspot); insecureServer->registerNode(nodeHotspotApple);
insecureServer->registerNode(nodeHotspotAndroid);
insecureServer->registerNode(nodeFavicon); insecureServer->registerNode(nodeFavicon);
insecureServer->registerNode(nodeRoot); insecureServer->registerNode(nodeRoot);
insecureServer->registerNode(nodeStaticBrowse); insecureServer->registerNode(nodeStaticBrowse);
@@ -833,6 +837,11 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
ResourceParameters *params = req->getParams(); ResourceParameters *params = req->getParams();
std::string content; std::string content;
Preferences preferences;
preferences.begin("meshtastic", false);
uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0);
if (!params->getQueryParameter("content", content)) { if (!params->getQueryParameter("content", content)) {
content = "json"; content = "json";
} }
@@ -931,6 +940,10 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->printf("\"is_charging\": %s\n", BoolToString(powerStatus->getIsCharging())); res->printf("\"is_charging\": %s\n", BoolToString(powerStatus->getIsCharging()));
res->println("},"); res->println("},");
res->println("\"device\": {");
res->printf("\"reboot_counter\": %d\n", myNodeInfo.reboot_count);
res->println("},");
res->println("\"radio\": {"); res->println("\"radio\": {");
res->printf("\"frequecy\": %f,\n", RadioLibInterface::instance->getFreq()); res->printf("\"frequecy\": %f,\n", RadioLibInterface::instance->getFreq());
res->printf("\"lora_channel\": %d\n", RadioLibInterface::instance->getChannelNum()); res->printf("\"lora_channel\": %d\n", RadioLibInterface::instance->getChannelNum());
@@ -938,8 +951,6 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->println("},"); res->println("},");
res->println("\"status\": \"ok\""); res->println("\"status\": \"ok\"");
res->println("}"); res->println("}");
} }

View File

@@ -40,18 +40,20 @@ void WiFiServerAPI::onConnectionChanged(bool connected)
} }
/// override close to also shutdown the TCP link /// override close to also shutdown the TCP link
void WiFiServerAPI::close() { void WiFiServerAPI::close()
{
client.stop(); // drop tcp connection client.stop(); // drop tcp connection
StreamAPI::close(); StreamAPI::close();
} }
bool WiFiServerAPI::loop() int32_t WiFiServerAPI::runOnce()
{ {
if (client.connected()) { if (client.connected()) {
StreamAPI::loop(); return StreamAPI::runOnce();
return true;
} else { } else {
return false; DEBUG_MSG("Client dropped connection, suspending API service\n");
enabled = false; // we no longer need to run
return 0;
} }
} }
@@ -78,18 +80,5 @@ int32_t WiFiServerPort::runOnce()
openAPI = new WiFiServerAPI(client); openAPI = new WiFiServerAPI(client);
} }
if (openAPI) { return 100; // only check occasionally for incoming connections
// Allow idle processing so the API can read from its incoming stream
if(!openAPI->loop()) {
// If our API link was up, shut it down
DEBUG_MSG("Client dropped connection, closing API client\n");
// Note: we can't call delete here because this object includes other state
// besides the stream API. Instead kill it later when we start a new instance
delete openAPI;
openAPI = NULL;
}
return 0; // run fast while our API server is running
} else
return 100; // only check occasionally for incoming connections
} }

View File

@@ -1,7 +1,6 @@
#pragma once #pragma once
#include "StreamAPI.h" #include "StreamAPI.h"
#include "concurrency/OSThread.h"
#include <WiFi.h> #include <WiFi.h>
/** /**
@@ -18,15 +17,14 @@ class WiFiServerAPI : public StreamAPI
virtual ~WiFiServerAPI(); virtual ~WiFiServerAPI();
/// @return true if we want to keep running, or false if we are ready to be destroyed
virtual bool loop(); // Check for dropped client connections
/// override close to also shutdown the TCP link /// override close to also shutdown the TCP link
virtual void close(); virtual void close();
protected: protected:
/// Hookable to find out when connection changes /// Hookable to find out when connection changes
virtual void onConnectionChanged(bool connected); virtual void onConnectionChanged(bool connected);
virtual int32_t runOnce(); // Check for dropped client connections
}; };
/** /**

View File

@@ -487,7 +487,6 @@ void reinitBluetooth()
DEBUG_MSG("Starting bluetooth\n"); DEBUG_MSG("Starting bluetooth\n");
if (isFirstTime) { if (isFirstTime) {
bluetoothPhoneAPI = new BluetoothPhoneAPI(); bluetoothPhoneAPI = new BluetoothPhoneAPI();
bluetoothPhoneAPI->init();
} }
// FIXME - if waking from light sleep, only esp_nimble_hci_init? // FIXME - if waking from light sleep, only esp_nimble_hci_init?

View File

@@ -2,9 +2,9 @@
#include "BluetoothCommon.h" #include "BluetoothCommon.h"
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
#include <bluefruit.h>
#include "mesh/mesh-pb-constants.h"
#include "mesh/PhoneAPI.h" #include "mesh/PhoneAPI.h"
#include "mesh/mesh-pb-constants.h"
#include <bluefruit.h>
static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_UUID_16)); static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_UUID_16));
static BLECharacteristic fromNum = BLECharacteristic(BLEUuid(FROMNUM_UUID_16)); static BLECharacteristic fromNum = BLECharacteristic(BLEUuid(FROMNUM_UUID_16));
@@ -155,7 +155,6 @@ void fromNumAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt
void setupMeshService(void) void setupMeshService(void)
{ {
bluetoothPhoneAPI = new BluetoothPhoneAPI(); bluetoothPhoneAPI = new BluetoothPhoneAPI();
bluetoothPhoneAPI->init();
meshBleService.begin(); meshBleService.begin();

View File

@@ -77,6 +77,13 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessag
handleGetRadio(mp); handleGetRadio(mp);
break; break;
case AdminMessage_reboot_seconds_tag: {
int32_t s = r->reboot_seconds;
DEBUG_MSG("Rebooting in %d seconds\n", s);
rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000);
break;
}
#ifdef PORTDUINO #ifdef PORTDUINO
case AdminMessage_exit_simulator_tag: case AdminMessage_exit_simulator_tag:
DEBUG_MSG("Exiting simulator\n"); DEBUG_MSG("Exiting simulator\n");

10
src/plugins/PluginDev.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
/*
* To developers:
* Use this to enable / disable features in your plugin that you don't want to risk checking into GitHub.
*
*/
// Enable development more for StoreForwardPlugin
bool StoreForward_Dev = false;

View File

@@ -10,10 +10,8 @@ PositionPlugin *positionPlugin;
PositionPlugin::PositionPlugin() PositionPlugin::PositionPlugin()
: ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields), concurrency::OSThread("PositionPlugin") : ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields), concurrency::OSThread("PositionPlugin")
{ {
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
setIntervalFromNow(60 * setIntervalFromNow(60 * 1000); // Send our initial position 60 seconds after we start (to give GPS time to setup)
1000); // Send our initial position 60 seconds after we start (to give GPS time to setup)
} }
bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position *pptr) bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position *pptr)

View File

@@ -255,7 +255,9 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
return 0; return 0;
} }
if (fileToWrite.println("time,from,sender name,sender lat,sender long,rx lat,rx long,rx snr,distance,payload")) { // 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")) {
DEBUG_MSG("File was written\n"); DEBUG_MSG("File was written\n");
} else { } else {
DEBUG_MSG("File write failed\n"); DEBUG_MSG("File write failed\n");
@@ -296,7 +298,9 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long
fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat
fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
fileToAppend.printf("%f,", mp.rx_snr); // RX SNR fileToAppend.printf("%d,", gpsStatus->getAltitude()); // RX Altitude
fileToAppend.printf("%f,", mp.rx_snr); // RX SNR
if (n->position.latitude_i && n->position.longitude_i && gpsStatus->getLatitude() && gpsStatus->getLongitude()) { if (n->position.latitude_i && n->position.longitude_i && gpsStatus->getLatitude() && gpsStatus->getLongitude()) {
float distance = latLongToMeter(n->position.latitude_i * 1e-7, n->position.longitude_i * 1e-7, float distance = latLongToMeter(n->position.latitude_i * 1e-7, n->position.longitude_i * 1e-7,

View File

@@ -5,11 +5,13 @@
#include "Router.h" #include "Router.h"
#include "configuration.h" #include "configuration.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include "plugins/PluginDev.h"
#include <Arduino.h> #include <Arduino.h>
#include <map> #include <map>
#define STOREFORWARD_MAX_PACKETS 0 #define STOREFORWARD_MAX_PACKETS 0
#define STOREFORWARD_SEND_HISTORY_SHORT 600 #define STOREFORWARD_SEND_HISTORY_PERIOD 10 * 60
#define STOREFORWARD_SEND_HISTORY_MAX 0
StoreForwardPlugin *storeForwardPlugin; StoreForwardPlugin *storeForwardPlugin;
@@ -22,9 +24,8 @@ int32_t StoreForwardPlugin::runOnce()
if (radioConfig.preferences.is_router) { if (radioConfig.preferences.is_router) {
// Maybe some cleanup functions? // Maybe some cleanup functions?
this->sawNodeReport();
this->historyReport(); this->historyReport();
return (10 * 1000); return (60 * 1000);
} else { } else {
/* /*
* If the plugin is turned on and is_router is not enabled, then we'll send a heartbeat every * If the plugin is turned on and is_router is not enabled, then we'll send a heartbeat every
@@ -64,14 +65,12 @@ void StoreForwardPlugin::populatePSRAM()
DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize()); DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize());
DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram()); DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram());
// PacketHistoryStruct *packetHistory = (PacketHistoryStruct *)ps_calloc(STOREFORWARD_MAX_PACKETS,
// sizeof(PacketHistoryStruct));
// Use a maximum of half the available PSRAM unless otherwise specified. // Use a maximum of half the available PSRAM unless otherwise specified.
uint32_t numberOfPackets = uint32_t numberOfPackets =
STOREFORWARD_MAX_PACKETS ? STOREFORWARD_MAX_PACKETS : ((ESP.getPsramSize() / 2) / sizeof(PacketHistoryStruct)); STOREFORWARD_MAX_PACKETS ? STOREFORWARD_MAX_PACKETS : ((ESP.getPsramSize() / 2) / sizeof(PacketHistoryStruct));
this->packetHistory = (PacketHistoryStruct *)ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)); // this->packetHistory = (PacketHistoryStruct *)ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct));
this->packetHistory = static_cast<PacketHistoryStruct *>(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
DEBUG_MSG("After PSRAM initilization:\n"); DEBUG_MSG("After PSRAM initilization:\n");
DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize()); DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize());
@@ -83,41 +82,17 @@ void StoreForwardPlugin::populatePSRAM()
} }
// We saw a node. // We saw a node.
uint32_t StoreForwardPlugin::sawNode(uint32_t 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
TODO: Move receivedRecord into the PSRAM // messages
if (sawSecAgo > STOREFORWARD_SEND_HISTORY_PERIOD) {
TODO: Gracefully handle the case where we run out of records. // Node has been away for a while.
Maybe replace the oldest record that hasn't been seen in a while and assume they won't be back. storeForwardPlugin->historySend(STOREFORWARD_SEND_HISTORY_PERIOD, whoWeSaw);
TODO: Implment this as a std::map for quicker lookups (maybe it doesn't matter?).
*/
// DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff,
// p->want_ack, p->hop_limit);
DEBUG_MSG("looking for node - from-0x%08x\n", node);
for (int i = 0; i < 50; i++) {
// DEBUG_MSG("Iterating through the seen nodes - %u %u %u\n", i, receivedRecord[i][0], receivedRecord[i][1]);
// First time seeing that node.
if (receivedRecord[i][0] == 0) {
// DEBUG_MSG("New node! Woohoo! Win!\n");
receivedRecord[i][0] = node;
receivedRecord[i][1] = millis();
return receivedRecord[i][1];
}
// We've seen this node before.
if (receivedRecord[i][0] == node) {
// DEBUG_MSG("We've seen this node before\n");
uint32_t lastSaw = receivedRecord[i][1];
receivedRecord[i][1] = millis();
return lastSaw;
} }
} }
return 0;
} }
void StoreForwardPlugin::historyReport() void StoreForwardPlugin::historyReport()
@@ -132,8 +107,15 @@ void StoreForwardPlugin::historyReport()
} }
DEBUG_MSG("StoreForwardPlugin::historyReport runtime - %u ms\n", millis() - startTimer); DEBUG_MSG("StoreForwardPlugin::historyReport runtime - %u ms\n", millis() - startTimer);
} }
/*
*
*/
void StoreForwardPlugin::historySend(uint32_t msAgo, uint32_t to) void StoreForwardPlugin::historySend(uint32_t msAgo, uint32_t to)
{ {
// Send "Welcome back"
this->sendPayloadWelcome(to, false);
for (int i = 0; i < this->packetHistoryCurrent; i++) { for (int i = 0; i < this->packetHistoryCurrent; i++) {
if (this->packetHistory[i].time) { if (this->packetHistory[i].time) {
// DEBUG_MSG("... time-%u to-0x%08x\n", this->packetHistory[i].time, this->packetHistory[i].to & 0xffffffff); // DEBUG_MSG("... time-%u to-0x%08x\n", this->packetHistory[i].time, this->packetHistory[i].to & 0xffffffff);
@@ -159,27 +141,6 @@ void StoreForwardPlugin::historyAdd(const MeshPacket *mp)
this->packetHistoryCurrent++; this->packetHistoryCurrent++;
} }
// We saw a node.
void StoreForwardPlugin::sawNodeReport()
{
/*
TODO: Move receivedRecord into the PSRAM
TODO: Gracefully handle the case where we run out of records.
Maybe replace the oldest record that hasn't been seen in a while and assume they won't be back.
TODO: Implment this as a std::map for quicker lookups (maybe it doesn't matter?).
*/
DEBUG_MSG("Iterating through the seen nodes in receivedRecord...\n");
for (int i = 0; i < 50; i++) {
if (receivedRecord[i][1]) {
DEBUG_MSG("... record-%u from-0x%08x secAgo-%u\n", i, receivedRecord[i][0], (millis() - receivedRecord[i][1]) / 1000);
}
}
}
MeshPacket *StoreForwardPlugin::allocReply() MeshPacket *StoreForwardPlugin::allocReply()
{ {
auto reply = allocDataPacket(); // Allocate a packet for sending auto reply = allocDataPacket(); // Allocate a packet for sending
@@ -194,8 +155,7 @@ void StoreForwardPlugin::sendPayload(NodeNum dest, bool wantReplies)
p->decoded.want_response = wantReplies; p->decoded.want_response = wantReplies;
p->want_ack = true; p->want_ack = true;
/*
*/
static char heartbeatString[20]; static char heartbeatString[20];
snprintf(heartbeatString, sizeof(heartbeatString), "1"); snprintf(heartbeatString, sizeof(heartbeatString), "1");
@@ -205,60 +165,50 @@ void StoreForwardPlugin::sendPayload(NodeNum dest, bool wantReplies)
service.sendToMesh(p); service.sendToMesh(p);
} }
void StoreForwardPlugin::sendPayloadWelcome(NodeNum dest, bool wantReplies)
{
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;
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);
service.sendToMesh(p);
}
bool StoreForwardPlugin::handleReceived(const MeshPacket &mp) bool StoreForwardPlugin::handleReceived(const MeshPacket &mp)
{ {
#ifndef NO_ESP32 #ifndef NO_ESP32
if (radioConfig.preferences.store_forward_plugin_enabled) { if (radioConfig.preferences.store_forward_plugin_enabled) {
if (getFrom(&mp) != nodeDB.getNodeNum()) { if (getFrom(&mp) != nodeDB.getNodeNum()) {
// DEBUG_MSG("Store & Forward Plugin -- Print Start ---------- ---------- ---------- ---------- ----------\n\n\n");
// DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff,
// p->want_ack, p->hop_limit);
printPacket("----- PACKET FROM RADIO -----", &mp); printPacket("----- PACKET FROM RADIO -----", &mp);
uint32_t sawTime = storeForwardPlugin->sawNode(getFrom(&mp) & 0xffffffff); // 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("We last saw this node (%u), %u sec ago\n", mp.from & 0xffffffff, (millis() - sawTime) / 1000);
DEBUG_MSG(" -------------- "); DEBUG_MSG(" -------------- ");
if (mp.decoded.portnum == PortNum_UNKNOWN_APP) { if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) {
DEBUG_MSG("Packet came from - PortNum_UNKNOWN_APP\n");
} else if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) {
DEBUG_MSG("Packet came from - PortNum_TEXT_MESSAGE_APP\n"); DEBUG_MSG("Packet came from - PortNum_TEXT_MESSAGE_APP\n");
storeForwardPlugin->historyAdd(&mp); storeForwardPlugin->historyAdd(&mp);
} else if (mp.decoded.portnum == PortNum_REMOTE_HARDWARE_APP) {
DEBUG_MSG("Packet came from - PortNum_REMOTE_HARDWARE_APP\n");
} else if (mp.decoded.portnum == PortNum_POSITION_APP) {
DEBUG_MSG("Packet came from - PortNum_POSITION_APP\n");
} else if (mp.decoded.portnum == PortNum_NODEINFO_APP) {
DEBUG_MSG("Packet came from - PortNum_NODEINFO_APP\n");
} else if (mp.decoded.portnum == PortNum_ROUTING_APP) {
DEBUG_MSG("Packet came from - PortNum_ROUTING_APP\n");
} else if (mp.decoded.portnum == PortNum_ADMIN_APP) {
DEBUG_MSG("Packet came from - PortNum_ADMIN_APP\n");
} else if (mp.decoded.portnum == PortNum_REPLY_APP) {
DEBUG_MSG("Packet came from - PortNum_REPLY_APP\n");
} else if (mp.decoded.portnum == PortNum_IP_TUNNEL_APP) {
DEBUG_MSG("Packet came from - PortNum_IP_TUNNEL_APP\n");
} else if (mp.decoded.portnum == PortNum_SERIAL_APP) {
DEBUG_MSG("Packet came from - PortNum_SERIAL_APP\n");
} else if (mp.decoded.portnum == PortNum_STORE_FORWARD_APP) {
DEBUG_MSG("Packet came from - PortNum_STORE_FORWARD_APP\n");
} else if (mp.decoded.portnum == PortNum_RANGE_TEST_APP) {
DEBUG_MSG("Packet came from - PortNum_RANGE_TEST_APP\n");
} else if (mp.decoded.portnum == PortNum_PRIVATE_APP) {
DEBUG_MSG("Packet came from - PortNum_PRIVATE_APP\n");
} else if (mp.decoded.portnum == PortNum_RANGE_TEST_APP) {
DEBUG_MSG("Packet came from - PortNum_RANGE_TEST_APP\n");
} else if (mp.decoded.portnum == PortNum_ATAK_FORWARDER) {
DEBUG_MSG("Packet came from - PortNum_ATAK_FORWARDER\n");
} else { } else {
DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum); DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum);
} }
if ((millis() - sawTime) > STOREFORWARD_SEND_HISTORY_SHORT) {
// Node has been away for a while.
storeForwardPlugin->historySend(sawTime, mp.from);
}
} }
} else { } else {
@@ -276,14 +226,18 @@ StoreForwardPlugin::StoreForwardPlugin()
#ifndef NO_ESP32 #ifndef NO_ESP32
isPromiscuous = true; // Brown chicken brown cow
/* /*
Uncomment the preferences below if you want to use the plugin Uncomment the preferences below if you want to use the plugin
without having to configure it from the PythonAPI or WebUI. without having to configure it from the PythonAPI or WebUI.
radioConfig.preferences.store_forward_plugin_enabled = 1;
radioConfig.preferences.is_router = 1;
*/ */
if (StoreForward_Dev) {
radioConfig.preferences.store_forward_plugin_enabled = 1;
radioConfig.preferences.is_router = 1;
}
if (radioConfig.preferences.store_forward_plugin_enabled) { if (radioConfig.preferences.store_forward_plugin_enabled) {
// Router // Router

View File

@@ -29,8 +29,7 @@ class StoreForwardPlugin : public SinglePortPlugin, private concurrency::OSThrea
Update our local reference of when we last saw that node. Update our local reference of when we last saw that node.
@return 0 if we have never seen that node before otherwise return the last time we saw the node. @return 0 if we have never seen that node before otherwise return the last time we saw the node.
*/ */
uint32_t sawNode(uint32_t); void sawNode(uint32_t whoWeSaw, uint32_t sawSecAgo);
void sawNodeReport();
void historyAdd(const MeshPacket *mp); void historyAdd(const MeshPacket *mp);
void historyReport(); void historyReport();
void historySend(uint32_t msAgo, uint32_t to); void historySend(uint32_t msAgo, uint32_t to);
@@ -40,6 +39,7 @@ class StoreForwardPlugin : public SinglePortPlugin, private concurrency::OSThrea
* Send our payload into the mesh * Send our payload into the mesh
*/ */
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
void sendPayloadWelcome(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
virtual MeshPacket *allocReply(); virtual MeshPacket *allocReply();
virtual bool wantPortnum(PortNum p) { return true; }; virtual bool wantPortnum(PortNum p) { return true; };
@@ -80,4 +80,3 @@ class StoreForwardPluginRadio : public SinglePortPlugin
extern StoreForwardPluginRadio *storeForwardPluginRadio; extern StoreForwardPluginRadio *storeForwardPluginRadio;
*/ */

View File

@@ -1,4 +1,4 @@
[VERSION] [VERSION]
major = 1 major = 1
minor = 2 minor = 2
build = 13 build = 16