mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-02 16:10:43 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfd147062f | ||
|
|
890ec7bdb2 | ||
|
|
76269b397f | ||
|
|
9fb6b1718f | ||
|
|
57c82988e2 | ||
|
|
1e3b037fea | ||
|
|
1e7808991d | ||
|
|
4f4cdf4f9e | ||
|
|
78f2c656d0 | ||
|
|
37ec969f96 | ||
|
|
f1a6693bb7 | ||
|
|
8ffd5a1d4f | ||
|
|
29eb5e8327 | ||
|
|
c175c21189 | ||
|
|
fc2862bd16 | ||
|
|
c9f814a9a7 | ||
|
|
92d2d3960b | ||
|
|
7872cb050d | ||
|
|
89029311c1 | ||
|
|
f6f586decb | ||
|
|
471c06b169 | ||
|
|
040bb1d1e0 | ||
|
|
bbaf5946f0 | ||
|
|
5286f23c9a | ||
|
|
7e9e33d462 | ||
|
|
04225f7bc2 | ||
|
|
dd0f1b2704 | ||
|
|
669807524e | ||
|
|
97a5405293 | ||
|
|
f298c7d053 | ||
|
|
a59f5344de | ||
|
|
13cfce48fa | ||
|
|
0261c243e0 | ||
|
|
ab325d6d2c | ||
|
|
b20930c111 | ||
|
|
770788d0a4 | ||
|
|
d02f615cad | ||
|
|
e17fe7e075 | ||
|
|
e193f63687 | ||
|
|
1eb37dded8 |
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
PYTHON=${PYTHON:-python}
|
PYTHON=${PYTHON:-python3}
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
PYTHON=${PYTHON:-python}
|
PYTHON=${PYTHON:-python3}
|
||||||
|
|
||||||
# Usage info
|
# Usage info
|
||||||
show_help() {
|
show_help() {
|
||||||
|
|||||||
@@ -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
2
proto
Submodule proto updated: 820fa497df...0ea2328026
@@ -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");
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
|
|
||||||
void reinitUpdateService();
|
void reinitUpdateService();
|
||||||
|
|
||||||
void bluetoothRebootCheck();
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,9 +7,10 @@
|
|||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include <Preferences.h>
|
||||||
|
#include <driver/rtc_io.h>
|
||||||
#include <nvs.h>
|
#include <nvs.h>
|
||||||
#include <nvs_flash.h>
|
#include <nvs_flash.h>
|
||||||
#include <driver/rtc_io.h>
|
|
||||||
|
|
||||||
void getMacAddr(uint8_t *dmac)
|
void getMacAddr(uint8_t *dmac)
|
||||||
{
|
{
|
||||||
@@ -45,6 +46,18 @@ void esp32Setup()
|
|||||||
DEBUG_MSG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d\n", nvs_stats.used_entries, nvs_stats.free_entries,
|
DEBUG_MSG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d\n", nvs_stats.used_entries, nvs_stats.free_entries,
|
||||||
nvs_stats.total_entries);
|
nvs_stats.total_entries);
|
||||||
|
|
||||||
|
DEBUG_MSG("Setup Preferences in Flash Storage\n");
|
||||||
|
|
||||||
|
// Create object to store our persistant data
|
||||||
|
Preferences preferences;
|
||||||
|
preferences.begin("meshtastic", false);
|
||||||
|
|
||||||
|
uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0);
|
||||||
|
rebootCounter++;
|
||||||
|
preferences.putUInt("rebootCounter", rebootCounter);
|
||||||
|
preferences.end();
|
||||||
|
DEBUG_MSG("Number of Device Reboots: %d\n", rebootCounter);
|
||||||
|
|
||||||
// enableModemSleep();
|
// enableModemSleep();
|
||||||
|
|
||||||
// Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any
|
// Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any
|
||||||
@@ -84,7 +97,6 @@ void esp32Loop()
|
|||||||
{
|
{
|
||||||
esp_task_wdt_reset(); // service our app level watchdog
|
esp_task_wdt_reset(); // service our app level watchdog
|
||||||
loopBLE();
|
loopBLE();
|
||||||
bluetoothRebootCheck();
|
|
||||||
|
|
||||||
// for debug printing
|
// for debug printing
|
||||||
// radio.radioIf.canSleep();
|
// radio.radioIf.canSleep();
|
||||||
|
|||||||
41
src/main.cpp
41
src/main.cpp
@@ -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();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
#include "mesh/http/WiFiAPClient.h"
|
#include "mesh/http/WiFiAPClient.h"
|
||||||
|
#include "plugins/esp32/StoreForwardPlugin.h"
|
||||||
|
#include <Preferences.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NodeDB nodeDB;
|
NodeDB nodeDB;
|
||||||
@@ -233,6 +235,15 @@ void NodeDB::init()
|
|||||||
myNodeInfo.hw_model_deprecated[0] = '\0';
|
myNodeInfo.hw_model_deprecated[0] = '\0';
|
||||||
// strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
|
// strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
|
||||||
|
|
||||||
|
#ifndef NO_ESP32
|
||||||
|
Preferences preferences;
|
||||||
|
preferences.begin("meshtastic", false);
|
||||||
|
myNodeInfo.reboot_count = preferences.getUInt("rebootCounter", 0);
|
||||||
|
preferences.end();
|
||||||
|
DEBUG_MSG("Number of Device Reboots: %d\n", myNodeInfo.reboot_count);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
resetRadioConfig(); // If bogus settings got saved, then fix them
|
resetRadioConfig(); // If bogus settings got saved, then fix them
|
||||||
|
|
||||||
DEBUG_MSG("region=%d, NODENUM=0x%x, dbsize=%d\n", radioConfig.preferences.region, myNodeInfo.my_node_num, *numNodes);
|
DEBUG_MSG("region=%d, NODENUM=0x%x, dbsize=%d\n", radioConfig.preferences.region, myNodeInfo.my_node_num, *numNodes);
|
||||||
@@ -408,8 +419,7 @@ uint32_t sinceLastSeen(const NodeInfo *n)
|
|||||||
{
|
{
|
||||||
uint32_t now = getTime();
|
uint32_t now = getTime();
|
||||||
|
|
||||||
uint32_t last_seen = n->position.time;
|
int delta = (int)(now - n->last_heard);
|
||||||
int delta = (int)(now - last_seen);
|
|
||||||
if (delta < 0) // our clock must be slightly off still - not set from GPS yet
|
if (delta < 0) // our clock must be slightly off still - not set from GPS yet
|
||||||
delta = 0;
|
delta = 0;
|
||||||
|
|
||||||
@@ -443,7 +453,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p)
|
|||||||
// Be careful to only update fields that have been set by the sender
|
// Be careful to only update fields that have been set by the sender
|
||||||
// A lot of position reports don't have time populated. In that case, be careful to not blow away the time we
|
// A lot of position reports don't have time populated. In that case, be careful to not blow away the time we
|
||||||
// recorded based on the packet rxTime
|
// recorded based on the packet rxTime
|
||||||
if (!info->position.time && p.time)
|
if (p.time)
|
||||||
info->position.time = p.time;
|
info->position.time = p.time;
|
||||||
if (p.battery_level)
|
if (p.battery_level)
|
||||||
info->position.battery_level = p.battery_level;
|
info->position.battery_level = p.battery_level;
|
||||||
@@ -451,6 +461,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p)
|
|||||||
info->position.latitude_i = p.latitude_i;
|
info->position.latitude_i = p.latitude_i;
|
||||||
info->position.longitude_i = p.longitude_i;
|
info->position.longitude_i = p.longitude_i;
|
||||||
}
|
}
|
||||||
|
if (p.altitude)
|
||||||
|
info->position.altitude = p.altitude;
|
||||||
info->has_position = true;
|
info->has_position = true;
|
||||||
updateGUIforNode = info;
|
updateGUIforNode = info;
|
||||||
notifyObservers(true); // Force an update whether or not our node counts have changed
|
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||||
@@ -491,10 +503,8 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
|
|
||||||
NodeInfo *info = getOrCreateNode(getFrom(&mp));
|
NodeInfo *info = getOrCreateNode(getFrom(&mp));
|
||||||
|
|
||||||
if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen
|
if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard
|
||||||
info->has_position = true; // at least the time is valid
|
info->last_heard = mp.rx_time;
|
||||||
info->position.time = mp.rx_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mp.rx_snr)
|
if (mp.rx_snr)
|
||||||
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
|
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ extern const pb_msgdesc_t ChannelFile_msg;
|
|||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define LegacyRadioConfig_size 4
|
#define LegacyRadioConfig_size 4
|
||||||
#define LegacyRadioConfig_LegacyPreferences_size 2
|
#define LegacyRadioConfig_LegacyPreferences_size 2
|
||||||
#define DeviceState_size 4920
|
#define DeviceState_size 5118
|
||||||
#define ChannelFile_size 832
|
#define ChannelFile_size 832
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ typedef struct _MyNodeInfo {
|
|||||||
CriticalErrorCode error_code;
|
CriticalErrorCode error_code;
|
||||||
uint32_t error_address;
|
uint32_t error_address;
|
||||||
uint32_t error_count;
|
uint32_t error_count;
|
||||||
|
uint32_t reboot_count;
|
||||||
uint32_t message_timeout_msec;
|
uint32_t message_timeout_msec;
|
||||||
uint32_t min_app_version;
|
uint32_t min_app_version;
|
||||||
uint32_t max_channels;
|
uint32_t max_channels;
|
||||||
@@ -161,6 +162,7 @@ typedef struct _NodeInfo {
|
|||||||
User user;
|
User user;
|
||||||
bool has_position;
|
bool has_position;
|
||||||
Position position;
|
Position position;
|
||||||
|
uint32_t last_heard;
|
||||||
float snr;
|
float snr;
|
||||||
} NodeInfo;
|
} NodeInfo;
|
||||||
|
|
||||||
@@ -191,6 +193,7 @@ typedef struct _ToRadio {
|
|||||||
union {
|
union {
|
||||||
MeshPacket packet;
|
MeshPacket packet;
|
||||||
uint32_t want_config_id;
|
uint32_t want_config_id;
|
||||||
|
bool disconnect;
|
||||||
};
|
};
|
||||||
} ToRadio;
|
} ToRadio;
|
||||||
|
|
||||||
@@ -232,8 +235,8 @@ extern "C" {
|
|||||||
#define Routing_init_default {0, {RouteDiscovery_init_default}}
|
#define Routing_init_default {0, {RouteDiscovery_init_default}}
|
||||||
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
|
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
|
||||||
#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0}
|
#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0}
|
||||||
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0}
|
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
|
||||||
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0}
|
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0}
|
||||||
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
|
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
|
||||||
#define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}}
|
#define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}}
|
||||||
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
||||||
@@ -243,8 +246,8 @@ extern "C" {
|
|||||||
#define Routing_init_zero {0, {RouteDiscovery_init_zero}}
|
#define Routing_init_zero {0, {RouteDiscovery_init_zero}}
|
||||||
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
|
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
|
||||||
#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0}
|
#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0}
|
||||||
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0}
|
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
|
||||||
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0}
|
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0}
|
||||||
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
|
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
|
||||||
#define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}}
|
#define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}}
|
||||||
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
|
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
|
||||||
@@ -269,6 +272,7 @@ extern "C" {
|
|||||||
#define MyNodeInfo_error_code_tag 7
|
#define MyNodeInfo_error_code_tag 7
|
||||||
#define MyNodeInfo_error_address_tag 8
|
#define MyNodeInfo_error_address_tag 8
|
||||||
#define MyNodeInfo_error_count_tag 9
|
#define MyNodeInfo_error_count_tag 9
|
||||||
|
#define MyNodeInfo_reboot_count_tag 10
|
||||||
#define MyNodeInfo_message_timeout_msec_tag 13
|
#define MyNodeInfo_message_timeout_msec_tag 13
|
||||||
#define MyNodeInfo_min_app_version_tag 14
|
#define MyNodeInfo_min_app_version_tag 14
|
||||||
#define MyNodeInfo_max_channels_tag 15
|
#define MyNodeInfo_max_channels_tag 15
|
||||||
@@ -298,6 +302,7 @@ extern "C" {
|
|||||||
#define NodeInfo_num_tag 1
|
#define NodeInfo_num_tag 1
|
||||||
#define NodeInfo_user_tag 2
|
#define NodeInfo_user_tag 2
|
||||||
#define NodeInfo_position_tag 3
|
#define NodeInfo_position_tag 3
|
||||||
|
#define NodeInfo_last_heard_tag 4
|
||||||
#define NodeInfo_snr_tag 7
|
#define NodeInfo_snr_tag 7
|
||||||
#define Routing_route_request_tag 1
|
#define Routing_route_request_tag 1
|
||||||
#define Routing_route_reply_tag 2
|
#define Routing_route_reply_tag 2
|
||||||
@@ -311,6 +316,7 @@ extern "C" {
|
|||||||
#define FromRadio_packet_tag 11
|
#define FromRadio_packet_tag 11
|
||||||
#define ToRadio_packet_tag 2
|
#define ToRadio_packet_tag 2
|
||||||
#define ToRadio_want_config_id_tag 100
|
#define ToRadio_want_config_id_tag 100
|
||||||
|
#define ToRadio_disconnect_tag 104
|
||||||
|
|
||||||
/* Struct field encoding specification for nanopb */
|
/* Struct field encoding specification for nanopb */
|
||||||
#define Position_FIELDLIST(X, a) \
|
#define Position_FIELDLIST(X, a) \
|
||||||
@@ -376,6 +382,7 @@ X(a, STATIC, SINGULAR, INT32, rx_rssi, 13)
|
|||||||
X(a, STATIC, SINGULAR, UINT32, num, 1) \
|
X(a, STATIC, SINGULAR, UINT32, num, 1) \
|
||||||
X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \
|
X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \
|
||||||
X(a, STATIC, OPTIONAL, MESSAGE, position, 3) \
|
X(a, STATIC, OPTIONAL, MESSAGE, position, 3) \
|
||||||
|
X(a, STATIC, SINGULAR, FIXED32, last_heard, 4) \
|
||||||
X(a, STATIC, SINGULAR, FLOAT, snr, 7)
|
X(a, STATIC, SINGULAR, FLOAT, snr, 7)
|
||||||
#define NodeInfo_CALLBACK NULL
|
#define NodeInfo_CALLBACK NULL
|
||||||
#define NodeInfo_DEFAULT NULL
|
#define NodeInfo_DEFAULT NULL
|
||||||
@@ -392,6 +399,7 @@ X(a, STATIC, SINGULAR, STRING, firmware_version, 6) \
|
|||||||
X(a, STATIC, SINGULAR, UENUM, error_code, 7) \
|
X(a, STATIC, SINGULAR, UENUM, error_code, 7) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, error_address, 8) \
|
X(a, STATIC, SINGULAR, UINT32, error_address, 8) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, error_count, 9) \
|
X(a, STATIC, SINGULAR, UINT32, error_count, 9) \
|
||||||
|
X(a, STATIC, SINGULAR, UINT32, reboot_count, 10) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, message_timeout_msec, 13) \
|
X(a, STATIC, SINGULAR, UINT32, message_timeout_msec, 13) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, min_app_version, 14) \
|
X(a, STATIC, SINGULAR, UINT32, min_app_version, 14) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, max_channels, 15)
|
X(a, STATIC, SINGULAR, UINT32, max_channels, 15)
|
||||||
@@ -423,7 +431,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 11)
|
|||||||
|
|
||||||
#define ToRadio_FIELDLIST(X, a) \
|
#define ToRadio_FIELDLIST(X, a) \
|
||||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 2) \
|
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 2) \
|
||||||
X(a, STATIC, ONEOF, UINT32, (payloadVariant,want_config_id,want_config_id), 100)
|
X(a, STATIC, ONEOF, UINT32, (payloadVariant,want_config_id,want_config_id), 100) \
|
||||||
|
X(a, STATIC, ONEOF, BOOL, (payloadVariant,disconnect,disconnect), 104)
|
||||||
#define ToRadio_CALLBACK NULL
|
#define ToRadio_CALLBACK NULL
|
||||||
#define ToRadio_DEFAULT NULL
|
#define ToRadio_DEFAULT NULL
|
||||||
#define ToRadio_payloadVariant_packet_MSGTYPE MeshPacket
|
#define ToRadio_payloadVariant_packet_MSGTYPE MeshPacket
|
||||||
@@ -460,8 +469,8 @@ extern const pb_msgdesc_t ToRadio_msg;
|
|||||||
#define Routing_size 42
|
#define Routing_size 42
|
||||||
#define Data_size 260
|
#define Data_size 260
|
||||||
#define MeshPacket_size 309
|
#define MeshPacket_size 309
|
||||||
#define NodeInfo_size 126
|
#define NodeInfo_size 131
|
||||||
#define MyNodeInfo_size 89
|
#define MyNodeInfo_size 95
|
||||||
#define LogRecord_size 81
|
#define LogRecord_size 81
|
||||||
#define FromRadio_size 318
|
#define FromRadio_size 318
|
||||||
#define ToRadio_size 312
|
#define ToRadio_size 312
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
|
#include "RadioLibInterface.h"
|
||||||
#include "airtime.h"
|
#include "airtime.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh/http/ContentHelper.h"
|
#include "mesh/http/ContentHelper.h"
|
||||||
@@ -10,8 +11,8 @@
|
|||||||
#include <HTTPBodyParser.hpp>
|
#include <HTTPBodyParser.hpp>
|
||||||
#include <HTTPMultipartBodyParser.hpp>
|
#include <HTTPMultipartBodyParser.hpp>
|
||||||
#include <HTTPURLEncodedBodyParser.hpp>
|
#include <HTTPURLEncodedBodyParser.hpp>
|
||||||
|
#include <Preferences.h>
|
||||||
#include <SPIFFS.h>
|
#include <SPIFFS.h>
|
||||||
#include "RadioLibInterface.h"
|
|
||||||
|
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
#include "esp_task_wdt.h"
|
#include "esp_task_wdt.h"
|
||||||
@@ -77,7 +78,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
|||||||
ResourceNode *nodeAPIv1ToRadio = new ResourceNode("/api/v1/toradio", "PUT", &handleAPIv1ToRadio);
|
ResourceNode *nodeAPIv1ToRadio = new ResourceNode("/api/v1/toradio", "PUT", &handleAPIv1ToRadio);
|
||||||
ResourceNode *nodeAPIv1FromRadio = new ResourceNode("/api/v1/fromradio", "GET", &handleAPIv1FromRadio);
|
ResourceNode *nodeAPIv1FromRadio = new ResourceNode("/api/v1/fromradio", "GET", &handleAPIv1FromRadio);
|
||||||
|
|
||||||
ResourceNode *nodeHotspot = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
|
ResourceNode *nodeHotspotApple = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
|
||||||
|
ResourceNode *nodeHotspotAndroid = new ResourceNode("/generate_204", "GET", &handleHotspot);
|
||||||
ResourceNode *nodeFavicon = new ResourceNode("/favicon.ico", "GET", &handleFavicon);
|
ResourceNode *nodeFavicon = new ResourceNode("/favicon.ico", "GET", &handleFavicon);
|
||||||
ResourceNode *nodeRoot = new ResourceNode("/", "GET", &handleRoot);
|
ResourceNode *nodeRoot = new ResourceNode("/", "GET", &handleRoot);
|
||||||
ResourceNode *nodeStaticBrowse = new ResourceNode("/static", "GET", &handleStaticBrowse);
|
ResourceNode *nodeStaticBrowse = new ResourceNode("/static", "GET", &handleStaticBrowse);
|
||||||
@@ -96,7 +98,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
|||||||
secureServer->registerNode(nodeAPIv1ToRadioOptions);
|
secureServer->registerNode(nodeAPIv1ToRadioOptions);
|
||||||
secureServer->registerNode(nodeAPIv1ToRadio);
|
secureServer->registerNode(nodeAPIv1ToRadio);
|
||||||
secureServer->registerNode(nodeAPIv1FromRadio);
|
secureServer->registerNode(nodeAPIv1FromRadio);
|
||||||
secureServer->registerNode(nodeHotspot);
|
secureServer->registerNode(nodeHotspotApple);
|
||||||
|
secureServer->registerNode(nodeHotspotAndroid);
|
||||||
secureServer->registerNode(nodeFavicon);
|
secureServer->registerNode(nodeFavicon);
|
||||||
secureServer->registerNode(nodeRoot);
|
secureServer->registerNode(nodeRoot);
|
||||||
secureServer->registerNode(nodeStaticBrowse);
|
secureServer->registerNode(nodeStaticBrowse);
|
||||||
@@ -117,7 +120,8 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
|||||||
insecureServer->registerNode(nodeAPIv1ToRadioOptions);
|
insecureServer->registerNode(nodeAPIv1ToRadioOptions);
|
||||||
insecureServer->registerNode(nodeAPIv1ToRadio);
|
insecureServer->registerNode(nodeAPIv1ToRadio);
|
||||||
insecureServer->registerNode(nodeAPIv1FromRadio);
|
insecureServer->registerNode(nodeAPIv1FromRadio);
|
||||||
insecureServer->registerNode(nodeHotspot);
|
insecureServer->registerNode(nodeHotspotApple);
|
||||||
|
insecureServer->registerNode(nodeHotspotAndroid);
|
||||||
insecureServer->registerNode(nodeFavicon);
|
insecureServer->registerNode(nodeFavicon);
|
||||||
insecureServer->registerNode(nodeRoot);
|
insecureServer->registerNode(nodeRoot);
|
||||||
insecureServer->registerNode(nodeStaticBrowse);
|
insecureServer->registerNode(nodeStaticBrowse);
|
||||||
@@ -833,6 +837,11 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
|||||||
ResourceParameters *params = req->getParams();
|
ResourceParameters *params = req->getParams();
|
||||||
std::string content;
|
std::string content;
|
||||||
|
|
||||||
|
Preferences preferences;
|
||||||
|
preferences.begin("meshtastic", false);
|
||||||
|
|
||||||
|
uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0);
|
||||||
|
|
||||||
if (!params->getQueryParameter("content", content)) {
|
if (!params->getQueryParameter("content", content)) {
|
||||||
content = "json";
|
content = "json";
|
||||||
}
|
}
|
||||||
@@ -931,6 +940,10 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
|||||||
res->printf("\"is_charging\": %s\n", BoolToString(powerStatus->getIsCharging()));
|
res->printf("\"is_charging\": %s\n", BoolToString(powerStatus->getIsCharging()));
|
||||||
res->println("},");
|
res->println("},");
|
||||||
|
|
||||||
|
res->println("\"device\": {");
|
||||||
|
res->printf("\"reboot_counter\": %d\n", myNodeInfo.reboot_count);
|
||||||
|
res->println("},");
|
||||||
|
|
||||||
res->println("\"radio\": {");
|
res->println("\"radio\": {");
|
||||||
res->printf("\"frequecy\": %f,\n", RadioLibInterface::instance->getFreq());
|
res->printf("\"frequecy\": %f,\n", RadioLibInterface::instance->getFreq());
|
||||||
res->printf("\"lora_channel\": %d\n", RadioLibInterface::instance->getChannelNum());
|
res->printf("\"lora_channel\": %d\n", RadioLibInterface::instance->getChannelNum());
|
||||||
@@ -938,8 +951,6 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
|||||||
|
|
||||||
res->println("},");
|
res->println("},");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
res->println("\"status\": \"ok\"");
|
res->println("\"status\": \"ok\"");
|
||||||
res->println("}");
|
res->println("}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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?
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,13 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessag
|
|||||||
handleGetRadio(mp);
|
handleGetRadio(mp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AdminMessage_reboot_seconds_tag: {
|
||||||
|
int32_t s = r->reboot_seconds;
|
||||||
|
DEBUG_MSG("Rebooting in %d seconds\n", s);
|
||||||
|
rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef PORTDUINO
|
#ifdef PORTDUINO
|
||||||
case AdminMessage_exit_simulator_tag:
|
case AdminMessage_exit_simulator_tag:
|
||||||
DEBUG_MSG("Exiting simulator\n");
|
DEBUG_MSG("Exiting simulator\n");
|
||||||
|
|||||||
10
src/plugins/PluginDev.h
Normal file
10
src/plugins/PluginDev.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To developers:
|
||||||
|
* Use this to enable / disable features in your plugin that you don't want to risk checking into GitHub.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Enable development more for StoreForwardPlugin
|
||||||
|
bool StoreForward_Dev = false;
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -255,7 +255,9 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileToWrite.println("time,from,sender name,sender lat,sender long,rx lat,rx long,rx snr,distance,payload")) {
|
// Print the CSV header
|
||||||
|
if (fileToWrite.println(
|
||||||
|
"time,from,sender name,sender lat,sender long,rx lat,rx long,rx snr,rx elevation,distance,payload")) {
|
||||||
DEBUG_MSG("File was written\n");
|
DEBUG_MSG("File was written\n");
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("File write failed\n");
|
DEBUG_MSG("File write failed\n");
|
||||||
@@ -296,7 +298,9 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
|
|||||||
fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long
|
fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long
|
||||||
fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat
|
fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat
|
||||||
fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
|
fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
|
||||||
fileToAppend.printf("%f,", mp.rx_snr); // RX SNR
|
fileToAppend.printf("%d,", gpsStatus->getAltitude()); // RX Altitude
|
||||||
|
|
||||||
|
fileToAppend.printf("%f,", mp.rx_snr); // RX SNR
|
||||||
|
|
||||||
if (n->position.latitude_i && n->position.longitude_i && gpsStatus->getLatitude() && gpsStatus->getLongitude()) {
|
if (n->position.latitude_i && n->position.longitude_i && gpsStatus->getLatitude() && gpsStatus->getLongitude()) {
|
||||||
float distance = latLongToMeter(n->position.latitude_i * 1e-7, n->position.longitude_i * 1e-7,
|
float distance = latLongToMeter(n->position.latitude_i * 1e-7, n->position.longitude_i * 1e-7,
|
||||||
|
|||||||
@@ -5,11 +5,13 @@
|
|||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
|
#include "plugins/PluginDev.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#define STOREFORWARD_MAX_PACKETS 0
|
#define STOREFORWARD_MAX_PACKETS 0
|
||||||
#define STOREFORWARD_SEND_HISTORY_SHORT 600
|
#define STOREFORWARD_SEND_HISTORY_PERIOD 10 * 60
|
||||||
|
#define STOREFORWARD_SEND_HISTORY_MAX 0
|
||||||
|
|
||||||
StoreForwardPlugin *storeForwardPlugin;
|
StoreForwardPlugin *storeForwardPlugin;
|
||||||
|
|
||||||
@@ -22,9 +24,8 @@ int32_t StoreForwardPlugin::runOnce()
|
|||||||
|
|
||||||
if (radioConfig.preferences.is_router) {
|
if (radioConfig.preferences.is_router) {
|
||||||
// Maybe some cleanup functions?
|
// Maybe some cleanup functions?
|
||||||
this->sawNodeReport();
|
|
||||||
this->historyReport();
|
this->historyReport();
|
||||||
return (10 * 1000);
|
return (60 * 1000);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* If the plugin is turned on and is_router is not enabled, then we'll send a heartbeat every
|
* If the plugin is turned on and is_router is not enabled, then we'll send a heartbeat every
|
||||||
@@ -64,14 +65,12 @@ void StoreForwardPlugin::populatePSRAM()
|
|||||||
DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize());
|
DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize());
|
||||||
DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram());
|
DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram());
|
||||||
|
|
||||||
// PacketHistoryStruct *packetHistory = (PacketHistoryStruct *)ps_calloc(STOREFORWARD_MAX_PACKETS,
|
|
||||||
// sizeof(PacketHistoryStruct));
|
|
||||||
|
|
||||||
// Use a maximum of half the available PSRAM unless otherwise specified.
|
// Use a maximum of half the available PSRAM unless otherwise specified.
|
||||||
uint32_t numberOfPackets =
|
uint32_t numberOfPackets =
|
||||||
STOREFORWARD_MAX_PACKETS ? STOREFORWARD_MAX_PACKETS : ((ESP.getPsramSize() / 2) / sizeof(PacketHistoryStruct));
|
STOREFORWARD_MAX_PACKETS ? STOREFORWARD_MAX_PACKETS : ((ESP.getPsramSize() / 2) / sizeof(PacketHistoryStruct));
|
||||||
|
|
||||||
this->packetHistory = (PacketHistoryStruct *)ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct));
|
// this->packetHistory = (PacketHistoryStruct *)ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct));
|
||||||
|
this->packetHistory = static_cast<PacketHistoryStruct *>(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
|
||||||
DEBUG_MSG("After PSRAM initilization:\n");
|
DEBUG_MSG("After PSRAM initilization:\n");
|
||||||
|
|
||||||
DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize());
|
DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize());
|
||||||
@@ -83,41 +82,17 @@ void StoreForwardPlugin::populatePSRAM()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We saw a node.
|
// We saw a node.
|
||||||
uint32_t StoreForwardPlugin::sawNode(uint32_t node)
|
void StoreForwardPlugin::sawNode(uint32_t whoWeSaw, uint32_t sawSecAgo)
|
||||||
{
|
{
|
||||||
|
if (radioConfig.preferences.is_router) {
|
||||||
|
|
||||||
/*
|
// If node has been away for more than 10 minutes, send the node the last 10 minutes of
|
||||||
TODO: Move receivedRecord into the PSRAM
|
// messages
|
||||||
|
if (sawSecAgo > STOREFORWARD_SEND_HISTORY_PERIOD) {
|
||||||
TODO: Gracefully handle the case where we run out of records.
|
// Node has been away for a while.
|
||||||
Maybe replace the oldest record that hasn't been seen in a while and assume they won't be back.
|
storeForwardPlugin->historySend(STOREFORWARD_SEND_HISTORY_PERIOD, whoWeSaw);
|
||||||
|
|
||||||
TODO: Implment this as a std::map for quicker lookups (maybe it doesn't matter?).
|
|
||||||
*/
|
|
||||||
// DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff,
|
|
||||||
// p->want_ack, p->hop_limit);
|
|
||||||
DEBUG_MSG("looking for node - from-0x%08x\n", node);
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
// DEBUG_MSG("Iterating through the seen nodes - %u %u %u\n", i, receivedRecord[i][0], receivedRecord[i][1]);
|
|
||||||
// First time seeing that node.
|
|
||||||
if (receivedRecord[i][0] == 0) {
|
|
||||||
// DEBUG_MSG("New node! Woohoo! Win!\n");
|
|
||||||
receivedRecord[i][0] = node;
|
|
||||||
receivedRecord[i][1] = millis();
|
|
||||||
|
|
||||||
return receivedRecord[i][1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// We've seen this node before.
|
|
||||||
if (receivedRecord[i][0] == node) {
|
|
||||||
// DEBUG_MSG("We've seen this node before\n");
|
|
||||||
uint32_t lastSaw = receivedRecord[i][1];
|
|
||||||
receivedRecord[i][1] = millis();
|
|
||||||
return lastSaw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StoreForwardPlugin::historyReport()
|
void StoreForwardPlugin::historyReport()
|
||||||
@@ -132,8 +107,15 @@ void StoreForwardPlugin::historyReport()
|
|||||||
}
|
}
|
||||||
DEBUG_MSG("StoreForwardPlugin::historyReport runtime - %u ms\n", millis() - startTimer);
|
DEBUG_MSG("StoreForwardPlugin::historyReport runtime - %u ms\n", millis() - startTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
void StoreForwardPlugin::historySend(uint32_t msAgo, uint32_t to)
|
void StoreForwardPlugin::historySend(uint32_t msAgo, uint32_t to)
|
||||||
{
|
{
|
||||||
|
// Send "Welcome back"
|
||||||
|
this->sendPayloadWelcome(to, false);
|
||||||
|
|
||||||
for (int i = 0; i < this->packetHistoryCurrent; i++) {
|
for (int i = 0; i < this->packetHistoryCurrent; i++) {
|
||||||
if (this->packetHistory[i].time) {
|
if (this->packetHistory[i].time) {
|
||||||
// DEBUG_MSG("... time-%u to-0x%08x\n", this->packetHistory[i].time, this->packetHistory[i].to & 0xffffffff);
|
// DEBUG_MSG("... time-%u to-0x%08x\n", this->packetHistory[i].time, this->packetHistory[i].to & 0xffffffff);
|
||||||
@@ -159,27 +141,6 @@ void StoreForwardPlugin::historyAdd(const MeshPacket *mp)
|
|||||||
this->packetHistoryCurrent++;
|
this->packetHistoryCurrent++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We saw a node.
|
|
||||||
void StoreForwardPlugin::sawNodeReport()
|
|
||||||
{
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: Move receivedRecord into the PSRAM
|
|
||||||
|
|
||||||
TODO: Gracefully handle the case where we run out of records.
|
|
||||||
Maybe replace the oldest record that hasn't been seen in a while and assume they won't be back.
|
|
||||||
|
|
||||||
TODO: Implment this as a std::map for quicker lookups (maybe it doesn't matter?).
|
|
||||||
*/
|
|
||||||
|
|
||||||
DEBUG_MSG("Iterating through the seen nodes in receivedRecord...\n");
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
if (receivedRecord[i][1]) {
|
|
||||||
DEBUG_MSG("... record-%u from-0x%08x secAgo-%u\n", i, receivedRecord[i][0], (millis() - receivedRecord[i][1]) / 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MeshPacket *StoreForwardPlugin::allocReply()
|
MeshPacket *StoreForwardPlugin::allocReply()
|
||||||
{
|
{
|
||||||
auto reply = allocDataPacket(); // Allocate a packet for sending
|
auto reply = allocDataPacket(); // Allocate a packet for sending
|
||||||
@@ -194,8 +155,7 @@ void StoreForwardPlugin::sendPayload(NodeNum dest, bool wantReplies)
|
|||||||
p->decoded.want_response = wantReplies;
|
p->decoded.want_response = wantReplies;
|
||||||
|
|
||||||
p->want_ack = true;
|
p->want_ack = true;
|
||||||
/*
|
|
||||||
*/
|
|
||||||
static char heartbeatString[20];
|
static char heartbeatString[20];
|
||||||
snprintf(heartbeatString, sizeof(heartbeatString), "1");
|
snprintf(heartbeatString, sizeof(heartbeatString), "1");
|
||||||
|
|
||||||
@@ -205,60 +165,50 @@ void StoreForwardPlugin::sendPayload(NodeNum dest, bool wantReplies)
|
|||||||
service.sendToMesh(p);
|
service.sendToMesh(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StoreForwardPlugin::sendPayloadWelcome(NodeNum dest, bool wantReplies)
|
||||||
|
{
|
||||||
|
DEBUG_MSG("*********************************\n");
|
||||||
|
DEBUG_MSG("*********************************\n");
|
||||||
|
DEBUG_MSG("*********************************\n");
|
||||||
|
DEBUG_MSG("Sending S&F Welcome Message\n");
|
||||||
|
DEBUG_MSG("*********************************\n");
|
||||||
|
DEBUG_MSG("*********************************\n");
|
||||||
|
DEBUG_MSG("*********************************\n");
|
||||||
|
MeshPacket *p = allocReply();
|
||||||
|
p->to = dest;
|
||||||
|
p->decoded.want_response = wantReplies;
|
||||||
|
|
||||||
|
p->want_ack = true;
|
||||||
|
|
||||||
|
p->decoded.portnum = PortNum_TEXT_MESSAGE_APP;
|
||||||
|
|
||||||
|
static char heartbeatString[80];
|
||||||
|
snprintf(heartbeatString, sizeof(heartbeatString), "Welcome back to the mesh. We have not seen you in x minutes!");
|
||||||
|
|
||||||
|
p->decoded.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply
|
||||||
|
memcpy(p->decoded.payload.bytes, heartbeatString, p->decoded.payload.size);
|
||||||
|
|
||||||
|
service.sendToMesh(p);
|
||||||
|
}
|
||||||
|
|
||||||
bool StoreForwardPlugin::handleReceived(const MeshPacket &mp)
|
bool StoreForwardPlugin::handleReceived(const MeshPacket &mp)
|
||||||
{
|
{
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
||||||
|
|
||||||
if (getFrom(&mp) != nodeDB.getNodeNum()) {
|
if (getFrom(&mp) != nodeDB.getNodeNum()) {
|
||||||
// DEBUG_MSG("Store & Forward Plugin -- Print Start ---------- ---------- ---------- ---------- ----------\n\n\n");
|
|
||||||
// DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff,
|
|
||||||
// p->want_ack, p->hop_limit);
|
|
||||||
printPacket("----- PACKET FROM RADIO -----", &mp);
|
printPacket("----- PACKET FROM RADIO -----", &mp);
|
||||||
uint32_t sawTime = storeForwardPlugin->sawNode(getFrom(&mp) & 0xffffffff);
|
// uint32_t sawTime = storeForwardPlugin->sawNode(getFrom(&mp) & 0xffffffff);
|
||||||
DEBUG_MSG("We last saw this node (%u), %u sec ago\n", mp.from & 0xffffffff, (millis() - sawTime) / 1000);
|
// DEBUG_MSG("We last saw this node (%u), %u sec ago\n", mp.from & 0xffffffff, (millis() - sawTime) / 1000);
|
||||||
DEBUG_MSG(" -------------- ");
|
DEBUG_MSG(" -------------- ");
|
||||||
if (mp.decoded.portnum == PortNum_UNKNOWN_APP) {
|
if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) {
|
||||||
DEBUG_MSG("Packet came from - PortNum_UNKNOWN_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_TEXT_MESSAGE_APP\n");
|
DEBUG_MSG("Packet came from - PortNum_TEXT_MESSAGE_APP\n");
|
||||||
|
|
||||||
storeForwardPlugin->historyAdd(&mp);
|
storeForwardPlugin->historyAdd(&mp);
|
||||||
|
|
||||||
} else if (mp.decoded.portnum == PortNum_REMOTE_HARDWARE_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_REMOTE_HARDWARE_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_POSITION_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_POSITION_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_NODEINFO_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_NODEINFO_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_ROUTING_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_ROUTING_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_ADMIN_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_ADMIN_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_REPLY_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_REPLY_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_IP_TUNNEL_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_IP_TUNNEL_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_SERIAL_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_SERIAL_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_STORE_FORWARD_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_STORE_FORWARD_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_RANGE_TEST_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_RANGE_TEST_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_PRIVATE_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_PRIVATE_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_RANGE_TEST_APP) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_RANGE_TEST_APP\n");
|
|
||||||
} else if (mp.decoded.portnum == PortNum_ATAK_FORWARDER) {
|
|
||||||
DEBUG_MSG("Packet came from - PortNum_ATAK_FORWARDER\n");
|
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum);
|
DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((millis() - sawTime) > STOREFORWARD_SEND_HISTORY_SHORT) {
|
|
||||||
// Node has been away for a while.
|
|
||||||
storeForwardPlugin->historySend(sawTime, mp.from);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -276,14 +226,18 @@ StoreForwardPlugin::StoreForwardPlugin()
|
|||||||
|
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
|
|
||||||
|
isPromiscuous = true; // Brown chicken brown cow
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Uncomment the preferences below if you want to use the plugin
|
Uncomment the preferences below if you want to use the plugin
|
||||||
without having to configure it from the PythonAPI or WebUI.
|
without having to configure it from the PythonAPI or WebUI.
|
||||||
|
|
||||||
radioConfig.preferences.store_forward_plugin_enabled = 1;
|
|
||||||
radioConfig.preferences.is_router = 1;
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (StoreForward_Dev) {
|
||||||
|
radioConfig.preferences.store_forward_plugin_enabled = 1;
|
||||||
|
radioConfig.preferences.is_router = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
||||||
|
|
||||||
// Router
|
// Router
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ class StoreForwardPlugin : public SinglePortPlugin, private concurrency::OSThrea
|
|||||||
Update our local reference of when we last saw that node.
|
Update our local reference of when we last saw that node.
|
||||||
@return 0 if we have never seen that node before otherwise return the last time we saw the node.
|
@return 0 if we have never seen that node before otherwise return the last time we saw the node.
|
||||||
*/
|
*/
|
||||||
uint32_t sawNode(uint32_t);
|
void sawNode(uint32_t whoWeSaw, uint32_t sawSecAgo);
|
||||||
void sawNodeReport();
|
|
||||||
void historyAdd(const MeshPacket *mp);
|
void historyAdd(const MeshPacket *mp);
|
||||||
void historyReport();
|
void historyReport();
|
||||||
void historySend(uint32_t msAgo, uint32_t to);
|
void historySend(uint32_t msAgo, uint32_t to);
|
||||||
@@ -40,6 +39,7 @@ class StoreForwardPlugin : public SinglePortPlugin, private concurrency::OSThrea
|
|||||||
* Send our payload into the mesh
|
* Send our payload into the mesh
|
||||||
*/
|
*/
|
||||||
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||||
|
void sendPayloadWelcome(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||||
virtual MeshPacket *allocReply();
|
virtual MeshPacket *allocReply();
|
||||||
virtual bool wantPortnum(PortNum p) { return true; };
|
virtual bool wantPortnum(PortNum p) { return true; };
|
||||||
|
|
||||||
@@ -80,4 +80,3 @@ class StoreForwardPluginRadio : public SinglePortPlugin
|
|||||||
|
|
||||||
extern StoreForwardPluginRadio *storeForwardPluginRadio;
|
extern StoreForwardPluginRadio *storeForwardPluginRadio;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[VERSION]
|
[VERSION]
|
||||||
major = 1
|
major = 1
|
||||||
minor = 2
|
minor = 2
|
||||||
build = 13
|
build = 16
|
||||||
|
|||||||
Reference in New Issue
Block a user