Merge pull request #82 from meshtastic/master

update from master
pull/773/head
Jm Casler 2021-03-29 17:04:57 -07:00 zatwierdzone przez GitHub
commit 7f7b07ce9d
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
36 zmienionych plików z 333 dodań i 229 usunięć

Wyświetl plik

@ -199,7 +199,7 @@ We'd love to have you join us on this merry little project. Please see our [deve
# Credits
This project is run by volunteers. Past contributors include:
This project is run by volunteers. We are a friendly group and welcome any contribution (code fixes, documentation, features, bug reports etc...). We try to be good about listing contributor names in release notes, but it has become unwieldy for the main-devs to keep updating the list below and we've neglected it too long. If you'd like your name included in this list please send a pull request to edit this README and simply add your line yourself. Thank you very much for your help!
- @astro-arphid: Added support for 433MHz radios in europe.
- @claesg: Various documentation fixes and 3D print enclosures

Wyświetl plik

@ -4,11 +4,20 @@ You probably don't care about this section - skip to the next one.
## before next release
* DONE naks are being dropped (though enqueuedLocal) sometimes before phone/PC gets them
* 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 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 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 move remote admin doc from forum into git
* DONE check crashlytics

2
proto

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

Wyświetl plik

@ -9,6 +9,23 @@
#include "sleep.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()
{
// 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);
screen->setOn(true);
screen->print("Using API...\n");
screen->print("Serial connected\n");
}
static void serialExit()
{
screen->print("Serial disconnected\n");
}
static void powerEnter()
{
screen->setOn(true);
setBluetoothEnable(true);
screen->print("Powered...\n");
if (!isPowered()) {
// If we got here, we are in the wrong state - we should be in powered, let that state ahndle things
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()
@ -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()
{
screen->onPress();
@ -164,26 +209,16 @@ State stateSDS(sdsEnter, NULL, NULL, "SDS");
State stateLS(lsEnter, lsIdle, lsExit, "LS");
State stateNB(nbEnter, NULL, NULL, "NB");
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 stateON(onEnter, NULL, NULL, "ON");
State statePOWER(powerEnter, NULL, powerExit, "POWER");
State stateON(onEnter, onIdle, NULL, "ON");
State statePOWER(powerEnter, powerIdle, powerExit, "POWER");
Fsm powerFSM(&stateBOOT);
void PowerFSM_setup()
{
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.
*/
bool hasPower = !isLowPower && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB());
bool hasPower = isPowered();
DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower);
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
}
// 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(&stateNB, &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(&statePOWER, &stateSERIAL, EVENT_SERIAL_CONNECTED, NULL, "serial API");
if (!isLowPower) {
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(&stateDARK, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
powerFSM.add_transition(&stateON, &statePOWER, EVENT_POWER_CONNECTED, NULL, "power connect");
}
// 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(&stateNB, &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(&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");

Wyświetl plik

@ -6,27 +6,29 @@
#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, ...)
{
va_list arg;
va_start(arg, format);
console.vprintf(format, arg);
console->vprintf(format, arg);
va_end(arg);
}
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
// 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);
StreamAPI::init();
emitRebooted();
}
@ -34,14 +36,14 @@ void SerialConsole::init()
* we override this to notice when we've received a protobuf over the serial
* 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
if (!radioConfig.preferences.debug_log_enabled)
setDestination(&noopPrint);
canWrite = true;
StreamAPI::handleToRadio(buf, len);
return StreamAPI::handleToRadio(buf, len);
}
/// Hookable to find out when connection changes

Wyświetl plik

