Merge pull request #761 from geeksville/dev1.2

Dev1.2
This commit is contained in:
Kevin Hester 2021-03-27 17:19:50 +08:00 committed by GitHub
commit bfd147062f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 306 additions and 218 deletions

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

@ -1 +1 @@
Subproject commit 820fa497dfde07e129cad6955bf2f4b2b9cecebc Subproject commit 0ea232802651fd6aaa53c93c09f4c2eb36470dd0

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

@ -97,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

@ -419,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;
@ -454,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;
@ -504,11 +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->last_heard = mp.rx_time;
info->has_position = true; // at least the time is valid
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 4926 #define DeviceState_size 5118
#define ChannelFile_size 832 #define ChannelFile_size 832
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -162,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;
@ -192,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;
@ -233,7 +235,7 @@ 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, 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}}
@ -244,7 +246,7 @@ 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, 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}}
@ -300,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
@ -313,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) \
@ -378,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
@ -426,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
@ -463,7 +469,7 @@ 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 95 #define MyNodeInfo_size 95
#define LogRecord_size 81 #define LogRecord_size 81
#define FromRadio_size 318 #define FromRadio_size 318

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");

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

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