@ -11,14 +11,11 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
public:
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
* 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)
{
@ -34,5 +31,6 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
// A simple wrapper to allow non class aware code write to the console
void consolePrintf(const char *format, ...);
void consoleInit();
extern SerialConsole console;
extern SerialConsole *console;

Wyświetl plik

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

Wyświetl plik

@ -1,5 +1,6 @@
#include "NotifiedWorkerThread.h"
#include "configuration.h"
#include "main.h"
#include <assert.h>
namespace concurrency
@ -28,6 +29,7 @@ IRAM_ATTR bool NotifiedWorkerThread::notifyCommon(uint32_t v, bool overwrite)
if (overwrite || notification == 0) {
enabled = true;
setInterval(0); // Run ASAP
runASAP = true;
notification = v;
if (debugNotification)

Wyświetl plik

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

Wyświetl plik

@ -16,7 +16,6 @@
int16_t updateResultHandle = -1;
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 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);
}
void bluetoothRebootCheck()
{
if (rebootAtMsec && millis() > rebootAtMsec) {
DEBUG_MSG("Rebooting for update\n");
ESP.restart();
}
}
/*
See bluetooth-api.md

Wyświetl plik

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

Wyświetl plik

@ -97,7 +97,6 @@ void esp32Loop()
{
esp_task_wdt_reset(); // service our app level watchdog
loopBLE();
bluetoothRebootCheck();
// for debug printing
// radio.radioIf.canSleep();

Wyświetl plik

@ -717,6 +717,7 @@ void Screen::handleSetOn(bool on)
dispdev.displayOn();
enabled = true;
setInterval(0); // Draw ASAP
runASAP = true;
} else {
DEBUG_MSG("Turning off screen\n");
dispdev.displayOff();
@ -1053,6 +1054,7 @@ void Screen::setFastFramerate()
targetFramerate = SCREEN_TRANSITION_FRAMERATE;
ui.setTargetFPS(targetFramerate);
setInterval(0); // redraw ASAP
runASAP = true;
}
void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)

Wyświetl plik

@ -177,6 +177,7 @@ class ButtonThread : public OSThread
OneButton userButtonAlt;
#endif
static bool shutdown_on_long_stop;
public:
static uint32_t longPressTime;
@ -250,15 +251,15 @@ class ButtonThread : public OSThread
power->shutdown();
}
#elif NRF52_SERIES
// Do actual shutdown when button released, otherwise the button release
// may wake the board immediatedly.
if (!shutdown_on_long_stop) {
DEBUG_MSG("Shutdown from long press");
playBeep();
ledOff(PIN_LED1);
ledOff(PIN_LED2);
shutdown_on_long_stop = true;
}
// Do actual shutdown when button released, otherwise the button release
// may wake the board immediatedly.
if (!shutdown_on_long_stop) {
DEBUG_MSG("Shutdown from long press");
playBeep();
ledOff(PIN_LED1);
ledOff(PIN_LED2);
shutdown_on_long_stop = true;
}
#endif
} else {
// 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);
#endif
// Debug
#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
initDeepSleep();
@ -576,13 +576,29 @@ Periodic axpDebugOutput(axpDebugRead);
axpDebugOutput.setup();
#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
}
}
// If a thread does something that might need for it to be rescheduled ASAP it can set this flag
// This will supress the current delay and instead try to run ASAP.
bool runASAP;
void loop()
{
// axpDebugOutput.loop();
runASAP = false;
#ifdef DEBUG_PORT
DEBUG_PORT.loop(); // Send/receive protobufs over the serial port
#endif
// axpDebugOutput.loop();
// heap_caps_check_integrity_all(true); // FIXME - disable this expensive check
@ -592,6 +608,7 @@ void loop()
#ifdef NRF52_SERIES
nrf52Loop();
#endif
rebootCheck();
// For debugging
// if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();
@ -616,6 +633,7 @@ void loop()
mainController.nextThread->tillRun(millis())); */
// We want to sleep as long as possible here - because it saves power
mainDelay.delay(delayMsec);
if (!runASAP)
mainDelay.delay(delayMsec);
// if (didWake) DEBUG_MSG("wake!\n");
}

Wyświetl plik

@ -20,4 +20,10 @@ extern graphics::Screen *screen;
// Return a human readable string of the form "Meshtastic_ab13"
const char *getDeviceName();
extern uint32_t rebootAtMsec;
// If a thread does something that might need for it to be rescheduled ASAP it can set this flag
// This will supress the current delay and instead try to run ASAP.
extern bool runASAP;
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop();

Wyświetl plik

@ -36,6 +36,7 @@ MeshPacket *MeshPlugin::allocAckNak(Routing_Error err, NodeNum to, PacketId idFr
Routing c = Routing_init_default;
c.error_reason = err;
c.which_variant = Routing_error_reason_tag;
// Now that we have moded sendAckNak up one level into the class heirarchy we can no longer assume we are a RoutingPlugin
// So we manually call pb_encode_to_bytes and specify routing port number

Wyświetl plik

@ -114,7 +114,7 @@ bool MeshService::reloadConfig()
void MeshService::reloadOwner()
{
assert(nodeInfoPlugin);
if(nodeInfoPlugin)
if (nodeInfoPlugin)
nodeInfoPlugin->sendOurNodeInfo();
nodeDB.saveToDisk();
}
@ -172,13 +172,12 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
assert(node);
if (node->has_position) {
if(positionPlugin) {
if (positionPlugin) {
DEBUG_MSG("Sending position ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
positionPlugin->sendOurPosition(dest, wantReplies);
}
}
else {
if(nodeInfoPlugin) {
} else {
if (nodeInfoPlugin) {
DEBUG_MSG("Sending nodeinfo ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
}
@ -198,10 +197,13 @@ NodeInfo *MeshService::refreshMyNodeInfo()
Position &position = node->position;
// Update our local node info with our position (even if we don't decide to update anyone else)
position.time =
// Update our local node info with our time (even if we don't decide to update anyone else)
node->last_heard =
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();
updateBatteryLevel(position.battery_level);

Wyświetl plik

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

Wyświetl plik

@ -419,8 +419,7 @@ uint32_t sinceLastSeen(const NodeInfo *n)
{
uint32_t now = getTime();
uint32_t last_seen = n->position.time;
int delta = (int)(now - last_seen);
int delta = (int)(now - n->last_heard);
if (delta < 0) // our clock must be slightly off still - not set from GPS yet
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
// A lot of position reports don't have time populated. In that case, be careful to not blow away the time we
// recorded based on the packet rxTime
if (!info->position.time && p.time)
if (p.time)
info->position.time = p.time;
if (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));
if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen
info->has_position = true; // at least the time is valid
info->position.time = mp.rx_time;
}
if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard
info->last_heard = mp.rx_time;
if (mp.rx_snr)
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.

Wyświetl plik

@ -1,10 +1,10 @@
#include "PhoneAPI.h"
#include "Channels.h"
#include "GPS.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "RadioInterface.h"
#include "Channels.h"
#include <assert.h>
#if FromRadio_size > MAX_TO_FROM_RADIO_SIZE
@ -17,66 +17,73 @@
PhoneAPI::PhoneAPI() {}
void PhoneAPI::init()
PhoneAPI::~PhoneAPI()
{
observe(&service.fromNumChanged);
}
PhoneAPI::~PhoneAPI() {
close();
}
void PhoneAPI::close() {
unobserve();
state = STATE_SEND_NOTHING;
bool oldConnected = isConnected;
isConnected = false;
if(oldConnected != isConnected)
onConnectionChanged(isConnected);
void PhoneAPI::handleStartConfig()
{
if (!isConnected()) {
onConnectionChanged(true);
observe(&service.fromNumChanged);
}
// 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()
{
if (isConnected) {
if (isConnected()) {
bool newConnected = (millis() - lastContactMsec < getPref_phone_timeout_secs() * 1000L);
if (!newConnected) {
isConnected = false;
onConnectionChanged(isConnected);
}
if (!newConnected)
close();
}
}
/**
* 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
lastContactMsec = millis();
if (!isConnected) {
isConnected = true;
onConnectionChanged(isConnected);
}
// return (lastContactMsec != 0) &&
memset(&toRadioScratch, 0, sizeof(toRadioScratch));
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
switch (toRadioScratch.which_payloadVariant) {
case ToRadio_packet_tag: {
MeshPacket &p = toRadioScratch.packet;
printPacket("PACKET FROM PHONE", &p);
service.handleToRadio(p);
break;
}
case ToRadio_packet_tag:
return handleToRadioPacket(toRadioScratch.packet);
case ToRadio_want_config_id_tag:
config_nonce = toRadioScratch.want_config_id;
DEBUG_MSG("Client wants config, nonce=%u\n", config_nonce);
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
handleStartConfig();
break;
case ToRadio_disconnect_tag:
close();
break;
default:
@ -86,6 +93,8 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
} else {
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:
// 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.
myNodeInfo.has_gps = (radioConfig.preferences.location_share == LocationSharing_LocDisabled)
? true
: (gps && gps->isConnected()); // Update with latest GPS connect info
myNodeInfo.has_gps = gps && gps->isConnected(); // Update with latest GPS connect info
fromRadioScratch.which_payloadVariant = FromRadio_my_info_tag;
fromRadioScratch.my_info = myNodeInfo;
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;
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
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);
fromRadioScratch.which_payloadVariant = FromRadio_node_info_tag;
fromRadioScratch.node_info = *info;
@ -159,16 +166,13 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
case STATE_SEND_PACKETS:
// Do we have a message from the mesh?
if (packetForPhone) {
printPacket("phone downloaded packet", packetForPhone);
// Encapsulate as a FromRadio packet
fromRadioScratch.which_payloadVariant = FromRadio_packet_tag;
fromRadioScratch.packet = *packetForPhone;
service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore
packetForPhone = NULL;
}
releasePhonePacket();
break;
default:
@ -187,6 +191,16 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
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
*/
@ -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
*/
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
int PhoneAPI::onNotify(uint32_t newValue)

Wyświetl plik

@ -22,6 +22,7 @@ class PhoneAPI
enum State {
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
// (disconnected)
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_OWNER, no need to send Owner specially, it is just part of the nodedb
@ -58,17 +59,15 @@ class PhoneAPI
/// Destructor - calls close()
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
// Unregisters our observer. A closed connection **can** be reopened by calling init again.
virtual void close();
/**
* 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
@ -83,17 +82,16 @@ class PhoneAPI
*/
bool available();
protected:
/// Are we currently connected to a client?
bool isConnected = false;
bool isConnected() { return state != STATE_SEND_NOTHING; }
protected:
/// Our fromradio packet while it is being assembled
FromRadio fromRadioScratch;
/// Hookable to find out when connection changes
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();
/**
@ -101,11 +99,22 @@ class PhoneAPI
*/
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
virtual int onNotify(uint32_t newValue);

Wyświetl plik

@ -79,9 +79,10 @@ class ReliableRouter : public FloodingRouter
/** Do our retransmission handling */
virtual int32_t runOnce()
{
auto d = FloodingRouter::runOnce();
// Note: We must doRetransmissions FIRST, because it might queue up work for the base class runOnce implementation
auto d = doRetransmissions();
int32_t r = doRetransmissions();
int32_t r = FloodingRouter::runOnce();
return min(d, r);
}
@ -109,7 +110,6 @@ class ReliableRouter : public FloodingRouter
PendingPacket *startRetransmission(MeshPacket *p);
private:
/**
* Stop any retransmissions we are doing of the specified node/packet ID pair
*

Wyświetl plik

@ -4,6 +4,7 @@
#include "NodeDB.h"
#include "RTC.h"
#include "configuration.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "plugins/RoutingPlugin.h"
@ -55,9 +56,11 @@ int32_t Router::runOnce()
{
MeshPacket *mp;
while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) {
// printPacket("handle fromRadioQ", mp);
perhapsHandleReceived(mp);
}
// DEBUG_MSG("sleeping forever!\n");
return INT32_MAX; // Wait a long time - until we get woken for the message queue
}
@ -117,7 +120,9 @@ void Router::abortSendAndNak(Routing_Error err, MeshPacket *p)
void Router::setReceivedMessage()
{
// DEBUG_MSG("set interval to ASAP\n");
setInterval(0); // Run ASAP, so we can figure out our correct sleep time
runASAP = true;
}
ErrorCode Router::sendLocal(MeshPacket *p)

Wyświetl plik

@ -5,48 +5,62 @@
#define START2 0xc3
#define HEADER_LEN 4
void StreamAPI::loop()
int32_t StreamAPI::runOnce()
{
auto result = readStream();
writeStream();
readStream();
checkConnectionTimeout();
return result;
}
/**
* 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
uint8_t c = stream->read();
uint32_t now = millis();
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
size_t ptr = rxPtr++; // assume we will probably advance the rxPtr
// 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
rxBuf[ptr] = c; // store all bytes (including framing)
rxBuf[ptr] = c; // store all bytes (including framing)
if (ptr == 0) { // looking for START1
if (c != START1)
rxPtr = 0; // failed to find framing
} else if (ptr == 1) { // looking for START2
if (c != START2)
rxPtr = 0; // failed to find 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
if (ptr == 0) { // looking for START1
if (c != START1)
rxPtr = 0; // failed to find framing
} else if (ptr == 1) { // looking for START2
if (c != START2)
rxPtr = 0; // failed to find 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
if (ptr == HEADER_LEN) {
// we _just_ finished our 4 byte header, validate length now (note: a length of zero is a valid
// protobuf also)
if (len > MAX_TO_FROM_RADIO_SIZE)
rxPtr = 0; // length is bogus, restart search for framing
}
if (ptr == HEADER_LEN) {
// we _just_ finished our 4 byte header, validate length now (note: a length of zero is a valid
// protobuf also)
if (len > MAX_TO_FROM_RADIO_SIZE)
rxPtr = 0; // length is bogus, restart search for framing
}
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
handleToRadio(rxBuf + HEADER_LEN, len);
rxPtr = 0; // start over again
if (rxPtr != 0 && ptr + 1 == len + HEADER_LEN) {
rxPtr = 0; // start over again on the next packet
// 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)
{
if (len != 0) {
DEBUG_MSG("emit tx %d\n", len);
// DEBUG_MSG("emit tx %d\n", len);
txBuf[0] = START1;
txBuf[1] = START2;
txBuf[2] = (len >> 8) & 0xff;
@ -93,6 +107,6 @@ void StreamAPI::emitRebooted()
fromRadioScratch.which_payloadVariant = FromRadio_rebooted_tag;
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));
}

Wyświetl plik

@ -2,6 +2,7 @@
#include "PhoneAPI.h"
#include "Stream.h"
#include "concurrency/OSThread.h"
// A To/FromRadio packet + our 32 bit header
#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.
*/
class StreamAPI : public PhoneAPI
class StreamAPI : public PhoneAPI, protected concurrency::OSThread
{
/**
* The stream we read/write from
@ -37,21 +38,23 @@ class StreamAPI : public PhoneAPI
uint8_t rxBuf[MAX_STREAM_BUF_SIZE];
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:
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
* phone.
* FIXME, to support better power behavior instead move to a thread and block on serial reads.
*/
void loop();
virtual int32_t runOnce();
private:
/**
* Read any rx chars from the link and call handleToRadio
*/
void readStream();
int32_t readStream();
/**
* 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
*/
void emitRebooted();
/**
* Send the current txBuffer over our stream
*/

Wyświetl plik

@ -26,6 +26,7 @@ typedef struct _AdminMessage {
bool confirm_set_channel;
bool confirm_set_radio;
bool exit_simulator;
int32_t reboot_seconds;
};
} AdminMessage;
@ -49,6 +50,7 @@ extern "C" {
#define AdminMessage_confirm_set_channel_tag 32
#define AdminMessage_confirm_set_radio_tag 33
#define AdminMessage_exit_simulator_tag 34
#define AdminMessage_reboot_seconds_tag 35
/* Struct field encoding specification for nanopb */
#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, 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,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_DEFAULT NULL
#define AdminMessage_variant_set_radio_MSGTYPE RadioConfig

Wyświetl plik

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

Wyświetl plik

@ -162,6 +162,7 @@ typedef struct _NodeInfo {
User user;
bool has_position;
Position position;
uint32_t last_heard;
float snr;
} NodeInfo;
@ -192,6 +193,7 @@ typedef struct _ToRadio {
union {
MeshPacket packet;
uint32_t want_config_id;
bool disconnect;
};
} ToRadio;
@ -233,7 +235,7 @@ extern "C" {
#define Routing_init_default {0, {RouteDiscovery_init_default}}
#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 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 LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
#define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}}
@ -244,7 +246,7 @@ extern "C" {
#define Routing_init_zero {0, {RouteDiscovery_init_zero}}
#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 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 LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
#define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}}
@ -300,6 +302,7 @@ extern "C" {
#define NodeInfo_num_tag 1
#define NodeInfo_user_tag 2
#define NodeInfo_position_tag 3
#define NodeInfo_last_heard_tag 4
#define NodeInfo_snr_tag 7
#define Routing_route_request_tag 1
#define Routing_route_reply_tag 2
@ -313,6 +316,7 @@ extern "C" {
#define FromRadio_packet_tag 11
#define ToRadio_packet_tag 2
#define ToRadio_want_config_id_tag 100
#define ToRadio_disconnect_tag 104
/* Struct field encoding specification for nanopb */
#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, OPTIONAL, MESSAGE, user, 2) \
X(a, STATIC, OPTIONAL, MESSAGE, position, 3) \
X(a, STATIC, SINGULAR, FIXED32, last_heard, 4) \
X(a, STATIC, SINGULAR, FLOAT, snr, 7)
#define NodeInfo_CALLBACK NULL
#define NodeInfo_DEFAULT NULL
@ -426,7 +431,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 11)
#define ToRadio_FIELDLIST(X, a) \
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_DEFAULT NULL
#define ToRadio_payloadVariant_packet_MSGTYPE MeshPacket
@ -463,7 +469,7 @@ extern const pb_msgdesc_t ToRadio_msg;
#define Routing_size 42
#define Data_size 260
#define MeshPacket_size 309
#define NodeInfo_size 126
#define NodeInfo_size 131
#define MyNodeInfo_size 95
#define LogRecord_size 81
#define FromRadio_size 318

Wyświetl plik

@ -11,7 +11,6 @@
#include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp>
#include <HTTPURLEncodedBodyParser.hpp>
#include <Preferences.h>
#include <SPIFFS.h>
#ifndef NO_ESP32
@ -837,11 +836,6 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
ResourceParameters *params = req->getParams();
std::string content;
Preferences preferences;
preferences.begin("meshtastic", false);
uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0);
if (!params->getQueryParameter("content", content)) {
content = "json";
}

Wyświetl plik

@ -40,18 +40,20 @@ void WiFiServerAPI::onConnectionChanged(bool connected)
}
/// override close to also shutdown the TCP link
void WiFiServerAPI::close() {
void WiFiServerAPI::close()
{
client.stop(); // drop tcp connection
StreamAPI::close();
}
bool WiFiServerAPI::loop()
int32_t WiFiServerAPI::runOnce()
{
if (client.connected()) {
StreamAPI::loop();
return true;
return StreamAPI::runOnce();
} 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);
}
if (openAPI) {
// 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
return 100; // only check occasionally for incoming connections
}

Wyświetl plik

@ -1,7 +1,6 @@
#pragma once
#include "StreamAPI.h"
#include "concurrency/OSThread.h"
#include <WiFi.h>
/**
@ -18,15 +17,14 @@ class WiFiServerAPI : public StreamAPI
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
virtual void close();
protected:
/// Hookable to find out when connection changes
virtual void onConnectionChanged(bool connected);
virtual int32_t runOnce(); // Check for dropped client connections
};
/**

Wyświetl plik

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

Wyświetl plik

@ -2,9 +2,9 @@
#include "BluetoothCommon.h"
#include "configuration.h"
#include "main.h"
#include <bluefruit.h>
#include "mesh/mesh-pb-constants.h"
#include "mesh/PhoneAPI.h"
#include "mesh/mesh-pb-constants.h"
#include <bluefruit.h>
static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_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)
{
bluetoothPhoneAPI = new BluetoothPhoneAPI();
bluetoothPhoneAPI->init();
meshBleService.begin();

Wyświetl plik

@ -77,6 +77,13 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessag
handleGetRadio(mp);
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
case AdminMessage_exit_simulator_tag:
DEBUG_MSG("Exiting simulator\n");

Wyświetl plik

@ -10,10 +10,8 @@ PositionPlugin *positionPlugin;
PositionPlugin::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
setIntervalFromNow(60 *
1000); // Send our initial position 60 seconds after we start (to give GPS time to setup)
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
setIntervalFromNow(60 * 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)

Wyświetl plik

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