Merge branch 'dev' into portduino
# Conflicts: # .vscode/settings.json
This commit is contained in:
+4
-3
@@ -178,10 +178,11 @@ void PowerFSM_setup()
|
||||
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.
|
||||
2) If we detect USB power from the power management chip, we must be getting power externally.
|
||||
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 = (powerStatus && !powerStatus->getHasBattery()) || (!isLowPower && powerStatus && powerStatus->getHasUSB());
|
||||
bool hasPower = !isLowPower && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB());
|
||||
|
||||
DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower);
|
||||
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
|
||||
|
||||
+42
-35
@@ -1,9 +1,9 @@
|
||||
#include "RedirectablePrint.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include "concurrency/OSThread.h"
|
||||
|
||||
/**
|
||||
* A printer that doesn't go anywhere
|
||||
@@ -20,7 +20,7 @@ size_t RedirectablePrint::write(uint8_t c)
|
||||
{
|
||||
// Always send the characters to our segger JTAG debugger
|
||||
#ifdef SEGGER_STDOUT_CH
|
||||
SEGGER_RTT_PutCharSkip(SEGGER_STDOUT_CH, c);
|
||||
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
|
||||
#endif
|
||||
|
||||
dest->write(c);
|
||||
@@ -38,7 +38,7 @@ size_t RedirectablePrint::vprintf(const char *format, va_list arg)
|
||||
va_end(arg);
|
||||
return 0;
|
||||
};
|
||||
if (len >= printBufLen) {
|
||||
if (len >= (int)printBufLen) {
|
||||
delete[] printBuf;
|
||||
printBufLen *= 2;
|
||||
printBuf = new char[printBufLen];
|
||||
@@ -55,45 +55,52 @@ size_t RedirectablePrint::vprintf(const char *format, va_list arg)
|
||||
|
||||
size_t RedirectablePrint::logDebug(const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
|
||||
// Cope with 0 len format strings, but look for new line terminator
|
||||
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
|
||||
|
||||
size_t r = 0;
|
||||
|
||||
// If we are the first message on a report, include the header
|
||||
if (!isContinuationMessage) {
|
||||
struct timeval tv;
|
||||
if (!gettimeofday(&tv, NULL)) {
|
||||
long hms = tv.tv_sec % SEC_PER_DAY;
|
||||
//hms += tz.tz_dsttime * SEC_PER_HOUR;
|
||||
//hms -= tz.tz_minuteswest * SEC_PER_MIN;
|
||||
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
|
||||
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||
if (!inDebugPrint) {
|
||||
inDebugPrint = true;
|
||||
|
||||
// Tear apart hms into h:m:s
|
||||
int hour = hms / SEC_PER_HOUR;
|
||||
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
|
||||
r += printf("%02d:%02d:%02d ", hour, min, sec);
|
||||
} else
|
||||
r += printf("??:??:?? ");
|
||||
// Cope with 0 len format strings, but look for new line terminator
|
||||
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
|
||||
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
if(thread) {
|
||||
print("[");
|
||||
print(thread->ThreadName);
|
||||
print("] ");
|
||||
// If we are the first message on a report, include the header
|
||||
if (!isContinuationMessage) {
|
||||
struct timeval tv;
|
||||
if (!gettimeofday(&tv, NULL)) {
|
||||
long hms = tv.tv_sec % SEC_PER_DAY;
|
||||
// hms += tz.tz_dsttime * SEC_PER_HOUR;
|
||||
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
|
||||
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
|
||||
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||
|
||||
// Tear apart hms into h:m:s
|
||||
int hour = hms / SEC_PER_HOUR;
|
||||
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
||||
|
||||
r += printf("%02d:%02d:%02d %u ", hour, min, sec, millis() / 1000);
|
||||
} else
|
||||
r += printf("??:??:?? %u ", millis() / 1000);
|
||||
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
if (thread) {
|
||||
print("[");
|
||||
// printf("%p ", thread);
|
||||
// assert(thread->ThreadName.length());
|
||||
print(thread->ThreadName);
|
||||
print("] ");
|
||||
}
|
||||
}
|
||||
|
||||
r += vprintf(format, arg);
|
||||
va_end(arg);
|
||||
|
||||
isContinuationMessage = !hasNewline;
|
||||
inDebugPrint = false;
|
||||
}
|
||||
|
||||
r += vprintf(format, arg);
|
||||
va_end(arg);
|
||||
|
||||
isContinuationMessage = !hasNewline;
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -19,6 +19,8 @@ class RedirectablePrint : public Print
|
||||
/// Used to allow multiple logDebug messages to appear on a single log line
|
||||
bool isContinuationMessage = false;
|
||||
|
||||
volatile bool inDebugPrint = false;
|
||||
|
||||
public:
|
||||
RedirectablePrint(Print *_dest) : dest(_dest) {}
|
||||
|
||||
|
||||
+10
-2
@@ -1,13 +1,21 @@
|
||||
#include "SerialConsole.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "configuration.h"
|
||||
#include "NodeDB.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#define Port Serial
|
||||
|
||||
SerialConsole console;
|
||||
|
||||
void consolePrintf(const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
console.vprintf(format, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port)
|
||||
{
|
||||
canWrite = false; // We don't send packets to our port until it has talked to us first
|
||||
@@ -29,7 +37,7 @@ void SerialConsole::init()
|
||||
void SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
|
||||
{
|
||||
// Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets
|
||||
if(!radioConfig.preferences.debug_log_enabled)
|
||||
if (!radioConfig.preferences.debug_log_enabled)
|
||||
setDestination(&noopPrint);
|
||||
canWrite = true;
|
||||
|
||||
|
||||
@@ -32,4 +32,7 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
|
||||
virtual void onConnectionChanged(bool connected);
|
||||
};
|
||||
|
||||
// A simple wrapper to allow non class aware code write to the console
|
||||
void consolePrintf(const char *format, ...);
|
||||
|
||||
extern SerialConsole console;
|
||||
|
||||
@@ -9,6 +9,7 @@ enum class Cmd {
|
||||
SET_OFF,
|
||||
ON_PRESS,
|
||||
START_BLUETOOTH_PIN_SCREEN,
|
||||
START_FIRMWARE_UPDATE_SCREEN,
|
||||
STOP_BLUETOOTH_PIN_SCREEN,
|
||||
STOP_BOOT_SCREEN,
|
||||
PRINT,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "concurrency/BinarySemaphoreFreeRTOS.h"
|
||||
#include "configuration.h"
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
|
||||
@@ -9,6 +10,7 @@ namespace concurrency
|
||||
BinarySemaphoreFreeRTOS::BinarySemaphoreFreeRTOS()
|
||||
{
|
||||
semaphore = xSemaphoreCreateBinary();
|
||||
assert(semaphore);
|
||||
}
|
||||
|
||||
BinarySemaphoreFreeRTOS::~BinarySemaphoreFreeRTOS()
|
||||
|
||||
@@ -32,8 +32,10 @@ OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_contro
|
||||
|
||||
ThreadName = _name;
|
||||
|
||||
if (controller)
|
||||
controller->add(this);
|
||||
if (controller) {
|
||||
bool added = controller->add(this);
|
||||
assert(added);
|
||||
}
|
||||
}
|
||||
|
||||
OSThread::~OSThread()
|
||||
|
||||
+47
-20
@@ -163,7 +163,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#if defined(TBEAM_V10)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tbeam"
|
||||
#define HW_VENDOR HardwareModel_TBEAM
|
||||
|
||||
// #define BUTTON_NEED_PULLUP // if set we need to turn on the internal CPU pullup during sleep
|
||||
|
||||
@@ -204,7 +204,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#elif defined(TBEAM_V07)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tbeam0.7"
|
||||
#define HW_VENDOR HardwareModel_TBEAM0p7
|
||||
|
||||
// #define BUTTON_NEED_PULLUP // if set we need to turn on the internal CPU pullup during sleep
|
||||
|
||||
@@ -228,7 +228,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V2)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "heltec"
|
||||
#define HW_VENDOR HardwareModel_HELTEC
|
||||
|
||||
// the default ESP32 Pin of 15 is the Oled SCL, set to 36 and 37 and works fine.
|
||||
// Tested on Neo6m module.
|
||||
@@ -258,7 +258,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#elif defined(TLORA_V1)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tlora-v1"
|
||||
#define HW_VENDOR HardwareModel_TLORA_V1
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
@@ -282,7 +282,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#elif defined(TLORA_V2)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tlora-v2"
|
||||
#define HW_VENDOR HardwareModel_TLORA_V2
|
||||
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
@@ -309,14 +309,41 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define LORA_DIO1 35 // Not really used
|
||||
#define LORA_DIO2 34 // Not really used
|
||||
|
||||
#elif defined(TLORA_V2_1_16)
|
||||
#elif defined(TLORA_V1_3)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "tlora-v2-1-1.6"
|
||||
#define HW_VENDOR HardwareModel_TLORA_V1p3
|
||||
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
#define GPS_TX_PIN 39
|
||||
#define GPS_TX_PIN 13 // per @eugene
|
||||
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
|
||||
#define I2C_SDA 4 // I2C pins for this board
|
||||
#define I2C_SCL 15
|
||||
|
||||
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
|
||||
|
||||
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
|
||||
#define LED_PIN 25 // If defined we will blink this LED
|
||||
#define BUTTON_PIN 36
|
||||
#define BUTTON_NEED_PULLUP
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 26 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 14
|
||||
#define LORA_DIO1 35 // Not really used
|
||||
#define LORA_DIO2 34 // Not really used
|
||||
|
||||
#elif defined(TLORA_V2_1_16)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR HardwareModel_TLORA_V2_1p6_
|
||||
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 36
|
||||
#define GPS_TX_PIN 13
|
||||
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
|
||||
@@ -327,9 +354,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
|
||||
#define LED_PIN 25 // If defined we will blink this LED
|
||||
#define BUTTON_PIN \
|
||||
12 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
|
||||
// between this pin and ground
|
||||
#define BUTTON_PIN 12 // If defined, this will be used for user button presses,
|
||||
|
||||
#define BUTTON_NEED_PULLUP
|
||||
|
||||
#define USE_RF95
|
||||
@@ -340,7 +366,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#elif defined(GENIEBLOCKS)
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "genieblocks"
|
||||
#define HW_VENDOR HardwareModel_GENIEBLOCKS
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 5
|
||||
@@ -348,15 +374,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define GPS_RESET_N 10
|
||||
#define GPS_EXTINT 23 // On MAX-M8 module pin name is EXTINT. On L70 module pin name is STANDBY.
|
||||
|
||||
#define BATTERY_PIN 39 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define BATTERY_PIN 39 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define BATTERY_EN_PIN 14 // Voltage voltage divider enable pin connected to mosfet
|
||||
|
||||
#define I2C_SDA 4 // I2C pins for this board
|
||||
#define I2C_SCL 2
|
||||
|
||||
#define LED_PIN 12 // If defined we will blink this LED
|
||||
//#define BUTTON_PIN 36 // If defined, this will be used for user button presses (ToDo problem on that line on debug screen --> Long press start!)
|
||||
//#define BUTTON_NEED_PULLUP //GPIOs 34 to 39 are GPIs – input only pins. These pins don’t have internal pull-ups or pull-down resistors.
|
||||
#define LED_PIN 12 // If defined we will blink this LED
|
||||
//#define BUTTON_PIN 36 // If defined, this will be used for user button presses (ToDo problem on that line on debug screen -->
|
||||
//Long press start!) #define BUTTON_NEED_PULLUP //GPIOs 34 to 39 are GPIs – input only pins. These pins don’t have internal
|
||||
//pull-ups or pull-down resistors.
|
||||
|
||||
#define USE_RF95
|
||||
#define LORA_DIO0 38 // a No connect on the SX1262 module
|
||||
@@ -372,7 +399,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifdef ARDUINO_NRF52840_PCA10056
|
||||
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#define HW_VENDOR "nrf52dk"
|
||||
#define HW_VENDOR HardwareModel_NRF52840DK
|
||||
|
||||
// This board uses 0 to be mean LED on
|
||||
#undef LED_INVERTED
|
||||
@@ -380,15 +407,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#elif defined(ARDUINO_NRF52840_PPR)
|
||||
|
||||
#define HW_VENDOR "ppr"
|
||||
#define HW_VENDOR HardwareModel_PPR
|
||||
|
||||
#elif NRF52_SERIES
|
||||
|
||||
#define HW_VENDOR "nrf52unknown" // FIXME - unknown nrf52 board
|
||||
#define HW_VENDOR HardwareModel_NRF52_UNKNOWN
|
||||
|
||||
#elif PORTDUINO
|
||||
|
||||
#define HW_VENDOR "portduino"
|
||||
#define HW_VENDOR HardwareModel_PORTDUINO
|
||||
|
||||
#define USE_SIM_RADIO
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "configuration.h"
|
||||
#include "nimble/BluetoothUtil.h"
|
||||
#include "NodeDB.h"
|
||||
#include "../graphics/Screen.h"
|
||||
#include "../main.h"
|
||||
|
||||
#include <CRC32.h>
|
||||
#include <Update.h>
|
||||
@@ -47,8 +49,9 @@ int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
|
||||
// void stopMeshBluetoothService();
|
||||
// stopMeshBluetoothService();
|
||||
|
||||
screen->startFirmwareUpdateScreen();
|
||||
if (RadioLibInterface::instance)
|
||||
RadioLibInterface::instance->sleep(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we are
|
||||
RadioLibInterface::instance->disable(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we are
|
||||
// writing flash - shut the radio off during updates
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@ class ESP32CryptoEngine : public CryptoEngine
|
||||
|
||||
mbedtls_aes_context aes;
|
||||
|
||||
/// How many bytes in our key
|
||||
uint8_t keySize = 0;
|
||||
|
||||
public:
|
||||
ESP32CryptoEngine() { mbedtls_aes_init(&aes); }
|
||||
|
||||
@@ -35,12 +32,12 @@ class ESP32CryptoEngine : public CryptoEngine
|
||||
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
|
||||
* provided pointer)
|
||||
*/
|
||||
virtual void setKey(size_t numBytes, uint8_t *bytes)
|
||||
virtual void setKey(const CryptoKey &k)
|
||||
{
|
||||
keySize = numBytes;
|
||||
DEBUG_MSG("Installing AES%d key!\n", numBytes * 8);
|
||||
if (numBytes != 0) {
|
||||
auto res = mbedtls_aes_setkey_enc(&aes, bytes, numBytes * 8);
|
||||
CryptoEngine::setKey(k);
|
||||
|
||||
if (key.length != 0) {
|
||||
auto res = mbedtls_aes_setkey_enc(&aes, key.bytes, key.length * 8);
|
||||
assert(!res);
|
||||
}
|
||||
}
|
||||
@@ -52,12 +49,12 @@ class ESP32CryptoEngine : public CryptoEngine
|
||||
*/
|
||||
virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes)
|
||||
{
|
||||
if (keySize != 0) {
|
||||
if (key.length > 0) {
|
||||
uint8_t stream_block[16];
|
||||
static uint8_t scratch[MAX_BLOCKSIZE];
|
||||
size_t nc_off = 0;
|
||||
|
||||
// DEBUG_MSG("ESP32 encrypt!\n");
|
||||
// DEBUG_MSG("ESP32 crypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t) packetNum, numBytes);
|
||||
initNonce(fromNode, packetNum);
|
||||
assert(numBytes <= MAX_BLOCKSIZE);
|
||||
memcpy(scratch, bytes, numBytes);
|
||||
|
||||
+15
-7
@@ -25,7 +25,7 @@ uint8_t GPS::i2cAddress = 0;
|
||||
|
||||
GPS *gps;
|
||||
|
||||
/// Multiple GPS instances might use the same serial port (in sequence), but we can
|
||||
/// Multiple GPS instances might use the same serial port (in sequence), but we can
|
||||
/// only init that port once.
|
||||
static bool didSerialInit;
|
||||
|
||||
@@ -33,7 +33,7 @@ bool GPS::setupGPS()
|
||||
{
|
||||
if (_serial_gps && !didSerialInit) {
|
||||
didSerialInit = true;
|
||||
|
||||
|
||||
#ifdef GPS_RX_PIN
|
||||
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
|
||||
#else
|
||||
@@ -73,6 +73,13 @@ bool GPS::setup()
|
||||
return ok;
|
||||
}
|
||||
|
||||
GPS::~GPS()
|
||||
{
|
||||
// we really should unregister our sleep observer
|
||||
notifySleepObserver.unobserve();
|
||||
notifyDeepSleepObserver.unobserve();
|
||||
}
|
||||
|
||||
// Allow defining the polarity of the WAKE output. default is active high
|
||||
#ifndef GPS_WAKE_ACTIVE
|
||||
#define GPS_WAKE_ACTIVE 1
|
||||
@@ -86,8 +93,8 @@ void GPS::wake()
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void GPS::sleep() {
|
||||
void GPS::sleep()
|
||||
{
|
||||
#ifdef PIN_GPS_WAKE
|
||||
digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE ? 0 : 1);
|
||||
pinMode(PIN_GPS_WAKE, OUTPUT);
|
||||
@@ -158,7 +165,8 @@ uint32_t GPS::getWakeTime() const
|
||||
return t; // already maxint
|
||||
|
||||
if (t == 0)
|
||||
t = radioConfig.preferences.is_router ? 5 * 60 : 15 * 60; // Allow up to 15 mins for each attempt (probably will be much less if we can find sats) or less if a router
|
||||
t = radioConfig.preferences.is_router ? 5 * 60 : 15 * 60; // Allow up to 15 mins for each attempt (probably will be much
|
||||
// less if we can find sats) or less if a router
|
||||
|
||||
t *= 1000; // msecs
|
||||
|
||||
@@ -179,8 +187,8 @@ uint32_t GPS::getSleepTime() const
|
||||
if (t == UINT32_MAX)
|
||||
return t; // already maxint
|
||||
|
||||
if (t == 0) // default - unset in preferences
|
||||
t = radioConfig.preferences.is_router ? 24 * 60 * 60 : 2 * 60; // 2 mins or once per day for routers
|
||||
if (t == 0) // default - unset in preferences
|
||||
t = radioConfig.preferences.is_router ? 24 * 60 * 60 : 2 * 60; // 2 mins or once per day for routers
|
||||
|
||||
t *= 1000;
|
||||
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ class GPS : private concurrency::OSThread
|
||||
|
||||
GPS() : concurrency::OSThread("GPS") {}
|
||||
|
||||
virtual ~GPS() {} // FIXME, we really should unregister our sleep observer
|
||||
virtual ~GPS();
|
||||
|
||||
/** We will notify this observable anytime GPS state has changed meaningfully */
|
||||
Observable<const meshtastic::GPSStatus *> newStatus;
|
||||
|
||||
+88
-11
@@ -32,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "plugins/TextMessagePlugin.h"
|
||||
#include "mesh/Channels.h"
|
||||
#include "target_specific.h"
|
||||
#include "utils.h"
|
||||
|
||||
@@ -64,6 +65,10 @@ uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
|
||||
// Threshold values for the GPS lock accuracy bar display
|
||||
uint32_t dopThresholds[5] = {2000, 1000, 500, 200, 100};
|
||||
|
||||
// At some point, we're going to ask all of the plugins if they would like to display a screen frame
|
||||
// we'll need to hold onto pointers for the plugins that can draw a frame.
|
||||
std::vector<MeshPlugin *> pluginFrames;
|
||||
|
||||
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
|
||||
static char ourId[5];
|
||||
|
||||
@@ -138,11 +143,37 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
drawIconScreen(region, display, state, x, y);
|
||||
}
|
||||
|
||||
#ifdef HAS_EINK
|
||||
/// Used on eink displays while in deep sleep
|
||||
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
drawIconScreen("Sleeping...", display, state, x, y);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void drawPluginFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
uint8_t plugin_frame;
|
||||
// there's a little but in the UI transition code
|
||||
// where it invokes the function at the correct offset
|
||||
// in the array of "drawScreen" functions; however,
|
||||
// the passed-state doesn't quite reflect the "current"
|
||||
// screen, so we have to detect it.
|
||||
if (state->frameState == IN_TRANSITION && state->transitionFrameRelationship == INCOMING) {
|
||||
// if we're transitioning from the end of the frame list back around to the first
|
||||
// frame, then we want this to be `0`
|
||||
plugin_frame = state->transitionFrameTarget;
|
||||
}
|
||||
else {
|
||||
// otherwise, just display the plugin frame that's aligned with the current frame
|
||||
plugin_frame = state->currentFrame;
|
||||
//DEBUG_MSG("Screen is not in transition. Frame: %d\n\n", plugin_frame);
|
||||
}
|
||||
//DEBUG_MSG("Drawing Plugin Frame %d\n\n", plugin_frame);
|
||||
MeshPlugin &pi = *pluginFrames.at(plugin_frame);
|
||||
pi.drawFrame(display,state,x,y);
|
||||
|
||||
}
|
||||
|
||||
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -164,6 +195,20 @@ static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
display->drawString(64 + x, 48 + y, buf);
|
||||
}
|
||||
|
||||
static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(64 + x, y, "Updating");
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait...");
|
||||
|
||||
//display->setFont(FONT_LARGE);
|
||||
//display->drawString(64 + x, 26 + y, btPIN);
|
||||
}
|
||||
|
||||
|
||||
/// Draw the last text message we received
|
||||
static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -186,7 +231,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
displayedNodeNum = 0; // Not currently showing a node pane
|
||||
|
||||
MeshPacket &mp = devicestate.rx_text_message;
|
||||
NodeInfo *node = nodeDB.getNode(mp.from);
|
||||
NodeInfo *node = nodeDB.getNode(getFrom(&mp));
|
||||
// DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from,
|
||||
// mp.decoded.variant.data.decoded.bytes);
|
||||
|
||||
@@ -201,8 +246,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
|
||||
// the max length of this buffer is much longer than we can possibly print
|
||||
static char tempBuf[96];
|
||||
assert(mp.decoded.which_payloadVariant == SubPacket_data_tag);
|
||||
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.data.payload.bytes);
|
||||
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.payload.bytes);
|
||||
|
||||
display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf);
|
||||
}
|
||||
@@ -749,7 +793,8 @@ void Screen::setup()
|
||||
powerStatusObserver.observe(&powerStatus->onNewStatus);
|
||||
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
|
||||
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||
textMessageObserver.observe(textMessagePlugin);
|
||||
if(textMessagePlugin)
|
||||
textMessageObserver.observe(textMessagePlugin);
|
||||
}
|
||||
|
||||
void Screen::forceDisplay()
|
||||
@@ -776,9 +821,6 @@ int32_t Screen::runOnce()
|
||||
showingBootScreen = false;
|
||||
}
|
||||
|
||||
// Update the screen last, after we've figured out what to show.
|
||||
debug_info()->setChannelNameStatus(getChannelName());
|
||||
|
||||
// Process incoming commands.
|
||||
for (;;) {
|
||||
ScreenCmd cmd;
|
||||
@@ -798,6 +840,9 @@ int32_t Screen::runOnce()
|
||||
case Cmd::START_BLUETOOTH_PIN_SCREEN:
|
||||
handleStartBluetoothPinScreen(cmd.bluetooth_pin);
|
||||
break;
|
||||
case Cmd::START_FIRMWARE_UPDATE_SCREEN:
|
||||
handleStartFirmwareUpdateScreen();
|
||||
break;
|
||||
case Cmd::STOP_BLUETOOTH_PIN_SCREEN:
|
||||
case Cmd::STOP_BOOT_SCREEN:
|
||||
setFrames();
|
||||
@@ -807,7 +852,7 @@ int32_t Screen::runOnce()
|
||||
free(cmd.print_text);
|
||||
break;
|
||||
default:
|
||||
DEBUG_MSG("BUG: invalid cmd");
|
||||
DEBUG_MSG("BUG: invalid cmd\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,6 +915,11 @@ void Screen::setFrames()
|
||||
DEBUG_MSG("showing standard frames\n");
|
||||
showingNormalScreen = true;
|
||||
|
||||
pluginFrames = MeshPlugin::GetMeshPluginsWithUIFrames();
|
||||
DEBUG_MSG("Showing %d plugin frames\n", pluginFrames.size());
|
||||
int totalFrameCount = MAX_NUM_NODES + NUM_EXTRA_FRAMES + pluginFrames.size();
|
||||
DEBUG_MSG("Total frame count: %d\n", totalFrameCount);
|
||||
|
||||
// We don't show the node info our our node (if we have it yet - we should)
|
||||
size_t numnodes = nodeStatus->getNumTotal();
|
||||
if (numnodes > 0)
|
||||
@@ -877,6 +927,18 @@ void Screen::setFrames()
|
||||
|
||||
size_t numframes = 0;
|
||||
|
||||
// put all of the plugin frames first.
|
||||
// this is a little bit of a dirty hack; since we're going to call
|
||||
// the same drawPluginFrame handler here for all of these plugin frames
|
||||
// and then we'll just assume that the state->currentFrame value
|
||||
// is the same offset into the pluginFrames vector
|
||||
// so that we can invoke the plugin's callback
|
||||
for (auto i = pluginFrames.begin(); i != pluginFrames.end(); ++i) {
|
||||
normalFrames[numframes++] = drawPluginFrame;
|
||||
}
|
||||
|
||||
DEBUG_MSG("Added plugins. numframes: %d\n", numframes);
|
||||
|
||||
// If we have a critical fault, show it first
|
||||
if (myNodeInfo.error_code)
|
||||
normalFrames[numframes++] = drawCriticalFaultFrame;
|
||||
@@ -905,6 +967,8 @@ void Screen::setFrames()
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_MSG("Finished building frames. numframes: %d\n", numframes);
|
||||
|
||||
ui.setFrames(normalFrames, numframes);
|
||||
ui.enableAllIndicators();
|
||||
|
||||
@@ -928,6 +992,18 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
void Screen::handleStartFirmwareUpdateScreen()
|
||||
{
|
||||
DEBUG_MSG("showing firmware screen\n");
|
||||
showingNormalScreen = false;
|
||||
|
||||
static FrameCallback btFrames[] = {drawFrameFirmware};
|
||||
|
||||
ui.disableAllIndicators();
|
||||
ui.setFrames(btFrames, 1);
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
void Screen::blink()
|
||||
{
|
||||
setFastFramerate();
|
||||
@@ -993,7 +1069,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
char channelStr[20];
|
||||
{
|
||||
concurrency::LockGuard guard(&lock);
|
||||
snprintf(channelStr, sizeof(channelStr), "%s", channelName.c_str());
|
||||
auto chName = channels.getPrimaryName();
|
||||
snprintf(channelStr, sizeof(channelStr), "%s", chName);
|
||||
}
|
||||
|
||||
// Display power status
|
||||
@@ -1200,8 +1277,8 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
|
||||
display->drawString(x, y, String("USB"));
|
||||
}
|
||||
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("Mode " + String(channelSettings.modem_config)), y,
|
||||
"Mode " + String(channelSettings.modem_config));
|
||||
auto mode = "Mode " + String(channels.getPrimary().modem_config);
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
|
||||
|
||||
// Line 2
|
||||
uint32_t currentMillis = millis();
|
||||
|
||||
@@ -39,13 +39,6 @@ class DebugInfo
|
||||
DebugInfo(const DebugInfo &) = delete;
|
||||
DebugInfo &operator=(const DebugInfo &) = delete;
|
||||
|
||||
/// Sets the name of the channel.
|
||||
void setChannelNameStatus(const char *name)
|
||||
{
|
||||
concurrency::LockGuard guard(&lock);
|
||||
channelName = name;
|
||||
}
|
||||
|
||||
private:
|
||||
friend Screen;
|
||||
|
||||
@@ -56,8 +49,6 @@ class DebugInfo
|
||||
void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
void drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
|
||||
std::string channelName;
|
||||
|
||||
/// Protects all of internal state.
|
||||
concurrency::Lock lock;
|
||||
};
|
||||
@@ -128,6 +119,14 @@ class Screen : public concurrency::OSThread
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
void startFirmwareUpdateScreen()
|
||||
{
|
||||
ScreenCmd cmd;
|
||||
cmd.cmd = Cmd::START_FIRMWARE_UPDATE_SCREEN;
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
|
||||
/// Stops showing the bluetooth PIN screen.
|
||||
void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
|
||||
|
||||
@@ -233,6 +232,7 @@ class Screen : public concurrency::OSThread
|
||||
void handleOnPress();
|
||||
void handleStartBluetoothPinScreen(uint32_t pin);
|
||||
void handlePrint(const char *text);
|
||||
void handleStartFirmwareUpdateScreen();
|
||||
|
||||
/// Rebuilds our list of frames (screens) to default ones.
|
||||
void setFrames();
|
||||
|
||||
+16
-4
@@ -283,7 +283,13 @@ void setup()
|
||||
concurrency::hasBeenSetup = true;
|
||||
|
||||
#ifdef SEGGER_STDOUT_CH
|
||||
SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, 1024, SEGGER_RTT_MODE_NO_BLOCK_TRIM);
|
||||
auto mode = false ? SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL : SEGGER_RTT_MODE_NO_BLOCK_TRIM;
|
||||
#ifdef NRF52840_XXAA
|
||||
auto buflen = 4096; // this board has a fair amount of ram
|
||||
#else
|
||||
auto buflen = 256; // this board has a fair amount of ram
|
||||
#endif
|
||||
SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, buflen, mode);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SEGGER
|
||||
@@ -309,12 +315,15 @@ void setup()
|
||||
|
||||
#ifdef BUTTON_PIN
|
||||
#ifndef NO_ESP32
|
||||
// If BUTTON_PIN is held down during the startup process,
|
||||
// force the device to go into a SoftAP mode.
|
||||
bool forceSoftAP = 0;
|
||||
|
||||
// If the button is connected to GPIO 12, don't enable the ability to use
|
||||
// meshtasticAdmin on the device.
|
||||
pinMode(BUTTON_PIN, INPUT);
|
||||
|
||||
#ifdef BUTTON_NEED_PULLUP
|
||||
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
||||
delay(10);
|
||||
#endif
|
||||
|
||||
// BUTTON_PIN is pulled high by a 12k resistor.
|
||||
@@ -360,7 +369,7 @@ void setup()
|
||||
#endif
|
||||
|
||||
// Hello
|
||||
DEBUG_MSG("Meshtastic swver=%s, hwver=%s\n", optstr(APP_VERSION), optstr(HW_VERSION));
|
||||
DEBUG_MSG("Meshtastic hwvendor=%d, swver=%s, hwver=%s\n", HW_VENDOR, optstr(APP_VERSION), optstr(HW_VERSION));
|
||||
|
||||
#ifndef NO_ESP32
|
||||
// Don't init display if we don't have one or we are waking headless due to a timer event
|
||||
@@ -588,6 +597,9 @@ void loop()
|
||||
#ifndef NO_ESP32
|
||||
esp32Loop();
|
||||
#endif
|
||||
#ifdef NRF52_SERIES
|
||||
nrf52Loop();
|
||||
#endif
|
||||
|
||||
// For debugging
|
||||
// if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();
|
||||
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* mtest - Perform a memory test
|
||||
*
|
||||
* (C) Copyright 2000
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
*
|
||||
* See file CREDITS for list of people who contributed to this
|
||||
* project.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
#include "configuration.h"
|
||||
|
||||
/*
|
||||
* Perform a memory test. A more complete alternative test can be
|
||||
* configured using CONFIG_CMD_MTEST_ALTERNATIVE. The complete test
|
||||
* loops until interrupted by ctrl-c or by a failure of one of the
|
||||
* sub-tests.
|
||||
*/
|
||||
#ifdef CONFIG_CMD_MTEST_ALTERNATIVE
|
||||
static int mem_test(uint32_t _start, uint32_t _end, uint32_t pattern_unused)
|
||||
{
|
||||
volatile uint32_t *start = (volatile uint32_t *)_start;
|
||||
volatile uint32_t *end = (volatile uint32_t *)_end;
|
||||
volatile uint32_t *addr;
|
||||
uint32_t val;
|
||||
uint32_t readback;
|
||||
vu_long addr_mask;
|
||||
vu_long offset;
|
||||
vu_long test_offset;
|
||||
vu_long pattern;
|
||||
vu_long temp;
|
||||
vu_long anti_pattern;
|
||||
vu_long num_words;
|
||||
#ifdef CFG_MEMTEST_SCRATCH
|
||||
volatile uint32_t *dummy = (vu_long *)CFG_MEMTEST_SCRATCH;
|
||||
#else
|
||||
volatile uint32_t *dummy = start;
|
||||
#endif
|
||||
int j;
|
||||
int iterations = 1;
|
||||
static const uint32_t bitpattern[] = {
|
||||
0x00000001, /* single bit */
|
||||
0x00000003, /* two adjacent bits */
|
||||
0x00000007, /* three adjacent bits */
|
||||
0x0000000F, /* four adjacent bits */
|
||||
0x00000005, /* two non-adjacent bits */
|
||||
0x00000015, /* three non-adjacent bits */
|
||||
0x00000055, /* four non-adjacent bits */
|
||||
0xaaaaaaaa, /* alternating 1/0 */
|
||||
};
|
||||
/* XXX: enforce alignment of start and end? */
|
||||
for (;;) {
|
||||
if (ctrlc()) {
|
||||
putchar('\n');
|
||||
return 1;
|
||||
}
|
||||
printf("Iteration: %6d\r", iterations);
|
||||
iterations++;
|
||||
/*
|
||||
* Data line test: write a pattern to the first
|
||||
* location, write the 1's complement to a 'parking'
|
||||
* address (changes the state of the data bus so a
|
||||
* floating bus doen't give a false OK), and then
|
||||
* read the value back. Note that we read it back
|
||||
* into a variable because the next time we read it,
|
||||
* it might be right (been there, tough to explain to
|
||||
* the quality guys why it prints a failure when the
|
||||
* "is" and "should be" are obviously the same in the
|
||||
* error message).
|
||||
*
|
||||
* Rather than exhaustively testing, we test some
|
||||
* patterns by shifting '1' bits through a field of
|
||||
* '0's and '0' bits through a field of '1's (i.e.
|
||||
* pattern and ~pattern).
|
||||
*/
|
||||
addr = start;
|
||||
/* XXX */
|
||||
if (addr == dummy)
|
||||
++addr;
|
||||
for (j = 0; j < sizeof(bitpattern) / sizeof(bitpattern[0]); j++) {
|
||||
val = bitpattern[j];
|
||||
for (; val != 0; val <<= 1) {
|
||||
*addr = val;
|
||||
*dummy = ~val; /* clear the test data off of the bus */
|
||||
readback = *addr;
|
||||
if (readback != val) {
|
||||
printf("FAILURE (data line): "
|
||||
"expected 0x%08lx, actual 0x%08lx at address 0x%p\n",
|
||||
val, readback, addr);
|
||||
}
|
||||
*addr = ~val;
|
||||
*dummy = val;
|
||||
readback = *addr;
|
||||
if (readback != ~val) {
|
||||
printf("FAILURE (data line): "
|
||||
"Is 0x%08lx, should be 0x%08lx at address 0x%p\n",
|
||||
readback, ~val, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Based on code whose Original Author and Copyright
|
||||
* information follows: Copyright (c) 1998 by Michael
|
||||
* Barr. This software is placed into the public
|
||||
* domain and may be used for any purpose. However,
|
||||
* this notice must not be changed or removed and no
|
||||
* warranty is either expressed or implied by its
|
||||
* publication or distribution.
|
||||
*/
|
||||
/*
|
||||
* Address line test
|
||||
*
|
||||
* Description: Test the address bus wiring in a
|
||||
* memory region by performing a walking
|
||||
* 1's test on the relevant bits of the
|
||||
* address and checking for aliasing.
|
||||
* This test will find single-bit
|
||||
* address failures such as stuck -high,
|
||||
* stuck-low, and shorted pins. The base
|
||||
* address and size of the region are
|
||||
* selected by the caller.
|
||||
*
|
||||
* Notes: For best results, the selected base
|
||||
* address should have enough LSB 0's to
|
||||
* guarantee single address bit changes.
|
||||
* For example, to test a 64-Kbyte
|
||||
* region, select a base address on a
|
||||
* 64-Kbyte boundary. Also, select the
|
||||
* region size as a power-of-two if at
|
||||
* all possible.
|
||||
*
|
||||
* Returns: 0 if the test succeeds, 1 if the test fails.
|
||||
*
|
||||
* ## NOTE ## Be sure to specify start and end
|
||||
* addresses such that addr_mask has
|
||||
* lots of bits set. For example an
|
||||
* address range of 01000000 02000000 is
|
||||
* bad while a range of 01000000
|
||||
* 01ffffff is perfect.
|
||||
*/
|
||||
addr_mask = ((uint32_t)end - (uint32_t)start) / sizeof(vu_long);
|
||||
pattern = (vu_long)0xaaaaaaaa;
|
||||
anti_pattern = (vu_long)0x55555555;
|
||||
debug("%s:%d: addr mask = 0x%.8lx\n", __FUNCTION__, __LINE__, addr_mask);
|
||||
/*
|
||||
* Write the default pattern at each of the
|
||||
* power-of-two offsets.
|
||||
*/
|
||||
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1)
|
||||
start[offset] = pattern;
|
||||
/*
|
||||
* Check for address bits stuck high.
|
||||
*/
|
||||
test_offset = 0;
|
||||
start[test_offset] = anti_pattern;
|
||||
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1) {
|
||||
temp = start[offset];
|
||||
if (temp != pattern) {
|
||||
printf("\nFAILURE: Address bit stuck high @ 0x%.8lx:"
|
||||
" expected 0x%.8lx, actual 0x%.8lx\n",
|
||||
(uint32_t)&start[offset], pattern, temp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
start[test_offset] = pattern;
|
||||
/*
|
||||
* Check for addr bits stuck low or shorted.
|
||||
*/
|
||||
for (test_offset = 1; (test_offset & addr_mask) != 0; test_offset <<= 1) {
|
||||
start[test_offset] = anti_pattern;
|
||||
for (offset = 1; (offset & addr_mask) != 0; offset <<= 1) {
|
||||
temp = start[offset];
|
||||
if ((temp != pattern) && (offset != test_offset)) {
|
||||
printf("\nFAILURE: Address bit stuck low or shorted @"
|
||||
" 0x%.8lx: expected 0x%.8lx, actual 0x%.8lx\n",
|
||||
(uint32_t)&start[offset], pattern, temp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
start[test_offset] = pattern;
|
||||
}
|
||||
/*
|
||||
* Description: Test the integrity of a physical
|
||||
* memory device by performing an
|
||||
* increment/decrement test over the
|
||||
* entire region. In the process every
|
||||
* storage bit in the device is tested
|
||||
* as a zero and a one. The base address
|
||||
* and the size of the region are
|
||||
* selected by the caller.
|
||||
*
|
||||
* Returns: 0 if the test succeeds, 1 if the test fails.
|
||||
*/
|
||||
num_words = ((uint32_t)end - (uint32_t)start) / sizeof(vu_long) + 1;
|
||||
/*
|
||||
* Fill memory with a known pattern.
|
||||
*/
|
||||
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
|
||||
start[offset] = pattern;
|
||||
}
|
||||
/*
|
||||
* Check each location and invert it for the second pass.
|
||||
*/
|
||||
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
|
||||
temp = start[offset];
|
||||
if (temp != pattern) {
|
||||
printf("\nFAILURE (read/write) @ 0x%.8lx:"
|
||||
" expected 0x%.8lx, actual 0x%.8lx)\n",
|
||||
(uint32_t)&start[offset], pattern, temp);
|
||||
return 1;
|
||||
}
|
||||
anti_pattern = ~pattern;
|
||||
start[offset] = anti_pattern;
|
||||
}
|
||||
/*
|
||||
* Check each location for the inverted pattern and zero it.
|
||||
*/
|
||||
for (pattern = 1, offset = 0; offset < num_words; pattern++, offset++) {
|
||||
anti_pattern = ~pattern;
|
||||
temp = start[offset];
|
||||
if (temp != anti_pattern) {
|
||||
printf("\nFAILURE (read/write): @ 0x%.8lx:"
|
||||
" expected 0x%.8lx, actual 0x%.8lx)\n",
|
||||
(uint32_t)&start[offset], anti_pattern, temp);
|
||||
return 1;
|
||||
}
|
||||
start[offset] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static int mem_test(uint32_t *_start, size_t len, bool doRead = true, bool doWrite = true)
|
||||
{
|
||||
volatile uint32_t *addr;
|
||||
volatile uint32_t *start = (volatile uint32_t *)_start;
|
||||
volatile uint32_t *end = start + len / sizeof(uint32_t);
|
||||
uint32_t pattern = 0;
|
||||
uint32_t val;
|
||||
uint32_t readback;
|
||||
uint32_t incr;
|
||||
int rcode = 0;
|
||||
incr = 1;
|
||||
|
||||
//DEBUG_MSG("memtest read=%d, write=%d\n", doRead, doWrite);
|
||||
|
||||
if (doWrite) {
|
||||
//DEBUG_MSG("writing\n");
|
||||
for (addr = start, val = pattern; addr < end; addr++) {
|
||||
*addr = val;
|
||||
val += incr;
|
||||
}
|
||||
}
|
||||
|
||||
if (doRead) {
|
||||
//DEBUG_MSG("reading\n");
|
||||
for (addr = start, val = pattern; addr < end; addr++) {
|
||||
readback = *addr;
|
||||
if (readback != val) {
|
||||
DEBUG_MSG("Mem error @ 0x%08X: "
|
||||
"found 0x%08lX, expected 0x%08lX\n",
|
||||
addr, readback, val);
|
||||
rcode++;
|
||||
}
|
||||
val += incr;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Flip the pattern each time to make lots of zeros and
|
||||
* then, the next time, lots of ones. We decrement
|
||||
* the "negative" patterns and increment the "positive"
|
||||
* patterns to preserve this feature.
|
||||
*/
|
||||
if(pattern & 0x80000000) {
|
||||
pattern = -pattern; /* complement & increment */
|
||||
}
|
||||
else {
|
||||
pattern = ~pattern;
|
||||
}
|
||||
#endif
|
||||
|
||||
return rcode;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define TESTBUF_LEN 16384
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void doMemTest()
|
||||
{
|
||||
static uint32_t *testBuf;
|
||||
static int iter;
|
||||
|
||||
if (!testBuf)
|
||||
testBuf = (uint32_t *)malloc(TESTBUF_LEN);
|
||||
|
||||
assert(testBuf);
|
||||
if (mem_test(testBuf, TESTBUF_LEN, iter % 2 == 1, iter % 2 == 0) > 0)
|
||||
assert(0); // FIXME report error better
|
||||
|
||||
iter++;
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
#include "Channels.h"
|
||||
#include "CryptoEngine.h"
|
||||
#include "NodeDB.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
|
||||
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
|
||||
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
|
||||
|
||||
Channels channels;
|
||||
|
||||
const char *Channels::adminChannel = "admin";
|
||||
const char *Channels::gpioChannel = "gpio";
|
||||
const char *Channels::serialChannel = "serial";
|
||||
|
||||
uint8_t xorHash(const uint8_t *p, size_t len)
|
||||
{
|
||||
uint8_t code = 0;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
code ^= p[i];
|
||||
return code;
|
||||
}
|
||||
|
||||
/** Given a channel number, return the (0 to 255) hash for that channel.
|
||||
* The hash is just an xor of the channel name followed by the channel PSK being used for encryption
|
||||
* If no suitable channel could be found, return -1
|
||||
*/
|
||||
int16_t Channels::generateHash(ChannelIndex channelNum)
|
||||
{
|
||||
auto k = getKey(channelNum);
|
||||
if (k.length < 0)
|
||||
return -1; // invalid
|
||||
else {
|
||||
const char *name = getName(channelNum);
|
||||
uint8_t h = xorHash((const uint8_t *) name, strlen(name));
|
||||
|
||||
h ^= xorHash(k.bytes, k.length);
|
||||
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a channel, fixing any errors as needed
|
||||
*/
|
||||
Channel &Channels::fixupChannel(ChannelIndex chIndex)
|
||||
{
|
||||
Channel &ch = getByIndex(chIndex);
|
||||
|
||||
ch.index = chIndex; // Preinit the index so it be ready to share with the phone (we'll never change it later)
|
||||
|
||||
if (!ch.has_settings) {
|
||||
// No settings! Must disable and skip
|
||||
ch.role = Channel_Role_DISABLED;
|
||||
memset(&ch.settings, 0, sizeof(ch.settings));
|
||||
ch.has_settings = true;
|
||||
} else {
|
||||
ChannelSettings &channelSettings = ch.settings;
|
||||
|
||||
// Convert the old string "Default" to our new short representation
|
||||
if (strcmp(channelSettings.name, "Default") == 0)
|
||||
*channelSettings.name = '\0';
|
||||
|
||||
/* Convert any old usage of the defaultpsk into our new short representation.
|
||||
if (channelSettings.psk.size == sizeof(defaultpsk) &&
|
||||
memcmp(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)) == 0) {
|
||||
*channelSettings.psk.bytes = 1;
|
||||
channelSettings.psk.size = 1;
|
||||
} */
|
||||
}
|
||||
|
||||
hashes[chIndex] = generateHash(chIndex);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a default channel to the specified channel index
|
||||
*/
|
||||
void Channels::initDefaultChannel(ChannelIndex chIndex)
|
||||
{
|
||||
Channel &ch = getByIndex(chIndex);
|
||||
ChannelSettings &channelSettings = ch.settings;
|
||||
|
||||
// radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast
|
||||
// channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide
|
||||
// bandwidth so incompatible radios can talk together
|
||||
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
|
||||
|
||||
channelSettings.tx_power = 0; // default
|
||||
uint8_t defaultpskIndex = 1;
|
||||
channelSettings.psk.bytes[0] = defaultpskIndex;
|
||||
channelSettings.psk.size = 1;
|
||||
strcpy(channelSettings.name, "");
|
||||
|
||||
ch.has_settings = true;
|
||||
ch.role = Channel_Role_PRIMARY;
|
||||
}
|
||||
|
||||
CryptoKey Channels::getKey(ChannelIndex chIndex)
|
||||
{
|
||||
Channel &ch = getByIndex(chIndex);
|
||||
ChannelSettings &channelSettings = ch.settings;
|
||||
assert(ch.has_settings);
|
||||
|
||||
CryptoKey k;
|
||||
memset(k.bytes, 0, sizeof(k.bytes)); // In case the user provided a short key, we want to pad the rest with zeros
|
||||
|
||||
if (ch.role == Channel_Role_DISABLED) {
|
||||
k.length = -1; // invalid
|
||||
} else {
|
||||
memcpy(k.bytes, channelSettings.psk.bytes, channelSettings.psk.size);
|
||||
k.length = channelSettings.psk.size;
|
||||
if (k.length == 0) {
|
||||
if (ch.role == Channel_Role_SECONDARY) {
|
||||
DEBUG_MSG("Unset PSK for secondary channel %s. using primary key\n", ch.settings.name);
|
||||
k = getKey(primaryIndex);
|
||||
} else
|
||||
DEBUG_MSG("Warning: User disabled encryption\n");
|
||||
} else if (k.length == 1) {
|
||||
// Convert the short single byte variants of psk into variant that can be used more generally
|
||||
|
||||
uint8_t pskIndex = k.bytes[0];
|
||||
DEBUG_MSG("Expanding short PSK #%d\n", pskIndex);
|
||||
if (pskIndex == 0)
|
||||
k.length = 0; // Turn off encryption
|
||||
else {
|
||||
memcpy(k.bytes, defaultpsk, sizeof(defaultpsk));
|
||||
k.length = sizeof(defaultpsk);
|
||||
// Bump up the last byte of PSK as needed
|
||||
uint8_t *last = k.bytes + sizeof(defaultpsk) - 1;
|
||||
*last = *last + pskIndex - 1; // index of 1 means no change vs defaultPSK
|
||||
}
|
||||
} else if (k.length < 16) {
|
||||
// Error! The user specified only the first few bits of an AES128 key. So by convention we just pad the rest of the
|
||||
// key with zeros
|
||||
DEBUG_MSG("Warning: User provided a too short AES128 key - padding\n");
|
||||
k.length = 16;
|
||||
} else if (k.length < 32 && k.length != 16) {
|
||||
// Error! The user specified only the first few bits of an AES256 key. So by convention we just pad the rest of the
|
||||
// key with zeros
|
||||
DEBUG_MSG("Warning: User provided a too short AES256 key - padding\n");
|
||||
k.length = 32;
|
||||
}
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
/** Given a channel index, change to use the crypto key specified by that index
|
||||
*/
|
||||
int16_t Channels::setCrypto(ChannelIndex chIndex)
|
||||
{
|
||||
CryptoKey k = getKey(chIndex);
|
||||
|
||||
if (k.length < 0)
|
||||
return -1;
|
||||
else {
|
||||
// Tell our crypto engine about the psk
|
||||
crypto->setKey(k);
|
||||
return getHash(chIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void Channels::initDefaults()
|
||||
{
|
||||
channelFile.channels_count = MAX_NUM_CHANNELS;
|
||||
for (int i = 0; i < channelFile.channels_count; i++)
|
||||
fixupChannel(i);
|
||||
initDefaultChannel(0);
|
||||
}
|
||||
|
||||
void Channels::onConfigChanged()
|
||||
{
|
||||
// Make sure the phone hasn't mucked anything up
|
||||
for (int i = 0; i < channelFile.channels_count; i++) {
|
||||
Channel &ch = fixupChannel(i);
|
||||
|
||||
if (ch.role == Channel_Role_PRIMARY)
|
||||
primaryIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
Channel &Channels::getByIndex(ChannelIndex chIndex)
|
||||
{
|
||||
assert(chIndex < channelFile.channels_count);
|
||||
Channel *ch = channelFile.channels + chIndex;
|
||||
return *ch;
|
||||
}
|
||||
|
||||
void Channels::setChannel(const Channel &c)
|
||||
{
|
||||
Channel &old = getByIndex(c.index);
|
||||
|
||||
// if this is the new primary, demote any existing roles
|
||||
if (c.role == Channel_Role_PRIMARY)
|
||||
for (int i = 0; i < getNumChannels(); i++)
|
||||
if (channelFile.channels[i].role == Channel_Role_PRIMARY)
|
||||
channelFile.channels[i].role = Channel_Role_SECONDARY;
|
||||
|
||||
old = c; // slam in the new settings/role
|
||||
}
|
||||
|
||||
const char *Channels::getName(size_t chIndex)
|
||||
{
|
||||
// Convert the short "" representation for Default into a usable string
|
||||
ChannelSettings &channelSettings = getByIndex(chIndex).settings;
|
||||
const char *channelName = channelSettings.name;
|
||||
if (!*channelName) { // emptystring
|
||||
// Per mesh.proto spec, if bandwidth is specified we must ignore modemConfig enum, we assume that in that case
|
||||
// the app fucked up and forgot to set channelSettings.name
|
||||
|
||||
if (channelSettings.bandwidth != 0)
|
||||
channelName = "Unset";
|
||||
else
|
||||
switch (channelSettings.modem_config) {
|
||||
case ChannelSettings_ModemConfig_Bw125Cr45Sf128:
|
||||
channelName = "Medium";
|
||||
break;
|
||||
case ChannelSettings_ModemConfig_Bw500Cr45Sf128:
|
||||
channelName = "ShortFast";
|
||||
break;
|
||||
case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512:
|
||||
channelName = "LongAlt";
|
||||
break;
|
||||
case ChannelSettings_ModemConfig_Bw125Cr48Sf4096:
|
||||
channelName = "LongSlow";
|
||||
break;
|
||||
default:
|
||||
channelName = "Invalid";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return channelName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs.
|
||||
* The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they
|
||||
their nodes
|
||||
* aren't talking to each other.
|
||||
*
|
||||
* This string is of the form "#name-X".
|
||||
*
|
||||
* Where X is either:
|
||||
* (for custom PSKS) a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together,
|
||||
*
|
||||
* This function will also need to be implemented in GUI apps that talk to the radio.
|
||||
*
|
||||
* https://github.com/meshtastic/Meshtastic-device/issues/269
|
||||
*/
|
||||
const char *Channels::getPrimaryName()
|
||||
{
|
||||
static char buf[32];
|
||||
|
||||
char suffix;
|
||||
// auto channelSettings = getPrimary();
|
||||
// if (channelSettings.psk.size != 1) {
|
||||
// We have a standard PSK, so generate a letter based hash.
|
||||
uint8_t code = getHash(primaryIndex);
|
||||
|
||||
suffix = 'A' + (code % 26);
|
||||
/* } else {
|
||||
suffix = '0' + channelSettings.psk.bytes[0];
|
||||
} */
|
||||
|
||||
snprintf(buf, sizeof(buf), "#%s-%c", getName(primaryIndex), suffix);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** Given a channel hash setup crypto for decoding that channel (or the primary channel if that channel is unsecured)
|
||||
*
|
||||
* This method is called before decoding inbound packets
|
||||
*
|
||||
* @return false if the channel hash or channel is invalid
|
||||
*/
|
||||
bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
|
||||
{
|
||||
if(chIndex > getNumChannels() || getHash(chIndex) != channelHash) {
|
||||
// DEBUG_MSG("Skipping channel %d (hash %x) due to invalid hash/index, want=%x\n", chIndex, getHash(chIndex), channelHash);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
DEBUG_MSG("Using channel %d (hash 0x%x)\n", chIndex, channelHash);
|
||||
setCrypto(chIndex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Given a channel index setup crypto for encoding that channel (or the primary channel if that channel is unsecured)
|
||||
*
|
||||
* This method is called before encoding outbound packets
|
||||
*
|
||||
* @eturn the (0 to 255) hash for that channel - if no suitable channel could be found, return -1
|
||||
*/
|
||||
int16_t Channels::setActiveByIndex(ChannelIndex channelIndex)
|
||||
{
|
||||
return setCrypto(channelIndex);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
|
||||
#include "CryptoEngine.h"
|
||||
#include "NodeDB.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
/** A channel number (index into the channel table)
|
||||
*/
|
||||
typedef uint8_t ChannelIndex;
|
||||
|
||||
/** A low quality hash of the channel PSK and the channel name. created by generateHash(chIndex)
|
||||
* Used as a hint to limit which PSKs are considered for packet decoding.
|
||||
*/
|
||||
typedef uint8_t ChannelHash;
|
||||
|
||||
/** The container/on device API for working with channels */
|
||||
class Channels
|
||||
{
|
||||
/// The index of the primary channel
|
||||
ChannelIndex primaryIndex = 0;
|
||||
|
||||
/** The channel index that was requested for sending/receving. Note: if this channel is a secondary
|
||||
channel and does not have a PSK, we will use the PSK from the primary channel. If this channel is disabled
|
||||
no sending or receiving will be allowed */
|
||||
ChannelIndex activeChannelIndex = 0;
|
||||
|
||||
/// the precomputed hashes for each of our channels, or -1 for invalid
|
||||
int16_t hashes[MAX_NUM_CHANNELS];
|
||||
|
||||
public:
|
||||
|
||||
/// Well known channel names
|
||||
static const char *adminChannel, *gpioChannel, *serialChannel;
|
||||
|
||||
const ChannelSettings &getPrimary() { return getByIndex(getPrimaryIndex()).settings; }
|
||||
|
||||
/** Return the Channel for a specified index */
|
||||
Channel &getByIndex(ChannelIndex chIndex);
|
||||
|
||||
/** Using the index inside the channel, update the specified channel's settings and role. If this channel is being promoted
|
||||
* to be primary, force all other channels to be secondary.
|
||||
*/
|
||||
void setChannel(const Channel &c);
|
||||
|
||||
const char *getName(size_t chIndex);
|
||||
|
||||
/** The index of the primary channel */
|
||||
ChannelIndex getPrimaryIndex() const { return primaryIndex; }
|
||||
|
||||
ChannelIndex getNumChannels() { return channelFile.channels_count; }
|
||||
|
||||
/**
|
||||
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different
|
||||
PSKs.
|
||||
* The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why
|
||||
they their nodes
|
||||
* aren't talking to each other.
|
||||
*
|
||||
* This string is of the form "#name-X".
|
||||
*
|
||||
* Where X is either:
|
||||
* (for custom PSKS) a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together,
|
||||
* OR (for the standard minimially secure PSKs) a number from 0 to 9.
|
||||
*
|
||||
* This function will also need to be implemented in GUI apps that talk to the radio.
|
||||
*
|
||||
* https://github.com/meshtastic/Meshtastic-device/issues/269
|
||||
*/
|
||||
const char *getPrimaryName();
|
||||
|
||||
/// Called by NodeDB on initial boot when the radio config settings are unset. Set a default single channel config.
|
||||
void initDefaults();
|
||||
|
||||
/// called when the user has just changed our radio config and we might need to change channel keys
|
||||
void onConfigChanged();
|
||||
|
||||
/** Given a channel hash setup crypto for decoding that channel (or the primary channel if that channel is unsecured)
|
||||
*
|
||||
* This method is called before decoding inbound packets
|
||||
*
|
||||
* @return false if the channel hash or channel is invalid
|
||||
*/
|
||||
bool decryptForHash(ChannelIndex chIndex, ChannelHash channelHash);
|
||||
|
||||
/** Given a channel index setup crypto for encoding that channel (or the primary channel if that channel is unsecured)
|
||||
*
|
||||
* This method is called before encoding outbound packets
|
||||
*
|
||||
* @eturn the (0 to 255) hash for that channel - if no suitable channel could be found, return -1
|
||||
*/
|
||||
int16_t setActiveByIndex(ChannelIndex channelIndex);
|
||||
|
||||
private:
|
||||
/** Given a channel index, change to use the crypto key specified by that index
|
||||
*
|
||||
* @eturn the (0 to 255) hash for that channel - if no suitable channel could be found, return -1
|
||||
*/
|
||||
int16_t setCrypto(ChannelIndex chIndex);
|
||||
|
||||
/** Return the channel index for the specified channel hash, or -1 for not found */
|
||||
int8_t getIndexByHash(ChannelHash channelHash);
|
||||
|
||||
/** Given a channel number, return the (0 to 255) hash for that channel
|
||||
* If no suitable channel could be found, return -1
|
||||
*
|
||||
* called by fixupChannel when a new channel is set
|
||||
*/
|
||||
int16_t generateHash(ChannelIndex channelNum);
|
||||
|
||||
int16_t getHash(ChannelIndex i) { return hashes[i]; }
|
||||
|
||||
/**
|
||||
* Validate a channel, fixing any errors as needed
|
||||
*/
|
||||
Channel &fixupChannel(ChannelIndex chIndex);
|
||||
|
||||
/**
|
||||
* Write a default channel to the specified channel index
|
||||
*/
|
||||
void initDefaultChannel(ChannelIndex chIndex);
|
||||
|
||||
/**
|
||||
* Return the key used for encrypting this channel (if channel is secondary and no key provided, use the primary channel's
|
||||
* PSK)
|
||||
*/
|
||||
CryptoKey getKey(ChannelIndex chIndex);
|
||||
};
|
||||
|
||||
/// Singleton channel table
|
||||
extern Channels channels;
|
||||
@@ -1,9 +1,14 @@
|
||||
#include "CryptoEngine.h"
|
||||
#include "configuration.h"
|
||||
|
||||
void CryptoEngine::setKey(size_t numBytes, uint8_t *bytes)
|
||||
void CryptoEngine::setKey(const CryptoKey &k)
|
||||
{
|
||||
DEBUG_MSG("WARNING: Using stub crypto - all crypto is sent in plaintext!\n");
|
||||
DEBUG_MSG("Installing AES%d key!\n", k.length * 8);
|
||||
/* for(uint8_t i = 0; i < k.length; i++)
|
||||
DEBUG_MSG("%02x ", k.bytes[i]);
|
||||
DEBUG_MSG("\n"); */
|
||||
|
||||
key = k;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+10
-1
@@ -2,6 +2,13 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
struct CryptoKey {
|
||||
uint8_t bytes[32];
|
||||
|
||||
/// # of bytes, or -1 to mean "invalid key - do not use"
|
||||
int8_t length;
|
||||
};
|
||||
|
||||
/**
|
||||
* see docs/software/crypto.md for details.
|
||||
*
|
||||
@@ -15,6 +22,8 @@ class CryptoEngine
|
||||
/** Our per packet nonce */
|
||||
uint8_t nonce[16];
|
||||
|
||||
CryptoKey key;
|
||||
|
||||
public:
|
||||
virtual ~CryptoEngine() {}
|
||||
|
||||
@@ -27,7 +36,7 @@ class CryptoEngine
|
||||
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
|
||||
* provided pointer)
|
||||
*/
|
||||
virtual void setKey(size_t numBytes, uint8_t *bytes);
|
||||
virtual void setKey(const CryptoKey &k);
|
||||
|
||||
/**
|
||||
* Encrypt a packet
|
||||
|
||||
+54
-49
@@ -62,57 +62,59 @@ ErrorCode DSRRouter::send(MeshPacket *p)
|
||||
return ReliableRouter::send(p);
|
||||
}
|
||||
|
||||
void DSRRouter::sniffReceived(const MeshPacket *p)
|
||||
void DSRRouter::sniffReceived(const MeshPacket *p, const Routing *c)
|
||||
{
|
||||
// Learn 0 hop routes by just hearing any adjacent nodes
|
||||
// But treat broadcasts carefully, because when flood broadcasts go out they keep the same original "from". So we want to
|
||||
// ignore rebroadcasts.
|
||||
// this will also add records for any ACKs we receive for our messages
|
||||
if (p->to != NODENUM_BROADCAST || p->hop_limit != HOP_RELIABLE) {
|
||||
addRoute(p->from, p->from, 0); // We are adjacent with zero hops
|
||||
addRoute(getFrom(p), getFrom(p), 0); // We are adjacent with zero hops
|
||||
}
|
||||
|
||||
switch (p->decoded.which_payloadVariant) {
|
||||
case SubPacket_route_request_tag:
|
||||
// Handle route discovery packets (will be a broadcast message)
|
||||
// FIXME - always start request with the senders nodenum
|
||||
if (weAreInRoute(p->decoded.route_request)) {
|
||||
DEBUG_MSG("Ignoring a route request that contains us\n");
|
||||
} else {
|
||||
updateRoutes(p->decoded.route_request,
|
||||
true); // Update our routing tables based on the route that came in so far on this request
|
||||
|
||||
if (p->decoded.dest == getNodeNum()) {
|
||||
// They were looking for us, send back a route reply (the sender address will be first in the list)
|
||||
sendRouteReply(p->decoded.route_request);
|
||||
if (c)
|
||||
switch (c->which_variant) {
|
||||
case Routing_route_request_tag:
|
||||
// Handle route discovery packets (will be a broadcast message)
|
||||
// FIXME - always start request with the senders nodenum
|
||||
if (weAreInRoute(c->route_request)) {
|
||||
DEBUG_MSG("Ignoring a route request that contains us\n");
|
||||
} else {
|
||||
// They were looking for someone else, forward it along (as a zero hop broadcast)
|
||||
NodeNum nextHop = getNextHop(p->decoded.dest);
|
||||
if (nextHop) {
|
||||
// in our route cache, reply to the requester (the sender address will be first in the list)
|
||||
sendRouteReply(p->decoded.route_request, nextHop);
|
||||
updateRoutes(c->route_request,
|
||||
true); // Update our routing tables based on the route that came in so far on this request
|
||||
|
||||
if (p->decoded.dest == getNodeNum()) {
|
||||
// They were looking for us, send back a route reply (the sender address will be first in the list)
|
||||
sendRouteReply(c->route_request);
|
||||
} else {
|
||||
// Not in our route cache, rebroadcast on their behalf (after adding ourselves to the request route)
|
||||
resendRouteRequest(p);
|
||||
// They were looking for someone else, forward it along (as a zero hop broadcast)
|
||||
NodeNum nextHop = getNextHop(p->decoded.dest);
|
||||
if (nextHop) {
|
||||
// in our route cache, reply to the requester (the sender address will be first in the list)
|
||||
sendRouteReply(c->route_request, nextHop);
|
||||
} else {
|
||||
// Not in our route cache, rebroadcast on their behalf (after adding ourselves to the request route)
|
||||
resendRouteRequest(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Routing_route_reply_tag:
|
||||
updateRoutes(c->route_reply, false);
|
||||
|
||||
// FIXME, if any of our current pending packets were waiting for this route, send them (and leave them as regular
|
||||
// pending packets until ack arrives)
|
||||
// FIXME, if we don't get a route reply at all (or a route error), timeout and generate a routeerror TIMEOUT on our
|
||||
// own...
|
||||
break;
|
||||
case Routing_error_reason_tag:
|
||||
removeRoute(p->decoded.dest);
|
||||
|
||||
// FIXME: if any pending packets were waiting on this route, delete them
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SubPacket_route_reply_tag:
|
||||
updateRoutes(p->decoded.route_reply, false);
|
||||
|
||||
// FIXME, if any of our current pending packets were waiting for this route, send them (and leave them as regular pending
|
||||
// packets until ack arrives)
|
||||
// FIXME, if we don't get a route reply at all (or a route error), timeout and generate a routeerror TIMEOUT on our own...
|
||||
break;
|
||||
case SubPacket_error_reason_tag:
|
||||
removeRoute(p->decoded.dest);
|
||||
|
||||
// FIXME: if any pending packets were waiting on this route, delete them
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// We simply ignore ACKs - because ReliableRouter will delete the pending packet for us
|
||||
|
||||
@@ -131,26 +133,29 @@ void DSRRouter::sniffReceived(const MeshPacket *p)
|
||||
assert(p->decoded.source); // I think this is guaranteed by now
|
||||
|
||||
// FIXME - what if the current packet _is_ a route error packet?
|
||||
sendRouteError(p, ErrorReason_NO_ROUTE);
|
||||
sendRouteError(p, Routing_Error_NO_ROUTE);
|
||||
}
|
||||
|
||||
// FIXME, stop local processing of this packet
|
||||
}
|
||||
|
||||
// handle naks - convert them to route error packets
|
||||
// All naks are generated locally, because we failed resending the packet too many times
|
||||
PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;
|
||||
if (nakId) {
|
||||
auto pending = findPendingPacket(p->to, nakId);
|
||||
if (pending && pending->packet->decoded.source) { // if source not set, this was not a multihop packet, just ignore
|
||||
removeRoute(pending->packet->decoded.dest); // We no longer have a route to the specified node
|
||||
if (c) {
|
||||
// handle naks - convert them to route error packets
|
||||
// All naks are generated locally, because we failed resending the packet too many times
|
||||
PacketId nakId = c->error_reason ? p->decoded.request_id : 0;
|
||||
if (nakId) {
|
||||
auto pending = findPendingPacket(p->to, nakId);
|
||||
if (pending &&
|
||||
pending->packet->decoded.source) { // if source not set, this was not a multihop packet, just ignore
|
||||
removeRoute(pending->packet->decoded.dest); // We no longer have a route to the specified node
|
||||
|
||||
sendRouteError(p, ErrorReason_GOT_NAK);
|
||||
sendRouteError(p, Routing_Error_GOT_NAK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ReliableRouter::sniffReceived(p);
|
||||
ReliableRouter::sniffReceived(p, c);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,7 +235,7 @@ void DSRRouter::sendNextHop(NodeNum n, const MeshPacket *p)
|
||||
/**
|
||||
* Send a route error packet towards whoever originally sent this message
|
||||
*/
|
||||
void DSRRouter::sendRouteError(const MeshPacket *p, ErrorReason err)
|
||||
void DSRRouter::sendRouteError(const MeshPacket *p, Routing_Error err)
|
||||
{
|
||||
DEBUG_MSG("FIXME not implemented sendRouteError\n");
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ class DSRRouter : public ReliableRouter
|
||||
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to
|
||||
* update routing tables etc... based on what we overhear (even for messages not destined to our node)
|
||||
*/
|
||||
virtual void sniffReceived(const MeshPacket *p);
|
||||
virtual void sniffReceived(const MeshPacket *p, const Routing *c);
|
||||
|
||||
/**
|
||||
* Send a packet on a suitable interface. This routine will
|
||||
@@ -70,7 +70,7 @@ class DSRRouter : public ReliableRouter
|
||||
/**
|
||||
* Send a route error packet towards whoever originally sent this message
|
||||
*/
|
||||
void sendRouteError(const MeshPacket *p, ErrorReason err);
|
||||
void sendRouteError(const MeshPacket *p, Routing_Error err);
|
||||
|
||||
/** make a copy of p, start discovery, but only if we don't
|
||||
* already a discovery in progress for that node number. Caller has already scheduled this message for retransmission
|
||||
|
||||
@@ -27,11 +27,11 @@ bool FloodingRouter::shouldFilterReceived(const MeshPacket *p)
|
||||
return Router::shouldFilterReceived(p);
|
||||
}
|
||||
|
||||
void FloodingRouter::sniffReceived(const MeshPacket *p)
|
||||
void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c)
|
||||
{
|
||||
// If a broadcast, possibly _also_ send copies out into the mesh.
|
||||
// (FIXME, do something smarter than naive flooding here)
|
||||
if (p->to == NODENUM_BROADCAST && p->hop_limit > 0) {
|
||||
if (p->to == NODENUM_BROADCAST && p->hop_limit > 0 && getFrom(p) != getNodeNum()) {
|
||||
if (p->id != 0) {
|
||||
MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
|
||||
|
||||
@@ -48,5 +48,5 @@ void FloodingRouter::sniffReceived(const MeshPacket *p)
|
||||
}
|
||||
|
||||
// handle the packet as normal
|
||||
Router::sniffReceived(p);
|
||||
Router::sniffReceived(p, c);
|
||||
}
|
||||
|
||||
@@ -55,5 +55,5 @@ class FloodingRouter : public Router, protected PacketHistory
|
||||
/**
|
||||
* Look for broadcasts we need to rebroadcast
|
||||
*/
|
||||
virtual void sniffReceived(const MeshPacket *p);
|
||||
virtual void sniffReceived(const MeshPacket *p, const Routing *c);
|
||||
};
|
||||
|
||||
@@ -101,25 +101,30 @@ template <class T> class MemoryPool : public Allocator<T>
|
||||
/// Return a buffer for use by others
|
||||
virtual void release(T *p)
|
||||
{
|
||||
assert(dead.enqueue(p, 0));
|
||||
assert(p >= buf &&
|
||||
(size_t)(p - buf) <
|
||||
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
|
||||
assert(dead.enqueue(p, 0));
|
||||
}
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
/// Return a buffer from an ISR, if higherPriWoken is set to true you have some work to do ;-)
|
||||
void releaseFromISR(T *p, BaseType_t *higherPriWoken)
|
||||
{
|
||||
assert(dead.enqueueFromISR(p, higherPriWoken));
|
||||
assert(p >= buf &&
|
||||
(size_t)(p - buf) <
|
||||
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
|
||||
assert(dead.enqueueFromISR(p, higherPriWoken));
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you
|
||||
/// probably don't want this version).
|
||||
virtual T *alloc(TickType_t maxWait) { return dead.dequeuePtr(maxWait); }
|
||||
virtual T *alloc(TickType_t maxWait)
|
||||
{
|
||||
T *p = dead.dequeuePtr(maxWait);
|
||||
assert(p);
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "MeshPacketQueue.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
/// @return the priority of the specified packet
|
||||
inline uint32_t getPriority(MeshPacket *p)
|
||||
{
|
||||
auto pri = p->priority;
|
||||
return pri;
|
||||
}
|
||||
|
||||
/// @return "true" if "p1" is ordered before "p2"
|
||||
bool CompareMeshPacket::operator()(MeshPacket *p1, MeshPacket *p2)
|
||||
{
|
||||
assert(p1 && p2);
|
||||
auto p1p = getPriority(p1), p2p = getPriority(p2);
|
||||
|
||||
// If priorities differ, use that
|
||||
// for equal priorities, order by id (older packets have higher priority - this will briefly be wrong when IDs roll over but
|
||||
// no big deal)
|
||||
return (p1p != p2p) ? (p1p < p2p) // prefer bigger priorities
|
||||
: (p1->id >= p2->id); // prefer smaller packet ids
|
||||
}
|
||||
|
||||
MeshPacketQueue::MeshPacketQueue(size_t _maxLen) : maxLen(_maxLen) {}
|
||||
|
||||
/** Some clients might not properly set priority, therefore we fix it here.
|
||||
*/
|
||||
void fixPriority(MeshPacket *p)
|
||||
{
|
||||
// We might receive acks from other nodes (and since generated remotely, they won't have priority assigned. Check for that
|
||||
// and fix it
|
||||
if (p->priority == MeshPacket_Priority_UNSET) {
|
||||
// if acks give high priority
|
||||
// if a reliable message give a bit higher default priority
|
||||
p->priority = (p->decoded.portnum == PortNum_ROUTING_APP) ? MeshPacket_Priority_ACK :
|
||||
(p->want_ack ? MeshPacket_Priority_RELIABLE : MeshPacket_Priority_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
/** enqueue a packet, return false if full */
|
||||
bool MeshPacketQueue::enqueue(MeshPacket *p)
|
||||
{
|
||||
|
||||
fixPriority(p);
|
||||
|
||||
// fixme if there is something lower priority in the queue that can be deleted to make space, delete that instead
|
||||
if (size() >= maxLen)
|
||||
return false;
|
||||
else {
|
||||
push(p);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
MeshPacket *MeshPacketQueue::dequeue()
|
||||
{
|
||||
if (empty())
|
||||
return NULL;
|
||||
else {
|
||||
auto p = top();
|
||||
pop(); // remove the first item
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
// this is kinda yucky, but I'm not sure if all arduino c++ compilers support closuers. And we only have one
|
||||
// thread that can run at a time - so safe
|
||||
static NodeNum findFrom;
|
||||
static PacketId findId;
|
||||
|
||||
static bool isMyPacket(MeshPacket *p)
|
||||
{
|
||||
return p->id == findId && getFrom(p) == findFrom;
|
||||
}
|
||||
|
||||
/** Attempt to find and remove a packet from this queue. Returns true the packet which was removed from the queue */
|
||||
MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
|
||||
{
|
||||
findFrom = from;
|
||||
findId = id;
|
||||
auto it = std::find_if(this->c.begin(), this->c.end(), isMyPacket);
|
||||
if (it != this->c.end()) {
|
||||
auto p = *it;
|
||||
this->c.erase(it);
|
||||
std::make_heap(this->c.begin(), this->c.end(), this->comp);
|
||||
return p;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "MeshTypes.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <queue>
|
||||
|
||||
// this is an strucure which implements the
|
||||
// operator overloading
|
||||
struct CompareMeshPacket {
|
||||
bool operator()(MeshPacket *p1, MeshPacket *p2);
|
||||
};
|
||||
|
||||
/**
|
||||
* A priority queue of packets.
|
||||
*
|
||||
*/
|
||||
class MeshPacketQueue : public std::priority_queue<MeshPacket *, std::vector<MeshPacket *>, CompareMeshPacket>
|
||||
{
|
||||
size_t maxLen;
|
||||
public:
|
||||
MeshPacketQueue(size_t _maxLen);
|
||||
|
||||
/** enqueue a packet, return false if full */
|
||||
bool enqueue(MeshPacket *p);
|
||||
|
||||
// bool isEmpty();
|
||||
|
||||
MeshPacket *dequeue();
|
||||
|
||||
/** Attempt to find and remove a packet from this queue. Returns true the packet which was removed from the queue */
|
||||
MeshPacket *remove(NodeNum from, PacketId id);
|
||||
};
|
||||
+97
-26
@@ -1,23 +1,30 @@
|
||||
#include "MeshPlugin.h"
|
||||
#include "NodeDB.h"
|
||||
#include "Channels.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "plugins/RoutingPlugin.h"
|
||||
#include <assert.h>
|
||||
|
||||
std::vector<MeshPlugin *> *MeshPlugin::plugins;
|
||||
|
||||
const MeshPacket *MeshPlugin::currentRequest;
|
||||
|
||||
/**
|
||||
* If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow
|
||||
* the RoutingPlugin to avoid sending redundant acks
|
||||
*/
|
||||
MeshPacket *MeshPlugin::currentReply;
|
||||
|
||||
MeshPlugin::MeshPlugin(const char *_name) : name(_name)
|
||||
{
|
||||
// Can't trust static initalizer order, so we check each time
|
||||
if(!plugins)
|
||||
if (!plugins)
|
||||
plugins = new std::vector<MeshPlugin *>();
|
||||
|
||||
plugins->push_back(this);
|
||||
}
|
||||
|
||||
void MeshPlugin::setup() {
|
||||
}
|
||||
void MeshPlugin::setup() {}
|
||||
|
||||
MeshPlugin::~MeshPlugin()
|
||||
{
|
||||
@@ -28,51 +35,115 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
|
||||
{
|
||||
// DEBUG_MSG("In call plugins\n");
|
||||
bool pluginFound = false;
|
||||
|
||||
assert(mp.which_payloadVariant == MeshPacket_decoded_tag); // I think we are guarnteed the packet is decoded by this point?
|
||||
|
||||
currentReply = NULL; // No reply yet
|
||||
|
||||
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
|
||||
auto ourNodeNum = nodeDB.getNodeNum();
|
||||
bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
|
||||
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
|
||||
auto &pi = **i;
|
||||
|
||||
pi.currentRequest = ∓
|
||||
if (pi.wantPortnum(mp.decoded.data.portnum)) {
|
||||
|
||||
/// received channel
|
||||
auto ch = channels.getByIndex(mp.channel);
|
||||
assert(ch.has_settings);
|
||||
|
||||
/// Is the channel this packet arrived on acceptable? (security check)
|
||||
bool rxChannelOk = !pi.boundChannel || (mp.from == 0) || (strcmp(ch.settings.name, pi.boundChannel) == 0);
|
||||
|
||||
/// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious)
|
||||
bool wantsPacket = rxChannelOk && (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
|
||||
// DEBUG_MSG("Plugin %s wantsPacket=%d\n", pi.name, wantsPacket);
|
||||
if (wantsPacket) {
|
||||
pluginFound = true;
|
||||
|
||||
bool handled = pi.handleReceived(mp);
|
||||
|
||||
// Possibly send replies
|
||||
if (mp.decoded.want_response)
|
||||
pi.sendResponse(mp);
|
||||
// Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious sniffing)
|
||||
// also: we only let the one plugin send a reply, once that happens, remaining plugins are not considered
|
||||
|
||||
DEBUG_MSG("Plugin %s handled=%d\n", pi.name, handled);
|
||||
if (handled)
|
||||
// NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary because
|
||||
// currently when the phone sends things, it sends things using the local node ID as the from address. A better
|
||||
// solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like any other
|
||||
// node.
|
||||
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
|
||||
pi.sendResponse(mp);
|
||||
DEBUG_MSG("Plugin %s sent a response\n", pi.name);
|
||||
} else {
|
||||
DEBUG_MSG("Plugin %s considered\n", pi.name);
|
||||
}
|
||||
if (handled) {
|
||||
DEBUG_MSG("Plugin %s handled and skipped other processing\n", pi.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pi.currentRequest = NULL;
|
||||
}
|
||||
|
||||
if(!pluginFound)
|
||||
DEBUG_MSG("No plugins interested in portnum=%d\n", mp.decoded.data.portnum);
|
||||
if (mp.decoded.want_response && toUs) {
|
||||
if (currentReply) {
|
||||
DEBUG_MSG("Sending response\n");
|
||||
service.sendToMesh(currentReply);
|
||||
currentReply = NULL;
|
||||
}
|
||||
else {
|
||||
// No one wanted to reply to this requst, tell the requster that happened
|
||||
DEBUG_MSG("No one responded, send a nak\n");
|
||||
routingPlugin->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pluginFound)
|
||||
DEBUG_MSG("No plugins interested in portnum=%d\n", mp.decoded.portnum);
|
||||
}
|
||||
|
||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||
* so that subclasses can (optionally) send a response back to the original sender. Implementing this method
|
||||
* is optional
|
||||
*/
|
||||
void MeshPlugin::sendResponse(const MeshPacket &req) {
|
||||
void MeshPlugin::sendResponse(const MeshPacket &req)
|
||||
{
|
||||
auto r = allocReply();
|
||||
if(r) {
|
||||
DEBUG_MSG("Sending response\n");
|
||||
if (r) {
|
||||
setReplyTo(r, req);
|
||||
service.sendToMesh(r);
|
||||
}
|
||||
else {
|
||||
DEBUG_MSG("WARNING: Client requested response but this plugin did not provide\n");
|
||||
currentReply = r;
|
||||
} else {
|
||||
// Ignore - this is now expected behavior for routing plugin (because it ignores some replies)
|
||||
// DEBUG_MSG("WARNING: Client requested response but this plugin did not provide\n");
|
||||
}
|
||||
}
|
||||
|
||||
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
||||
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
||||
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
|
||||
*/
|
||||
void setReplyTo(MeshPacket *p, const MeshPacket &to) {
|
||||
p->to = to.from;
|
||||
p->want_ack = to.want_ack;
|
||||
}
|
||||
*/
|
||||
void setReplyTo(MeshPacket *p, const MeshPacket &to)
|
||||
{
|
||||
assert(p->which_payloadVariant == MeshPacket_decoded_tag); // Should already be set by now
|
||||
p->to = getFrom(&to);
|
||||
p->channel = to.channel; // Use the same channel that the request came in on
|
||||
|
||||
// No need for an ack if we are just delivering locally (it just generates an ignored ack)
|
||||
p->want_ack = (to.from != 0) ? to.want_ack : false;
|
||||
if (p->priority == MeshPacket_Priority_UNSET)
|
||||
p->priority = MeshPacket_Priority_RELIABLE;
|
||||
p->decoded.request_id = to.id;
|
||||
}
|
||||
|
||||
std::vector<MeshPlugin *> MeshPlugin::GetMeshPluginsWithUIFrames()
|
||||
{
|
||||
|
||||
std::vector<MeshPlugin *> pluginsWithUIFrames;
|
||||
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
|
||||
auto &pi = **i;
|
||||
if (pi.wantUIFrame()) {
|
||||
DEBUG_MSG("Plugin wants a UI Frame\n");
|
||||
pluginsWithUIFrames.push_back(&pi);
|
||||
}
|
||||
}
|
||||
return pluginsWithUIFrames;
|
||||
}
|
||||
|
||||
+35
-4
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "mesh/MeshTypes.h"
|
||||
#include <OLEDDisplay.h>
|
||||
#include <OLEDDisplayUi.h>
|
||||
#include <vector>
|
||||
/** A baseclass for any mesh "plugin".
|
||||
*
|
||||
@@ -28,13 +30,31 @@ class MeshPlugin
|
||||
*/
|
||||
static void callPlugins(const MeshPacket &mp);
|
||||
|
||||
static std::vector<MeshPlugin *> GetMeshPluginsWithUIFrames();
|
||||
|
||||
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; }
|
||||
|
||||
protected:
|
||||
const char *name;
|
||||
|
||||
/* Most plugins only care about packets that are destined for their node (i.e. broadcasts or has their node as the specific
|
||||
recipient) But some plugs might want to 'sniff' packets that are merely being routed (passing through the current node). Those
|
||||
plugins can set this to true and their handleReceived() will be called for every packet.
|
||||
*/
|
||||
bool isPromiscuous = false;
|
||||
|
||||
/** If a bound channel name is set, we will only accept received packets that come in on that channel.
|
||||
* A special exception (FIXME, not sure if this is a good idea) - packets that arrive on the local interface
|
||||
* are allowed on any channel (this lets the local user do anything).
|
||||
*
|
||||
* We will send responses on the same channel that the request arrived on.
|
||||
*/
|
||||
const char *boundChannel = NULL;
|
||||
|
||||
/**
|
||||
* If this plugin is currently handling a request currentRequest will be preset
|
||||
* to the packet with the request. This is mostly useful for reply handlers.
|
||||
*
|
||||
*
|
||||
* Note: this can be static because we are guaranteed to be processing only one
|
||||
* plugin at a time.
|
||||
*/
|
||||
@@ -49,7 +69,7 @@ class MeshPlugin
|
||||
/**
|
||||
* @return true if you want to receive the specified portnum
|
||||
*/
|
||||
virtual bool wantPortnum(PortNum p) = 0;
|
||||
virtual bool wantPacket(const MeshPacket *p) = 0;
|
||||
|
||||
/** Called to handle a particular incoming message
|
||||
|
||||
@@ -61,7 +81,18 @@ class MeshPlugin
|
||||
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||
virtual MeshPacket *allocReply() { return NULL; }
|
||||
|
||||
/***
|
||||
* @return true if you want to be alloced a UI screen frame
|
||||
*/
|
||||
virtual bool wantUIFrame() { return false; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow
|
||||
* the RoutingPlugin to avoid sending redundant acks
|
||||
*/
|
||||
static MeshPacket *currentReply;
|
||||
friend class ReliableRouter;
|
||||
|
||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||
* so that subclasses can (optionally) send a response back to the original sender. This method calls allocReply()
|
||||
@@ -70,7 +101,7 @@ class MeshPlugin
|
||||
void sendResponse(const MeshPacket &req);
|
||||
};
|
||||
|
||||
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
||||
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
|
||||
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
|
||||
*/
|
||||
*/
|
||||
void setReplyTo(MeshPacket *p, const MeshPacket &to);
|
||||
+37
-71
@@ -13,8 +13,8 @@
|
||||
#include "RTC.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "plugins/PositionPlugin.h"
|
||||
#include "plugins/NodeInfoPlugin.h"
|
||||
#include "plugins/PositionPlugin.h"
|
||||
#include "power.h"
|
||||
|
||||
/*
|
||||
@@ -51,23 +51,6 @@ MeshService service;
|
||||
|
||||
#include "Router.h"
|
||||
|
||||
static int32_t sendOwnerCb()
|
||||
{
|
||||
static uint32_t currentGeneration;
|
||||
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
|
||||
assert(nodeInfoPlugin);
|
||||
nodeInfoPlugin->sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
|
||||
|
||||
return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000;
|
||||
}
|
||||
|
||||
static concurrency::Periodic *sendOwnerPeriod;
|
||||
|
||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||
{
|
||||
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
|
||||
@@ -75,18 +58,13 @@ MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||
|
||||
void MeshService::init()
|
||||
{
|
||||
sendOwnerPeriod = new concurrency::Periodic("SendOwner", sendOwnerCb);
|
||||
sendOwnerPeriod->setIntervalFromNow(30 * 1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup)
|
||||
|
||||
// moved much earlier in boot (called from setup())
|
||||
// nodeDB.init();
|
||||
|
||||
if (gps)
|
||||
gpsObserver.observe(&gps->newStatus);
|
||||
packetReceivedObserver.observe(&router->notifyPacketReceived);
|
||||
}
|
||||
|
||||
|
||||
int MeshService::handleFromRadio(const MeshPacket *mp)
|
||||
{
|
||||
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
|
||||
@@ -126,7 +104,7 @@ bool MeshService::reloadConfig()
|
||||
// This will also update the region as needed
|
||||
bool didReset = nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
|
||||
|
||||
configChanged.notifyObservers(NULL);
|
||||
configChanged.notifyObservers(NULL); // This will cause radio hardware to change freqs etc
|
||||
nodeDB.saveToDisk();
|
||||
|
||||
return didReset;
|
||||
@@ -136,7 +114,8 @@ bool MeshService::reloadConfig()
|
||||
void MeshService::reloadOwner()
|
||||
{
|
||||
assert(nodeInfoPlugin);
|
||||
nodeInfoPlugin->sendOurNodeInfo();
|
||||
if(nodeInfoPlugin)
|
||||
nodeInfoPlugin->sendOurNodeInfo();
|
||||
nodeDB.saveToDisk();
|
||||
}
|
||||
|
||||
@@ -147,8 +126,12 @@ void MeshService::reloadOwner()
|
||||
*/
|
||||
void MeshService::handleToRadio(MeshPacket &p)
|
||||
{
|
||||
if (p.from == 0) // If the phone didn't set a sending node ID, use ours
|
||||
p.from = nodeDB.getNodeNum();
|
||||
if (p.from != 0) { // We don't let phones assign nodenums to their sent messages
|
||||
DEBUG_MSG("Warning: phone tried to pick a nodenum, we don't allow that.\n");
|
||||
p.from = 0;
|
||||
} else {
|
||||
// p.from = nodeDB.getNodeNum();
|
||||
}
|
||||
|
||||
if (p.id == 0)
|
||||
p.id = generatePacketId(); // If the phone didn't supply one, then pick one
|
||||
@@ -169,22 +152,16 @@ void MeshService::handleToRadio(MeshPacket &p)
|
||||
}
|
||||
}
|
||||
|
||||
/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
|
||||
bool MeshService::cancelSending(PacketId id)
|
||||
{
|
||||
return router->cancelSending(nodeDB.getNodeNum(), id);
|
||||
}
|
||||
|
||||
void MeshService::sendToMesh(MeshPacket *p)
|
||||
{
|
||||
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
|
||||
|
||||
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
|
||||
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
|
||||
// devices can get time.
|
||||
if (p->which_payloadVariant == MeshPacket_decoded_tag && p->decoded.which_payloadVariant == SubPacket_position_tag &&
|
||||
p->decoded.position.time) {
|
||||
if (getRTCQuality() < RTCQualityGPS) {
|
||||
DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time);
|
||||
p->decoded.position.time = 0;
|
||||
} else
|
||||
DEBUG_MSG("Providing time to mesh %u\n", p->decoded.position.time);
|
||||
}
|
||||
|
||||
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
|
||||
router->sendLocal(p);
|
||||
}
|
||||
@@ -194,29 +171,36 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
|
||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
assert(node);
|
||||
|
||||
DEBUG_MSG("Sending network ping to 0x%x, with position=%d, wantReplies=%d\n", dest, node->has_position, wantReplies);
|
||||
assert(positionPlugin && nodeInfoPlugin);
|
||||
if (node->has_position)
|
||||
positionPlugin->sendOurPosition(dest, wantReplies);
|
||||
else
|
||||
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
|
||||
if (node->has_position) {
|
||||
if(positionPlugin) {
|
||||
DEBUG_MSG("Sending position ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
|
||||
positionPlugin->sendOurPosition(dest, wantReplies);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(nodeInfoPlugin) {
|
||||
DEBUG_MSG("Sending nodeinfo ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
|
||||
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NodeInfo *MeshService::refreshMyNodeInfo() {
|
||||
NodeInfo *MeshService::refreshMyNodeInfo()
|
||||
{
|
||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||
assert(node);
|
||||
|
||||
// We might not have a position yet for our local node, in that case, at least try to send the time
|
||||
if(!node->has_position) {
|
||||
if (!node->has_position) {
|
||||
memset(&node->position, 0, sizeof(node->position));
|
||||
node->has_position = true;
|
||||
}
|
||||
|
||||
|
||||
Position &position = node->position;
|
||||
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
position.time = getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid.
|
||||
position.time =
|
||||
getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid
|
||||
|
||||
position.battery_level = powerStatus->getBatteryChargePercent();
|
||||
updateBatteryLevel(position.battery_level);
|
||||
@@ -235,11 +219,10 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
|
||||
pos.altitude = gps->altitude;
|
||||
pos.latitude_i = gps->latitude;
|
||||
pos.longitude_i = gps->longitude;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// The GPS has lost lock, if we are fixed position we should just keep using
|
||||
// the old position
|
||||
if(!radioConfig.preferences.fixed_position) {
|
||||
if (radioConfig.preferences.fixed_position) {
|
||||
DEBUG_MSG("WARNING: Using fixed position\n");
|
||||
} else {
|
||||
// throw away old position
|
||||
@@ -249,27 +232,10 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level);
|
||||
DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.time, pos.latitude_i, pos.battery_level);
|
||||
|
||||
// Update our current position in the local DB
|
||||
nodeDB.updatePosition(nodeDB.getNodeNum(), pos);
|
||||
|
||||
// We limit our GPS broadcasts to a max rate
|
||||
static uint32_t lastGpsSend;
|
||||
uint32_t now = millis();
|
||||
if (lastGpsSend == 0 || now - lastGpsSend > getPref_position_broadcast_secs() * 1000) {
|
||||
lastGpsSend = now;
|
||||
|
||||
static uint32_t currentGeneration;
|
||||
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies);
|
||||
assert(positionPlugin);
|
||||
positionPlugin->sendOurPosition(NODENUM_BROADCAST, requestReplies);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ class MeshService
|
||||
{
|
||||
CallbackObserver<MeshService, const meshtastic::GPSStatus *> gpsObserver =
|
||||
CallbackObserver<MeshService, const meshtastic::GPSStatus *>(this, &MeshService::onGPSChanged);
|
||||
CallbackObserver<MeshService, const MeshPacket *> packetReceivedObserver =
|
||||
CallbackObserver<MeshService, const MeshPacket *>(this, &MeshService::handleFromRadio);
|
||||
|
||||
/// received packets waiting for the phone to process them
|
||||
/// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure
|
||||
@@ -79,6 +77,9 @@ class MeshService
|
||||
/// cache
|
||||
void sendToMesh(MeshPacket *p);
|
||||
|
||||
/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
|
||||
bool cancelSending(PacketId id);
|
||||
|
||||
/// Pull the latest power and time info into my nodeinfo
|
||||
NodeInfo *refreshMyNodeInfo();
|
||||
|
||||
@@ -88,9 +89,10 @@ class MeshService
|
||||
/// returns 0 to allow futher processing
|
||||
int onGPSChanged(const meshtastic::GPSStatus *arg);
|
||||
|
||||
/// Handle a packet that just arrived from the radio. This method does _not_ 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 needs
|
||||
/// to keep the packet around it makes a copy
|
||||
int handleFromRadio(const MeshPacket *p);
|
||||
friend class RoutingPlugin;
|
||||
};
|
||||
|
||||
extern MeshService service;
|
||||
|
||||
+10
-1
@@ -13,6 +13,9 @@ typedef uint32_t PacketId; // A packet sequence number
|
||||
#define ERRNO_OK 0
|
||||
#define ERRNO_NO_INTERFACES 33
|
||||
#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER
|
||||
#define ERRNO_DISABLED 34 // the itnerface is disabled
|
||||
#define ERRNO_TOO_LARGE 35
|
||||
#define ERRNO_NO_CHANNEL 36
|
||||
|
||||
/**
|
||||
* the max number of hops a message can pass through, used as the default max for hop_limit in MeshPacket.
|
||||
@@ -29,4 +32,10 @@ typedef uint32_t PacketId; // A packet sequence number
|
||||
typedef int ErrorCode;
|
||||
|
||||
/// Alloc and free packets to our global, ISR safe pool
|
||||
extern Allocator<MeshPacket> &packetPool;
|
||||
extern Allocator<MeshPacket> &packetPool;
|
||||
|
||||
/**
|
||||
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on the local node.
|
||||
* If from is zero this function returns our node number instead
|
||||
*/
|
||||
NodeNum getFrom(const MeshPacket *p);
|
||||
+163
-231
@@ -4,10 +4,10 @@
|
||||
|
||||
#include "FS.h"
|
||||
|
||||
#include "Channels.h"
|
||||
#include "CryptoEngine.h"
|
||||
#include "FSCommon.h"
|
||||
#include "GPS.h"
|
||||
#include "main.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PacketHistory.h"
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include "error.h"
|
||||
#include "main.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
@@ -29,8 +30,8 @@ NodeDB nodeDB;
|
||||
// we have plenty of ram so statically alloc this tempbuf (for now)
|
||||
EXT_RAM_ATTR DeviceState devicestate;
|
||||
MyNodeInfo &myNodeInfo = devicestate.my_node;
|
||||
RadioConfig &radioConfig = devicestate.radio;
|
||||
ChannelSettings &channelSettings = radioConfig.channel_settings;
|
||||
RadioConfig radioConfig;
|
||||
ChannelFile channelFile;
|
||||
|
||||
/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings
|
||||
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
||||
@@ -64,155 +65,36 @@ static uint8_t ourMacAddr[6];
|
||||
*/
|
||||
NodeNum displayedNodeNum;
|
||||
|
||||
/// A usable (but bigger) version of the channel name in the channelSettings object
|
||||
const char *channelName;
|
||||
|
||||
/// A usable psk - which has been constructed based on the (possibly short psk) in channelSettings
|
||||
static uint8_t activePSK[32];
|
||||
static uint8_t activePSKSize;
|
||||
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
|
||||
|
||||
/**
|
||||
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs.
|
||||
* The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they
|
||||
their nodes
|
||||
* aren't talking to each other.
|
||||
*
|
||||
* This string is of the form "#name-X".
|
||||
*
|
||||
* Where X is either:
|
||||
* (for custom PSKS) a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together,
|
||||
* OR (for the standard minimially secure PSKs) a number from 0 to 9.
|
||||
*
|
||||
* This function will also need to be implemented in GUI apps that talk to the radio.
|
||||
*
|
||||
* https://github.com/meshtastic/Meshtastic-device/issues/269
|
||||
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on
|
||||
* the local node. If from is zero this function returns our node number instead
|
||||
*/
|
||||
const char *getChannelName()
|
||||
NodeNum getFrom(const MeshPacket *p)
|
||||
{
|
||||
static char buf[32];
|
||||
|
||||
char suffix;
|
||||
if (channelSettings.psk.size != 1) {
|
||||
// We have a standard PSK, so generate a letter based hash.
|
||||
uint8_t code = 0;
|
||||
for (int i = 0; i < activePSKSize; i++)
|
||||
code ^= activePSK[i];
|
||||
|
||||
suffix = 'A' + (code % 26);
|
||||
} else {
|
||||
suffix = '0' + channelSettings.psk.bytes[0];
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "#%s-%c", channelName, suffix);
|
||||
return buf;
|
||||
return (p->from == 0) ? nodeDB.getNodeNum() : p->from;
|
||||
}
|
||||
|
||||
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
|
||||
|
||||
bool NodeDB::resetRadioConfig()
|
||||
{
|
||||
bool didFactoryReset = false;
|
||||
|
||||
radioGeneration++;
|
||||
|
||||
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
|
||||
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
|
||||
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
|
||||
|
||||
if (radioConfig.preferences.factory_reset) {
|
||||
DEBUG_MSG("Performing factory reset!\n");
|
||||
installDefaultDeviceState();
|
||||
didFactoryReset = true;
|
||||
} else if (!channelSettings.psk.size) {
|
||||
DEBUG_MSG("Setting default preferences!\n");
|
||||
} else if (channelFile.channels_count == 0) {
|
||||
DEBUG_MSG("Setting default channel and radio preferences!\n");
|
||||
|
||||
channels.initDefaults();
|
||||
|
||||
radioConfig.has_channel_settings = true;
|
||||
radioConfig.has_preferences = true;
|
||||
|
||||
// radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast
|
||||
// channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide
|
||||
// bandwidth so incompatible radios can talk together
|
||||
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
|
||||
|
||||
channelSettings.tx_power = 0; // default
|
||||
uint8_t defaultpskIndex = 1;
|
||||
channelSettings.psk.bytes[0] = defaultpskIndex;
|
||||
channelSettings.psk.size = 1;
|
||||
strcpy(channelSettings.name, "");
|
||||
}
|
||||
|
||||
// Convert the old string "Default" to our new short representation
|
||||
if (strcmp(channelSettings.name, "Default") == 0)
|
||||
*channelSettings.name = '\0';
|
||||
|
||||
// Convert the short "" representation for Default into a usable string
|
||||
channelName = channelSettings.name;
|
||||
if (!*channelName) { // emptystring
|
||||
// Per mesh.proto spec, if bandwidth is specified we must ignore modemConfig enum, we assume that in that case
|
||||
// the app fucked up and forgot to set channelSettings.name
|
||||
|
||||
if (channelSettings.bandwidth != 0)
|
||||
channelName = "Unset";
|
||||
else
|
||||
switch (channelSettings.modem_config) {
|
||||
case ChannelSettings_ModemConfig_Bw125Cr45Sf128:
|
||||
channelName = "Medium";
|
||||
break;
|
||||
case ChannelSettings_ModemConfig_Bw500Cr45Sf128:
|
||||
channelName = "ShortFast";
|
||||
break;
|
||||
case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512:
|
||||
channelName = "LongAlt";
|
||||
break;
|
||||
case ChannelSettings_ModemConfig_Bw125Cr48Sf4096:
|
||||
channelName = "LongSlow";
|
||||
break;
|
||||
default:
|
||||
channelName = "Invalid";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert any old usage of the defaultpsk into our new short representation.
|
||||
if (channelSettings.psk.size == sizeof(defaultpsk) &&
|
||||
memcmp(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)) == 0) {
|
||||
*channelSettings.psk.bytes = 1;
|
||||
channelSettings.psk.size = 1;
|
||||
}
|
||||
|
||||
memset(activePSK, 0, sizeof(activePSK)); // In case the user provided a short key, we want to pad the rest with zeros
|
||||
memcpy(activePSK, channelSettings.psk.bytes, channelSettings.psk.size);
|
||||
activePSKSize = channelSettings.psk.size;
|
||||
if(activePSKSize == 0)
|
||||
DEBUG_MSG("Warning: User disabled encryption\n");
|
||||
else if (activePSKSize == 1) {
|
||||
// Convert the short single byte variants of psk into variant that can be used more generally
|
||||
|
||||
uint8_t pskIndex = activePSK[0];
|
||||
DEBUG_MSG("Expanding short PSK #%d\n", pskIndex);
|
||||
if (pskIndex == 0)
|
||||
activePSKSize = 0; // Turn off encryption
|
||||
else {
|
||||
memcpy(activePSK, defaultpsk, sizeof(defaultpsk));
|
||||
activePSKSize = sizeof(defaultpsk);
|
||||
// Bump up the last byte of PSK as needed
|
||||
uint8_t *last = activePSK + sizeof(defaultpsk) - 1;
|
||||
*last = *last + pskIndex - 1; // index of 1 means no change vs defaultPSK
|
||||
}
|
||||
} else if(activePSKSize < 16) {
|
||||
// Error! The user specified only the first few bits of an AES128 key. So by convention we just pad the rest of the key
|
||||
// with zeros
|
||||
DEBUG_MSG("Warning: User provided a too short AES128 key - padding\n");
|
||||
activePSKSize = 16;
|
||||
} else if(activePSKSize < 32 && activePSKSize != 16) {
|
||||
// Error! The user specified only the first few bits of an AES256 key. So by convention we just pad the rest of the key
|
||||
// with zeros
|
||||
DEBUG_MSG("Warning: User provided a too short AES256 key - padding\n");
|
||||
activePSKSize = 32;
|
||||
}
|
||||
|
||||
// Tell our crypto engine about the psk
|
||||
crypto->setKey(activePSKSize, activePSK);
|
||||
channels.onConfigChanged();
|
||||
|
||||
// temp hack for quicker testing
|
||||
// devicestate.no_save = true;
|
||||
@@ -237,6 +119,18 @@ bool NodeDB::resetRadioConfig()
|
||||
return didFactoryReset;
|
||||
}
|
||||
|
||||
void NodeDB::installDefaultRadioConfig()
|
||||
{
|
||||
memset(&radioConfig, 0, sizeof(radioConfig));
|
||||
radioConfig.has_preferences = true;
|
||||
resetRadioConfig();
|
||||
}
|
||||
|
||||
void NodeDB::installDefaultChannels()
|
||||
{
|
||||
memset(&channelFile, 0, sizeof(channelFile));
|
||||
}
|
||||
|
||||
void NodeDB::installDefaultDeviceState()
|
||||
{
|
||||
// We try to preserve the region setting because it will really bum users out if we discard it
|
||||
@@ -249,15 +143,11 @@ void NodeDB::installDefaultDeviceState()
|
||||
|
||||
// init our devicestate with valid flags so protobuf writing/reading will work
|
||||
devicestate.has_my_node = true;
|
||||
devicestate.has_radio = true;
|
||||
devicestate.has_owner = true;
|
||||
devicestate.radio.has_channel_settings = true;
|
||||
devicestate.radio.has_preferences = true;
|
||||
devicestate.node_db_count = 0;
|
||||
devicestate.version = DEVICESTATE_CUR_VER;
|
||||
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
|
||||
|
||||
resetRadioConfig();
|
||||
|
||||
// default to no GPS, until one has been found by probing
|
||||
myNodeInfo.has_gps = false;
|
||||
myNodeInfo.message_timeout_msec = FLOOD_EXPIRE_TIME;
|
||||
@@ -277,8 +167,11 @@ void NodeDB::installDefaultDeviceState()
|
||||
// Restore region if possible
|
||||
if (oldRegionCode != RegionCode_Unset)
|
||||
radioConfig.preferences.region = oldRegionCode;
|
||||
if (oldRegion.length())
|
||||
if (oldRegion.length()) // If the old style region was set, try to keep it up-to-date
|
||||
strcpy(myNodeInfo.region, oldRegion.c_str());
|
||||
|
||||
installDefaultChannels();
|
||||
installDefaultRadioConfig();
|
||||
}
|
||||
|
||||
void NodeDB::init()
|
||||
@@ -289,33 +182,37 @@ void NodeDB::init()
|
||||
loadFromDisk();
|
||||
// saveToDisk();
|
||||
|
||||
// We set node_num and packet_id _after_ loading from disk, because we always want to use the values this
|
||||
// rom was compiled for, not what happens to be in the save file.
|
||||
myNodeInfo.node_num_bits = sizeof(NodeNum) * 8;
|
||||
myNodeInfo.packet_id_bits = sizeof(PacketId) * 8;
|
||||
myNodeInfo.max_channels = MAX_NUM_CHANNELS; // tell others the max # of channels we can understand
|
||||
|
||||
myNodeInfo.error_code =
|
||||
CriticalErrorCode_None; // For the error code, only show values from this boot (discard value from flash)
|
||||
myNodeInfo.error_address = 0;
|
||||
|
||||
// likewise - we always want the app requirements to come from the running appload
|
||||
myNodeInfo.min_app_version = 20120; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
|
||||
myNodeInfo.min_app_version = 20200; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
|
||||
|
||||
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
|
||||
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
|
||||
pickNewNodeNum();
|
||||
|
||||
// Set our board type so we can share it with others
|
||||
owner.hw_model = HW_VENDOR;
|
||||
|
||||
// Include our owner in the node db under our nodenum
|
||||
NodeInfo *info = getOrCreateNode(getNodeNum());
|
||||
info->user = owner;
|
||||
info->has_user = true;
|
||||
|
||||
// removed from 1.2 (though we do use old values if found)
|
||||
// We set these _after_ loading from disk - because they come from the build and are more trusted than
|
||||
// what is stored in flash
|
||||
if (xstr(HW_VERSION)[0])
|
||||
strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
|
||||
else
|
||||
DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build flag
|
||||
//if (xstr(HW_VERSION)[0])
|
||||
// strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
|
||||
// else DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build flag
|
||||
|
||||
// DEBUG_MSG("legacy region %d\n", devicestate.legacyRadio.preferences.region);
|
||||
if(radioConfig.preferences.region == RegionCode_Unset)
|
||||
radioConfig.preferences.region = devicestate.legacyRadio.preferences.region;
|
||||
|
||||
// Check for the old style of region code strings, if found, convert to the new enum.
|
||||
// Those strings will look like "1.0-EU433"
|
||||
@@ -329,12 +226,14 @@ void NodeDB::init()
|
||||
}
|
||||
|
||||
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
|
||||
strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
|
||||
|
||||
// hw_model is no longer stored in myNodeInfo (as of 1.2.11) - we now store it as an enum in nodeinfo
|
||||
myNodeInfo.hw_model_deprecated[0] = '\0';
|
||||
// strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
|
||||
|
||||
resetRadioConfig(); // If bogus settings got saved, then fix them
|
||||
|
||||
DEBUG_MSG("legacy_region=%s, region=%d, NODENUM=0x%x, dbsize=%d\n", myNodeInfo.region, 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);
|
||||
}
|
||||
|
||||
// We reserve a few nodenums for future use
|
||||
@@ -364,84 +263,134 @@ void NodeDB::pickNewNodeNum()
|
||||
myNodeInfo.my_node_num = r;
|
||||
}
|
||||
|
||||
const char *preffile = "/db.proto";
|
||||
const char *preftmp = "/db.proto.tmp";
|
||||
static const char *preffileOld = "/db.proto";
|
||||
static const char *preffile = "/prefs/db.proto";
|
||||
static const char *radiofile = "/prefs/radio.proto";
|
||||
static const char *channelfile = "/prefs/channels.proto";
|
||||
// const char *preftmp = "/db.proto.tmp";
|
||||
|
||||
void NodeDB::loadFromDisk()
|
||||
/** Load a protobuf from a file, return true for success */
|
||||
bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct)
|
||||
{
|
||||
#ifdef FS
|
||||
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
|
||||
|
||||
auto f = FS.open(preffile);
|
||||
auto f = FS.open(filename);
|
||||
|
||||
// FIXME, temporary hack until every node in the universe is 1.2 or later - look for prefs in the old location (so we can
|
||||
// preserve region)
|
||||
if (!f && filename == preffile) {
|
||||
filename = preffileOld;
|
||||
f = FS.open(filename);
|
||||
}
|
||||
|
||||
bool okay = false;
|
||||
if (f) {
|
||||
DEBUG_MSG("Loading saved preferences\n");
|
||||
pb_istream_t stream = {&readcb, &f, DeviceState_size};
|
||||
DEBUG_MSG("Loading %s\n", filename);
|
||||
pb_istream_t stream = {&readcb, &f, protoSize};
|
||||
|
||||
// DEBUG_MSG("Preload channel name=%s\n", channelSettings.name);
|
||||
|
||||
memset(&devicestate, 0, sizeof(devicestate));
|
||||
if (!pb_decode(&stream, DeviceState_fields, &devicestate)) {
|
||||
memset(dest_struct, 0, objSize);
|
||||
if (!pb_decode(&stream, fields, dest_struct)) {
|
||||
DEBUG_MSG("Error: can't decode protobuf %s\n", PB_GET_ERROR(&stream));
|
||||
installDefaultDeviceState(); // Our in RAM copy might now be corrupt
|
||||
// FIXME - report failure to phone
|
||||
} else {
|
||||
if (devicestate.version < DEVICESTATE_MIN_VER) {
|
||||
DEBUG_MSG("Warn: devicestate is old, discarding\n");
|
||||
installDefaultDeviceState();
|
||||
} else {
|
||||
DEBUG_MSG("Loaded saved preferences version %d\n", devicestate.version);
|
||||
}
|
||||
|
||||
// DEBUG_MSG("Postload channel name=%s\n", channelSettings.name);
|
||||
okay = true;
|
||||
}
|
||||
|
||||
f.close();
|
||||
} else {
|
||||
DEBUG_MSG("No saved preferences found\n");
|
||||
DEBUG_MSG("No %s preferences found\n", filename);
|
||||
}
|
||||
|
||||
#else
|
||||
DEBUG_MSG("ERROR: Filesystem not implemented\n");
|
||||
#endif
|
||||
return okay;
|
||||
}
|
||||
|
||||
void NodeDB::loadFromDisk()
|
||||
{
|
||||
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
|
||||
if (!loadProto(preffile, DeviceState_size, sizeof(devicestate), DeviceState_fields, &devicestate)) {
|
||||
installDefaultDeviceState(); // Our in RAM copy might now be corrupt
|
||||
} else {
|
||||
if (devicestate.version < DEVICESTATE_MIN_VER) {
|
||||
DEBUG_MSG("Warn: devicestate %d is old, discarding\n", devicestate.version);
|
||||
installDefaultDeviceState();
|
||||
} else {
|
||||
DEBUG_MSG("Loaded saved preferences version %d\n", devicestate.version);
|
||||
}
|
||||
}
|
||||
|
||||
if (!loadProto(radiofile, RadioConfig_size, sizeof(RadioConfig), RadioConfig_fields, &radioConfig)) {
|
||||
installDefaultRadioConfig(); // Our in RAM copy might now be corrupt
|
||||
}
|
||||
|
||||
if (!loadProto(channelfile, ChannelFile_size, sizeof(ChannelFile), ChannelFile_fields, &channelFile)) {
|
||||
installDefaultChannels(); // Our in RAM copy might now be corrupt
|
||||
}
|
||||
}
|
||||
|
||||
/** Save a protobuf from a file, return true for success */
|
||||
bool saveProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, const void *dest_struct)
|
||||
{
|
||||
#ifdef FS
|
||||
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
|
||||
String filenameTmp = filename;
|
||||
filenameTmp += ".tmp";
|
||||
auto f = FS.open(filenameTmp.c_str(), FILE_O_WRITE);
|
||||
bool okay = false;
|
||||
if (f) {
|
||||
DEBUG_MSG("Saving %s\n", filename);
|
||||
pb_ostream_t stream = {&writecb, &f, protoSize};
|
||||
|
||||
if (!pb_encode(&stream, fields, dest_struct)) {
|
||||
DEBUG_MSG("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
|
||||
} else {
|
||||
okay = true;
|
||||
}
|
||||
|
||||
f.close();
|
||||
|
||||
// brief window of risk here ;-)
|
||||
if (!FS.remove(filename))
|
||||
DEBUG_MSG("Warning: Can't remove old pref file\n");
|
||||
if (!FS.rename(filenameTmp.c_str(), filename))
|
||||
DEBUG_MSG("Error: can't rename new pref file\n");
|
||||
} else {
|
||||
DEBUG_MSG("Can't write prefs\n");
|
||||
}
|
||||
#else
|
||||
DEBUG_MSG("ERROR: Filesystem not implemented\n");
|
||||
#endif
|
||||
return okay;
|
||||
}
|
||||
|
||||
void NodeDB::saveChannelsToDisk()
|
||||
{
|
||||
if (!devicestate.no_save) {
|
||||
#ifdef FS
|
||||
FS.mkdir("/prefs");
|
||||
#endif
|
||||
saveProto(channelfile, ChannelFile_size, sizeof(ChannelFile), ChannelFile_fields, &channelFile);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeDB::saveToDisk()
|
||||
{
|
||||
#ifdef FS
|
||||
if (!devicestate.no_save) {
|
||||
auto f = FS.open(preftmp, FILE_O_WRITE);
|
||||
if (f) {
|
||||
DEBUG_MSG("Writing preferences\n");
|
||||
#ifdef FS
|
||||
FS.mkdir("/prefs");
|
||||
#endif
|
||||
bool okay = saveProto(preffile, DeviceState_size, sizeof(devicestate), DeviceState_fields, &devicestate);
|
||||
okay &= saveProto(radiofile, RadioConfig_size, sizeof(RadioConfig), RadioConfig_fields, &radioConfig);
|
||||
saveChannelsToDisk();
|
||||
|
||||
pb_ostream_t stream = {&writecb, &f, SIZE_MAX, 0};
|
||||
|
||||
// DEBUG_MSG("Presave channel name=%s\n", channelSettings.name);
|
||||
|
||||
devicestate.version = DEVICESTATE_CUR_VER;
|
||||
if (!pb_encode(&stream, DeviceState_fields, &devicestate)) {
|
||||
DEBUG_MSG("Error: can't write protobuf %s\n", PB_GET_ERROR(&stream));
|
||||
// FIXME - report failure to phone
|
||||
|
||||
f.close();
|
||||
} else {
|
||||
// Success - replace the old file
|
||||
f.close();
|
||||
|
||||
// brief window of risk here ;-)
|
||||
if (!FS.remove(preffile))
|
||||
DEBUG_MSG("Warning: Can't remove old pref file\n");
|
||||
if (!FS.rename(preftmp, preffile))
|
||||
DEBUG_MSG("Error: can't rename new pref file\n");
|
||||
}
|
||||
} else {
|
||||
DEBUG_MSG("ERROR: can't write prefs\n"); // FIXME report to app
|
||||
}
|
||||
// remove any pre 1.2 pref files, turn on after 1.2 is in beta
|
||||
// if(okay) FS.remove(preffileOld);
|
||||
} else {
|
||||
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE - not saving to flash *****\n");
|
||||
}
|
||||
#else
|
||||
DEBUG_MSG("ERROR filesystem not implemented\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
const NodeInfo *NodeDB::readNextInfo()
|
||||
@@ -489,7 +438,17 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p)
|
||||
|
||||
DEBUG_MSG("DB update position node=0x%x time=%u, latI=%d, lonI=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i);
|
||||
|
||||
info->position = p;
|
||||
// Be careful to only update fields that have been set by the sender
|
||||
// A lot of position reports don't have time populated. In that case, be careful to not blow away the time we
|
||||
// recorded based on the packet rxTime
|
||||
if (!info->position.time && p.time)
|
||||
info->position.time = p.time;
|
||||
if (p.battery_level)
|
||||
info->position.battery_level = p.battery_level;
|
||||
if (p.latitude_i || p.longitude_i) {
|
||||
info->position.latitude_i = p.latitude_i;
|
||||
info->position.longitude_i = p.longitude_i;
|
||||
}
|
||||
info->has_position = true;
|
||||
updateGUIforNode = info;
|
||||
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
@@ -526,10 +485,9 @@ void NodeDB::updateUser(uint32_t nodeId, const User &p)
|
||||
void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
{
|
||||
if (mp.which_payloadVariant == MeshPacket_decoded_tag) {
|
||||
const SubPacket &p = mp.decoded;
|
||||
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
|
||||
|
||||
NodeInfo *info = getOrCreateNode(mp.from);
|
||||
NodeInfo *info = getOrCreateNode(getFrom(&mp));
|
||||
|
||||
if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen
|
||||
info->has_position = true; // at least the time is valid
|
||||
@@ -537,31 +495,6 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
||||
}
|
||||
|
||||
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
|
||||
|
||||
switch (p.which_payloadVariant) {
|
||||
case SubPacket_position_tag: {
|
||||
// handle a legacy position packet
|
||||
DEBUG_MSG("WARNING: Processing a (deprecated) position packet from %d\n", mp.from);
|
||||
updatePosition(mp.from, p.position);
|
||||
break;
|
||||
}
|
||||
|
||||
case SubPacket_data_tag: {
|
||||
if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum())
|
||||
MeshPlugin::callPlugins(mp);
|
||||
break;
|
||||
}
|
||||
|
||||
case SubPacket_user_tag: {
|
||||
DEBUG_MSG("WARNING: Processing a (deprecated) user packet from %d\n", mp.from);
|
||||
updateUser(mp.from, p.user);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
notifyObservers(); // If the node counts have changed, notify observers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,11 +533,10 @@ void recordCriticalError(CriticalErrorCode code, uint32_t address)
|
||||
// Print error to screen and serial port
|
||||
String lcd = String("Critical error ") + code + "!\n";
|
||||
screen->print(lcd.c_str());
|
||||
DEBUG_MSG("NOTE! Recording critical error %d, address=%x\n", code, address);
|
||||
|
||||
DEBUG_MSG("NOTE! Recording critical error %d, address=%lx\n", code, address);
|
||||
|
||||
// Record error to DB
|
||||
myNodeInfo.error_code = code;
|
||||
myNodeInfo.error_address = address;
|
||||
myNodeInfo.error_count++;
|
||||
|
||||
}
|
||||
|
||||
+8
-25
@@ -9,11 +9,10 @@
|
||||
#include "mesh-pb-constants.h"
|
||||
|
||||
extern DeviceState devicestate;
|
||||
extern ChannelFile channelFile;
|
||||
extern MyNodeInfo &myNodeInfo;
|
||||
extern RadioConfig &radioConfig;
|
||||
extern ChannelSettings &channelSettings;
|
||||
extern RadioConfig radioConfig;
|
||||
extern User &owner;
|
||||
extern const char *channelName;
|
||||
|
||||
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
|
||||
uint32_t sinceLastSeen(const NodeInfo *n);
|
||||
@@ -44,7 +43,7 @@ class NodeDB
|
||||
void init();
|
||||
|
||||
/// write to flash
|
||||
void saveToDisk();
|
||||
void saveToDisk(), saveChannelsToDisk();
|
||||
|
||||
/** Reinit radio config if needed, because either:
|
||||
* a) sometimes a buggy android app might send us bogus settings or
|
||||
@@ -119,7 +118,7 @@ class NodeDB
|
||||
void loadFromDisk();
|
||||
|
||||
/// Reinit device state from scratch (not loading from disk)
|
||||
void installDefaultDeviceState();
|
||||
void installDefaultDeviceState(), installDefaultRadioConfig(), installDefaultChannels();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -130,23 +129,6 @@ extern NodeNum displayedNodeNum;
|
||||
|
||||
extern NodeDB nodeDB;
|
||||
|
||||
/**
|
||||
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs.
|
||||
* The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they
|
||||
their nodes
|
||||
* aren't talking to each other.
|
||||
*
|
||||
* This string is of the form "#name-XY".
|
||||
*
|
||||
* Where X is a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together.
|
||||
* Y is not yet used but should eventually indicate 'speed/range' of the link
|
||||
*
|
||||
* This function will also need to be implemented in GUI apps that talk to the radio.
|
||||
*
|
||||
* https://github.com/meshtastic/Meshtastic-device/issues/269
|
||||
*/
|
||||
const char *getChannelName();
|
||||
|
||||
/*
|
||||
If is_router is set, we use a number of different default values
|
||||
|
||||
@@ -173,7 +155,7 @@ const char *getChannelName();
|
||||
*/
|
||||
|
||||
// Our delay functions check for this for times that should never expire
|
||||
#define DELAY_FOREVER 0xffffffff
|
||||
#define NODE_DELAY_FOREVER 0xffffffff
|
||||
|
||||
#define IF_ROUTER(routerVal, normalVal) (radioConfig.preferences.is_router ? (routerVal) : (normalVal))
|
||||
|
||||
@@ -187,8 +169,8 @@ PREF_GET(position_broadcast_secs, IF_ROUTER(12 * 60 * 60, 15 * 60))
|
||||
PREF_GET(wait_bluetooth_secs, IF_ROUTER(1, 60))
|
||||
|
||||
PREF_GET(screen_on_secs, 60)
|
||||
PREF_GET(mesh_sds_timeout_secs, IF_ROUTER(DELAY_FOREVER, 2 * 60 * 60))
|
||||
PREF_GET(phone_sds_timeout_sec, IF_ROUTER(DELAY_FOREVER, 2 * 60 * 60))
|
||||
PREF_GET(mesh_sds_timeout_secs, IF_ROUTER(NODE_DELAY_FOREVER, 2 * 60 * 60))
|
||||
PREF_GET(phone_sds_timeout_sec, IF_ROUTER(NODE_DELAY_FOREVER, 2 * 60 * 60))
|
||||
PREF_GET(sds_secs, 365 * 24 * 60 * 60)
|
||||
|
||||
// We default to sleeping (with bluetooth off for 5 minutes at a time). This seems to be a good tradeoff between
|
||||
@@ -202,3 +184,4 @@ PREF_GET(min_wake_secs, 10)
|
||||
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
||||
*/
|
||||
extern uint32_t radioGeneration;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
|
||||
// DEBUG_MSG("Deleting old broadcast record %d\n", i);
|
||||
recentPackets.erase(recentPackets.begin() + i); // delete old record
|
||||
} else {
|
||||
if (r.id == p->id && r.sender == p->from) {
|
||||
if (r.id == p->id && r.sender == getFrom(p)) {
|
||||
DEBUG_MSG("Found existing packet record for fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
|
||||
// Update the time on this record to now
|
||||
@@ -43,7 +43,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
|
||||
if (withUpdate) {
|
||||
PacketRecord r;
|
||||
r.id = p->id;
|
||||
r.sender = p->from;
|
||||
r.sender = getFrom(p);
|
||||
r.rxTimeMsec = now;
|
||||
recentPackets.push_back(r);
|
||||
printPacket("Adding packet record", p);
|
||||
|
||||
+5
-67
@@ -4,6 +4,7 @@
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "Channels.h"
|
||||
#include <assert.h>
|
||||
|
||||
#if FromRadio_size > MAX_TO_FROM_RADIO_SIZE
|
||||
@@ -58,6 +59,7 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
||||
}
|
||||
// return (lastContactMsec != 0) &&
|
||||
|
||||
memset(&toRadioScratch, 0, sizeof(toRadioScratch));
|
||||
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
|
||||
switch (toRadioScratch.which_payloadVariant) {
|
||||
case ToRadio_packet_tag: {
|
||||
@@ -77,16 +79,6 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
||||
// this will break once we have multiple instances of PhoneAPI running independently
|
||||
break;
|
||||
|
||||
case ToRadio_set_owner_tag:
|
||||
DEBUG_MSG("Client is setting owner\n");
|
||||
handleSetOwner(toRadioScratch.set_owner);
|
||||
break;
|
||||
|
||||
case ToRadio_set_radio_tag:
|
||||
DEBUG_MSG("Client is setting radio\n");
|
||||
handleSetRadio(toRadioScratch.set_radio);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_MSG("Error: unexpected ToRadio variant\n");
|
||||
break;
|
||||
@@ -133,24 +125,11 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
: (gps && gps->isConnected()); // Update with latest GPS connect info
|
||||
fromRadioScratch.which_payloadVariant = FromRadio_my_info_tag;
|
||||
fromRadioScratch.my_info = myNodeInfo;
|
||||
state = STATE_SEND_RADIO;
|
||||
state = STATE_SEND_NODEINFO;
|
||||
|
||||
service.refreshMyNodeInfo(); // Update my NodeInfo because the client will be asking for it soon.
|
||||
break;
|
||||
|
||||
case STATE_SEND_RADIO:
|
||||
fromRadioScratch.which_payloadVariant = FromRadio_radio_tag;
|
||||
|
||||
fromRadioScratch.radio = radioConfig;
|
||||
|
||||
// NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior.
|
||||
// So even if we internally use 0 to represent 'use default' we still need to send the value we are
|
||||
// using to the app (so that even old phone apps work with new device loads).
|
||||
fromRadioScratch.radio.preferences.ls_secs = getPref_ls_secs();
|
||||
|
||||
state = STATE_SEND_NODEINFO;
|
||||
break;
|
||||
|
||||
case STATE_SEND_NODEINFO: {
|
||||
const NodeInfo *info = nodeInfoForPhone;
|
||||
nodeInfoForPhone = NULL; // We just consumed a nodeinfo, will need a new one next time
|
||||
@@ -177,7 +156,6 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
state = STATE_SEND_PACKETS;
|
||||
break;
|
||||
|
||||
case STATE_LEGACY: // Treat as the same as send packets
|
||||
case STATE_SEND_PACKETS:
|
||||
// Do we have a message from the mesh?
|
||||
if (packetForPhone) {
|
||||
@@ -200,9 +178,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
// Do we have a message from the mesh?
|
||||
if (fromRadioScratch.which_payloadVariant != 0) {
|
||||
// Encapsulate as a FromRadio packet
|
||||
DEBUG_MSG("encoding toPhone packet to phone variant=%d", fromRadioScratch.which_payloadVariant);
|
||||
size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch);
|
||||
DEBUG_MSG(", %d bytes\n", numbytes);
|
||||
// DEBUG_MSG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payloadVariant, numbytes);
|
||||
return numbytes;
|
||||
}
|
||||
|
||||
@@ -227,13 +204,9 @@ bool PhoneAPI::available()
|
||||
nodeInfoForPhone = nodeDB.readNextInfo();
|
||||
return true; // Always say we have something, because we might need to advance our state machine
|
||||
|
||||
case STATE_SEND_RADIO:
|
||||
return true;
|
||||
|
||||
case STATE_SEND_COMPLETE_ID:
|
||||
return true;
|
||||
|
||||
case STATE_LEGACY: // Treat as the same as send packets
|
||||
case STATE_SEND_PACKETS: {
|
||||
// Try to pull a new packet from the service (if we haven't already)
|
||||
if (!packetForPhone)
|
||||
@@ -250,41 +223,6 @@ bool PhoneAPI::available()
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// The following routines are only public for now - until the rev1 bluetooth API is removed
|
||||
//
|
||||
|
||||
void PhoneAPI::handleSetOwner(const User &o)
|
||||
{
|
||||
int changed = 0;
|
||||
|
||||
if (*o.long_name) {
|
||||
changed |= strcmp(owner.long_name, o.long_name);
|
||||
strcpy(owner.long_name, o.long_name);
|
||||
}
|
||||
if (*o.short_name) {
|
||||
changed |= strcmp(owner.short_name, o.short_name);
|
||||
strcpy(owner.short_name, o.short_name);
|
||||
}
|
||||
if (*o.id) {
|
||||
changed |= strcmp(owner.id, o.id);
|
||||
strcpy(owner.id, o.id);
|
||||
}
|
||||
|
||||
if (changed) // If nothing really changed, don't broadcast on the network or write to flash
|
||||
service.reloadOwner();
|
||||
}
|
||||
|
||||
void PhoneAPI::handleSetRadio(const RadioConfig &r)
|
||||
{
|
||||
radioConfig = r;
|
||||
|
||||
bool didReset = service.reloadConfig();
|
||||
if (didReset) {
|
||||
state = STATE_SEND_MY_INFO; // Squirt a completely new set of configs to the client
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a packet that the phone wants us to send. It is our responsibility to free the packet to the pool
|
||||
*/
|
||||
@@ -296,7 +234,7 @@ int PhoneAPI::onNotify(uint32_t newValue)
|
||||
checkConnectionTimeout(); // a handy place to check if we've heard from the phone (since the BLE version doesn't call this
|
||||
// from idle)
|
||||
|
||||
if (state == STATE_SEND_PACKETS || state == STATE_LEGACY) {
|
||||
if (state == STATE_SEND_PACKETS) {
|
||||
DEBUG_MSG("Telling client we have new packets %u\n", newValue);
|
||||
onNowHasData(newValue);
|
||||
} else
|
||||
|
||||
+2
-9
@@ -20,10 +20,10 @@ class PhoneAPI
|
||||
: public Observer<uint32_t> // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member
|
||||
{
|
||||
enum State {
|
||||
STATE_LEGACY, // (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_MY_INFO, // send our my info record
|
||||
STATE_SEND_RADIO,
|
||||
// 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_NODEINFO, // states progress in this order as the device sends to to the client
|
||||
STATE_SEND_COMPLETE_ID,
|
||||
@@ -83,13 +83,6 @@ class PhoneAPI
|
||||
*/
|
||||
bool available();
|
||||
|
||||
//
|
||||
// The following routines are only public for now - until the rev1 bluetooth API is removed
|
||||
//
|
||||
|
||||
void handleSetOwner(const User &o);
|
||||
void handleSetRadio(const RadioConfig &r);
|
||||
|
||||
protected:
|
||||
/// Are we currently connected to a client?
|
||||
bool isConnected = false;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* If you are using protobufs to encode your packets (recommended) you can use this as a baseclass for your plugin
|
||||
* and avoid a bunch of boilerplate code.
|
||||
*/
|
||||
template <class T> class ProtobufPlugin : private SinglePortPlugin
|
||||
template <class T> class ProtobufPlugin : protected SinglePortPlugin
|
||||
{
|
||||
const pb_msgdesc_t *fields;
|
||||
|
||||
@@ -25,8 +25,11 @@ template <class T> class ProtobufPlugin : private SinglePortPlugin
|
||||
|
||||
/**
|
||||
* Handle a received message, the data field in the message is already decoded and is provided
|
||||
*
|
||||
* In general decoded will always be !NULL. But in some special applications (where you have handling packets
|
||||
* for multiple port numbers, decoding will ONLY be attempted for packets where the portnum matches our expected ourPortNum.
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const T &decoded) = 0;
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const T *decoded) = 0;
|
||||
|
||||
/**
|
||||
* Return a mesh packet which has been preinited with a particular protobuf data payload and port number.
|
||||
@@ -38,8 +41,8 @@ template <class T> class ProtobufPlugin : private SinglePortPlugin
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
MeshPacket *p = allocDataPacket();
|
||||
|
||||
p->decoded.data.payload.size =
|
||||
pb_encode_to_bytes(p->decoded.data.payload.bytes, sizeof(p->decoded.data.payload.bytes), fields, &payload);
|
||||
p->decoded.payload.size =
|
||||
pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), fields, &payload);
|
||||
// DEBUG_MSG("did encode\n");
|
||||
return p;
|
||||
}
|
||||
@@ -54,13 +57,19 @@ template <class T> class ProtobufPlugin : private SinglePortPlugin
|
||||
// FIXME - we currently update position data in the DB only if the message was a broadcast or destined to us
|
||||
// it would be better to update even if the message was destined to others.
|
||||
|
||||
auto &p = mp.decoded.data;
|
||||
DEBUG_MSG("Received %s from=0x%0x, id=0x%x, payloadlen=%d\n", name, mp.from, mp.id, p.payload.size);
|
||||
auto &p = mp.decoded;
|
||||
DEBUG_MSG("Received %s from=0x%0x, id=0x%x, portnum=%d, payloadlen=%d\n", name, mp.from, mp.id, p.portnum, p.payload.size);
|
||||
|
||||
T scratch;
|
||||
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
|
||||
return handleReceivedProtobuf(mp, scratch);
|
||||
T *decoded = NULL;
|
||||
if(mp.decoded.portnum == ourPortNum) {
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
|
||||
decoded = &scratch;
|
||||
else
|
||||
DEBUG_MSG("Error decoding protobuf plugin!\n");
|
||||
}
|
||||
|
||||
return false; // Let others look at this message also if they want
|
||||
return handleReceivedProtobuf(mp, decoded);
|
||||
}
|
||||
};
|
||||
|
||||
+80
-27
@@ -1,10 +1,12 @@
|
||||
|
||||
#include "configuration.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "Channels.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "assert.h"
|
||||
#include "configuration.h"
|
||||
#include "Router.h"
|
||||
#include "sleep.h"
|
||||
#include <assert.h>
|
||||
#include <pb_decode.h>
|
||||
@@ -23,9 +25,27 @@ const RegionInfo regions[] = {
|
||||
RDEF(KR, 921.9f, 0.2f, 8, 0), // KR channel settings (KR920-923) Start from TTN download channel
|
||||
// freq. (921.9f is for download, others are for uplink)
|
||||
RDEF(TW, 923.0f, 0.2f, 10, 0), // TW channel settings (AS2 bandplan 923-925MHz)
|
||||
RDEF(RU, 868.9f, 0.2f, 2, 20), // See notes below
|
||||
RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last
|
||||
};
|
||||
|
||||
/* Notes about the RU bandplan (from @denis-d in https://meshtastic.discourse.group/t/russian-band-plan-proposal/2786/2):
|
||||
|
||||
According to Annex 12 to GKRCh (National Radio Frequency Commission) decision № 18-46-03-1 (September 11th 2018) https://digital.gov.ru/uploaded/files/prilozhenie-12-k-reshenyu-gkrch-18-46-03-1.pdf 1
|
||||
We have 3 options for 868 MHz:
|
||||
|
||||
864,0 - 865,0 MHz ERP 25mW, Duty Cycle 0.1% (3.6 sec in hour) or LBT (Listen Before Talk), prohibited in airports.
|
||||
866,0 - 868,0 MHz ERP 25mW, Duty Cycle 1% or LBT, PSD (Power Spectrum Density) 1000mW/MHz, prohibited in airports
|
||||
868,7 - 869,2 MHz ERP 100mW, Duty Cycle 10% or LBT, no resctrictions
|
||||
and according to RP2-1.0.2 LoRaWAN® Regional Parameters RP2-1.0.2 LoRaWAN® Regional Parameters - LoRa Alliance®
|
||||
I propose to use following meshtastic bandplan:
|
||||
1 channel - central frequency 868.9MHz 125kHz band
|
||||
Protective band - 75kHz
|
||||
2 channel - central frequency 869.1MHz 125kHz band
|
||||
|
||||
RDEF(RU, 868.9f, 0.2f, 2, 20)
|
||||
*/
|
||||
|
||||
const RegionInfo *myRegion;
|
||||
|
||||
void initRegion()
|
||||
@@ -36,7 +56,7 @@ void initRegion()
|
||||
myRegion = r;
|
||||
DEBUG_MSG("Wanted region %d, using %s\n", radioConfig.preferences.region, r->name);
|
||||
|
||||
myNodeInfo.num_channels = myRegion->numChannels; // Tell our android app how many channels we have
|
||||
myNodeInfo.num_bands = myRegion->numChannels; // Tell our android app how many channels we have
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,27 +137,13 @@ uint32_t RadioInterface::getTxDelayMsec()
|
||||
|
||||
void printPacket(const char *prefix, const MeshPacket *p)
|
||||
{
|
||||
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("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d Ch0x%x", prefix, p->id, p->from & 0xff, p->to & 0xff,
|
||||
p->want_ack, p->hop_limit, p->channel);
|
||||
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
||||
auto &s = p->decoded;
|
||||
switch (s.which_payloadVariant) {
|
||||
case SubPacket_data_tag:
|
||||
DEBUG_MSG(" Portnum=%d", s.data.portnum);
|
||||
break;
|
||||
case SubPacket_position_tag:
|
||||
DEBUG_MSG(" Payload:Position");
|
||||
break;
|
||||
case SubPacket_user_tag:
|
||||
DEBUG_MSG(" Payload:User");
|
||||
break;
|
||||
case 0:
|
||||
DEBUG_MSG(" Payload:None");
|
||||
break;
|
||||
default:
|
||||
DEBUG_MSG(" Payload:%d", s.which_payloadVariant);
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG_MSG(" Portnum=%d", s.portnum);
|
||||
|
||||
if (s.want_response)
|
||||
DEBUG_MSG(" WANTRESP");
|
||||
|
||||
@@ -147,10 +153,14 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
||||
if (s.dest != 0)
|
||||
DEBUG_MSG(" dest=%08x", s.dest);
|
||||
|
||||
if(s.request_id)
|
||||
DEBUG_MSG(" requestId=%0x", s.request_id);
|
||||
|
||||
/* now inside Data and therefore kinda opaque
|
||||
if (s.which_ackVariant == SubPacket_success_id_tag)
|
||||
DEBUG_MSG(" successId=%08x", s.ackVariant.success_id);
|
||||
else if (s.which_ackVariant == SubPacket_fail_id_tag)
|
||||
DEBUG_MSG(" failId=%08x", s.ackVariant.fail_id);
|
||||
DEBUG_MSG(" failId=%08x", s.ackVariant.fail_id); */
|
||||
} else {
|
||||
DEBUG_MSG(" encrypted");
|
||||
}
|
||||
@@ -161,12 +171,15 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
||||
if (p->rx_snr != 0.0) {
|
||||
DEBUG_MSG(" rxSNR=%g", p->rx_snr);
|
||||
}
|
||||
if (p->priority != 0)
|
||||
DEBUG_MSG(" priority=%d", p->priority);
|
||||
|
||||
DEBUG_MSG(")\n");
|
||||
}
|
||||
|
||||
RadioInterface::RadioInterface()
|
||||
{
|
||||
assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
|
||||
assert(sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
|
||||
|
||||
// Can't print strings this early - serial not setup yet
|
||||
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
|
||||
@@ -209,6 +222,38 @@ unsigned long hash(const char *str)
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save our frequency for later reuse.
|
||||
*/
|
||||
void RadioInterface::saveFreq(float freq)
|
||||
{
|
||||
savedFreq = freq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save our channel for later reuse.
|
||||
*/
|
||||
void RadioInterface::saveChannelNum(uint32_t channel_num)
|
||||
{
|
||||
savedChannelNum = channel_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save our frequency for later reuse.
|
||||
*/
|
||||
float RadioInterface::getFreq()
|
||||
{
|
||||
return savedFreq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save our channel for later reuse.
|
||||
*/
|
||||
uint32_t RadioInterface::getChannelNum()
|
||||
{
|
||||
return savedChannelNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull our channel settings etc... from protobufs to the dumb interface settings
|
||||
*/
|
||||
@@ -217,6 +262,7 @@ void RadioInterface::applyModemConfig()
|
||||
// Set up default configuration
|
||||
// No Sync Words in LORA mode
|
||||
|
||||
auto channelSettings = channels.getPrimary();
|
||||
if (channelSettings.spread_factor == 0) {
|
||||
switch (channelSettings.modem_config) {
|
||||
case ChannelSettings_ModemConfig_Bw125Cr45Sf128: ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium
|
||||
@@ -261,18 +307,20 @@ void RadioInterface::applyModemConfig()
|
||||
assert(myRegion); // Should have been found in init
|
||||
|
||||
// If user has manually specified a channel num, then use that, otherwise generate one by hashing the name
|
||||
int channel_num =
|
||||
(channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelName)) % myRegion->numChannels;
|
||||
const char *channelName = channels.getName(channels.getPrimaryIndex());
|
||||
int channel_num = channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelName) % myRegion->numChannels;
|
||||
freq = myRegion->freq + myRegion->spacing * channel_num;
|
||||
|
||||
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelName, channelSettings.modem_config, channel_num,
|
||||
power);
|
||||
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelName, channelSettings.modem_config, channel_num, power);
|
||||
DEBUG_MSG("Radio myRegion->freq: %f\n", myRegion->freq);
|
||||
DEBUG_MSG("Radio myRegion->spacing: %f\n", myRegion->spacing);
|
||||
DEBUG_MSG("Radio myRegion->numChannels: %d\n", myRegion->numChannels);
|
||||
DEBUG_MSG("Radio channel_num: %d\n", channel_num);
|
||||
DEBUG_MSG("Radio frequency: %f\n", freq);
|
||||
DEBUG_MSG("Short packet time: %u msec\n", shortPacketMsec);
|
||||
|
||||
saveChannelNum(channel_num);
|
||||
saveFreq(freq);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,6 +353,10 @@ void RadioInterface::deliverToReceiver(MeshPacket *p)
|
||||
{
|
||||
assert(rxDest);
|
||||
assert(rxDest->enqueue(p, 0)); // NOWAIT - fixme, if queue is full, delete older messages
|
||||
|
||||
// Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME
|
||||
if (router)
|
||||
router->setReceivedMessage();
|
||||
}
|
||||
|
||||
/***
|
||||
@@ -324,6 +376,7 @@ size_t RadioInterface::beginSending(MeshPacket *p)
|
||||
h->from = p->from;
|
||||
h->to = p->to;
|
||||
h->id = p->id;
|
||||
h->channel = p->channel;
|
||||
assert(p->hop_limit <= HOP_MAX);
|
||||
h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0);
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ typedef struct {
|
||||
* The bottom three bits of flags are use to store hop_limit when sent over the wire.
|
||||
**/
|
||||
uint8_t flags;
|
||||
|
||||
/** The channel hash - used as a hint for the decoder to limit which channels we consider */
|
||||
uint8_t channel;
|
||||
} PacketHeader;
|
||||
|
||||
/**
|
||||
@@ -54,6 +57,8 @@ class RadioInterface
|
||||
uint32_t shortPacketMsec;
|
||||
|
||||
protected:
|
||||
bool disabled = false;
|
||||
|
||||
float bw = 125;
|
||||
uint8_t sf = 9;
|
||||
uint8_t cr = 7;
|
||||
@@ -98,6 +103,9 @@ class RadioInterface
|
||||
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
|
||||
virtual bool sleep() { return true; }
|
||||
|
||||
/// Disable this interface (while disabled, no packets can be sent or received)
|
||||
void disable() { disabled = true; sleep(); }
|
||||
|
||||
/**
|
||||
* Send a packet (possibly by enquing in a private fifo). This routine will
|
||||
* later free() the packet to pool. This routine is not allowed to stall.
|
||||
@@ -105,6 +113,9 @@ class RadioInterface
|
||||
*/
|
||||
virtual ErrorCode send(MeshPacket *p) = 0;
|
||||
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
virtual bool cancelSending(NodeNum from, PacketId id) { return false; }
|
||||
|
||||
// methods from radiohead
|
||||
|
||||
/// Initialise the Driver transport hardware and software.
|
||||
@@ -133,9 +144,22 @@ class RadioInterface
|
||||
uint32_t getPacketTime(MeshPacket *p);
|
||||
uint32_t getPacketTime(uint32_t totalPacketLen);
|
||||
|
||||
/**
|
||||
* Get the channel we saved.
|
||||
*/
|
||||
uint32_t getChannelNum();
|
||||
|
||||
/**
|
||||
* Get the frequency we saved.
|
||||
*/
|
||||
float getFreq();
|
||||
|
||||
protected:
|
||||
int8_t power = 17; // Set by applyModemConfig()
|
||||
|
||||
float savedFreq;
|
||||
uint32_t savedChannelNum;
|
||||
|
||||
/***
|
||||
* given a packet set sendingPacket and decode the protobufs into radiobuf. Returns # of bytes to send (including the
|
||||
* PacketHeader & payload).
|
||||
@@ -157,6 +181,16 @@ class RadioInterface
|
||||
*/
|
||||
virtual void applyModemConfig();
|
||||
|
||||
/**
|
||||
* Save the frequency we selected for later reuse.
|
||||
*/
|
||||
virtual void saveFreq(float savedFreq);
|
||||
|
||||
/**
|
||||
* Save the chanel we selected for later reuse.
|
||||
*/
|
||||
virtual void saveChannelNum(uint32_t savedChannelNum);
|
||||
|
||||
private:
|
||||
/// Return 0 if sleep is okay
|
||||
int preflightSleepCb(void *unused = NULL) { return canSleep() ? 0 : 1; }
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#include "MeshTypes.h"
|
||||
#include "NodeDB.h"
|
||||
#include "SPILock.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "error.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include <configuration.h>
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
@@ -68,13 +68,12 @@ bool RadioLibInterface::canSendImmediately()
|
||||
bool busyTx = sendingPacket != NULL;
|
||||
bool busyRx = isReceiving && isActivelyReceiving();
|
||||
|
||||
|
||||
if (busyTx || busyRx) {
|
||||
if (busyTx)
|
||||
DEBUG_MSG("Can not send yet, busyTx\n");
|
||||
// If we've been trying to send the same packet more than one minute and we haven't gotten a
|
||||
// TX IRQ from the radio, the radio is probably broken.
|
||||
if (busyTx && (millis() - lastTxStart > 60000)){
|
||||
if (busyTx && (millis() - lastTxStart > 60000)) {
|
||||
DEBUG_MSG("Hardware Failure! busyTx for more than 60s\n");
|
||||
recordCriticalError(CriticalErrorCode_TransmitFailed);
|
||||
#ifndef NO_ESP32
|
||||
@@ -94,13 +93,18 @@ bool RadioLibInterface::canSendImmediately()
|
||||
/// bluetooth comms code. If the txmit queue is empty it might return an error
|
||||
ErrorCode RadioLibInterface::send(MeshPacket *p)
|
||||
{
|
||||
if (disabled) {
|
||||
packetPool.release(p);
|
||||
return ERRNO_DISABLED;
|
||||
}
|
||||
|
||||
// Sometimes when testing it is useful to be able to never turn on the xmitter
|
||||
#ifndef LORA_DISABLE_SENDING
|
||||
printPacket("enqueuing for send", p);
|
||||
uint32_t xmitMsec = getPacketTime(p);
|
||||
|
||||
DEBUG_MSG("txGood=%d,rxGood=%d,rxBad=%d\n", txGood, rxGood, rxBad);
|
||||
ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
|
||||
if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks
|
||||
packetPool.release(p);
|
||||
@@ -110,7 +114,6 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
|
||||
// Count the packet toward our TX airtime utilization.
|
||||
// We only count it if it can be added to the TX queue.
|
||||
airTime->logAirtime(TX_LOG, xmitMsec);
|
||||
//airTime.logAirtime(TX_LOG, xmitMsec);
|
||||
|
||||
// We want all sending/receiving to be done by our daemon thread, We use a delay here because this packet might have been sent
|
||||
// in response to a packet we just received. So we want to make sure the other side has had a chance to reconfigure its radio
|
||||
@@ -119,19 +122,31 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
|
||||
return res;
|
||||
#else
|
||||
packetPool.release(p);
|
||||
return ERRNO_UNKNOWN;
|
||||
return ERRNO_DISABLED;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RadioLibInterface::canSleep()
|
||||
{
|
||||
bool res = txQueue.isEmpty();
|
||||
bool res = txQueue.empty();
|
||||
if (!res) // only print debug messages if we are vetoing sleep
|
||||
DEBUG_MSG("radio wait to sleep, txEmpty=%d\n", res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
bool RadioLibInterface::cancelSending(NodeNum from, PacketId id)
|
||||
{
|
||||
auto p = txQueue.remove(from, id);
|
||||
if (p)
|
||||
packetPool.release(p); // free the packet we just removed
|
||||
|
||||
bool result = (p != NULL);
|
||||
DEBUG_MSG("cancelSending id=0x%x, removed=%d\n", id, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** radio helper thread callback.
|
||||
|
||||
We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and
|
||||
@@ -165,12 +180,12 @@ void RadioLibInterface::onNotify(uint32_t notification)
|
||||
|
||||
// If we are not currently in receive mode, then restart the timer and try again later (this can happen if the main thread
|
||||
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
|
||||
if (!txQueue.isEmpty()) {
|
||||
if (!txQueue.empty()) {
|
||||
if (!canSendImmediately()) {
|
||||
startTransmitTimer(); // try again in a little while
|
||||
} else {
|
||||
// Send any outgoing packets we have ready
|
||||
MeshPacket *txp = txQueue.dequeuePtr(0);
|
||||
MeshPacket *txp = txQueue.dequeue();
|
||||
assert(txp);
|
||||
startSend(txp);
|
||||
}
|
||||
@@ -186,7 +201,7 @@ void RadioLibInterface::onNotify(uint32_t notification)
|
||||
void RadioLibInterface::startTransmitTimer(bool withDelay)
|
||||
{
|
||||
// If we have work to do and the timer wasn't already scheduled, schedule it now
|
||||
if (!txQueue.isEmpty()) {
|
||||
if (!txQueue.empty()) {
|
||||
uint32_t delay = !withDelay ? 1 : getTxDelayMsec();
|
||||
// DEBUG_MSG("xmit timer %d\n", delay);
|
||||
notifyLater(delay, TRANSMIT_DELAY_COMPLETED, false); // This will implicitly enable
|
||||
@@ -229,13 +244,14 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
size_t length = iface->getPacketLength();
|
||||
|
||||
xmitMsec = getPacketTime(length);
|
||||
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
|
||||
//airTime.logAirtime(RX_ALL_LOG, xmitMsec);
|
||||
|
||||
int state = iface->readData(radiobuf, length);
|
||||
if (state != ERR_NONE) {
|
||||
DEBUG_MSG("ignoring received packet due to error=%d\n", state);
|
||||
rxBad++;
|
||||
|
||||
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
|
||||
|
||||
} else {
|
||||
// Skip the 4 headers that are at the beginning of the rxBuf
|
||||
int32_t payloadLen = length - sizeof(PacketHeader);
|
||||
@@ -245,6 +261,7 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
if (payloadLen < 0) {
|
||||
DEBUG_MSG("ignoring received packet too short\n");
|
||||
rxBad++;
|
||||
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
|
||||
} else {
|
||||
const PacketHeader *h = (PacketHeader *)radiobuf;
|
||||
|
||||
@@ -258,6 +275,7 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
mp->from = h->from;
|
||||
mp->to = h->to;
|
||||
mp->id = h->id;
|
||||
mp->channel = h->channel;
|
||||
assert(HOP_MAX <= PACKET_FLAGS_HOP_MASK); // If hopmax changes, carefully check this code
|
||||
mp->hop_limit = h->flags & PACKET_FLAGS_HOP_MASK;
|
||||
mp->want_ack = !!(h->flags & PACKET_FLAGS_WANT_ACK_MASK);
|
||||
@@ -265,15 +283,14 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
addReceiveMetadata(mp);
|
||||
|
||||
mp->which_payloadVariant = MeshPacket_encrypted_tag; // Mark that the payload is still encrypted at this point
|
||||
assert(((uint32_t) payloadLen) <= sizeof(mp->encrypted.bytes));
|
||||
assert(((uint32_t)payloadLen) <= sizeof(mp->encrypted.bytes));
|
||||
memcpy(mp->encrypted.bytes, payload, payloadLen);
|
||||
mp->encrypted.size = payloadLen;
|
||||
|
||||
printPacket("Lora RX", mp);
|
||||
|
||||
xmitMsec = getPacketTime(mp);
|
||||
//xmitMsec = getPacketTime(mp);
|
||||
airTime->logAirtime(RX_LOG, xmitMsec);
|
||||
//airTime.logAirtime(RX_LOG, xmitMsec);
|
||||
|
||||
deliverToReceiver(mp);
|
||||
}
|
||||
@@ -284,15 +301,20 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
void RadioLibInterface::startSend(MeshPacket *txp)
|
||||
{
|
||||
printPacket("Starting low level send", txp);
|
||||
setStandby(); // Cancel any already in process receives
|
||||
if (disabled) {
|
||||
DEBUG_MSG("startSend is dropping tx packet because we are disabled\n");
|
||||
packetPool.release(txp);
|
||||
} else {
|
||||
setStandby(); // Cancel any already in process receives
|
||||
|
||||
configHardwareForSend(); // must be after setStandby
|
||||
configHardwareForSend(); // must be after setStandby
|
||||
|
||||
size_t numbytes = beginSending(txp);
|
||||
size_t numbytes = beginSending(txp);
|
||||
|
||||
int res = iface->startTransmit(radiobuf, numbytes);
|
||||
assert(res == ERR_NONE);
|
||||
int res = iface->startTransmit(radiobuf, numbytes);
|
||||
assert(res == ERR_NONE);
|
||||
|
||||
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
|
||||
enableInterrupt(isrTxLevel0);
|
||||
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
|
||||
enableInterrupt(isrTxLevel0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "../concurrency/OSThread.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "MeshPacketQueue.h"
|
||||
|
||||
#ifdef CubeCell_BoardPlus
|
||||
#define RADIOLIB_SOFTWARE_SERIAL_UNSUPPORTED
|
||||
@@ -74,15 +75,18 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
*/
|
||||
uint32_t rxBad = 0, rxGood = 0, txGood = 0;
|
||||
|
||||
PointerQueue<MeshPacket> txQueue = PointerQueue<MeshPacket>(MAX_TX_QUEUE);
|
||||
MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* FIXME, use a meshtastic sync word, but hashed with the Channel name. Currently picking the same default
|
||||
* the RF95 used (0x14). Note: do not use 0x34 - that is reserved for lorawan
|
||||
* We use a meshtastic sync word, but hashed with the Channel name. For releases before 1.2 we used 0x12 (or for very old loads 0x14)
|
||||
* Note: do not use 0x34 - that is reserved for lorawan
|
||||
*
|
||||
* We now use 0x2b (so that someday we can possibly use NOT 2b - because that would be funny pun). We will be staying with this code
|
||||
* for a long time.
|
||||
*/
|
||||
uint8_t syncWord = SX126X_SYNC_WORD_PRIVATE;
|
||||
const uint8_t syncWord = 0x2b;
|
||||
|
||||
float currentLimit = 100; // FIXME
|
||||
|
||||
@@ -136,6 +140,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
*/
|
||||
virtual bool isActivelyReceiving() = 0;
|
||||
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
virtual bool cancelSending(NodeNum from, PacketId id);
|
||||
|
||||
private:
|
||||
/** if we have something waiting to send, start a short random timer so we can come check for collision before actually doing
|
||||
* the transmit
|
||||
|
||||
+53
-20
@@ -1,4 +1,5 @@
|
||||
#include "ReliableRouter.h"
|
||||
#include "MeshPlugin.h"
|
||||
#include "MeshTypes.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
@@ -13,9 +14,10 @@ ErrorCode ReliableRouter::send(MeshPacket *p)
|
||||
{
|
||||
if (p->want_ack) {
|
||||
// If someone asks for acks on broadcast, we need the hop limit to be at least one, so that first node that receives our
|
||||
// message will rebroadcast
|
||||
// message will rebroadcast. But asking for hop_limit 0 in that context means the client app has no preference on hop counts
|
||||
// and we want this message to get through the whole mesh, so use the default.
|
||||
if (p->to == NODENUM_BROADCAST && p->hop_limit == 0)
|
||||
p->hop_limit = 1;
|
||||
p->hop_limit = HOP_RELIABLE;
|
||||
|
||||
auto copy = packetPool.allocCopy(*p);
|
||||
startRetransmission(copy);
|
||||
@@ -26,15 +28,26 @@ ErrorCode ReliableRouter::send(MeshPacket *p)
|
||||
|
||||
bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
|
||||
{
|
||||
// Note: do not use getFrom() here, because we want to ignore messages sent from phone
|
||||
if (p->to == NODENUM_BROADCAST && p->from == getNodeNum()) {
|
||||
printPacket("Rx someone rebroadcasting for us", p);
|
||||
|
||||
// We are seeing someone rebroadcast one of our broadcast attempts.
|
||||
// If this is the first time we saw this, cancel any retransmissions we have queued up and generate an internal ack for
|
||||
// the original sending process.
|
||||
if (stopRetransmission(p->from, p->id)) {
|
||||
DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n");
|
||||
sendAckNak(ErrorReason_NONE, p->from, p->id);
|
||||
|
||||
// FIXME - we might want to turn off this "optimization", it does save lots of airtime but it assumes that once we've heard one
|
||||
// one adjacent node hear our packet that a) probably other adjacent nodes heard it and b) we can trust those nodes to reach
|
||||
// our destination. Both of which might be incorrect.
|
||||
auto key = GlobalPacketId(getFrom(p), p->id);
|
||||
auto old = findPendingPacket(key);
|
||||
if (old) {
|
||||
DEBUG_MSG("generating implicit ack\n");
|
||||
// NOTE: we do NOT check p->wantAck here because p is the INCOMING rebroadcast and that packet is not expected to be
|
||||
// marked as wantAck
|
||||
sendAckNak(Routing_Error_NONE, getFrom(p), p->id, old->packet->channel);
|
||||
|
||||
stopRetransmission(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,35 +66,39 @@ bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
|
||||
*
|
||||
* Otherwise, let superclass handle it.
|
||||
*/
|
||||
void ReliableRouter::sniffReceived(const MeshPacket *p)
|
||||
void ReliableRouter::sniffReceived(const MeshPacket *p, const Routing *c)
|
||||
{
|
||||
NodeNum ourNode = getNodeNum();
|
||||
|
||||
if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability
|
||||
// - not DSR routing)
|
||||
if (p->want_ack) {
|
||||
sendAckNak(ErrorReason_NONE, p->from, p->id);
|
||||
if (MeshPlugin::currentReply)
|
||||
DEBUG_MSG("Someone else has replied to this message, no need for a 2nd ack\n");
|
||||
else
|
||||
sendAckNak(Routing_Error_NONE, getFrom(p), p->id, p->channel);
|
||||
}
|
||||
|
||||
// If the payload is valid, look for ack/nak
|
||||
// We consider an ack to be either a !routing packet with a request ID or a routing packet with !error
|
||||
PacketId ackId = ((c && c->error_reason == Routing_Error_NONE) || !c) ? p->decoded.request_id : 0;
|
||||
|
||||
PacketId ackId = p->decoded.which_ackVariant == SubPacket_success_id_tag ? p->decoded.ackVariant.success_id : 0;
|
||||
PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;
|
||||
// A nak is a routing packt that has an error code
|
||||
PacketId nakId = (c && c->error_reason != Routing_Error_NONE) ? p->decoded.request_id : 0;
|
||||
|
||||
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
|
||||
if (ackId || nakId) {
|
||||
if (ackId) {
|
||||
DEBUG_MSG("Received a ack=%d, stopping retransmissions\n", ackId);
|
||||
DEBUG_MSG("Received a ack for 0x%x, stopping retransmissions\n", ackId);
|
||||
stopRetransmission(p->to, ackId);
|
||||
} else {
|
||||
DEBUG_MSG("Received a nak=%d, stopping retransmissions\n", nakId);
|
||||
DEBUG_MSG("Received a nak for 0x%x, stopping retransmissions\n", nakId);
|
||||
stopRetransmission(p->to, nakId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle the packet as normal
|
||||
FloodingRouter::sniffReceived(p);
|
||||
FloodingRouter::sniffReceived(p, c);
|
||||
}
|
||||
|
||||
#define NUM_RETRANSMISSIONS 3
|
||||
@@ -129,8 +146,9 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p)
|
||||
auto id = GlobalPacketId(p);
|
||||
auto rec = PendingPacket(p);
|
||||
|
||||
stopRetransmission(getFrom(p), p->id);
|
||||
|
||||
setNextTx(&rec);
|
||||
stopRetransmission(p->from, p->id);
|
||||
pending[id] = rec;
|
||||
|
||||
return &pending[id];
|
||||
@@ -150,18 +168,21 @@ int32_t ReliableRouter::doRetransmissions()
|
||||
++nextIt; // we use this odd pattern because we might be deleting it...
|
||||
auto &p = it->second;
|
||||
|
||||
bool stillValid = true; // assume we'll keep this record around
|
||||
|
||||
// FIXME, handle 51 day rolloever here!!!
|
||||
if (p.nextTxMsec <= now) {
|
||||
if (p.numRetransmissions == 0) {
|
||||
DEBUG_MSG("Reliable send failed, returning a nak fr=0x%x,to=0x%x,id=%d\n", p.packet->from, p.packet->to,
|
||||
DEBUG_MSG("Reliable send failed, returning a nak for fr=0x%x,to=0x%x,id=0x%x\n", p.packet->from, p.packet->to,
|
||||
p.packet->id);
|
||||
sendAckNak(ErrorReason_MAX_RETRANSMIT, p.packet->from, p.packet->id);
|
||||
sendAckNak(Routing_Error_MAX_RETRANSMIT, getFrom(p.packet), p.packet->id, p.packet->channel);
|
||||
// Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived - which
|
||||
// allows the DSR version to still be able to look at the PendingPacket
|
||||
stopRetransmission(it->first);
|
||||
stillValid = false; // just deleted it
|
||||
} else {
|
||||
DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=%d, tries left=%d\n", p.packet->from, p.packet->to,
|
||||
p.packet->id, p.numRetransmissions);
|
||||
DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=0x%x, tries left=%d\n", p.packet->from,
|
||||
p.packet->to, p.packet->id, p.numRetransmissions);
|
||||
|
||||
// Note: we call the superclass version because we don't want to have our version of send() add a new
|
||||
// retransmission record
|
||||
@@ -171,8 +192,10 @@ int32_t ReliableRouter::doRetransmissions()
|
||||
--p.numRetransmissions;
|
||||
setNextTx(&p);
|
||||
}
|
||||
} else {
|
||||
// Not yet time
|
||||
}
|
||||
|
||||
if (stillValid) {
|
||||
// Update our desired sleep delay
|
||||
int32_t t = p.nextTxMsec - now;
|
||||
|
||||
d = min(t, d);
|
||||
@@ -180,4 +203,14 @@ int32_t ReliableRouter::doRetransmissions()
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
void ReliableRouter::setNextTx(PendingPacket *pending)
|
||||
{
|
||||
assert(iface);
|
||||
auto d = iface->getRetransmissionMsec(pending->packet);
|
||||
pending->nextTxMsec = millis() + d;
|
||||
DEBUG_MSG("Setting next retransmission in %u msecs: ", d);
|
||||
printPacket("", pending->packet);
|
||||
setReceivedMessage(); // Run ASAP, so we can figure out our correct sleep time
|
||||
}
|
||||
@@ -15,7 +15,7 @@ struct GlobalPacketId {
|
||||
|
||||
GlobalPacketId(const MeshPacket *p)
|
||||
{
|
||||
node = p->from;
|
||||
node = getFrom(p);
|
||||
id = p->id;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ class ReliableRouter : public FloodingRouter
|
||||
/**
|
||||
* Look for acks/naks or someone retransmitting us
|
||||
*/
|
||||
virtual void sniffReceived(const MeshPacket *p);
|
||||
virtual void sniffReceived(const MeshPacket *p, const Routing *c);
|
||||
|
||||
/**
|
||||
* Try to find the pending packet record for this ID (or NULL if not found)
|
||||
@@ -125,7 +125,5 @@ class ReliableRouter : public FloodingRouter
|
||||
*/
|
||||
int32_t doRetransmissions();
|
||||
|
||||
void setNextTx(PendingPacket *pending) {
|
||||
assert(iface);
|
||||
pending->nextTxMsec = millis() + iface->getRetransmissionMsec(pending->packet); }
|
||||
void setNextTx(PendingPacket *pending);
|
||||
};
|
||||
|
||||
+95
-61
@@ -1,9 +1,11 @@
|
||||
#include "Router.h"
|
||||
#include "Channels.h"
|
||||
#include "CryptoEngine.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include <NodeDB.h>
|
||||
#include "plugins/RoutingPlugin.h"
|
||||
|
||||
/**
|
||||
* Router todo
|
||||
@@ -80,7 +82,6 @@ PacketId generatePacketId()
|
||||
|
||||
i++;
|
||||
PacketId id = (i % numPacketId) + 1; // return number between 1 and numPacketId (ie - never zero)
|
||||
myNodeInfo.current_packet_id = id; // Kinda crufty - we keep updating this so the phone can see a current value
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -102,26 +103,20 @@ MeshPacket *Router::allocForSending()
|
||||
/**
|
||||
* Send an ack or a nak packet back towards whoever sent idFrom
|
||||
*/
|
||||
void Router::sendAckNak(ErrorReason err, NodeNum to, PacketId idFrom)
|
||||
void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
|
||||
{
|
||||
auto p = allocForSending();
|
||||
p->hop_limit = 0; // Assume just immediate neighbors for now
|
||||
p->to = to;
|
||||
DEBUG_MSG("Sending an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
|
||||
routingPlugin->sendAckNak(err, to, idFrom, chIndex);
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
p->decoded.ackVariant.success_id = idFrom;
|
||||
p->decoded.which_ackVariant = SubPacket_success_id_tag;
|
||||
} else {
|
||||
p->decoded.ackVariant.fail_id = idFrom;
|
||||
p->decoded.which_ackVariant = SubPacket_fail_id_tag;
|
||||
void Router::abortSendAndNak(Routing_Error err, MeshPacket *p)
|
||||
{
|
||||
DEBUG_MSG("Error=%d, returning NAK and dropping packet.\n", err);
|
||||
sendAckNak(Routing_Error_NO_INTERFACE, getFrom(p), p->id, p->channel);
|
||||
packetPool.release(p);
|
||||
}
|
||||
|
||||
// Also send back the error reason
|
||||
p->decoded.which_payloadVariant = SubPacket_error_reason_tag;
|
||||
p->decoded.error_reason = err;
|
||||
}
|
||||
|
||||
sendLocal(p); // we sometimes send directly to the local node
|
||||
void Router::setReceivedMessage() {
|
||||
setInterval(0); // Run ASAP, so we can figure out our correct sleep time
|
||||
}
|
||||
|
||||
ErrorCode Router::sendLocal(MeshPacket *p)
|
||||
@@ -130,14 +125,11 @@ ErrorCode Router::sendLocal(MeshPacket *p)
|
||||
if (p->to == nodeDB.getNodeNum()) {
|
||||
printPacket("Enqueuing local", p);
|
||||
fromRadioQueue.enqueue(p);
|
||||
setReceivedMessage();
|
||||
return ERRNO_OK;
|
||||
} else if (!iface) {
|
||||
// We must be sending to remote nodes also, fail if no interface found
|
||||
|
||||
// ERROR! no radio found, report failure back to the client and drop the packet
|
||||
DEBUG_MSG("Error: No interface, returning NAK and dropping packet.\n");
|
||||
sendAckNak(ErrorReason_NO_INTERFACE, p->from, p->id);
|
||||
packetPool.release(p);
|
||||
abortSendAndNak(Routing_Error_NO_INTERFACE, p);
|
||||
|
||||
return ERRNO_NO_INTERFACES;
|
||||
} else {
|
||||
@@ -151,6 +143,13 @@ ErrorCode Router::sendLocal(MeshPacket *p)
|
||||
}
|
||||
}
|
||||
|
||||
void printBytes(const char *label, const uint8_t *p, size_t numbytes) {
|
||||
DEBUG_MSG("%s: ", label);
|
||||
for(size_t i = 0; i < numbytes; i++)
|
||||
DEBUG_MSG("%02x ", p[i]);
|
||||
DEBUG_MSG("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet on a suitable interface. This routine will
|
||||
* later free() the packet to pool. This routine is not allowed to stall.
|
||||
@@ -160,14 +159,18 @@ ErrorCode Router::send(MeshPacket *p)
|
||||
{
|
||||
assert(p->to != nodeDB.getNodeNum()); // should have already been handled by sendLocal
|
||||
|
||||
PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;
|
||||
assert(
|
||||
!nakId); // I don't think we ever send 0hop naks over the wire (other than to the phone), test that assumption with assert
|
||||
// PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0;
|
||||
// assert(!nakId); // I don't think we ever send 0hop naks over the wire (other than to the phone), test that assumption with
|
||||
// assert
|
||||
|
||||
// Never set the want_ack flag on broadcast packets sent over the air.
|
||||
if (p->to == NODENUM_BROADCAST)
|
||||
p->want_ack = false;
|
||||
|
||||
// Up until this point we might have been using 0 for the from address (if it started with the phone), but when we send over
|
||||
// the lora we need to make sure we have replaced it with our local address
|
||||
p->from = getFrom(p);
|
||||
|
||||
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
|
||||
|
||||
assert(p->which_payloadVariant == MeshPacket_encrypted_tag ||
|
||||
@@ -177,10 +180,27 @@ ErrorCode Router::send(MeshPacket *p)
|
||||
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
||||
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
|
||||
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), SubPacket_fields, &p->decoded);
|
||||
// printPacket("pre encrypt", p); // portnum valid here
|
||||
|
||||
assert(numbytes <= MAX_RHPACKETLEN);
|
||||
crypto->encrypt(p->from, p->id, numbytes, bytes);
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded);
|
||||
|
||||
if (numbytes > MAX_RHPACKETLEN) {
|
||||
abortSendAndNak(Routing_Error_TOO_LARGE, p);
|
||||
return ERRNO_TOO_LARGE;
|
||||
}
|
||||
|
||||
//printBytes("plaintext", bytes, numbytes);
|
||||
|
||||
auto hash = channels.setActiveByIndex(p->channel);
|
||||
if (hash < 0) {
|
||||
// No suitable channel could be found for sending
|
||||
abortSendAndNak(Routing_Error_NO_CHANNEL, p);
|
||||
return ERRNO_NO_CHANNEL;
|
||||
}
|
||||
|
||||
// Now that we are encrypting the packet channel should be the hash (no longer the index)
|
||||
p->channel = hash;
|
||||
crypto->encrypt(getFrom(p), p->id, numbytes, bytes);
|
||||
|
||||
// Copy back into the packet and set the variant type
|
||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||
@@ -189,21 +209,20 @@ ErrorCode Router::send(MeshPacket *p)
|
||||
}
|
||||
|
||||
assert(iface); // This should have been detected already in sendLocal (or we just received a packet from outside)
|
||||
// if (iface) {
|
||||
// DEBUG_MSG("Sending packet via interface fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
return iface->send(p);
|
||||
/* } else {
|
||||
DEBUG_MSG("Dropping packet - no interfaces - fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id);
|
||||
packetPool.release(p);
|
||||
return ERRNO_NO_INTERFACES;
|
||||
} */
|
||||
}
|
||||
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
bool Router::cancelSending(NodeNum from, PacketId id)
|
||||
{
|
||||
return iface ? iface->cancelSending(from, id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to
|
||||
* update routing tables etc... based on what we overhear (even for messages not destined to our node)
|
||||
*/
|
||||
void Router::sniffReceived(const MeshPacket *p)
|
||||
void Router::sniffReceived(const MeshPacket *p, const Routing *c)
|
||||
{
|
||||
DEBUG_MSG("FIXME-update-db Sniffing packet\n");
|
||||
// FIXME, update nodedb here for any packet that passes through us
|
||||
@@ -216,23 +235,39 @@ bool Router::perhapsDecode(MeshPacket *p)
|
||||
|
||||
assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
|
||||
|
||||
// FIXME - someday don't send routing packets encrypted. That would allow us to route for other channels without
|
||||
// being able to decrypt their data.
|
||||
// Try to decrypt the packet if we can
|
||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||
memcpy(bytes, p->encrypted.bytes,
|
||||
p->encrypted.size); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
|
||||
crypto->decrypt(p->from, p->id, p->encrypted.size, bytes);
|
||||
// Try to find a channel that works with this hash
|
||||
for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
|
||||
// Try to use this hash/channel pair
|
||||
if (channels.decryptForHash(chIndex, p->channel)) {
|
||||
// Try to decrypt the packet if we can
|
||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||
size_t rawSize = p->encrypted.size;
|
||||
assert(rawSize <= sizeof(bytes));
|
||||
memcpy(bytes, p->encrypted.bytes,
|
||||
rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
|
||||
crypto->decrypt(p->from, p->id, rawSize, bytes);
|
||||
|
||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||
if (!pb_decode_from_bytes(bytes, p->encrypted.size, SubPacket_fields, &p->decoded)) {
|
||||
DEBUG_MSG("Invalid protobufs in received mesh packet!\n");
|
||||
return false;
|
||||
} else {
|
||||
// parsing was successful
|
||||
p->which_payloadVariant = MeshPacket_decoded_tag;
|
||||
return true;
|
||||
//printBytes("plaintext", bytes, p->encrypted.size);
|
||||
|
||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||
memset(&p->decoded, 0, sizeof(p->decoded));
|
||||
if (!pb_decode_from_bytes(bytes, rawSize, Data_fields, &p->decoded)) {
|
||||
DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?)!\n");
|
||||
} else if(p->decoded.portnum == PortNum_UNKNOWN_APP) {
|
||||
DEBUG_MSG("Invalid portnum (bad psk?)!\n");
|
||||
}
|
||||
else {
|
||||
// parsing was successful
|
||||
p->which_payloadVariant = MeshPacket_decoded_tag; // change type to decoded
|
||||
p->channel = chIndex; // change to store the index instead of the hash
|
||||
printPacket("decoded message", p);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_MSG("No suitable channel found for decoding, hash was 0x%x!\n", p->channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
NodeNum Router::getNodeNum()
|
||||
@@ -250,22 +285,21 @@ void Router::handleReceived(MeshPacket *p)
|
||||
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
||||
|
||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||
if (perhapsDecode(p)) {
|
||||
bool decoded = perhapsDecode(p);
|
||||
if (decoded) {
|
||||
// parsing was successful, queue for our recipient
|
||||
printPacket("handleReceived", p);
|
||||
|
||||
sniffReceived(p);
|
||||
|
||||
if (p->to == NODENUM_BROADCAST || p->to == getNodeNum()) {
|
||||
printPacket("Delivering rx packet", p);
|
||||
notifyPacketReceived.notifyObservers(p);
|
||||
}
|
||||
// call any promiscious plugins here, make a (non promisiocous) plugin for forwarding messages to phone api
|
||||
// sniffReceived(p);
|
||||
MeshPlugin::callPlugins(*p);
|
||||
}
|
||||
}
|
||||
|
||||
void Router::perhapsHandleReceived(MeshPacket *p)
|
||||
{
|
||||
assert(radioConfig.has_preferences);
|
||||
bool ignore = is_in_repeated(radioConfig.preferences.ignore_incoming, p->from);
|
||||
bool ignore = is_in_repeated(radioConfig.preferences.ignore_incoming, getFrom(p));
|
||||
|
||||
if (ignore)
|
||||
DEBUG_MSG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from);
|
||||
|
||||
+21
-7
@@ -6,6 +6,7 @@
|
||||
#include "PointerQueue.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "Channels.h"
|
||||
|
||||
/**
|
||||
* A mesh aware router that supports multiple interfaces.
|
||||
@@ -21,10 +22,6 @@ class Router : protected concurrency::OSThread
|
||||
RadioInterface *iface = NULL;
|
||||
|
||||
public:
|
||||
/// Local services that want to see _every_ packet this node receives can observe this.
|
||||
/// Observers should always return 0 and _copy_ any packets they want to keep for use later (this packet will be getting
|
||||
/// freed)
|
||||
Observable<const MeshPacket *> notifyPacketReceived;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@@ -55,14 +52,26 @@ class Router : protected concurrency::OSThread
|
||||
*/
|
||||
ErrorCode sendLocal(MeshPacket *p);
|
||||
|
||||
/// Allocate and return a meshpacket which defaults as send to broadcast from the current node.
|
||||
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
|
||||
bool cancelSending(NodeNum from, PacketId id);
|
||||
|
||||
/** Allocate and return a meshpacket which defaults as send to broadcast from the current node.
|
||||
* The returned packet is guaranteed to have a unique packet ID already assigned
|
||||
*/
|
||||
MeshPacket *allocForSending();
|
||||
|
||||
/**
|
||||
* @return our local nodenum */
|
||||
NodeNum getNodeNum();
|
||||
|
||||
/** Wake up the router thread ASAP, because we just queued a message for it.
|
||||
* FIXME, this is kinda a hack because we don't have a nice way yet to say 'wake us because we are 'blocked on this queue'
|
||||
*/
|
||||
void setReceivedMessage();
|
||||
|
||||
protected:
|
||||
friend class RoutingPlugin;
|
||||
|
||||
/**
|
||||
* Send a packet on a suitable interface. This routine will
|
||||
* later free() the packet to pool. This routine is not allowed to stall.
|
||||
@@ -74,6 +83,8 @@ class Router : protected concurrency::OSThread
|
||||
|
||||
/**
|
||||
* Should this incoming filter be dropped?
|
||||
*
|
||||
* FIXME, move this into the new RoutingPlugin and do the filtering there using the regular plugin logic
|
||||
*
|
||||
* Called immedately on receiption, before any further processing.
|
||||
* @return true to abandon the packet
|
||||
@@ -84,7 +95,7 @@ class Router : protected concurrency::OSThread
|
||||
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to
|
||||
* update routing tables etc... based on what we overhear (even for messages not destined to our node)
|
||||
*/
|
||||
virtual void sniffReceived(const MeshPacket *p);
|
||||
virtual void sniffReceived(const MeshPacket *p, const Routing *c);
|
||||
|
||||
/**
|
||||
* Remove any encryption and decode the protobufs inside this packet (if necessary).
|
||||
@@ -96,7 +107,7 @@ class Router : protected concurrency::OSThread
|
||||
/**
|
||||
* Send an ack or a nak packet back towards whoever sent idFrom
|
||||
*/
|
||||
void sendAckNak(ErrorReason err, NodeNum to, PacketId idFrom);
|
||||
void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -118,6 +129,9 @@ class Router : protected concurrency::OSThread
|
||||
* Note: this method will free the provided packet.
|
||||
*/
|
||||
void handleReceived(MeshPacket *p);
|
||||
|
||||
/** Frees the provided packet, and generates a NAK indicating the speicifed error while sending */
|
||||
void abortSendAndNak(Routing_Error err, MeshPacket *p);
|
||||
};
|
||||
|
||||
extern Router *router;
|
||||
|
||||
@@ -21,7 +21,7 @@ class SinglePortPlugin : public MeshPlugin
|
||||
/**
|
||||
* @return true if you want to receive the specified portnum
|
||||
*/
|
||||
virtual bool wantPortnum(PortNum p) { return p == ourPortNum; }
|
||||
virtual bool wantPacket(const MeshPacket *p) { return p->decoded.portnum == ourPortNum; }
|
||||
|
||||
/**
|
||||
* Return a mesh packet which has been preinited as a data packet with a particular port number.
|
||||
@@ -32,8 +32,7 @@ class SinglePortPlugin : public MeshPlugin
|
||||
{
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
MeshPacket *p = router->allocForSending();
|
||||
p->decoded.which_payloadVariant = SubPacket_data_tag;
|
||||
p->decoded.data.portnum = ourPortNum;
|
||||
p->decoded.portnum = ourPortNum;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -71,12 +71,18 @@ void StreamAPI::writeStream()
|
||||
void StreamAPI::emitTxBuffer(size_t len)
|
||||
{
|
||||
if (len != 0) {
|
||||
DEBUG_MSG("emit tx %d\n", len);
|
||||
txBuf[0] = START1;
|
||||
txBuf[1] = START2;
|
||||
txBuf[2] = (len >> 8) & 0xff;
|
||||
txBuf[3] = len & 0xff;
|
||||
|
||||
stream->write(txBuf, len + HEADER_LEN);
|
||||
auto totalLen = len + HEADER_LEN;
|
||||
stream->write(txBuf, totalLen);
|
||||
/* for(size_t i = 0; i < totalLen; i++) {
|
||||
stream->write(txBuf[i]);
|
||||
// stream->flush();
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#include "admin.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
PB_BIND(AdminMessage, AdminMessage, 2)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#ifndef PB_ADMIN_PB_H_INCLUDED
|
||||
#define PB_ADMIN_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
#include "mesh.pb.h"
|
||||
#include "radioconfig.pb.h"
|
||||
#include "channel.pb.h"
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _AdminMessage {
|
||||
pb_size_t which_variant;
|
||||
union {
|
||||
RadioConfig set_radio;
|
||||
User set_owner;
|
||||
Channel set_channel;
|
||||
bool get_radio_request;
|
||||
RadioConfig get_radio_response;
|
||||
uint32_t get_channel_request;
|
||||
Channel get_channel_response;
|
||||
bool confirm_set_channel;
|
||||
bool confirm_set_radio;
|
||||
};
|
||||
} AdminMessage;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define AdminMessage_init_default {0, {RadioConfig_init_default}}
|
||||
#define AdminMessage_init_zero {0, {RadioConfig_init_zero}}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define AdminMessage_set_radio_tag 1
|
||||
#define AdminMessage_set_owner_tag 2
|
||||
#define AdminMessage_set_channel_tag 3
|
||||
#define AdminMessage_get_radio_request_tag 4
|
||||
#define AdminMessage_get_radio_response_tag 5
|
||||
#define AdminMessage_get_channel_request_tag 6
|
||||
#define AdminMessage_get_channel_response_tag 7
|
||||
#define AdminMessage_confirm_set_channel_tag 32
|
||||
#define AdminMessage_confirm_set_radio_tag 33
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define AdminMessage_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_radio,set_radio), 1) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_owner,set_owner), 2) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_channel,set_channel), 3) \
|
||||
X(a, STATIC, ONEOF, BOOL, (variant,get_radio_request,get_radio_request), 4) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,get_radio_response,get_radio_response), 5) \
|
||||
X(a, STATIC, ONEOF, UINT32, (variant,get_channel_request,get_channel_request), 6) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,get_channel_response,get_channel_response), 7) \
|
||||
X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_channel,confirm_set_channel), 32) \
|
||||
X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_radio,confirm_set_radio), 33)
|
||||
#define AdminMessage_CALLBACK NULL
|
||||
#define AdminMessage_DEFAULT NULL
|
||||
#define AdminMessage_variant_set_radio_MSGTYPE RadioConfig
|
||||
#define AdminMessage_variant_set_owner_MSGTYPE User
|
||||
#define AdminMessage_variant_set_channel_MSGTYPE Channel
|
||||
#define AdminMessage_variant_get_radio_response_MSGTYPE RadioConfig
|
||||
#define AdminMessage_variant_get_channel_response_MSGTYPE Channel
|
||||
|
||||
extern const pb_msgdesc_t AdminMessage_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define AdminMessage_fields &AdminMessage_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define AdminMessage_size 351
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -9,4 +9,7 @@
|
||||
PB_BIND(ServiceEnvelope, ServiceEnvelope, 2)
|
||||
|
||||
|
||||
PB_BIND(ChannelSet, ChannelSet, AUTO)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,12 +5,17 @@
|
||||
#define PB_APPONLY_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
#include "mesh.pb.h"
|
||||
#include "channel.pb.h"
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _ChannelSet {
|
||||
pb_callback_t settings;
|
||||
} ChannelSet;
|
||||
|
||||
typedef struct _ServiceEnvelope {
|
||||
bool has_packet;
|
||||
MeshPacket packet;
|
||||
@@ -25,9 +30,12 @@ extern "C" {
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define ServiceEnvelope_init_default {false, MeshPacket_init_default, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||
#define ChannelSet_init_default {{{NULL}, NULL}}
|
||||
#define ServiceEnvelope_init_zero {false, MeshPacket_init_zero, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||
#define ChannelSet_init_zero {{{NULL}, NULL}}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define ChannelSet_settings_tag 1
|
||||
#define ServiceEnvelope_packet_tag 1
|
||||
#define ServiceEnvelope_channel_id_tag 2
|
||||
#define ServiceEnvelope_gateway_id_tag 3
|
||||
@@ -41,13 +49,22 @@ X(a, CALLBACK, SINGULAR, STRING, gateway_id, 3)
|
||||
#define ServiceEnvelope_DEFAULT NULL
|
||||
#define ServiceEnvelope_packet_MSGTYPE MeshPacket
|
||||
|
||||
#define ChannelSet_FIELDLIST(X, a) \
|
||||
X(a, CALLBACK, REPEATED, MESSAGE, settings, 1)
|
||||
#define ChannelSet_CALLBACK pb_default_field_callback
|
||||
#define ChannelSet_DEFAULT NULL
|
||||
#define ChannelSet_settings_MSGTYPE ChannelSettings
|
||||
|
||||
extern const pb_msgdesc_t ServiceEnvelope_msg;
|
||||
extern const pb_msgdesc_t ChannelSet_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define ServiceEnvelope_fields &ServiceEnvelope_msg
|
||||
#define ChannelSet_fields &ChannelSet_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
/* ServiceEnvelope_size depends on runtime parameters */
|
||||
/* ChannelSet_size depends on runtime parameters */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#include "channel.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
PB_BIND(ChannelSettings, ChannelSettings, AUTO)
|
||||
|
||||
|
||||
PB_BIND(Channel, Channel, AUTO)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#ifndef PB_CHANNEL_PB_H_INCLUDED
|
||||
#define PB_CHANNEL_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Enum definitions */
|
||||
typedef enum _ChannelSettings_ModemConfig {
|
||||
ChannelSettings_ModemConfig_Bw125Cr45Sf128 = 0,
|
||||
ChannelSettings_ModemConfig_Bw500Cr45Sf128 = 1,
|
||||
ChannelSettings_ModemConfig_Bw31_25Cr48Sf512 = 2,
|
||||
ChannelSettings_ModemConfig_Bw125Cr48Sf4096 = 3
|
||||
} ChannelSettings_ModemConfig;
|
||||
|
||||
typedef enum _Channel_Role {
|
||||
Channel_Role_DISABLED = 0,
|
||||
Channel_Role_PRIMARY = 1,
|
||||
Channel_Role_SECONDARY = 2
|
||||
} Channel_Role;
|
||||
|
||||
/* Struct definitions */
|
||||
typedef PB_BYTES_ARRAY_T(32) ChannelSettings_psk_t;
|
||||
typedef struct _ChannelSettings {
|
||||
int8_t tx_power;
|
||||
ChannelSettings_ModemConfig modem_config;
|
||||
ChannelSettings_psk_t psk;
|
||||
char name[12];
|
||||
uint16_t bandwidth;
|
||||
uint32_t spread_factor;
|
||||
uint8_t coding_rate;
|
||||
uint8_t channel_num;
|
||||
uint32_t id;
|
||||
bool uplink_enabled;
|
||||
bool downlink_enabled;
|
||||
} ChannelSettings;
|
||||
|
||||
typedef struct _Channel {
|
||||
int8_t index;
|
||||
bool has_settings;
|
||||
ChannelSettings settings;
|
||||
Channel_Role role;
|
||||
} Channel;
|
||||
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_Bw125Cr45Sf128
|
||||
#define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_Bw125Cr48Sf4096
|
||||
#define _ChannelSettings_ModemConfig_ARRAYSIZE ((ChannelSettings_ModemConfig)(ChannelSettings_ModemConfig_Bw125Cr48Sf4096+1))
|
||||
|
||||
#define _Channel_Role_MIN Channel_Role_DISABLED
|
||||
#define _Channel_Role_MAX Channel_Role_SECONDARY
|
||||
#define _Channel_Role_ARRAYSIZE ((Channel_Role)(Channel_Role_SECONDARY+1))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0}
|
||||
#define Channel_init_default {0, false, ChannelSettings_init_default, _Channel_Role_MIN}
|
||||
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0}
|
||||
#define Channel_init_zero {0, false, ChannelSettings_init_zero, _Channel_Role_MIN}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define ChannelSettings_tx_power_tag 1
|
||||
#define ChannelSettings_modem_config_tag 3
|
||||
#define ChannelSettings_psk_tag 4
|
||||
#define ChannelSettings_name_tag 5
|
||||
#define ChannelSettings_bandwidth_tag 6
|
||||
#define ChannelSettings_spread_factor_tag 7
|
||||
#define ChannelSettings_coding_rate_tag 8
|
||||
#define ChannelSettings_channel_num_tag 9
|
||||
#define ChannelSettings_id_tag 10
|
||||
#define ChannelSettings_uplink_enabled_tag 16
|
||||
#define ChannelSettings_downlink_enabled_tag 17
|
||||
#define Channel_index_tag 1
|
||||
#define Channel_settings_tag 2
|
||||
#define Channel_role_tag 3
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define ChannelSettings_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, INT32, tx_power, 1) \
|
||||
X(a, STATIC, SINGULAR, UENUM, modem_config, 3) \
|
||||
X(a, STATIC, SINGULAR, BYTES, psk, 4) \
|
||||
X(a, STATIC, SINGULAR, STRING, name, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, bandwidth, 6) \
|
||||
X(a, STATIC, SINGULAR, UINT32, spread_factor, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, coding_rate, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, channel_num, 9) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, id, 10) \
|
||||
X(a, STATIC, SINGULAR, BOOL, uplink_enabled, 16) \
|
||||
X(a, STATIC, SINGULAR, BOOL, downlink_enabled, 17)
|
||||
#define ChannelSettings_CALLBACK NULL
|
||||
#define ChannelSettings_DEFAULT NULL
|
||||
|
||||
#define Channel_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, INT32, index, 1) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, settings, 2) \
|
||||
X(a, STATIC, SINGULAR, UENUM, role, 3)
|
||||
#define Channel_CALLBACK NULL
|
||||
#define Channel_DEFAULT NULL
|
||||
#define Channel_settings_MSGTYPE ChannelSettings
|
||||
|
||||
extern const pb_msgdesc_t ChannelSettings_msg;
|
||||
extern const pb_msgdesc_t Channel_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define ChannelSettings_fields &ChannelSettings_msg
|
||||
#define Channel_fields &Channel_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define ChannelSettings_size 87
|
||||
#define Channel_size 102
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -6,7 +6,16 @@
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
PB_BIND(LegacyRadioConfig, LegacyRadioConfig, AUTO)
|
||||
|
||||
|
||||
PB_BIND(LegacyRadioConfig_LegacyPreferences, LegacyRadioConfig_LegacyPreferences, AUTO)
|
||||
|
||||
|
||||
PB_BIND(DeviceState, DeviceState, 2)
|
||||
|
||||
|
||||
PB_BIND(ChannelFile, ChannelFile, 2)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,15 +5,31 @@
|
||||
#define PB_DEVICEONLY_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
#include "mesh.pb.h"
|
||||
#include "channel.pb.h"
|
||||
#include "radioconfig.pb.h"
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _ChannelFile {
|
||||
pb_size_t channels_count;
|
||||
Channel channels[8];
|
||||
} ChannelFile;
|
||||
|
||||
typedef struct _LegacyRadioConfig_LegacyPreferences {
|
||||
RegionCode region;
|
||||
} LegacyRadioConfig_LegacyPreferences;
|
||||
|
||||
typedef struct _LegacyRadioConfig {
|
||||
bool has_preferences;
|
||||
LegacyRadioConfig_LegacyPreferences preferences;
|
||||
} LegacyRadioConfig;
|
||||
|
||||
typedef struct _DeviceState {
|
||||
bool has_radio;
|
||||
RadioConfig radio;
|
||||
bool has_legacyRadio;
|
||||
LegacyRadioConfig legacyRadio;
|
||||
bool has_my_node;
|
||||
MyNodeInfo my_node;
|
||||
bool has_owner;
|
||||
@@ -27,8 +43,6 @@ typedef struct _DeviceState {
|
||||
uint32_t version;
|
||||
bool no_save;
|
||||
bool did_gps_reset;
|
||||
pb_size_t secondary_channels_count;
|
||||
ChannelSettings secondary_channels[7];
|
||||
} DeviceState;
|
||||
|
||||
|
||||
@@ -37,11 +51,20 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0, 0, {ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default}}
|
||||
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0, 0, {ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero}}
|
||||
#define LegacyRadioConfig_init_default {false, LegacyRadioConfig_LegacyPreferences_init_default}
|
||||
#define LegacyRadioConfig_LegacyPreferences_init_default {_RegionCode_MIN}
|
||||
#define DeviceState_init_default {false, LegacyRadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0}
|
||||
#define ChannelFile_init_default {0, {Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default}}
|
||||
#define LegacyRadioConfig_init_zero {false, LegacyRadioConfig_LegacyPreferences_init_zero}
|
||||
#define LegacyRadioConfig_LegacyPreferences_init_zero {_RegionCode_MIN}
|
||||
#define DeviceState_init_zero {false, LegacyRadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0}
|
||||
#define ChannelFile_init_zero {0, {Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero}}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define DeviceState_radio_tag 1
|
||||
#define ChannelFile_channels_tag 1
|
||||
#define LegacyRadioConfig_LegacyPreferences_region_tag 15
|
||||
#define LegacyRadioConfig_preferences_tag 1
|
||||
#define DeviceState_legacyRadio_tag 1
|
||||
#define DeviceState_my_node_tag 2
|
||||
#define DeviceState_owner_tag 3
|
||||
#define DeviceState_node_db_tag 4
|
||||
@@ -50,11 +73,21 @@ extern "C" {
|
||||
#define DeviceState_version_tag 8
|
||||
#define DeviceState_no_save_tag 9
|
||||
#define DeviceState_did_gps_reset_tag 11
|
||||
#define DeviceState_secondary_channels_tag 12
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define LegacyRadioConfig_FIELDLIST(X, a) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, preferences, 1)
|
||||
#define LegacyRadioConfig_CALLBACK NULL
|
||||
#define LegacyRadioConfig_DEFAULT NULL
|
||||
#define LegacyRadioConfig_preferences_MSGTYPE LegacyRadioConfig_LegacyPreferences
|
||||
|
||||
#define LegacyRadioConfig_LegacyPreferences_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, region, 15)
|
||||
#define LegacyRadioConfig_LegacyPreferences_CALLBACK NULL
|
||||
#define LegacyRadioConfig_LegacyPreferences_DEFAULT NULL
|
||||
|
||||
#define DeviceState_FIELDLIST(X, a) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, radio, 1) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, legacyRadio, 1) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, my_node, 2) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \
|
||||
@@ -62,25 +95,38 @@ X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, version, 8) \
|
||||
X(a, STATIC, SINGULAR, BOOL, no_save, 9) \
|
||||
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, secondary_channels, 12)
|
||||
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11)
|
||||
#define DeviceState_CALLBACK NULL
|
||||
#define DeviceState_DEFAULT NULL
|
||||
#define DeviceState_radio_MSGTYPE RadioConfig
|
||||
#define DeviceState_legacyRadio_MSGTYPE LegacyRadioConfig
|
||||
#define DeviceState_my_node_MSGTYPE MyNodeInfo
|
||||
#define DeviceState_owner_MSGTYPE User
|
||||
#define DeviceState_node_db_MSGTYPE NodeInfo
|
||||
#define DeviceState_receive_queue_MSGTYPE MeshPacket
|
||||
#define DeviceState_rx_text_message_MSGTYPE MeshPacket
|
||||
#define DeviceState_secondary_channels_MSGTYPE ChannelSettings
|
||||
|
||||
#define ChannelFile_FIELDLIST(X, a) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, channels, 1)
|
||||
#define ChannelFile_CALLBACK NULL
|
||||
#define ChannelFile_DEFAULT NULL
|
||||
#define ChannelFile_channels_MSGTYPE Channel
|
||||
|
||||
extern const pb_msgdesc_t LegacyRadioConfig_msg;
|
||||
extern const pb_msgdesc_t LegacyRadioConfig_LegacyPreferences_msg;
|
||||
extern const pb_msgdesc_t DeviceState_msg;
|
||||
extern const pb_msgdesc_t ChannelFile_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define LegacyRadioConfig_fields &LegacyRadioConfig_msg
|
||||
#define LegacyRadioConfig_LegacyPreferences_fields &LegacyRadioConfig_LegacyPreferences_msg
|
||||
#define DeviceState_fields &DeviceState_msg
|
||||
#define ChannelFile_fields &ChannelFile_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define DeviceState_size 6262
|
||||
#define LegacyRadioConfig_size 4
|
||||
#define LegacyRadioConfig_LegacyPreferences_size 2
|
||||
#define DeviceState_size 4898
|
||||
#define ChannelFile_size 832
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#include "environmental_measurement.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
PB_BIND(EnvironmentalMeasurement, EnvironmentalMeasurement, AUTO)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#ifndef PB_ENVIRONMENTAL_MEASUREMENT_PB_H_INCLUDED
|
||||
#define PB_ENVIRONMENTAL_MEASUREMENT_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _EnvironmentalMeasurement {
|
||||
float temperature;
|
||||
float relative_humidity;
|
||||
float barometric_pressure;
|
||||
} EnvironmentalMeasurement;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define EnvironmentalMeasurement_init_default {0, 0, 0}
|
||||
#define EnvironmentalMeasurement_init_zero {0, 0, 0}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define EnvironmentalMeasurement_temperature_tag 1
|
||||
#define EnvironmentalMeasurement_relative_humidity_tag 2
|
||||
#define EnvironmentalMeasurement_barometric_pressure_tag 3
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define EnvironmentalMeasurement_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, temperature, 1) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, relative_humidity, 2) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, barometric_pressure, 3)
|
||||
#define EnvironmentalMeasurement_CALLBACK NULL
|
||||
#define EnvironmentalMeasurement_DEFAULT NULL
|
||||
|
||||
extern const pb_msgdesc_t EnvironmentalMeasurement_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define EnvironmentalMeasurement_fields &EnvironmentalMeasurement_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define EnvironmentalMeasurement_size 15
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -9,30 +9,21 @@
|
||||
PB_BIND(Position, Position, AUTO)
|
||||
|
||||
|
||||
PB_BIND(Data, Data, AUTO)
|
||||
|
||||
|
||||
PB_BIND(User, User, AUTO)
|
||||
|
||||
|
||||
PB_BIND(RouteDiscovery, RouteDiscovery, AUTO)
|
||||
|
||||
|
||||
PB_BIND(SubPacket, SubPacket, 2)
|
||||
PB_BIND(Routing, Routing, AUTO)
|
||||
|
||||
|
||||
PB_BIND(Data, Data, 2)
|
||||
|
||||
|
||||
PB_BIND(MeshPacket, MeshPacket, 2)
|
||||
|
||||
|
||||
PB_BIND(ChannelSettings, ChannelSettings, AUTO)
|
||||
|
||||
|
||||
PB_BIND(RadioConfig, RadioConfig, 2)
|
||||
|
||||
|
||||
PB_BIND(RadioConfig_UserPreferences, RadioConfig_UserPreferences, 2)
|
||||
|
||||
|
||||
PB_BIND(NodeInfo, NodeInfo, AUTO)
|
||||
|
||||
|
||||
@@ -55,6 +46,3 @@ PB_BIND(ToRadio, ToRadio, 2)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+183
-467
@@ -11,66 +11,29 @@
|
||||
#endif
|
||||
|
||||
/* Enum definitions */
|
||||
typedef enum _ErrorReason {
|
||||
ErrorReason_NONE = 0,
|
||||
ErrorReason_NO_ROUTE = 1,
|
||||
ErrorReason_GOT_NAK = 2,
|
||||
ErrorReason_TIMEOUT = 3,
|
||||
ErrorReason_NO_INTERFACE = 4,
|
||||
ErrorReason_MAX_RETRANSMIT = 5
|
||||
} ErrorReason;
|
||||
typedef enum _HardwareModel {
|
||||
HardwareModel_UNSET = 0,
|
||||
HardwareModel_TLORA_V2 = 1,
|
||||
HardwareModel_TLORA_V1 = 2,
|
||||
HardwareModel_TLORA_V2_1_1p6 = 3,
|
||||
HardwareModel_TBEAM = 4,
|
||||
HardwareModel_HELTEC = 5,
|
||||
HardwareModel_TBEAM0p7 = 6,
|
||||
HardwareModel_T_ECHO = 7,
|
||||
HardwareModel_TLORA_V1_1p3 = 8,
|
||||
HardwareModel_LORA_RELAY_V1 = 32,
|
||||
HardwareModel_NRF52840DK = 33,
|
||||
HardwareModel_PPR = 34,
|
||||
HardwareModel_GENIEBLOCKS = 35,
|
||||
HardwareModel_NRF52_UNKNOWN = 36,
|
||||
HardwareModel_PORTDUINO = 37
|
||||
} HardwareModel;
|
||||
|
||||
typedef enum _Constants {
|
||||
Constants_Unused = 0,
|
||||
Constants_DATA_PAYLOAD_LEN = 240
|
||||
} Constants;
|
||||
|
||||
typedef enum _RegionCode {
|
||||
RegionCode_Unset = 0,
|
||||
RegionCode_US = 1,
|
||||
RegionCode_EU433 = 2,
|
||||
RegionCode_EU865 = 3,
|
||||
RegionCode_CN = 4,
|
||||
RegionCode_JP = 5,
|
||||
RegionCode_ANZ = 6,
|
||||
RegionCode_KR = 7,
|
||||
RegionCode_TW = 8
|
||||
} RegionCode;
|
||||
|
||||
typedef enum _ChargeCurrent {
|
||||
ChargeCurrent_MAUnset = 0,
|
||||
ChargeCurrent_MA100 = 1,
|
||||
ChargeCurrent_MA190 = 2,
|
||||
ChargeCurrent_MA280 = 3,
|
||||
ChargeCurrent_MA360 = 4,
|
||||
ChargeCurrent_MA450 = 5,
|
||||
ChargeCurrent_MA550 = 6,
|
||||
ChargeCurrent_MA630 = 7,
|
||||
ChargeCurrent_MA700 = 8,
|
||||
ChargeCurrent_MA780 = 9,
|
||||
ChargeCurrent_MA880 = 10,
|
||||
ChargeCurrent_MA960 = 11,
|
||||
ChargeCurrent_MA1000 = 12,
|
||||
ChargeCurrent_MA1080 = 13,
|
||||
ChargeCurrent_MA1160 = 14,
|
||||
ChargeCurrent_MA1240 = 15,
|
||||
ChargeCurrent_MA1320 = 16
|
||||
} ChargeCurrent;
|
||||
|
||||
typedef enum _GpsOperation {
|
||||
GpsOperation_GpsOpUnset = 0,
|
||||
GpsOperation_GpsOpStationary = 1,
|
||||
GpsOperation_GpsOpMobile = 2,
|
||||
GpsOperation_GpsOpTimeOnly = 3,
|
||||
GpsOperation_GpsOpDisabled = 4
|
||||
} GpsOperation;
|
||||
|
||||
typedef enum _LocationSharing {
|
||||
LocationSharing_LocUnset = 0,
|
||||
LocationSharing_LocEnabled = 1,
|
||||
LocationSharing_LocDisabled = 2
|
||||
} LocationSharing;
|
||||
|
||||
typedef enum _CriticalErrorCode {
|
||||
CriticalErrorCode_None = 0,
|
||||
CriticalErrorCode_TxWatchdog = 1,
|
||||
@@ -80,15 +43,31 @@ typedef enum _CriticalErrorCode {
|
||||
CriticalErrorCode_UBloxInitFailed = 5,
|
||||
CriticalErrorCode_NoAXP192 = 6,
|
||||
CriticalErrorCode_InvalidRadioSetting = 7,
|
||||
CriticalErrorCode_TransmitFailed = 8
|
||||
CriticalErrorCode_TransmitFailed = 8,
|
||||
CriticalErrorCode_Brownout = 9
|
||||
} CriticalErrorCode;
|
||||
|
||||
typedef enum _ChannelSettings_ModemConfig {
|
||||
ChannelSettings_ModemConfig_Bw125Cr45Sf128 = 0,
|
||||
ChannelSettings_ModemConfig_Bw500Cr45Sf128 = 1,
|
||||
ChannelSettings_ModemConfig_Bw31_25Cr48Sf512 = 2,
|
||||
ChannelSettings_ModemConfig_Bw125Cr48Sf4096 = 3
|
||||
} ChannelSettings_ModemConfig;
|
||||
typedef enum _Routing_Error {
|
||||
Routing_Error_NONE = 0,
|
||||
Routing_Error_NO_ROUTE = 1,
|
||||
Routing_Error_GOT_NAK = 2,
|
||||
Routing_Error_TIMEOUT = 3,
|
||||
Routing_Error_NO_INTERFACE = 4,
|
||||
Routing_Error_MAX_RETRANSMIT = 5,
|
||||
Routing_Error_NO_CHANNEL = 6,
|
||||
Routing_Error_TOO_LARGE = 7,
|
||||
Routing_Error_NO_RESPONSE = 8
|
||||
} Routing_Error;
|
||||
|
||||
typedef enum _MeshPacket_Priority {
|
||||
MeshPacket_Priority_UNSET = 0,
|
||||
MeshPacket_Priority_MIN = 1,
|
||||
MeshPacket_Priority_BACKGROUND = 10,
|
||||
MeshPacket_Priority_DEFAULT = 64,
|
||||
MeshPacket_Priority_RELIABLE = 70,
|
||||
MeshPacket_Priority_ACK = 120,
|
||||
MeshPacket_Priority_MAX = 127
|
||||
} MeshPacket_Priority;
|
||||
|
||||
typedef enum _LogRecord_Level {
|
||||
LogRecord_Level_UNSET = 0,
|
||||
@@ -101,25 +80,14 @@ typedef enum _LogRecord_Level {
|
||||
} LogRecord_Level;
|
||||
|
||||
/* Struct definitions */
|
||||
typedef PB_BYTES_ARRAY_T(32) ChannelSettings_psk_t;
|
||||
typedef struct _ChannelSettings {
|
||||
int32_t tx_power;
|
||||
ChannelSettings_ModemConfig modem_config;
|
||||
ChannelSettings_psk_t psk;
|
||||
char name[12];
|
||||
uint32_t bandwidth;
|
||||
uint32_t spread_factor;
|
||||
uint32_t coding_rate;
|
||||
uint32_t channel_num;
|
||||
uint32_t id;
|
||||
bool uplink_enabled;
|
||||
bool downlink_enabled;
|
||||
} ChannelSettings;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(240) Data_payload_t;
|
||||
typedef PB_BYTES_ARRAY_T(237) Data_payload_t;
|
||||
typedef struct _Data {
|
||||
PortNum portnum;
|
||||
Data_payload_t payload;
|
||||
bool want_response;
|
||||
uint32_t dest;
|
||||
uint32_t source;
|
||||
uint32_t request_id;
|
||||
} Data;
|
||||
|
||||
typedef struct _LogRecord {
|
||||
@@ -132,77 +100,29 @@ typedef struct _LogRecord {
|
||||
typedef struct _MyNodeInfo {
|
||||
uint32_t my_node_num;
|
||||
bool has_gps;
|
||||
int32_t num_channels;
|
||||
uint32_t num_bands;
|
||||
char region[12];
|
||||
char hw_model[16];
|
||||
char hw_model_deprecated[16];
|
||||
char firmware_version[12];
|
||||
CriticalErrorCode error_code;
|
||||
uint32_t error_address;
|
||||
uint32_t error_count;
|
||||
uint32_t packet_id_bits;
|
||||
uint32_t current_packet_id;
|
||||
uint32_t node_num_bits;
|
||||
uint32_t message_timeout_msec;
|
||||
uint32_t min_app_version;
|
||||
uint32_t max_channels;
|
||||
} MyNodeInfo;
|
||||
|
||||
typedef struct _Position {
|
||||
int32_t altitude;
|
||||
int32_t battery_level;
|
||||
int32_t latitude_i;
|
||||
int32_t longitude_i;
|
||||
int32_t altitude;
|
||||
int32_t battery_level;
|
||||
uint32_t time;
|
||||
} Position;
|
||||
|
||||
typedef struct _RadioConfig_UserPreferences {
|
||||
uint32_t position_broadcast_secs;
|
||||
uint32_t send_owner_interval;
|
||||
uint32_t wait_bluetooth_secs;
|
||||
uint32_t screen_on_secs;
|
||||
uint32_t phone_timeout_secs;
|
||||
uint32_t phone_sds_timeout_sec;
|
||||
uint32_t mesh_sds_timeout_secs;
|
||||
uint32_t sds_secs;
|
||||
uint32_t ls_secs;
|
||||
uint32_t min_wake_secs;
|
||||
char wifi_ssid[33];
|
||||
char wifi_password[64];
|
||||
bool wifi_ap_mode;
|
||||
RegionCode region;
|
||||
ChargeCurrent charge_current;
|
||||
LocationSharing location_share;
|
||||
GpsOperation gps_operation;
|
||||
uint32_t gps_update_interval;
|
||||
uint32_t gps_attempt_time;
|
||||
bool is_router;
|
||||
bool is_low_power;
|
||||
bool fixed_position;
|
||||
bool factory_reset;
|
||||
bool debug_log_enabled;
|
||||
pb_size_t ignore_incoming_count;
|
||||
uint32_t ignore_incoming[3];
|
||||
bool serialplugin_enabled;
|
||||
bool serialplugin_echo;
|
||||
uint32_t serialplugin_rxd;
|
||||
uint32_t serialplugin_txd;
|
||||
uint32_t serialplugin_timeout;
|
||||
uint32_t serialplugin_mode;
|
||||
bool ext_notification_plugin_enabled;
|
||||
uint32_t ext_notification_plugin_output_ms;
|
||||
uint32_t ext_notification_plugin_output;
|
||||
bool ext_notification_plugin_active;
|
||||
bool ext_notification_plugin_alert_message;
|
||||
bool ext_notification_plugin_alert_bell;
|
||||
bool range_test_plugin_enabled;
|
||||
uint32_t range_test_plugin_sender;
|
||||
bool range_test_plugin_save;
|
||||
bool store_forward_plugin_enabled;
|
||||
uint32_t store_forward_plugin_records;
|
||||
} RadioConfig_UserPreferences;
|
||||
|
||||
typedef struct _RouteDiscovery {
|
||||
pb_size_t route_count;
|
||||
int32_t route[8];
|
||||
uint32_t route[8];
|
||||
} RouteDiscovery;
|
||||
|
||||
typedef struct _User {
|
||||
@@ -210,75 +130,55 @@ typedef struct _User {
|
||||
char long_name[40];
|
||||
char short_name[5];
|
||||
pb_byte_t macaddr[6];
|
||||
HardwareModel hw_model;
|
||||
} User;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t;
|
||||
typedef struct _MeshPacket {
|
||||
uint32_t from;
|
||||
uint32_t to;
|
||||
uint8_t channel;
|
||||
pb_size_t which_payloadVariant;
|
||||
union {
|
||||
Data decoded;
|
||||
MeshPacket_encrypted_t encrypted;
|
||||
};
|
||||
uint32_t id;
|
||||
uint32_t rx_time;
|
||||
float rx_snr;
|
||||
uint8_t hop_limit;
|
||||
bool want_ack;
|
||||
MeshPacket_Priority priority;
|
||||
} MeshPacket;
|
||||
|
||||
typedef struct _NodeInfo {
|
||||
uint32_t num;
|
||||
bool has_user;
|
||||
User user;
|
||||
bool has_position;
|
||||
Position position;
|
||||
uint32_t next_hop;
|
||||
float snr;
|
||||
} NodeInfo;
|
||||
|
||||
typedef struct _RadioConfig {
|
||||
bool has_preferences;
|
||||
RadioConfig_UserPreferences preferences;
|
||||
bool has_channel_settings;
|
||||
ChannelSettings channel_settings;
|
||||
} RadioConfig;
|
||||
|
||||
typedef struct _SubPacket {
|
||||
pb_size_t which_payloadVariant;
|
||||
typedef struct _Routing {
|
||||
pb_size_t which_variant;
|
||||
union {
|
||||
Position position;
|
||||
Data data;
|
||||
User user;
|
||||
RouteDiscovery route_request;
|
||||
RouteDiscovery route_reply;
|
||||
ErrorReason error_reason;
|
||||
Routing_Error error_reason;
|
||||
};
|
||||
uint32_t original_id;
|
||||
bool want_response;
|
||||
uint32_t dest;
|
||||
pb_size_t which_ackVariant;
|
||||
union {
|
||||
uint32_t success_id;
|
||||
uint32_t fail_id;
|
||||
} ackVariant;
|
||||
uint32_t source;
|
||||
} SubPacket;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t;
|
||||
typedef struct _MeshPacket {
|
||||
uint32_t from;
|
||||
uint32_t to;
|
||||
pb_size_t which_payloadVariant;
|
||||
union {
|
||||
SubPacket decoded;
|
||||
MeshPacket_encrypted_t encrypted;
|
||||
};
|
||||
uint32_t channel_index;
|
||||
uint32_t id;
|
||||
float rx_snr;
|
||||
uint32_t rx_time;
|
||||
uint32_t hop_limit;
|
||||
bool want_ack;
|
||||
} MeshPacket;
|
||||
} Routing;
|
||||
|
||||
typedef struct _FromRadio {
|
||||
uint32_t num;
|
||||
pb_size_t which_payloadVariant;
|
||||
union {
|
||||
MeshPacket packet;
|
||||
MyNodeInfo my_info;
|
||||
NodeInfo node_info;
|
||||
RadioConfig radio;
|
||||
LogRecord log_record;
|
||||
uint32_t config_complete_id;
|
||||
bool rebooted;
|
||||
ChannelSettings channel;
|
||||
MeshPacket packet;
|
||||
};
|
||||
} FromRadio;
|
||||
|
||||
@@ -287,45 +187,30 @@ typedef struct _ToRadio {
|
||||
union {
|
||||
MeshPacket packet;
|
||||
uint32_t want_config_id;
|
||||
RadioConfig set_radio;
|
||||
User set_owner;
|
||||
ChannelSettings set_channel;
|
||||
};
|
||||
} ToRadio;
|
||||
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _ErrorReason_MIN ErrorReason_NONE
|
||||
#define _ErrorReason_MAX ErrorReason_MAX_RETRANSMIT
|
||||
#define _ErrorReason_ARRAYSIZE ((ErrorReason)(ErrorReason_MAX_RETRANSMIT+1))
|
||||
#define _HardwareModel_MIN HardwareModel_UNSET
|
||||
#define _HardwareModel_MAX HardwareModel_PORTDUINO
|
||||
#define _HardwareModel_ARRAYSIZE ((HardwareModel)(HardwareModel_PORTDUINO+1))
|
||||
|
||||
#define _Constants_MIN Constants_Unused
|
||||
#define _Constants_MAX Constants_DATA_PAYLOAD_LEN
|
||||
#define _Constants_ARRAYSIZE ((Constants)(Constants_DATA_PAYLOAD_LEN+1))
|
||||
|
||||
#define _RegionCode_MIN RegionCode_Unset
|
||||
#define _RegionCode_MAX RegionCode_TW
|
||||
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1))
|
||||
|
||||
#define _ChargeCurrent_MIN ChargeCurrent_MAUnset
|
||||
#define _ChargeCurrent_MAX ChargeCurrent_MA1320
|
||||
#define _ChargeCurrent_ARRAYSIZE ((ChargeCurrent)(ChargeCurrent_MA1320+1))
|
||||
|
||||
#define _GpsOperation_MIN GpsOperation_GpsOpUnset
|
||||
#define _GpsOperation_MAX GpsOperation_GpsOpDisabled
|
||||
#define _GpsOperation_ARRAYSIZE ((GpsOperation)(GpsOperation_GpsOpDisabled+1))
|
||||
|
||||
#define _LocationSharing_MIN LocationSharing_LocUnset
|
||||
#define _LocationSharing_MAX LocationSharing_LocDisabled
|
||||
#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1))
|
||||
|
||||
#define _CriticalErrorCode_MIN CriticalErrorCode_None
|
||||
#define _CriticalErrorCode_MAX CriticalErrorCode_TransmitFailed
|
||||
#define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_TransmitFailed+1))
|
||||
#define _CriticalErrorCode_MAX CriticalErrorCode_Brownout
|
||||
#define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_Brownout+1))
|
||||
|
||||
#define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_Bw125Cr45Sf128
|
||||
#define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_Bw125Cr48Sf4096
|
||||
#define _ChannelSettings_ModemConfig_ARRAYSIZE ((ChannelSettings_ModemConfig)(ChannelSettings_ModemConfig_Bw125Cr48Sf4096+1))
|
||||
#define _Routing_Error_MIN Routing_Error_NONE
|
||||
#define _Routing_Error_MAX Routing_Error_NO_RESPONSE
|
||||
#define _Routing_Error_ARRAYSIZE ((Routing_Error)(Routing_Error_NO_RESPONSE+1))
|
||||
|
||||
#define _MeshPacket_Priority_MIN MeshPacket_Priority_UNSET
|
||||
#define _MeshPacket_Priority_MAX MeshPacket_Priority_MAX
|
||||
#define _MeshPacket_Priority_ARRAYSIZE ((MeshPacket_Priority)(MeshPacket_Priority_MAX+1))
|
||||
|
||||
#define _LogRecord_Level_MIN LogRecord_Level_UNSET
|
||||
#define _LogRecord_Level_MAX LogRecord_Level_CRITICAL
|
||||
@@ -338,301 +223,153 @@ extern "C" {
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define Position_init_default {0, 0, 0, 0, 0}
|
||||
#define Data_init_default {_PortNum_MIN, {0, {0}}}
|
||||
#define User_init_default {"", "", "", {0}}
|
||||
#define User_init_default {"", "", "", {0}, _HardwareModel_MIN}
|
||||
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0}
|
||||
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0, 0}
|
||||
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0}
|
||||
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
|
||||
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
|
||||
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define Routing_init_default {0, {RouteDiscovery_init_default}}
|
||||
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
|
||||
#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN}
|
||||
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0}
|
||||
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0}
|
||||
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
|
||||
#define FromRadio_init_default {0, 0, {MeshPacket_init_default}}
|
||||
#define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}}
|
||||
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
||||
#define Position_init_zero {0, 0, 0, 0, 0}
|
||||
#define Data_init_zero {_PortNum_MIN, {0, {0}}}
|
||||
#define User_init_zero {"", "", "", {0}}
|
||||
#define User_init_zero {"", "", "", {0}, _HardwareModel_MIN}
|
||||
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0}
|
||||
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0, 0}
|
||||
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0}
|
||||
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
|
||||
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
|
||||
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define Routing_init_zero {0, {RouteDiscovery_init_zero}}
|
||||
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
|
||||
#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN}
|
||||
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0}
|
||||
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0}
|
||||
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
|
||||
#define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}}
|
||||
#define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}}
|
||||
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define ChannelSettings_tx_power_tag 1
|
||||
#define ChannelSettings_modem_config_tag 3
|
||||
#define ChannelSettings_psk_tag 4
|
||||
#define ChannelSettings_name_tag 5
|
||||
#define ChannelSettings_bandwidth_tag 6
|
||||
#define ChannelSettings_spread_factor_tag 7
|
||||
#define ChannelSettings_coding_rate_tag 8
|
||||
#define ChannelSettings_channel_num_tag 9
|
||||
#define ChannelSettings_id_tag 10
|
||||
#define ChannelSettings_uplink_enabled_tag 16
|
||||
#define ChannelSettings_downlink_enabled_tag 17
|
||||
#define Data_portnum_tag 1
|
||||
#define Data_payload_tag 2
|
||||
#define Data_want_response_tag 3
|
||||
#define Data_dest_tag 4
|
||||
#define Data_source_tag 5
|
||||
#define Data_request_id_tag 6
|
||||
#define LogRecord_message_tag 1
|
||||
#define LogRecord_time_tag 2
|
||||
#define LogRecord_source_tag 3
|
||||
#define LogRecord_level_tag 4
|
||||
#define MyNodeInfo_my_node_num_tag 1
|
||||
#define MyNodeInfo_has_gps_tag 2
|
||||
#define MyNodeInfo_num_channels_tag 3
|
||||
#define MyNodeInfo_num_bands_tag 3
|
||||
#define MyNodeInfo_region_tag 4
|
||||
#define MyNodeInfo_hw_model_tag 5
|
||||
#define MyNodeInfo_hw_model_deprecated_tag 5
|
||||
#define MyNodeInfo_firmware_version_tag 6
|
||||
#define MyNodeInfo_error_code_tag 7
|
||||
#define MyNodeInfo_error_address_tag 8
|
||||
#define MyNodeInfo_error_count_tag 9
|
||||
#define MyNodeInfo_packet_id_bits_tag 10
|
||||
#define MyNodeInfo_current_packet_id_tag 11
|
||||
#define MyNodeInfo_node_num_bits_tag 12
|
||||
#define MyNodeInfo_message_timeout_msec_tag 13
|
||||
#define MyNodeInfo_min_app_version_tag 14
|
||||
#define MyNodeInfo_max_channels_tag 15
|
||||
#define Position_latitude_i_tag 1
|
||||
#define Position_longitude_i_tag 2
|
||||
#define Position_altitude_tag 3
|
||||
#define Position_battery_level_tag 4
|
||||
#define Position_latitude_i_tag 7
|
||||
#define Position_longitude_i_tag 8
|
||||
#define Position_time_tag 9
|
||||
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
|
||||
#define RadioConfig_UserPreferences_send_owner_interval_tag 2
|
||||
#define RadioConfig_UserPreferences_wait_bluetooth_secs_tag 4
|
||||
#define RadioConfig_UserPreferences_screen_on_secs_tag 5
|
||||
#define RadioConfig_UserPreferences_phone_timeout_secs_tag 6
|
||||
#define RadioConfig_UserPreferences_phone_sds_timeout_sec_tag 7
|
||||
#define RadioConfig_UserPreferences_mesh_sds_timeout_secs_tag 8
|
||||
#define RadioConfig_UserPreferences_sds_secs_tag 9
|
||||
#define RadioConfig_UserPreferences_ls_secs_tag 10
|
||||
#define RadioConfig_UserPreferences_min_wake_secs_tag 11
|
||||
#define RadioConfig_UserPreferences_wifi_ssid_tag 12
|
||||
#define RadioConfig_UserPreferences_wifi_password_tag 13
|
||||
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
|
||||
#define RadioConfig_UserPreferences_region_tag 15
|
||||
#define RadioConfig_UserPreferences_charge_current_tag 16
|
||||
#define RadioConfig_UserPreferences_location_share_tag 32
|
||||
#define RadioConfig_UserPreferences_gps_operation_tag 33
|
||||
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
|
||||
#define RadioConfig_UserPreferences_gps_attempt_time_tag 36
|
||||
#define RadioConfig_UserPreferences_is_router_tag 37
|
||||
#define RadioConfig_UserPreferences_is_low_power_tag 38
|
||||
#define RadioConfig_UserPreferences_fixed_position_tag 39
|
||||
#define RadioConfig_UserPreferences_factory_reset_tag 100
|
||||
#define RadioConfig_UserPreferences_debug_log_enabled_tag 101
|
||||
#define RadioConfig_UserPreferences_ignore_incoming_tag 103
|
||||
#define RadioConfig_UserPreferences_serialplugin_enabled_tag 120
|
||||
#define RadioConfig_UserPreferences_serialplugin_echo_tag 121
|
||||
#define RadioConfig_UserPreferences_serialplugin_rxd_tag 122
|
||||
#define RadioConfig_UserPreferences_serialplugin_txd_tag 123
|
||||
#define RadioConfig_UserPreferences_serialplugin_timeout_tag 124
|
||||
#define RadioConfig_UserPreferences_serialplugin_mode_tag 125
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_enabled_tag 126
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_output_ms_tag 127
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_output_tag 128
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_active_tag 129
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_alert_message_tag 130
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_alert_bell_tag 131
|
||||
#define RadioConfig_UserPreferences_range_test_plugin_enabled_tag 132
|
||||
#define RadioConfig_UserPreferences_range_test_plugin_sender_tag 133
|
||||
#define RadioConfig_UserPreferences_range_test_plugin_save_tag 134
|
||||
#define RadioConfig_UserPreferences_store_forward_plugin_enabled_tag 136
|
||||
#define RadioConfig_UserPreferences_store_forward_plugin_records_tag 137
|
||||
#define RouteDiscovery_route_tag 2
|
||||
#define User_id_tag 1
|
||||
#define User_long_name_tag 2
|
||||
#define User_short_name_tag 3
|
||||
#define User_macaddr_tag 4
|
||||
#define User_hw_model_tag 6
|
||||
#define MeshPacket_from_tag 1
|
||||
#define MeshPacket_to_tag 2
|
||||
#define MeshPacket_channel_tag 3
|
||||
#define MeshPacket_decoded_tag 4
|
||||
#define MeshPacket_encrypted_tag 5
|
||||
#define MeshPacket_id_tag 6
|
||||
#define MeshPacket_rx_time_tag 7
|
||||
#define MeshPacket_rx_snr_tag 8
|
||||
#define MeshPacket_hop_limit_tag 10
|
||||
#define MeshPacket_want_ack_tag 11
|
||||
#define MeshPacket_priority_tag 12
|
||||
#define NodeInfo_num_tag 1
|
||||
#define NodeInfo_user_tag 2
|
||||
#define NodeInfo_position_tag 3
|
||||
#define NodeInfo_next_hop_tag 5
|
||||
#define NodeInfo_snr_tag 7
|
||||
#define RadioConfig_preferences_tag 1
|
||||
#define RadioConfig_channel_settings_tag 2
|
||||
#define SubPacket_position_tag 1
|
||||
#define SubPacket_data_tag 3
|
||||
#define SubPacket_user_tag 4
|
||||
#define SubPacket_route_request_tag 6
|
||||
#define SubPacket_route_reply_tag 7
|
||||
#define SubPacket_error_reason_tag 13
|
||||
#define SubPacket_original_id_tag 2
|
||||
#define SubPacket_want_response_tag 5
|
||||
#define SubPacket_dest_tag 9
|
||||
#define SubPacket_success_id_tag 10
|
||||
#define SubPacket_fail_id_tag 11
|
||||
#define SubPacket_source_tag 12
|
||||
#define MeshPacket_from_tag 1
|
||||
#define MeshPacket_to_tag 2
|
||||
#define MeshPacket_decoded_tag 3
|
||||
#define MeshPacket_encrypted_tag 8
|
||||
#define MeshPacket_channel_index_tag 4
|
||||
#define MeshPacket_id_tag 6
|
||||
#define MeshPacket_rx_snr_tag 7
|
||||
#define MeshPacket_rx_time_tag 9
|
||||
#define MeshPacket_hop_limit_tag 10
|
||||
#define MeshPacket_want_ack_tag 11
|
||||
#define Routing_route_request_tag 1
|
||||
#define Routing_route_reply_tag 2
|
||||
#define Routing_error_reason_tag 3
|
||||
#define FromRadio_num_tag 1
|
||||
#define FromRadio_packet_tag 2
|
||||
#define FromRadio_my_info_tag 3
|
||||
#define FromRadio_node_info_tag 4
|
||||
#define FromRadio_radio_tag 6
|
||||
#define FromRadio_log_record_tag 7
|
||||
#define FromRadio_config_complete_id_tag 8
|
||||
#define FromRadio_rebooted_tag 9
|
||||
#define FromRadio_channel_tag 10
|
||||
#define ToRadio_packet_tag 1
|
||||
#define FromRadio_packet_tag 11
|
||||
#define ToRadio_packet_tag 2
|
||||
#define ToRadio_want_config_id_tag 100
|
||||
#define ToRadio_set_radio_tag 101
|
||||
#define ToRadio_set_owner_tag 102
|
||||
#define ToRadio_set_channel_tag 103
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define Position_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 1) \
|
||||
X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 2) \
|
||||
X(a, STATIC, SINGULAR, INT32, altitude, 3) \
|
||||
X(a, STATIC, SINGULAR, INT32, battery_level, 4) \
|
||||
X(a, STATIC, SINGULAR, SINT32, latitude_i, 7) \
|
||||
X(a, STATIC, SINGULAR, SINT32, longitude_i, 8) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, time, 9)
|
||||
#define Position_CALLBACK NULL
|
||||
#define Position_DEFAULT NULL
|
||||
|
||||
#define Data_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, portnum, 1) \
|
||||
X(a, STATIC, SINGULAR, BYTES, payload, 2)
|
||||
#define Data_CALLBACK NULL
|
||||
#define Data_DEFAULT NULL
|
||||
|
||||
#define User_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, STRING, id, 1) \
|
||||
X(a, STATIC, SINGULAR, STRING, long_name, 2) \
|
||||
X(a, STATIC, SINGULAR, STRING, short_name, 3) \
|
||||
X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4)
|
||||
X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4) \
|
||||
X(a, STATIC, SINGULAR, UENUM, hw_model, 6)
|
||||
#define User_CALLBACK NULL
|
||||
#define User_DEFAULT NULL
|
||||
|
||||
#define RouteDiscovery_FIELDLIST(X, a) \
|
||||
X(a, STATIC, REPEATED, INT32, route, 2)
|
||||
X(a, STATIC, REPEATED, FIXED32, route, 2)
|
||||
#define RouteDiscovery_CALLBACK NULL
|
||||
#define RouteDiscovery_DEFAULT NULL
|
||||
|
||||
#define SubPacket_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,position,position), 1) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,data,data), 3) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,user,user), 4) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,route_request,route_request), 6) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,route_reply,route_reply), 7) \
|
||||
X(a, STATIC, ONEOF, UENUM, (payloadVariant,error_reason,error_reason), 13) \
|
||||
X(a, STATIC, SINGULAR, UINT32, original_id, 2) \
|
||||
X(a, STATIC, SINGULAR, BOOL, want_response, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, dest, 9) \
|
||||
X(a, STATIC, ONEOF, UINT32, (ackVariant,success_id,ackVariant.success_id), 10) \
|
||||
X(a, STATIC, ONEOF, UINT32, (ackVariant,fail_id,ackVariant.fail_id), 11) \
|
||||
X(a, STATIC, SINGULAR, UINT32, source, 12)
|
||||
#define SubPacket_CALLBACK NULL
|
||||
#define SubPacket_DEFAULT NULL
|
||||
#define SubPacket_payloadVariant_position_MSGTYPE Position
|
||||
#define SubPacket_payloadVariant_data_MSGTYPE Data
|
||||
#define SubPacket_payloadVariant_user_MSGTYPE User
|
||||
#define SubPacket_payloadVariant_route_request_MSGTYPE RouteDiscovery
|
||||
#define SubPacket_payloadVariant_route_reply_MSGTYPE RouteDiscovery
|
||||
#define Routing_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,route_request,route_request), 1) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,route_reply,route_reply), 2) \
|
||||
X(a, STATIC, ONEOF, UENUM, (variant,error_reason,error_reason), 3)
|
||||
#define Routing_CALLBACK NULL
|
||||
#define Routing_DEFAULT NULL
|
||||
#define Routing_variant_route_request_MSGTYPE RouteDiscovery
|
||||
#define Routing_variant_route_reply_MSGTYPE RouteDiscovery
|
||||
|
||||
#define Data_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, portnum, 1) \
|
||||
X(a, STATIC, SINGULAR, BYTES, payload, 2) \
|
||||
X(a, STATIC, SINGULAR, BOOL, want_response, 3) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, dest, 4) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, source, 5) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, request_id, 6)
|
||||
#define Data_CALLBACK NULL
|
||||
#define Data_DEFAULT NULL
|
||||
|
||||
#define MeshPacket_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, from, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, to, 2) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,decoded,decoded), 3) \
|
||||
X(a, STATIC, ONEOF, BYTES, (payloadVariant,encrypted,encrypted), 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, channel_index, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, id, 6) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, rx_snr, 7) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, rx_time, 9) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, from, 1) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, to, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, channel, 3) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,decoded,decoded), 4) \
|
||||
X(a, STATIC, ONEOF, BYTES, (payloadVariant,encrypted,encrypted), 5) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, id, 6) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, rx_time, 7) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, rx_snr, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, hop_limit, 10) \
|
||||
X(a, STATIC, SINGULAR, BOOL, want_ack, 11)
|
||||
X(a, STATIC, SINGULAR, BOOL, want_ack, 11) \
|
||||
X(a, STATIC, SINGULAR, UENUM, priority, 12)
|
||||
#define MeshPacket_CALLBACK NULL
|
||||
#define MeshPacket_DEFAULT NULL
|
||||
#define MeshPacket_payloadVariant_decoded_MSGTYPE SubPacket
|
||||
|
||||
#define ChannelSettings_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, INT32, tx_power, 1) \
|
||||
X(a, STATIC, SINGULAR, UENUM, modem_config, 3) \
|
||||
X(a, STATIC, SINGULAR, BYTES, psk, 4) \
|
||||
X(a, STATIC, SINGULAR, STRING, name, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, bandwidth, 6) \
|
||||
X(a, STATIC, SINGULAR, UINT32, spread_factor, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, coding_rate, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, channel_num, 9) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, id, 10) \
|
||||
X(a, STATIC, SINGULAR, BOOL, uplink_enabled, 16) \
|
||||
X(a, STATIC, SINGULAR, BOOL, downlink_enabled, 17)
|
||||
#define ChannelSettings_CALLBACK NULL
|
||||
#define ChannelSettings_DEFAULT NULL
|
||||
|
||||
#define RadioConfig_FIELDLIST(X, a) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, preferences, 1) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, channel_settings, 2)
|
||||
#define RadioConfig_CALLBACK NULL
|
||||
#define RadioConfig_DEFAULT NULL
|
||||
#define RadioConfig_preferences_MSGTYPE RadioConfig_UserPreferences
|
||||
#define RadioConfig_channel_settings_MSGTYPE ChannelSettings
|
||||
|
||||
#define RadioConfig_UserPreferences_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, position_broadcast_secs, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, send_owner_interval, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, wait_bluetooth_secs, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, screen_on_secs, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, phone_timeout_secs, 6) \
|
||||
X(a, STATIC, SINGULAR, UINT32, phone_sds_timeout_sec, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, mesh_sds_timeout_secs, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, sds_secs, 9) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ls_secs, 10) \
|
||||
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 11) \
|
||||
X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \
|
||||
X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \
|
||||
X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \
|
||||
X(a, STATIC, SINGULAR, UENUM, region, 15) \
|
||||
X(a, STATIC, SINGULAR, UENUM, charge_current, 16) \
|
||||
X(a, STATIC, SINGULAR, UENUM, location_share, 32) \
|
||||
X(a, STATIC, SINGULAR, UENUM, gps_operation, 33) \
|
||||
X(a, STATIC, SINGULAR, UINT32, gps_update_interval, 34) \
|
||||
X(a, STATIC, SINGULAR, UINT32, gps_attempt_time, 36) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_router, 37) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_low_power, 38) \
|
||||
X(a, STATIC, SINGULAR, BOOL, fixed_position, 39) \
|
||||
X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \
|
||||
X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \
|
||||
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
|
||||
X(a, STATIC, SINGULAR, BOOL, serialplugin_enabled, 120) \
|
||||
X(a, STATIC, SINGULAR, BOOL, serialplugin_echo, 121) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_rxd, 122) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_txd, 123) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_timeout, 124) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_mode, 125) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_enabled, 126) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ext_notification_plugin_output_ms, 127) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ext_notification_plugin_output, 128) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_active, 129) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_alert_message, 130) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_alert_bell, 131) \
|
||||
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_enabled, 132) \
|
||||
X(a, STATIC, SINGULAR, UINT32, range_test_plugin_sender, 133) \
|
||||
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_save, 134) \
|
||||
X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_enabled, 136) \
|
||||
X(a, STATIC, SINGULAR, UINT32, store_forward_plugin_records, 137)
|
||||
#define RadioConfig_UserPreferences_CALLBACK NULL
|
||||
#define RadioConfig_UserPreferences_DEFAULT NULL
|
||||
#define MeshPacket_payloadVariant_decoded_MSGTYPE Data
|
||||
|
||||
#define NodeInfo_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, num, 1) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, position, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, next_hop, 5) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, snr, 7)
|
||||
#define NodeInfo_CALLBACK NULL
|
||||
#define NodeInfo_DEFAULT NULL
|
||||
@@ -642,18 +379,16 @@ X(a, STATIC, SINGULAR, FLOAT, snr, 7)
|
||||
#define MyNodeInfo_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, my_node_num, 1) \
|
||||
X(a, STATIC, SINGULAR, BOOL, has_gps, 2) \
|
||||
X(a, STATIC, SINGULAR, INT32, num_channels, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, num_bands, 3) \
|
||||
X(a, STATIC, SINGULAR, STRING, region, 4) \
|
||||
X(a, STATIC, SINGULAR, STRING, hw_model, 5) \
|
||||
X(a, STATIC, SINGULAR, STRING, hw_model_deprecated, 5) \
|
||||
X(a, STATIC, SINGULAR, STRING, firmware_version, 6) \
|
||||
X(a, STATIC, SINGULAR, UENUM, error_code, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, error_address, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, error_count, 9) \
|
||||
X(a, STATIC, SINGULAR, UINT32, packet_id_bits, 10) \
|
||||
X(a, STATIC, SINGULAR, UINT32, current_packet_id, 11) \
|
||||
X(a, STATIC, SINGULAR, UINT32, node_num_bits, 12) \
|
||||
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)
|
||||
#define MyNodeInfo_CALLBACK NULL
|
||||
#define MyNodeInfo_DEFAULT NULL
|
||||
|
||||
@@ -667,45 +402,32 @@ X(a, STATIC, SINGULAR, UENUM, level, 4)
|
||||
|
||||
#define FromRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, num, 1) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 2) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,my_info,my_info), 3) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,node_info,node_info), 4) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,radio,radio), 6) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,log_record,log_record), 7) \
|
||||
X(a, STATIC, ONEOF, UINT32, (payloadVariant,config_complete_id,config_complete_id), 8) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payloadVariant,rebooted,rebooted), 9) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,channel,channel), 10)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 11)
|
||||
#define FromRadio_CALLBACK NULL
|
||||
#define FromRadio_DEFAULT NULL
|
||||
#define FromRadio_payloadVariant_packet_MSGTYPE MeshPacket
|
||||
#define FromRadio_payloadVariant_my_info_MSGTYPE MyNodeInfo
|
||||
#define FromRadio_payloadVariant_node_info_MSGTYPE NodeInfo
|
||||
#define FromRadio_payloadVariant_radio_MSGTYPE RadioConfig
|
||||
#define FromRadio_payloadVariant_log_record_MSGTYPE LogRecord
|
||||
#define FromRadio_payloadVariant_channel_MSGTYPE ChannelSettings
|
||||
#define FromRadio_payloadVariant_packet_MSGTYPE MeshPacket
|
||||
|
||||
#define ToRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 1) \
|
||||
X(a, STATIC, ONEOF, UINT32, (payloadVariant,want_config_id,want_config_id), 100) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,set_radio,set_radio), 101) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,set_owner,set_owner), 102) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,set_channel,set_channel), 103)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payloadVariant,packet,packet), 2) \
|
||||
X(a, STATIC, ONEOF, UINT32, (payloadVariant,want_config_id,want_config_id), 100)
|
||||
#define ToRadio_CALLBACK NULL
|
||||
#define ToRadio_DEFAULT NULL
|
||||
#define ToRadio_payloadVariant_packet_MSGTYPE MeshPacket
|
||||
#define ToRadio_payloadVariant_set_radio_MSGTYPE RadioConfig
|
||||
#define ToRadio_payloadVariant_set_owner_MSGTYPE User
|
||||
#define ToRadio_payloadVariant_set_channel_MSGTYPE ChannelSettings
|
||||
|
||||
extern const pb_msgdesc_t Position_msg;
|
||||
extern const pb_msgdesc_t Data_msg;
|
||||
extern const pb_msgdesc_t User_msg;
|
||||
extern const pb_msgdesc_t RouteDiscovery_msg;
|
||||
extern const pb_msgdesc_t SubPacket_msg;
|
||||
extern const pb_msgdesc_t Routing_msg;
|
||||
extern const pb_msgdesc_t Data_msg;
|
||||
extern const pb_msgdesc_t MeshPacket_msg;
|
||||
extern const pb_msgdesc_t ChannelSettings_msg;
|
||||
extern const pb_msgdesc_t RadioConfig_msg;
|
||||
extern const pb_msgdesc_t RadioConfig_UserPreferences_msg;
|
||||
extern const pb_msgdesc_t NodeInfo_msg;
|
||||
extern const pb_msgdesc_t MyNodeInfo_msg;
|
||||
extern const pb_msgdesc_t LogRecord_msg;
|
||||
@@ -714,14 +436,11 @@ extern const pb_msgdesc_t ToRadio_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define Position_fields &Position_msg
|
||||
#define Data_fields &Data_msg
|
||||
#define User_fields &User_msg
|
||||
#define RouteDiscovery_fields &RouteDiscovery_msg
|
||||
#define SubPacket_fields &SubPacket_msg
|
||||
#define Routing_fields &Routing_msg
|
||||
#define Data_fields &Data_msg
|
||||
#define MeshPacket_fields &MeshPacket_msg
|
||||
#define ChannelSettings_fields &ChannelSettings_msg
|
||||
#define RadioConfig_fields &RadioConfig_msg
|
||||
#define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg
|
||||
#define NodeInfo_fields &NodeInfo_msg
|
||||
#define MyNodeInfo_fields &MyNodeInfo_msg
|
||||
#define LogRecord_fields &LogRecord_msg
|
||||
@@ -729,20 +448,17 @@ extern const pb_msgdesc_t ToRadio_msg;
|
||||
#define ToRadio_fields &ToRadio_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define Position_size 39
|
||||
#define Data_size 246
|
||||
#define User_size 72
|
||||
#define RouteDiscovery_size 88
|
||||
#define SubPacket_size 275
|
||||
#define MeshPacket_size 320
|
||||
#define ChannelSettings_size 95
|
||||
#define RadioConfig_size 405
|
||||
#define RadioConfig_UserPreferences_size 305
|
||||
#define NodeInfo_size 132
|
||||
#define MyNodeInfo_size 106
|
||||
#define Position_size 37
|
||||
#define User_size 74
|
||||
#define RouteDiscovery_size 40
|
||||
#define Routing_size 42
|
||||
#define Data_size 260
|
||||
#define MeshPacket_size 298
|
||||
#define NodeInfo_size 126
|
||||
#define MyNodeInfo_size 89
|
||||
#define LogRecord_size 81
|
||||
#define FromRadio_size 414
|
||||
#define ToRadio_size 409
|
||||
#define FromRadio_size 307
|
||||
#define ToRadio_size 301
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -16,19 +16,23 @@ typedef enum _PortNum {
|
||||
PortNum_REMOTE_HARDWARE_APP = 2,
|
||||
PortNum_POSITION_APP = 3,
|
||||
PortNum_NODEINFO_APP = 4,
|
||||
PortNum_ROUTING_APP = 5,
|
||||
PortNum_ADMIN_APP = 6,
|
||||
PortNum_REPLY_APP = 32,
|
||||
PortNum_IP_TUNNEL_APP = 33,
|
||||
PortNum_SERIAL_APP = 64,
|
||||
PortNum_STORE_FORWARD_APP = 65,
|
||||
PortNum_RANGE_TEST_APP = 66,
|
||||
PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 67,
|
||||
PortNum_PRIVATE_APP = 256,
|
||||
PortNum_ATAK_FORWARDER = 257
|
||||
PortNum_ATAK_FORWARDER = 257,
|
||||
PortNum_MAX = 511
|
||||
} PortNum;
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _PortNum_MIN PortNum_UNKNOWN_APP
|
||||
#define _PortNum_MAX PortNum_ATAK_FORWARDER
|
||||
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_ATAK_FORWARDER+1))
|
||||
#define _PortNum_MAX PortNum_MAX
|
||||
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_MAX+1))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#include "radioconfig.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
PB_BIND(RadioConfig, RadioConfig, 2)
|
||||
|
||||
|
||||
PB_BIND(RadioConfig_UserPreferences, RadioConfig_UserPreferences, 2)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#ifndef PB_RADIOCONFIG_PB_H_INCLUDED
|
||||
#define PB_RADIOCONFIG_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Enum definitions */
|
||||
typedef enum _RegionCode {
|
||||
RegionCode_Unset = 0,
|
||||
RegionCode_US = 1,
|
||||
RegionCode_EU433 = 2,
|
||||
RegionCode_EU865 = 3,
|
||||
RegionCode_CN = 4,
|
||||
RegionCode_JP = 5,
|
||||
RegionCode_ANZ = 6,
|
||||
RegionCode_KR = 7,
|
||||
RegionCode_TW = 8,
|
||||
RegionCode_RU = 9
|
||||
} RegionCode;
|
||||
|
||||
typedef enum _ChargeCurrent {
|
||||
ChargeCurrent_MAUnset = 0,
|
||||
ChargeCurrent_MA100 = 1,
|
||||
ChargeCurrent_MA190 = 2,
|
||||
ChargeCurrent_MA280 = 3,
|
||||
ChargeCurrent_MA360 = 4,
|
||||
ChargeCurrent_MA450 = 5,
|
||||
ChargeCurrent_MA550 = 6,
|
||||
ChargeCurrent_MA630 = 7,
|
||||
ChargeCurrent_MA700 = 8,
|
||||
ChargeCurrent_MA780 = 9,
|
||||
ChargeCurrent_MA880 = 10,
|
||||
ChargeCurrent_MA960 = 11,
|
||||
ChargeCurrent_MA1000 = 12,
|
||||
ChargeCurrent_MA1080 = 13,
|
||||
ChargeCurrent_MA1160 = 14,
|
||||
ChargeCurrent_MA1240 = 15,
|
||||
ChargeCurrent_MA1320 = 16
|
||||
} ChargeCurrent;
|
||||
|
||||
typedef enum _GpsOperation {
|
||||
GpsOperation_GpsOpUnset = 0,
|
||||
GpsOperation_GpsOpStationary = 1,
|
||||
GpsOperation_GpsOpMobile = 2,
|
||||
GpsOperation_GpsOpTimeOnly = 3,
|
||||
GpsOperation_GpsOpDisabled = 4
|
||||
} GpsOperation;
|
||||
|
||||
typedef enum _LocationSharing {
|
||||
LocationSharing_LocUnset = 0,
|
||||
LocationSharing_LocEnabled = 1,
|
||||
LocationSharing_LocDisabled = 2
|
||||
} LocationSharing;
|
||||
|
||||
typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType {
|
||||
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0
|
||||
} RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType;
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _RadioConfig_UserPreferences {
|
||||
uint32_t position_broadcast_secs;
|
||||
uint32_t send_owner_interval;
|
||||
uint32_t wait_bluetooth_secs;
|
||||
uint32_t screen_on_secs;
|
||||
uint32_t phone_timeout_secs;
|
||||
uint32_t phone_sds_timeout_sec;
|
||||
uint32_t mesh_sds_timeout_secs;
|
||||
uint32_t sds_secs;
|
||||
uint32_t ls_secs;
|
||||
uint32_t min_wake_secs;
|
||||
char wifi_ssid[33];
|
||||
char wifi_password[64];
|
||||
bool wifi_ap_mode;
|
||||
RegionCode region;
|
||||
ChargeCurrent charge_current;
|
||||
LocationSharing location_share;
|
||||
GpsOperation gps_operation;
|
||||
uint32_t gps_update_interval;
|
||||
uint32_t gps_attempt_time;
|
||||
bool is_router;
|
||||
bool is_low_power;
|
||||
bool fixed_position;
|
||||
bool factory_reset;
|
||||
bool debug_log_enabled;
|
||||
pb_size_t ignore_incoming_count;
|
||||
uint32_t ignore_incoming[3];
|
||||
bool serialplugin_enabled;
|
||||
bool serialplugin_echo;
|
||||
uint32_t serialplugin_rxd;
|
||||
uint32_t serialplugin_txd;
|
||||
uint32_t serialplugin_timeout;
|
||||
uint32_t serialplugin_mode;
|
||||
bool ext_notification_plugin_enabled;
|
||||
uint32_t ext_notification_plugin_output_ms;
|
||||
uint32_t ext_notification_plugin_output;
|
||||
bool ext_notification_plugin_active;
|
||||
bool ext_notification_plugin_alert_message;
|
||||
bool ext_notification_plugin_alert_bell;
|
||||
bool range_test_plugin_enabled;
|
||||
uint32_t range_test_plugin_sender;
|
||||
bool range_test_plugin_save;
|
||||
uint32_t store_forward_plugin_records;
|
||||
bool environmental_measurement_plugin_measurement_enabled;
|
||||
bool environmental_measurement_plugin_screen_enabled;
|
||||
uint32_t environmental_measurement_plugin_read_error_count_threshold;
|
||||
uint32_t environmental_measurement_plugin_update_interval;
|
||||
uint32_t environmental_measurement_plugin_recovery_interval;
|
||||
bool environmental_measurement_plugin_display_farenheit;
|
||||
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType environmental_measurement_plugin_sensor_type;
|
||||
uint32_t environmental_measurement_plugin_sensor_pin;
|
||||
bool store_forward_plugin_enabled;
|
||||
} RadioConfig_UserPreferences;
|
||||
|
||||
typedef struct _RadioConfig {
|
||||
bool has_preferences;
|
||||
RadioConfig_UserPreferences preferences;
|
||||
} RadioConfig;
|
||||
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _RegionCode_MIN RegionCode_Unset
|
||||
#define _RegionCode_MAX RegionCode_RU
|
||||
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_RU+1))
|
||||
|
||||
#define _ChargeCurrent_MIN ChargeCurrent_MAUnset
|
||||
#define _ChargeCurrent_MAX ChargeCurrent_MA1320
|
||||
#define _ChargeCurrent_ARRAYSIZE ((ChargeCurrent)(ChargeCurrent_MA1320+1))
|
||||
|
||||
#define _GpsOperation_MIN GpsOperation_GpsOpUnset
|
||||
#define _GpsOperation_MAX GpsOperation_GpsOpDisabled
|
||||
#define _GpsOperation_ARRAYSIZE ((GpsOperation)(GpsOperation_GpsOpDisabled+1))
|
||||
|
||||
#define _LocationSharing_MIN LocationSharing_LocUnset
|
||||
#define _LocationSharing_MAX LocationSharing_LocDisabled
|
||||
#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1))
|
||||
|
||||
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11
|
||||
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MAX RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11
|
||||
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_ARRAYSIZE ((RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType)(RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11+1))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default}
|
||||
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0}
|
||||
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero}
|
||||
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
|
||||
#define RadioConfig_UserPreferences_send_owner_interval_tag 2
|
||||
#define RadioConfig_UserPreferences_wait_bluetooth_secs_tag 4
|
||||
#define RadioConfig_UserPreferences_screen_on_secs_tag 5
|
||||
#define RadioConfig_UserPreferences_phone_timeout_secs_tag 6
|
||||
#define RadioConfig_UserPreferences_phone_sds_timeout_sec_tag 7
|
||||
#define RadioConfig_UserPreferences_mesh_sds_timeout_secs_tag 8
|
||||
#define RadioConfig_UserPreferences_sds_secs_tag 9
|
||||
#define RadioConfig_UserPreferences_ls_secs_tag 10
|
||||
#define RadioConfig_UserPreferences_min_wake_secs_tag 11
|
||||
#define RadioConfig_UserPreferences_wifi_ssid_tag 12
|
||||
#define RadioConfig_UserPreferences_wifi_password_tag 13
|
||||
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
|
||||
#define RadioConfig_UserPreferences_region_tag 15
|
||||
#define RadioConfig_UserPreferences_charge_current_tag 16
|
||||
#define RadioConfig_UserPreferences_location_share_tag 32
|
||||
#define RadioConfig_UserPreferences_gps_operation_tag 33
|
||||
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
|
||||
#define RadioConfig_UserPreferences_gps_attempt_time_tag 36
|
||||
#define RadioConfig_UserPreferences_is_router_tag 37
|
||||
#define RadioConfig_UserPreferences_is_low_power_tag 38
|
||||
#define RadioConfig_UserPreferences_fixed_position_tag 39
|
||||
#define RadioConfig_UserPreferences_factory_reset_tag 100
|
||||
#define RadioConfig_UserPreferences_debug_log_enabled_tag 101
|
||||
#define RadioConfig_UserPreferences_ignore_incoming_tag 103
|
||||
#define RadioConfig_UserPreferences_serialplugin_enabled_tag 120
|
||||
#define RadioConfig_UserPreferences_serialplugin_echo_tag 121
|
||||
#define RadioConfig_UserPreferences_serialplugin_rxd_tag 122
|
||||
#define RadioConfig_UserPreferences_serialplugin_txd_tag 123
|
||||
#define RadioConfig_UserPreferences_serialplugin_timeout_tag 124
|
||||
#define RadioConfig_UserPreferences_serialplugin_mode_tag 125
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_enabled_tag 126
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_output_ms_tag 127
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_output_tag 128
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_active_tag 129
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_alert_message_tag 130
|
||||
#define RadioConfig_UserPreferences_ext_notification_plugin_alert_bell_tag 131
|
||||
#define RadioConfig_UserPreferences_range_test_plugin_enabled_tag 132
|
||||
#define RadioConfig_UserPreferences_range_test_plugin_sender_tag 133
|
||||
#define RadioConfig_UserPreferences_range_test_plugin_save_tag 134
|
||||
#define RadioConfig_UserPreferences_store_forward_plugin_records_tag 137
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_measurement_enabled_tag 140
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_screen_enabled_tag 141
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_read_error_count_threshold_tag 142
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_update_interval_tag 143
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_recovery_interval_tag 144
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_display_farenheit_tag 145
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_type_tag 146
|
||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_pin_tag 147
|
||||
#define RadioConfig_UserPreferences_store_forward_plugin_enabled_tag 148
|
||||
#define RadioConfig_preferences_tag 1
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define RadioConfig_FIELDLIST(X, a) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, preferences, 1)
|
||||
#define RadioConfig_CALLBACK NULL
|
||||
#define RadioConfig_DEFAULT NULL
|
||||
#define RadioConfig_preferences_MSGTYPE RadioConfig_UserPreferences
|
||||
|
||||
#define RadioConfig_UserPreferences_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, position_broadcast_secs, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, send_owner_interval, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, wait_bluetooth_secs, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, screen_on_secs, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, phone_timeout_secs, 6) \
|
||||
X(a, STATIC, SINGULAR, UINT32, phone_sds_timeout_sec, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, mesh_sds_timeout_secs, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, sds_secs, 9) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ls_secs, 10) \
|
||||
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 11) \
|
||||
X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \
|
||||
X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \
|
||||
X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \
|
||||
X(a, STATIC, SINGULAR, UENUM, region, 15) \
|
||||
X(a, STATIC, SINGULAR, UENUM, charge_current, 16) \
|
||||
X(a, STATIC, SINGULAR, UENUM, location_share, 32) \
|
||||
X(a, STATIC, SINGULAR, UENUM, gps_operation, 33) \
|
||||
X(a, STATIC, SINGULAR, UINT32, gps_update_interval, 34) \
|
||||
X(a, STATIC, SINGULAR, UINT32, gps_attempt_time, 36) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_router, 37) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_low_power, 38) \
|
||||
X(a, STATIC, SINGULAR, BOOL, fixed_position, 39) \
|
||||
X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \
|
||||
X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \
|
||||
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
|
||||
X(a, STATIC, SINGULAR, BOOL, serialplugin_enabled, 120) \
|
||||
X(a, STATIC, SINGULAR, BOOL, serialplugin_echo, 121) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_rxd, 122) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_txd, 123) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_timeout, 124) \
|
||||
X(a, STATIC, SINGULAR, UINT32, serialplugin_mode, 125) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_enabled, 126) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ext_notification_plugin_output_ms, 127) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ext_notification_plugin_output, 128) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_active, 129) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_alert_message, 130) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_alert_bell, 131) \
|
||||
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_enabled, 132) \
|
||||
X(a, STATIC, SINGULAR, UINT32, range_test_plugin_sender, 133) \
|
||||
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_save, 134) \
|
||||
X(a, STATIC, SINGULAR, UINT32, store_forward_plugin_records, 137) \
|
||||
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_measurement_enabled, 140) \
|
||||
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_screen_enabled, 141) \
|
||||
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_read_error_count_threshold, 142) \
|
||||
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_update_interval, 143) \
|
||||
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_recovery_interval, 144) \
|
||||
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_display_farenheit, 145) \
|
||||
X(a, STATIC, SINGULAR, UENUM, environmental_measurement_plugin_sensor_type, 146) \
|
||||
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_sensor_pin, 147) \
|
||||
X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_enabled, 148)
|
||||
#define RadioConfig_UserPreferences_CALLBACK NULL
|
||||
#define RadioConfig_UserPreferences_DEFAULT NULL
|
||||
|
||||
extern const pb_msgdesc_t RadioConfig_msg;
|
||||
extern const pb_msgdesc_t RadioConfig_UserPreferences_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define RadioConfig_fields &RadioConfig_msg
|
||||
#define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define RadioConfig_size 348
|
||||
#define RadioConfig_UserPreferences_size 345
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <HTTPMultipartBodyParser.hpp>
|
||||
#include <HTTPURLEncodedBodyParser.hpp>
|
||||
#include <SPIFFS.h>
|
||||
#include "RadioLibInterface.h"
|
||||
|
||||
#ifndef NO_ESP32
|
||||
#include "esp_task_wdt.h"
|
||||
@@ -928,10 +929,17 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
||||
res->printf("\"has_battery\": %s,\n", BoolToString(powerStatus->getHasBattery()));
|
||||
res->printf("\"has_usb\": %s,\n", BoolToString(powerStatus->getHasUSB()));
|
||||
res->printf("\"is_charging\": %s\n", BoolToString(powerStatus->getIsCharging()));
|
||||
res->println("},");
|
||||
|
||||
res->println("\"radio\": {");
|
||||
res->printf("\"frequecy\": %f,\n", RadioLibInterface::instance->getFreq());
|
||||
res->printf("\"lora_channel\": %d\n", RadioLibInterface::instance->getChannelNum());
|
||||
res->println("}");
|
||||
|
||||
res->println("},");
|
||||
|
||||
|
||||
|
||||
res->println("\"status\": \"ok\"");
|
||||
res->println("}");
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc
|
||||
|
||||
pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize);
|
||||
if (!pb_encode(&stream, fields, src_struct)) {
|
||||
DEBUG_MSG("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
|
||||
assert(0); // FIXME - panic
|
||||
DEBUG_MSG("Panic: can't encode protobuf %s, did you make a field too large?\n", PB_GET_ERROR(&stream));
|
||||
assert(0); // If this asser fails it probably means you made a field too large for the max limits specified in mesh.options
|
||||
} else {
|
||||
return stream.bytes_written;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "mesh/generated/mesh.pb.h"
|
||||
#include "mesh/generated/deviceonly.pb.h"
|
||||
#include "mesh/generated/admin.pb.h"
|
||||
|
||||
// this file defines constants which come from mesh.options
|
||||
|
||||
@@ -16,6 +17,9 @@
|
||||
/// max number of nodes allowed in the mesh
|
||||
#define MAX_NUM_NODES (member_size(DeviceState, node_db) / member_size(DeviceState, node_db[0]))
|
||||
|
||||
/// Max number of channels allowed
|
||||
#define MAX_NUM_CHANNELS (member_size(ChannelFile, channels) / member_size(ChannelFile, channels[0]))
|
||||
|
||||
/// helper function for encoding a record as a protobuf, any failures to encode are fatal and we will panic
|
||||
/// returns the encoded packet size
|
||||
size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc_t *fields, const void *src_struct);
|
||||
|
||||
@@ -224,7 +224,8 @@ void NRF52Bluetooth::setup()
|
||||
|
||||
// Configure and Start the Device Information Service
|
||||
DEBUG_MSG("Configuring the Device Information Service\n");
|
||||
bledis.setManufacturer(HW_VENDOR);
|
||||
// FIXME, we should set a mfg string based on our HW_VENDOR enum
|
||||
// bledis.setManufacturer(HW_VENDOR);
|
||||
bledis.setModel(optstr(HW_VERSION));
|
||||
bledis.setFirmwareRev(optstr(APP_VERSION));
|
||||
bledis.begin();
|
||||
|
||||
@@ -6,30 +6,13 @@
|
||||
class NRF52CryptoEngine : public CryptoEngine
|
||||
{
|
||||
|
||||
/// How many bytes in our key
|
||||
uint8_t keySize = 0;
|
||||
const uint8_t *keyBytes;
|
||||
|
||||
|
||||
public:
|
||||
NRF52CryptoEngine() {}
|
||||
|
||||
~NRF52CryptoEngine() {}
|
||||
|
||||
/**
|
||||
* Set the key used for encrypt, decrypt.
|
||||
*
|
||||
* As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext.
|
||||
*
|
||||
* @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt)
|
||||
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
|
||||
* provided pointer)
|
||||
*/
|
||||
virtual void setKey(size_t numBytes, uint8_t *bytes)
|
||||
{
|
||||
keySize = numBytes;
|
||||
keyBytes = bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a packet
|
||||
*
|
||||
@@ -39,11 +22,11 @@ class NRF52CryptoEngine : public CryptoEngine
|
||||
{
|
||||
// DEBUG_MSG("NRF52 encrypt!\n");
|
||||
|
||||
if (keySize != 0) {
|
||||
if (key.length > 0) {
|
||||
ocrypto_aes_ctr_ctx ctx;
|
||||
|
||||
initNonce(fromNode, packetNum);
|
||||
ocrypto_aes_ctr_init(&ctx, keyBytes, keySize, nonce);
|
||||
ocrypto_aes_ctr_init(&ctx, key.bytes, key.length, nonce);
|
||||
|
||||
ocrypto_aes_ctr_encrypt(&ctx, bytes, bytes, numBytes);
|
||||
}
|
||||
@@ -53,11 +36,11 @@ class NRF52CryptoEngine : public CryptoEngine
|
||||
{
|
||||
// DEBUG_MSG("NRF52 decrypt!\n");
|
||||
|
||||
if (keySize != 0) {
|
||||
if (key.length > 0) {
|
||||
ocrypto_aes_ctr_ctx ctx;
|
||||
|
||||
initNonce(fromNode, packetNum);
|
||||
ocrypto_aes_ctr_init(&ctx, keyBytes, keySize, nonce);
|
||||
ocrypto_aes_ctr_init(&ctx, key.bytes, key.length, nonce);
|
||||
|
||||
ocrypto_aes_ctr_decrypt(&ctx, bytes, bytes, numBytes);
|
||||
}
|
||||
|
||||
@@ -82,8 +82,34 @@ extern "C" void HardFault_Impl(uint32_t stack[])
|
||||
// while (1) ;
|
||||
}
|
||||
|
||||
#ifndef INC_FREERTOS_H
|
||||
// This is a generic cortex M entrypoint that doesn't assume freertos
|
||||
|
||||
extern "C" void HardFault_Handler(void)
|
||||
{
|
||||
asm volatile(" mrs r0,msp\n"
|
||||
" b HardFault_Impl \n");
|
||||
}
|
||||
#else
|
||||
|
||||
/* The prototype shows it is a naked function - in effect this is just an
|
||||
assembly function. */
|
||||
extern "C" void HardFault_Handler( void ) __attribute__( ( naked ) );
|
||||
|
||||
/* The fault handler implementation calls a function called
|
||||
prvGetRegistersFromStack(). */
|
||||
extern "C" void HardFault_Handler(void)
|
||||
{
|
||||
__asm volatile
|
||||
(
|
||||
" tst lr, #4 \n"
|
||||
" ite eq \n"
|
||||
" mrseq r0, msp \n"
|
||||
" mrsne r0, psp \n"
|
||||
" ldr r1, [r0, #24] \n"
|
||||
" ldr r2, handler2_address_const \n"
|
||||
" bx r2 \n"
|
||||
" handler2_address_const: .word HardFault_Impl \n"
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "NRF52Bluetooth.h"
|
||||
#include "configuration.h"
|
||||
#include "error.h"
|
||||
#include "graphics/TFTDisplay.h"
|
||||
#include <SPI.h>
|
||||
#include <Wire.h>
|
||||
@@ -51,14 +52,14 @@ void getMacAddr(uint8_t *dmac)
|
||||
NRF52Bluetooth *nrf52Bluetooth;
|
||||
|
||||
static bool bleOn = false;
|
||||
static const bool enableBle = false; // Set to false for easier debugging
|
||||
static const bool useSoftDevice = false; // Set to false for easier debugging
|
||||
|
||||
void setBluetoothEnable(bool on)
|
||||
{
|
||||
if (on != bleOn) {
|
||||
if (on) {
|
||||
if (!nrf52Bluetooth) {
|
||||
if (!enableBle)
|
||||
if (!useSoftDevice)
|
||||
DEBUG_MSG("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n");
|
||||
else {
|
||||
nrf52Bluetooth = new NRF52Bluetooth();
|
||||
@@ -87,6 +88,49 @@ int printf(const char *fmt, ...)
|
||||
|
||||
#include "BQ25713.h"
|
||||
|
||||
void initBrownout()
|
||||
{
|
||||
auto vccthresh = POWER_POFCON_THRESHOLD_V28;
|
||||
auto vcchthresh = POWER_POFCON_THRESHOLDVDDH_V27;
|
||||
|
||||
if (useSoftDevice) {
|
||||
auto err_code = sd_power_pof_enable(POWER_POFCON_POF_Enabled);
|
||||
assert(err_code == NRF_SUCCESS);
|
||||
|
||||
err_code = sd_power_pof_threshold_set(vccthresh);
|
||||
assert(err_code == NRF_SUCCESS);
|
||||
}
|
||||
else {
|
||||
NRF_POWER->POFCON = POWER_POFCON_POF_Msk | (vccthresh << POWER_POFCON_THRESHOLD_Pos) | (vcchthresh << POWER_POFCON_THRESHOLDVDDH_Pos);
|
||||
}
|
||||
}
|
||||
|
||||
void checkSDEvents()
|
||||
{
|
||||
if (useSoftDevice) {
|
||||
uint32_t evt;
|
||||
while (NRF_ERROR_NOT_FOUND == sd_evt_get(&evt)) {
|
||||
switch (evt) {
|
||||
case NRF_EVT_POWER_FAILURE_WARNING:
|
||||
recordCriticalError(CriticalErrorCode_Brownout);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_MSG("Unexpected SDevt %d\n", evt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(NRF_POWER->EVENTS_POFWARN)
|
||||
recordCriticalError(CriticalErrorCode_Brownout);
|
||||
}
|
||||
}
|
||||
|
||||
void nrf52Loop()
|
||||
{
|
||||
checkSDEvents();
|
||||
}
|
||||
|
||||
void nrf52Setup()
|
||||
{
|
||||
|
||||
@@ -112,6 +156,8 @@ void nrf52Setup()
|
||||
// randomSeed(r);
|
||||
DEBUG_MSG("FIXME, call randomSeed\n");
|
||||
// ::printf("TESTING PRINTF\n");
|
||||
|
||||
initBrownout();
|
||||
}
|
||||
|
||||
void cpuDeepSleep(uint64_t msecToWake)
|
||||
@@ -128,7 +174,7 @@ void cpuDeepSleep(uint64_t msecToWake)
|
||||
// https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled
|
||||
|
||||
auto ok = sd_power_system_off();
|
||||
if(ok != NRF_SUCCESS) {
|
||||
if (ok != NRF_SUCCESS) {
|
||||
DEBUG_MSG("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!\n");
|
||||
NRF_POWER->SYSTEMOFF = 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
#include "AdminPlugin.h"
|
||||
#include "Channels.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
|
||||
AdminPlugin *adminPlugin;
|
||||
|
||||
void AdminPlugin::handleGetChannel(const MeshPacket &req, uint32_t channelIndex)
|
||||
{
|
||||
if (req.decoded.want_response) {
|
||||
// We create the reply here
|
||||
AdminMessage r = AdminMessage_init_default;
|
||||
r.get_channel_response = channels.getByIndex(channelIndex);
|
||||
r.which_variant = AdminMessage_get_channel_response_tag;
|
||||
reply = allocDataProtobuf(r);
|
||||
}
|
||||
}
|
||||
|
||||
void AdminPlugin::handleGetRadio(const MeshPacket &req)
|
||||
{
|
||||
if (req.decoded.want_response) {
|
||||
// We create the reply here
|
||||
AdminMessage r = AdminMessage_init_default;
|
||||
r.get_radio_response = radioConfig;
|
||||
|
||||
// NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior.
|
||||
// So even if we internally use 0 to represent 'use default' we still need to send the value we are
|
||||
// using to the app (so that even old phone apps work with new device loads).
|
||||
r.get_radio_response.preferences.ls_secs = getPref_ls_secs();
|
||||
|
||||
r.which_variant = AdminMessage_get_radio_response_tag;
|
||||
reply = allocDataProtobuf(r);
|
||||
}
|
||||
}
|
||||
|
||||
bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, const AdminMessage *r)
|
||||
{
|
||||
assert(r);
|
||||
switch (r->which_variant) {
|
||||
case AdminMessage_set_owner_tag:
|
||||
DEBUG_MSG("Client is setting owner\n");
|
||||
handleSetOwner(r->set_owner);
|
||||
break;
|
||||
|
||||
case AdminMessage_set_radio_tag:
|
||||
DEBUG_MSG("Client is setting radio\n");
|
||||
handleSetRadio(r->set_radio);
|
||||
break;
|
||||
|
||||
case AdminMessage_set_channel_tag:
|
||||
DEBUG_MSG("Client is setting channel\n");
|
||||
handleSetChannel(r->set_channel);
|
||||
break;
|
||||
|
||||
case AdminMessage_get_channel_request_tag:
|
||||
DEBUG_MSG("Client is getting channel %d\n", r->get_channel_request - 1);
|
||||
handleGetChannel(mp, r->get_channel_request - 1);
|
||||
break;
|
||||
|
||||
case AdminMessage_get_radio_request_tag:
|
||||
DEBUG_MSG("Client is getting radio\n");
|
||||
handleGetRadio(mp);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Probably a message sent by us or sent to our local node. FIXME, we should avoid scanning these messages
|
||||
DEBUG_MSG("Ignoring nonrelevant admin %d\n", r->which_variant);
|
||||
break;
|
||||
}
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
void AdminPlugin::handleSetOwner(const User &o)
|
||||
{
|
||||
int changed = 0;
|
||||
|
||||
if (*o.long_name) {
|
||||
changed |= strcmp(owner.long_name, o.long_name);
|
||||
strcpy(owner.long_name, o.long_name);
|
||||
}
|
||||
if (*o.short_name) {
|
||||
changed |= strcmp(owner.short_name, o.short_name);
|
||||
strcpy(owner.short_name, o.short_name);
|
||||
}
|
||||
if (*o.id) {
|
||||
changed |= strcmp(owner.id, o.id);
|
||||
strcpy(owner.id, o.id);
|
||||
}
|
||||
|
||||
if (changed) // If nothing really changed, don't broadcast on the network or write to flash
|
||||
service.reloadOwner();
|
||||
}
|
||||
|
||||
void AdminPlugin::handleSetChannel(const Channel &cc)
|
||||
{
|
||||
channels.setChannel(cc);
|
||||
|
||||
// Just update and save the channels - no need to update the radio for ! primary channel changes
|
||||
if (cc.index == 0) {
|
||||
// FIXME, this updates the user preferences also, which isn't needed - we really just want to notify on configChanged
|
||||
service.reloadConfig();
|
||||
}
|
||||
else {
|
||||
channels.onConfigChanged(); // tell the radios about this change
|
||||
nodeDB.saveChannelsToDisk();
|
||||
}
|
||||
}
|
||||
|
||||
void AdminPlugin::handleSetRadio(const RadioConfig &r)
|
||||
{
|
||||
radioConfig = r;
|
||||
|
||||
service.reloadConfig();
|
||||
}
|
||||
|
||||
MeshPacket *AdminPlugin::allocReply()
|
||||
{
|
||||
auto r = reply;
|
||||
reply = NULL; // Only use each reply once
|
||||
return r;
|
||||
}
|
||||
|
||||
AdminPlugin::AdminPlugin() : ProtobufPlugin("Admin", PortNum_ADMIN_APP, AdminMessage_fields)
|
||||
{
|
||||
// restrict to the admin channel for rx
|
||||
boundChannel = Channels::adminChannel;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "ProtobufPlugin.h"
|
||||
|
||||
/**
|
||||
* Routing plugin for router control messages
|
||||
*/
|
||||
class AdminPlugin : public ProtobufPlugin<AdminMessage>
|
||||
{
|
||||
MeshPacket *reply = NULL;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
* name is for debugging output
|
||||
*/
|
||||
AdminPlugin();
|
||||
|
||||
protected:
|
||||
/** Called to handle a particular incoming message
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const AdminMessage *p);
|
||||
|
||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||
virtual MeshPacket *allocReply();
|
||||
|
||||
private:
|
||||
void handleSetOwner(const User &o);
|
||||
void handleSetChannel(const Channel &cc);
|
||||
void handleSetRadio(const RadioConfig &r);
|
||||
|
||||
void handleGetChannel(const MeshPacket &req, uint32_t channelIndex);
|
||||
void handleGetRadio(const MeshPacket &req);
|
||||
};
|
||||
|
||||
extern AdminPlugin *adminPlugin;
|
||||
@@ -6,12 +6,12 @@
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <assert.h>
|
||||
//#include <assert.h>
|
||||
|
||||
/*
|
||||
|
||||
Documentation:
|
||||
https://github.com/mc-hamster/Meshtastic-device/blob/master/docs/software/plugins/ExternalNotificationPlugin.md
|
||||
https://github.com/meshtastic/Meshtastic-device/blob/master/docs/software/plugins/ExternalNotificationPlugin.md
|
||||
|
||||
This plugin supports:
|
||||
https://github.com/meshtastic/Meshtastic-device/issues/654
|
||||
@@ -43,25 +43,17 @@
|
||||
|
||||
*/
|
||||
|
||||
|
||||
// Default configurations
|
||||
#define EXT_NOTIFICATION_PLUGIN_OUTPUT 13
|
||||
#define EXT_NOTIFICATION_PLUGIN_OUTPUT_MS 1000
|
||||
|
||||
#define ASCII_BELL 0x07
|
||||
|
||||
ExternalNotificationPlugin *externalNotificationPlugin;
|
||||
ExternalNotificationPluginRadio *externalNotificationPluginRadio;
|
||||
|
||||
ExternalNotificationPlugin::ExternalNotificationPlugin() : concurrency::OSThread("ExternalNotificationPlugin") {}
|
||||
|
||||
bool externalCurrentState = 0;
|
||||
uint32_t externalTurnedOn = 0;
|
||||
|
||||
int32_t ExternalNotificationPlugin::runOnce()
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
|
||||
/*
|
||||
Uncomment the preferences below if you want to use the plugin
|
||||
without having to configure it from the PythonAPI or WebUI.
|
||||
@@ -75,47 +67,19 @@ int32_t ExternalNotificationPlugin::runOnce()
|
||||
// radioConfig.preferences.ext_notification_plugin_output_ms = 1000;
|
||||
// radioConfig.preferences.ext_notification_plugin_output = 13;
|
||||
|
||||
if (radioConfig.preferences.ext_notification_plugin_enabled) {
|
||||
if (externalCurrentState) {
|
||||
|
||||
if (firstTime) {
|
||||
|
||||
DEBUG_MSG("Initializing External Notification Plugin\n");
|
||||
|
||||
// Set the direction of a pin
|
||||
pinMode((radioConfig.preferences.ext_notification_plugin_output
|
||||
? radioConfig.preferences.ext_notification_plugin_output
|
||||
: EXT_NOTIFICATION_PLUGIN_OUTPUT),
|
||||
OUTPUT);
|
||||
|
||||
// Turn off the pin
|
||||
// If the output is turned on, turn it back off after the given period of time.
|
||||
if (externalTurnedOn + (radioConfig.preferences.ext_notification_plugin_output_ms
|
||||
? radioConfig.preferences.ext_notification_plugin_output_ms
|
||||
: EXT_NOTIFICATION_PLUGIN_OUTPUT_MS) <
|
||||
millis()) {
|
||||
DEBUG_MSG("Turning off external notification\n");
|
||||
setExternalOff();
|
||||
|
||||
externalNotificationPluginRadio = new ExternalNotificationPluginRadio();
|
||||
|
||||
firstTime = 0;
|
||||
|
||||
} else {
|
||||
if (externalCurrentState) {
|
||||
|
||||
// If the output is turned on, turn it back off after the given period of time.
|
||||
if (externalTurnedOn + (radioConfig.preferences.ext_notification_plugin_output_ms
|
||||
? radioConfig.preferences.ext_notification_plugin_output_ms
|
||||
: EXT_NOTIFICATION_PLUGIN_OUTPUT_MS) <
|
||||
millis()) {
|
||||
DEBUG_MSG("Turning off external notification\n");
|
||||
setExternalOff();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (25);
|
||||
} else {
|
||||
DEBUG_MSG("External Notification Plugin Disabled\n");
|
||||
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
#endif
|
||||
return (25);
|
||||
}
|
||||
|
||||
void ExternalNotificationPlugin::setExternalOn()
|
||||
@@ -139,15 +103,55 @@ void ExternalNotificationPlugin::setExternalOff()
|
||||
|
||||
// --------
|
||||
|
||||
bool ExternalNotificationPluginRadio::handleReceived(const MeshPacket &mp)
|
||||
ExternalNotificationPlugin::ExternalNotificationPlugin()
|
||||
: SinglePortPlugin("ExternalNotificationPlugin", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread(
|
||||
"ExternalNotificationPlugin")
|
||||
{
|
||||
// restrict to the admin channel for rx
|
||||
boundChannel = Channels::gpioChannel;
|
||||
|
||||
#ifndef NO_ESP32
|
||||
|
||||
/*
|
||||
Uncomment the preferences below if you want to use the plugin
|
||||
without having to configure it from the PythonAPI or WebUI.
|
||||
*/
|
||||
|
||||
// radioConfig.preferences.ext_notification_plugin_enabled = 1;
|
||||
// radioConfig.preferences.ext_notification_plugin_alert_message = 1;
|
||||
|
||||
// radioConfig.preferences.ext_notification_plugin_active = 1;
|
||||
// radioConfig.preferences.ext_notification_plugin_alert_bell = 1;
|
||||
// radioConfig.preferences.ext_notification_plugin_output_ms = 1000;
|
||||
// radioConfig.preferences.ext_notification_plugin_output = 13;
|
||||
|
||||
if (radioConfig.preferences.ext_notification_plugin_enabled) {
|
||||
|
||||
DEBUG_MSG("Initializing External Notification Plugin\n");
|
||||
|
||||
// Set the direction of a pin
|
||||
pinMode((radioConfig.preferences.ext_notification_plugin_output ? radioConfig.preferences.ext_notification_plugin_output
|
||||
: EXT_NOTIFICATION_PLUGIN_OUTPUT),
|
||||
OUTPUT);
|
||||
|
||||
// Turn off the pin
|
||||
setExternalOff();
|
||||
} else {
|
||||
DEBUG_MSG("External Notification Plugin Disabled\n");
|
||||
enabled = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ExternalNotificationPlugin::handleReceived(const MeshPacket &mp)
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
|
||||
if (radioConfig.preferences.ext_notification_plugin_enabled) {
|
||||
|
||||
auto &p = mp.decoded.data;
|
||||
auto &p = mp.decoded;
|
||||
|
||||
if (mp.from != nodeDB.getNodeNum()) {
|
||||
if (getFrom(&mp) != nodeDB.getNodeNum()) {
|
||||
|
||||
// TODO: This may be a problem if messages are sent in unicide, but I'm not sure if it will.
|
||||
// Need to know if and how this could be a problem.
|
||||
@@ -155,14 +159,14 @@ bool ExternalNotificationPluginRadio::handleReceived(const MeshPacket &mp)
|
||||
DEBUG_MSG("externalNotificationPlugin - Notification Bell\n");
|
||||
for (int i = 0; i < p.payload.size; i++) {
|
||||
if (p.payload.bytes[i] == ASCII_BELL) {
|
||||
externalNotificationPlugin->setExternalOn();
|
||||
setExternalOn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (radioConfig.preferences.ext_notification_plugin_alert_message) {
|
||||
DEBUG_MSG("externalNotificationPlugin - Notification Plugin\n");
|
||||
externalNotificationPlugin->setExternalOn();
|
||||
setExternalOn();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
|
||||
class ExternalNotificationPlugin : private concurrency::OSThread
|
||||
/*
|
||||
* Radio interface for ExternalNotificationPlugin
|
||||
*
|
||||
*/
|
||||
class ExternalNotificationPlugin : public SinglePortPlugin, private concurrency::OSThread
|
||||
{
|
||||
bool firstTime = 1;
|
||||
|
||||
public:
|
||||
ExternalNotificationPlugin();
|
||||
|
||||
@@ -19,29 +20,13 @@ class ExternalNotificationPlugin : private concurrency::OSThread
|
||||
void getExternal();
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern ExternalNotificationPlugin *externalNotificationPlugin;
|
||||
|
||||
/*
|
||||
* Radio interface for ExternalNotificationPlugin
|
||||
*
|
||||
*/
|
||||
class ExternalNotificationPluginRadio : public SinglePortPlugin
|
||||
{
|
||||
|
||||
public:
|
||||
ExternalNotificationPluginRadio() : SinglePortPlugin("ExternalNotificationPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
|
||||
|
||||
protected:
|
||||
//virtual MeshPacket *allocReply();
|
||||
// virtual MeshPacket *allocReply();
|
||||
|
||||
/** Called to handle a particular incoming message
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceived(const MeshPacket &mp);
|
||||
};
|
||||
|
||||
extern ExternalNotificationPluginRadio *externalNotificationPluginRadio;
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
@@ -8,29 +8,36 @@
|
||||
|
||||
NodeInfoPlugin *nodeInfoPlugin;
|
||||
|
||||
bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User &p)
|
||||
bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User *pptr)
|
||||
{
|
||||
// FIXME - we currently update NodeInfo data in the DB only if the message was a broadcast or destined to us
|
||||
// it would be better to update even if the message was destined to others.
|
||||
auto p = *pptr;
|
||||
|
||||
nodeDB.updateUser(mp.from, p);
|
||||
nodeDB.updateUser(getFrom(&mp), p);
|
||||
|
||||
bool wasBroadcast = mp.to == NODENUM_BROADCAST;
|
||||
|
||||
// Show new nodes on LCD screen
|
||||
if (wasBroadcast) {
|
||||
String lcd = String("Joined: ") + p.long_name + "\n";
|
||||
screen->print(lcd.c_str());
|
||||
if(screen)
|
||||
screen->print(lcd.c_str());
|
||||
}
|
||||
|
||||
// DEBUG_MSG("did handleReceived\n");
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
// cancel any not yet sent (now stale) position packets
|
||||
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
|
||||
service.cancelSending(prevPacketId);
|
||||
|
||||
MeshPacket *p = allocReply();
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
p->priority = MeshPacket_Priority_BACKGROUND;
|
||||
prevPacketId = p->id;
|
||||
|
||||
service.sendToMesh(p);
|
||||
}
|
||||
@@ -42,3 +49,26 @@ MeshPacket *NodeInfoPlugin::allocReply()
|
||||
DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
|
||||
return allocDataProtobuf(u);
|
||||
}
|
||||
|
||||
NodeInfoPlugin::NodeInfoPlugin()
|
||||
: ProtobufPlugin("nodeinfo", PortNum_NODEINFO_APP, User_fields), concurrency::OSThread("NodeInfoPlugin")
|
||||
{
|
||||
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
|
||||
setIntervalFromNow(30 *
|
||||
1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup)
|
||||
}
|
||||
|
||||
int32_t NodeInfoPlugin::runOnce()
|
||||
{
|
||||
static uint32_t currentGeneration;
|
||||
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
|
||||
sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
|
||||
|
||||
return getPref_position_broadcast_secs() * 1000;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,18 @@
|
||||
/**
|
||||
* NodeInfo plugin for sending/receiving NodeInfos into the mesh
|
||||
*/
|
||||
class NodeInfoPlugin : public ProtobufPlugin<User>
|
||||
class NodeInfoPlugin : public ProtobufPlugin<User>, private concurrency::OSThread
|
||||
{
|
||||
/// The id of the last packet we sent, to allow us to cancel it if we make something fresher
|
||||
PacketId prevPacketId = 0;
|
||||
|
||||
uint32_t currentGeneration = 0;
|
||||
public:
|
||||
/** Constructor
|
||||
* name is for debugging output
|
||||
*/
|
||||
NodeInfoPlugin() : ProtobufPlugin("nodeinfo", PortNum_NODEINFO_APP, User_fields) {}
|
||||
|
||||
NodeInfoPlugin();
|
||||
|
||||
/**
|
||||
* Send our NodeInfo into the mesh
|
||||
*/
|
||||
@@ -22,11 +26,14 @@ class NodeInfoPlugin : public ProtobufPlugin<User>
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const User &p);
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const User *p);
|
||||
|
||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||
virtual MeshPacket *allocReply();
|
||||
|
||||
/** Does our periodic broadcast */
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern NodeInfoPlugin *nodeInfoPlugin;
|
||||
+25
-2
@@ -3,14 +3,24 @@
|
||||
#include "plugins/PositionPlugin.h"
|
||||
#include "plugins/RemoteHardwarePlugin.h"
|
||||
#include "plugins/ReplyPlugin.h"
|
||||
#include "plugins/TextMessagePlugin.h"
|
||||
#include "plugins/SerialPlugin.h"
|
||||
#include "plugins/TextMessagePlugin.h"
|
||||
#include "plugins/RoutingPlugin.h"
|
||||
#include "plugins/AdminPlugin.h"
|
||||
#ifndef NO_ESP32
|
||||
#include "plugins/SerialPlugin.h"
|
||||
#include "plugins/esp32/EnvironmentalMeasurementPlugin.h"
|
||||
#include "plugins/esp32/RangeTestPlugin.h"
|
||||
#include "plugins/esp32/StoreForwardPlugin.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Create plugin instances here. If you are adding a new plugin, you must 'new' it here (or somewhere else)
|
||||
*/
|
||||
void setupPlugins()
|
||||
{
|
||||
adminPlugin = new AdminPlugin();
|
||||
nodeInfoPlugin = new NodeInfoPlugin();
|
||||
positionPlugin = new PositionPlugin();
|
||||
textMessagePlugin = new TextMessagePlugin();
|
||||
@@ -24,7 +34,20 @@ void setupPlugins()
|
||||
#ifndef NO_ESP32
|
||||
// Only run on an esp32 based device.
|
||||
|
||||
new SerialPlugin(); // Maintained by MC Hamster (Jm Casler) jm@casler.org
|
||||
new ExternalNotificationPlugin(); // Maintained by MC Hamster (Jm Casler) jm@casler.org
|
||||
/*
|
||||
Maintained by MC Hamster (Jm Casler) jm@casler.org
|
||||
*/
|
||||
new SerialPlugin();
|
||||
new ExternalNotificationPlugin();
|
||||
|
||||
// rangeTestPlugin = new RangeTestPlugin();
|
||||
storeForwardPlugin = new StoreForwardPlugin();
|
||||
|
||||
new RangeTestPlugin();
|
||||
// new StoreForwardPlugin();
|
||||
new EnvironmentalMeasurementPlugin();
|
||||
#endif
|
||||
|
||||
// NOTE! This plugin must be added LAST because it likes to check for replies from other plugins and avoid sending extra acks
|
||||
routingPlugin = new RoutingPlugin();
|
||||
}
|
||||
@@ -7,10 +7,18 @@
|
||||
|
||||
PositionPlugin *positionPlugin;
|
||||
|
||||
bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position &p)
|
||||
PositionPlugin::PositionPlugin()
|
||||
: ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields), concurrency::OSThread("PositionPlugin")
|
||||
{
|
||||
// FIXME - we currently update position data in the DB only if the message was a broadcast or destined to us
|
||||
// it would be better to update even if the message was destined to others.
|
||||
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
|
||||
setIntervalFromNow(60 *
|
||||
1000); // Send our initial position 60 seconds after we start (to give GPS time to setup)
|
||||
|
||||
}
|
||||
|
||||
bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position *pptr)
|
||||
{
|
||||
auto p = *pptr;
|
||||
|
||||
if (p.time) {
|
||||
struct timeval tv;
|
||||
@@ -22,7 +30,7 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position
|
||||
perhapsSetRTC(RTCQualityFromNet, &tv);
|
||||
}
|
||||
|
||||
nodeDB.updatePosition(mp.from, p);
|
||||
nodeDB.updatePosition(getFrom(&mp), p);
|
||||
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
@@ -31,16 +39,51 @@ MeshPacket *PositionPlugin::allocReply()
|
||||
{
|
||||
NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position
|
||||
assert(node->has_position);
|
||||
|
||||
return allocDataProtobuf(node->position);
|
||||
|
||||
Position p = node->position;
|
||||
|
||||
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
|
||||
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
|
||||
// devices can get time.
|
||||
if (getRTCQuality() < RTCQualityGPS) {
|
||||
DEBUG_MSG("Stripping time %u from position send\n", p.time);
|
||||
p.time = 0;
|
||||
} else
|
||||
DEBUG_MSG("Providing time to mesh %u\n", p.time);
|
||||
|
||||
return allocDataProtobuf(p);
|
||||
}
|
||||
|
||||
void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
// cancel any not yet sent (now stale) position packets
|
||||
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
|
||||
service.cancelSending(prevPacketId);
|
||||
|
||||
MeshPacket *p = allocReply();
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
p->priority = MeshPacket_Priority_BACKGROUND;
|
||||
prevPacketId = p->id;
|
||||
|
||||
service.sendToMesh(p);
|
||||
}
|
||||
|
||||
int32_t PositionPlugin::runOnce()
|
||||
{
|
||||
|
||||
// We limit our GPS broadcasts to a max rate
|
||||
uint32_t now = millis();
|
||||
if (lastGpsSend == 0 || now - lastGpsSend >= getPref_position_broadcast_secs() * 1000) {
|
||||
lastGpsSend = now;
|
||||
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies);
|
||||
sendOurPosition(NODENUM_BROADCAST, requestReplies);
|
||||
}
|
||||
|
||||
return 5000; // to save power only wake for our callback occasionally
|
||||
}
|
||||
@@ -1,17 +1,27 @@
|
||||
#pragma once
|
||||
#include "ProtobufPlugin.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
|
||||
/**
|
||||
* Position plugin for sending/receiving positions into the mesh
|
||||
*/
|
||||
class PositionPlugin : public ProtobufPlugin<Position>
|
||||
class PositionPlugin : public ProtobufPlugin<Position>, private concurrency::OSThread
|
||||
{
|
||||
/// The id of the last packet we sent, to allow us to cancel it if we make something fresher
|
||||
PacketId prevPacketId = 0;
|
||||
|
||||
/// We limit our GPS broadcasts to a max rate
|
||||
uint32_t lastGpsSend = 0;
|
||||
|
||||
/// We force a rebroadcast if the radio settings change
|
||||
uint32_t currentGeneration = 0;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
* name is for debugging output
|
||||
*/
|
||||
PositionPlugin() : ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields) {}
|
||||
|
||||
PositionPlugin();
|
||||
|
||||
/**
|
||||
* Send our position into the mesh
|
||||
*/
|
||||
@@ -23,11 +33,14 @@ class PositionPlugin : public ProtobufPlugin<Position>
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const Position &p);
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const Position *p);
|
||||
|
||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||
virtual MeshPacket *allocReply();
|
||||
|
||||
/** Does our periodic broadcast */
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern PositionPlugin *positionPlugin;
|
||||
@@ -47,8 +47,9 @@ RemoteHardwarePlugin::RemoteHardwarePlugin()
|
||||
{
|
||||
}
|
||||
|
||||
bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const HardwareMessage &p)
|
||||
bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const HardwareMessage *pptr)
|
||||
{
|
||||
auto p = *pptr;
|
||||
DEBUG_MSG("Received RemoteHardware typ=%d\n", p.typ);
|
||||
|
||||
switch (p.typ) {
|
||||
@@ -68,7 +69,8 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H
|
||||
|
||||
case HardwareMessage_Type_READ_GPIOS: {
|
||||
// Print notification to LCD screen
|
||||
screen->print("Read GPIOs\n");
|
||||
if(screen)
|
||||
screen->print("Read GPIOs\n");
|
||||
|
||||
uint64_t res = digitalReads(p.gpio_mask);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class RemoteHardwarePlugin : public ProtobufPlugin<HardwareMessage>, private con
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const HardwareMessage &p);
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const HardwareMessage *p);
|
||||
|
||||
/**
|
||||
* Periodically read the gpios we have been asked to WATCH, if they have changed,
|
||||
|
||||
@@ -9,7 +9,7 @@ MeshPacket *ReplyPlugin::allocReply()
|
||||
{
|
||||
assert(currentRequest); // should always be !NULL
|
||||
auto req = *currentRequest;
|
||||
auto &p = req.decoded.data;
|
||||
auto &p = req.decoded;
|
||||
// The incoming message is in p.payload
|
||||
DEBUG_MSG("Received message from=0x%0x, id=%d, msg=%.*s\n", req.from, req.id, p.payload.size, p.payload.bytes);
|
||||
|
||||
@@ -17,8 +17,8 @@ MeshPacket *ReplyPlugin::allocReply()
|
||||
|
||||
const char *replyStr = "Message Received";
|
||||
auto reply = allocDataPacket(); // Allocate a packet for sending
|
||||
reply->decoded.data.payload.size = strlen(replyStr); // You must specify how many bytes are in the reply
|
||||
memcpy(reply->decoded.data.payload.bytes, replyStr, reply->decoded.data.payload.size);
|
||||
reply->decoded.payload.size = strlen(replyStr); // You must specify how many bytes are in the reply
|
||||
memcpy(reply->decoded.payload.bytes, replyStr, reply->decoded.payload.size);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "RoutingPlugin.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
|
||||
RoutingPlugin *routingPlugin;
|
||||
|
||||
bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Routing *r)
|
||||
{
|
||||
printPacket("Routing sniffing", &mp);
|
||||
router->sniffReceived(&mp, r);
|
||||
|
||||
// FIXME - move this to a non promsicious PhoneAPI plugin?
|
||||
// Note: we are careful not to send back packets that started with the phone back to the phone
|
||||
if ((mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) && (mp.from != 0)) {
|
||||
printPacket("Delivering rx packet", &mp);
|
||||
service.handleFromRadio(&mp);
|
||||
}
|
||||
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
|
||||
MeshPacket *RoutingPlugin::allocReply()
|
||||
{
|
||||
assert(currentRequest);
|
||||
|
||||
// We only consider making replies if the request was a legit routing packet (not just something we were sniffing)
|
||||
if(currentRequest->decoded.portnum == PortNum_ROUTING_APP) {
|
||||
assert(0); // 1.2 refactoring fixme, Not sure if anything needs this yet?
|
||||
// return allocDataProtobuf(u);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void RoutingPlugin::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
|
||||
{
|
||||
Routing c = Routing_init_default;
|
||||
|
||||
c.error_reason = err;
|
||||
|
||||
auto p = allocDataProtobuf(c);
|
||||
p->priority = MeshPacket_Priority_ACK;
|
||||
|
||||
p->hop_limit = 0; // Assume just immediate neighbors for now
|
||||
p->to = to;
|
||||
p->decoded.request_id = idFrom;
|
||||
p->channel = chIndex;
|
||||
DEBUG_MSG("Sending an err=%d,to=0x%x,idFrom=0x%x,id=0x%x\n", err, to, idFrom, p->id);
|
||||
|
||||
router->sendLocal(p); // we sometimes send directly to the local node
|
||||
}
|
||||
|
||||
RoutingPlugin::RoutingPlugin()
|
||||
: ProtobufPlugin("routing", PortNum_ROUTING_APP, Routing_fields)
|
||||
{
|
||||
isPromiscuous = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "ProtobufPlugin.h"
|
||||
#include "Channels.h"
|
||||
|
||||
/**
|
||||
* Routing plugin for router control messages
|
||||
*/
|
||||
class RoutingPlugin : public ProtobufPlugin<Routing>
|
||||
{
|
||||
public:
|
||||
/** Constructor
|
||||
* name is for debugging output
|
||||
*/
|
||||
RoutingPlugin();
|
||||
|
||||
void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
|
||||
|
||||
protected:
|
||||
friend class Router;
|
||||
|
||||
/** Called to handle a particular incoming message
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const Routing *p);
|
||||
|
||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||
virtual MeshPacket *allocReply();
|
||||
|
||||
/// Override wantPacket to say we want to see all packets, not just those for our port number
|
||||
virtual bool wantPacket(const MeshPacket *p) { return true; }
|
||||
};
|
||||
|
||||
extern RoutingPlugin *routingPlugin;
|
||||
@@ -61,6 +61,12 @@ SerialPlugin::SerialPlugin() : concurrency::OSThread("SerialPlugin") {}
|
||||
|
||||
char serialStringChar[Constants_DATA_PAYLOAD_LEN];
|
||||
|
||||
SerialPluginRadio::SerialPluginRadio() : SinglePortPlugin("SerialPluginRadio", PortNum_SERIAL_APP)
|
||||
{
|
||||
// restrict to the admin channel for rx
|
||||
boundChannel = Channels::serialChannel;
|
||||
}
|
||||
|
||||
int32_t SerialPlugin::runOnce()
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
@@ -124,7 +130,8 @@ int32_t SerialPlugin::runOnce()
|
||||
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
#else
|
||||
return INT32_MAX;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -144,8 +151,8 @@ void SerialPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
|
||||
|
||||
p->want_ack = SERIALPLUGIN_ACK;
|
||||
|
||||
p->decoded.data.payload.size = strlen(serialStringChar); // You must specify how many bytes are in the reply
|
||||
memcpy(p->decoded.data.payload.bytes, serialStringChar, p->decoded.data.payload.size);
|
||||
p->decoded.payload.size = strlen(serialStringChar); // You must specify how many bytes are in the reply
|
||||
memcpy(p->decoded.payload.bytes, serialStringChar, p->decoded.payload.size);
|
||||
|
||||
service.sendToMesh(p);
|
||||
}
|
||||
@@ -156,11 +163,11 @@ bool SerialPluginRadio::handleReceived(const MeshPacket &mp)
|
||||
|
||||
if (radioConfig.preferences.serialplugin_enabled) {
|
||||
|
||||
auto &p = mp.decoded.data;
|
||||
auto &p = mp.decoded;
|
||||
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
|
||||
// nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
|
||||
|
||||
if (mp.from == nodeDB.getNodeNum()) {
|
||||
if (getFrom(&mp) == nodeDB.getNodeNum()) {
|
||||
|
||||
/*
|
||||
* If radioConfig.preferences.serialplugin_echo is true, then echo the packets that are sent out back to the TX
|
||||
|
||||
@@ -33,8 +33,7 @@ class SerialPluginRadio : public SinglePortPlugin
|
||||
from the main code.
|
||||
*/
|
||||
|
||||
// SerialPluginRadio() : SinglePortPlugin("SerialPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
|
||||
SerialPluginRadio() : SinglePortPlugin("SerialPluginRadio", PortNum_SERIAL_APP) {}
|
||||
SerialPluginRadio();
|
||||
|
||||
/**
|
||||
* Send our payload into the mesh
|
||||
|
||||
@@ -7,8 +7,8 @@ TextMessagePlugin *textMessagePlugin;
|
||||
|
||||
bool TextMessagePlugin::handleReceived(const MeshPacket &mp)
|
||||
{
|
||||
auto &p = mp.decoded.data;
|
||||
DEBUG_MSG("Received text msg from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);
|
||||
auto &p = mp.decoded;
|
||||
DEBUG_MSG("Received text msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);
|
||||
|
||||
// We only store/display messages destined for us.
|
||||
// Keep a copy of the most recent text message.
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
#include "EnvironmentalMeasurementPlugin.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "../mesh/generated/environmental_measurement.pb.h"
|
||||
#include <DHT.h>
|
||||
#include <OLEDDisplay.h>
|
||||
#include <OLEDDisplayUi.h>
|
||||
|
||||
#define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 // Some sensors (the DHT11) have a minimum required duration between read attempts
|
||||
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
|
||||
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
|
||||
|
||||
|
||||
#ifdef HAS_EINK
|
||||
// The screen is bigger so use bigger fonts
|
||||
#define FONT_SMALL ArialMT_Plain_16
|
||||
#define FONT_MEDIUM ArialMT_Plain_24
|
||||
#define FONT_LARGE ArialMT_Plain_24
|
||||
#else
|
||||
#define FONT_SMALL ArialMT_Plain_10
|
||||
#define FONT_MEDIUM ArialMT_Plain_16
|
||||
#define FONT_LARGE ArialMT_Plain_24
|
||||
#endif
|
||||
|
||||
#define fontHeight(font) ((font)[1] + 1) // height is position 1
|
||||
|
||||
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
|
||||
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
|
||||
|
||||
|
||||
int32_t EnvironmentalMeasurementPlugin::runOnce() {
|
||||
#ifndef NO_ESP32 // this only works on ESP32 devices
|
||||
|
||||
/*
|
||||
Uncomment the preferences below if you want to use the plugin
|
||||
without having to configure it from the PythonAPI or WebUI.
|
||||
*/
|
||||
|
||||
/*radioConfig.preferences.environmental_measurement_plugin_measurement_enabled = 1;
|
||||
radioConfig.preferences.environmental_measurement_plugin_screen_enabled = 1;
|
||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold = 5;
|
||||
radioConfig.preferences.environmental_measurement_plugin_update_interval = 30;
|
||||
radioConfig.preferences.environmental_measurement_plugin_recovery_interval = 60;
|
||||
radioConfig.preferences.environmental_measurement_plugin_display_farenheit = true;
|
||||
radioConfig.preferences.environmental_measurement_plugin_sensor_pin = 13;
|
||||
radioConfig.preferences.environmental_measurement_plugin_sensor_type = RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType::RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11;*/
|
||||
|
||||
if (! (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){
|
||||
// If this plugin is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
if (firstTime) {
|
||||
// This is the first time the OSThread library has called this function, so do some setup
|
||||
|
||||
firstTime = 0;
|
||||
|
||||
if (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled)
|
||||
{
|
||||
DEBUG_MSG("EnvironmentalMeasurement: Initializing\n");
|
||||
// it's possible to have this plugin enabled, only for displaying values on the screen.
|
||||
// therefore, we should only enable the sensor loop if measurement is also enabled
|
||||
switch(radioConfig.preferences.environmental_measurement_plugin_sensor_type) {
|
||||
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11:
|
||||
dht = new DHT(radioConfig.preferences.environmental_measurement_plugin_sensor_pin,DHT11);
|
||||
this->dht->begin();
|
||||
this->dht->read();
|
||||
DEBUG_MSG("EnvironmentalMeasurement: Opened DHT11 on pin: %d\n",radioConfig.preferences.environmental_measurement_plugin_sensor_pin);
|
||||
break;
|
||||
default:
|
||||
DEBUG_MSG("EnvironmentalMeasurement: Invalid sensor type selected; Disabling plugin");
|
||||
return (INT32_MAX);
|
||||
break;
|
||||
}
|
||||
// begin reading measurements from the sensor
|
||||
// DHT have a max read-rate of 1HZ, so we should wait at least 1 second
|
||||
// after initializing the sensor before we try to read from it.
|
||||
// returning the interval here means that the next time OSThread
|
||||
// calls our plugin, we'll run the other branch of this if statement
|
||||
// and actually do a "sendOurEnvironmentalMeasurement()"
|
||||
return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
|
||||
}
|
||||
return (INT32_MAX);
|
||||
}
|
||||
else {
|
||||
if (!radioConfig.preferences.environmental_measurement_plugin_measurement_enabled)
|
||||
{
|
||||
// if we somehow got to a second run of this plugin with measurement disabled, then just wait forever
|
||||
// I can't imagine we'd ever get here though.
|
||||
return (INT32_MAX);
|
||||
}
|
||||
// this is not the first time OSThread library has called this function
|
||||
// so just do what we intend to do on the interval
|
||||
if(sensor_read_error_count > radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold)
|
||||
{
|
||||
if (radioConfig.preferences.environmental_measurement_plugin_recovery_interval > 0 ) {
|
||||
DEBUG_MSG(
|
||||
"EnvironmentalMeasurement: TEMPORARILY DISABLED; The environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Will retry reads in %d seconds\n",
|
||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold,
|
||||
radioConfig.preferences.environmental_measurement_plugin_recovery_interval);
|
||||
sensor_read_error_count = 0;
|
||||
return(radioConfig.preferences.environmental_measurement_plugin_recovery_interval*1000);
|
||||
}
|
||||
DEBUG_MSG(
|
||||
"EnvironmentalMeasurement: DISABLED; The environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Reads will not be retried until after device reset\n",
|
||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold);
|
||||
return(INT32_MAX);
|
||||
|
||||
|
||||
}
|
||||
else if (sensor_read_error_count > 0){
|
||||
DEBUG_MSG("EnvironmentalMeasurement: There have been %d sensor read failures. Will retry %d more times\n",
|
||||
sensor_read_error_count,
|
||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold-sensor_read_error_count);
|
||||
}
|
||||
if (!sendOurEnvironmentalMeasurement() ){
|
||||
// if we failed to read the sensor, then try again
|
||||
// as soon as we can according to the maximum polling frequency
|
||||
return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
|
||||
}
|
||||
}
|
||||
// The return of runOnce is an int32 representing the desired number of
|
||||
// miliseconds until the function should be called again by the
|
||||
// OSThread library. Multiply the preference value by 1000 to convert seconds to miliseconds
|
||||
return(radioConfig.preferences.environmental_measurement_plugin_update_interval * 1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool EnvironmentalMeasurementPlugin::wantUIFrame() {
|
||||
return radioConfig.preferences.environmental_measurement_plugin_screen_enabled;
|
||||
}
|
||||
|
||||
String GetSenderName(const MeshPacket &mp) {
|
||||
String sender;
|
||||
|
||||
auto node = nodeDB.getNode(getFrom(&mp));
|
||||
if (node){
|
||||
sender = node->user.short_name;
|
||||
}
|
||||
else {
|
||||
sender = "UNK";
|
||||
}
|
||||
return sender;
|
||||
}
|
||||
|
||||
uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp) {
|
||||
uint32_t now = getTime();
|
||||
|
||||
uint32_t last_seen = mp->rx_time;
|
||||
int delta = (int)(now - last_seen);
|
||||
if (delta < 0) // our clock must be slightly off still - not set from GPS yet
|
||||
delta = 0;
|
||||
|
||||
return delta;
|
||||
|
||||
}
|
||||
|
||||
|
||||
float EnvironmentalMeasurementPlugin::CelsiusToFarenheit(float c) {
|
||||
return (c*9)/5 + 32;
|
||||
}
|
||||
|
||||
|
||||
void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x, y, "Environment");
|
||||
if (lastMeasurementPacket == nullptr) {
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement");
|
||||
//DEBUG_MSG("EnvironmentalMeasurement: No previous measurement; not drawing frame\n");
|
||||
return;
|
||||
}
|
||||
|
||||
EnvironmentalMeasurement lastMeasurement;
|
||||
|
||||
|
||||
uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket);
|
||||
String lastSender = GetSenderName(*lastMeasurementPacket);
|
||||
|
||||
auto &p = lastMeasurementPacket->decoded;
|
||||
if (!pb_decode_from_bytes(p.payload.bytes,
|
||||
p.payload.size,
|
||||
EnvironmentalMeasurement_fields,
|
||||
&lastMeasurement)) {
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error");
|
||||
DEBUG_MSG("EnvironmentalMeasurement: unable to decode last packet");
|
||||
return;
|
||||
}
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
String last_temp = String(lastMeasurement.temperature,0) +"°C";
|
||||
if (radioConfig.preferences.environmental_measurement_plugin_display_farenheit){
|
||||
last_temp = String(CelsiusToFarenheit(lastMeasurement.temperature),0) +"°F";;
|
||||
}
|
||||
|
||||
display->drawString(x, y += fontHeight(FONT_MEDIUM), lastSender+": "+last_temp +"/"+ String(lastMeasurement.relative_humidity,0) + "%("+String(agoSecs)+"s)");
|
||||
|
||||
}
|
||||
|
||||
bool EnvironmentalMeasurementPlugin::handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *p)
|
||||
{
|
||||
if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){
|
||||
// If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume
|
||||
return false;
|
||||
}
|
||||
|
||||
String sender = GetSenderName(mp);
|
||||
|
||||
DEBUG_MSG("EnvironmentalMeasurement: Received data from %s\n", sender);
|
||||
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p->relative_humidity);
|
||||
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", p->temperature);
|
||||
|
||||
lastMeasurementPacket = packetPool.allocCopy(mp);
|
||||
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
EnvironmentalMeasurement m;
|
||||
|
||||
m.barometric_pressure = 0; // TODO: Add support for barometric sensors
|
||||
DEBUG_MSG("-----------------------------------------\n");
|
||||
|
||||
DEBUG_MSG("EnvironmentalMeasurement: Read data\n");
|
||||
if (!this->dht->read(true)){
|
||||
sensor_read_error_count++;
|
||||
DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n");
|
||||
return false;
|
||||
}
|
||||
m.relative_humidity = this->dht->readHumidity();
|
||||
m.temperature = this->dht->readTemperature();
|
||||
|
||||
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity);
|
||||
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature);
|
||||
|
||||
sensor_read_error_count = 0;
|
||||
|
||||
MeshPacket *p = allocDataProtobuf(m);
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
|
||||
service.sendToMesh(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include "ProtobufPlugin.h"
|
||||
#include "../mesh/generated/environmental_measurement.pb.h"
|
||||
#include <OLEDDisplay.h>
|
||||
#include <OLEDDisplayUi.h>
|
||||
#include <DHT.h>
|
||||
|
||||
class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public ProtobufPlugin<EnvironmentalMeasurement>
|
||||
{
|
||||
public:
|
||||
EnvironmentalMeasurementPlugin(): concurrency::OSThread("EnvironmentalMeasurementPlugin"), ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg) {
|
||||
lastMeasurementPacket = nullptr;
|
||||
}
|
||||
virtual bool wantUIFrame();
|
||||
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
|
||||
protected:
|
||||
/** Called to handle a particular incoming message
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *p);
|
||||
virtual int32_t runOnce();
|
||||
/**
|
||||
* Send our EnvironmentalMeasurement into the mesh
|
||||
*/
|
||||
bool sendOurEnvironmentalMeasurement(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||
|
||||
private:
|
||||
float CelsiusToFarenheit(float c);
|
||||
bool firstTime = 1;
|
||||
DHT* dht;
|
||||
const MeshPacket *lastMeasurementPacket;
|
||||
uint32_t sensor_read_error_count = 0;
|
||||
};
|
||||
@@ -0,0 +1,315 @@
|
||||
#include "RangeTestPlugin.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <SPIFFS.h>
|
||||
//#include <assert.h>
|
||||
|
||||
/*
|
||||
As a sender, I can send packets every n-seonds. These packets include an incramented PacketID.
|
||||
|
||||
As a receiver, I can receive packets from multiple senders. These packets can be saved to the spiffs.
|
||||
*/
|
||||
|
||||
RangeTestPlugin *rangeTestPlugin;
|
||||
RangeTestPluginRadio *rangeTestPluginRadio;
|
||||
|
||||
RangeTestPlugin::RangeTestPlugin() : concurrency::OSThread("RangeTestPlugin") {}
|
||||
|
||||
uint32_t packetSequence = 0;
|
||||
|
||||
#define SEC_PER_DAY 86400
|
||||
#define SEC_PER_HOUR 3600
|
||||
#define SEC_PER_MIN 60
|
||||
|
||||
int32_t RangeTestPlugin::runOnce()
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
|
||||
/*
|
||||
Uncomment the preferences below if you want to use the plugin
|
||||
without having to configure it from the PythonAPI or WebUI.
|
||||
*/
|
||||
|
||||
//radioConfig.preferences.range_test_plugin_enabled = 1;
|
||||
//radioConfig.preferences.range_test_plugin_sender = 45;
|
||||
//radioConfig.preferences.range_test_plugin_save = 1;
|
||||
|
||||
// Fixed position is useful when testing indoors.
|
||||
// radioConfig.preferences.fixed_position = 1;
|
||||
|
||||
uint32_t senderHeartbeat = radioConfig.preferences.range_test_plugin_sender * 1000;
|
||||
|
||||
if (radioConfig.preferences.range_test_plugin_enabled) {
|
||||
|
||||
if (firstTime) {
|
||||
|
||||
// Interface with the serial peripheral from in here.
|
||||
|
||||
rangeTestPluginRadio = new RangeTestPluginRadio();
|
||||
|
||||
firstTime = 0;
|
||||
|
||||
if (radioConfig.preferences.range_test_plugin_sender) {
|
||||
DEBUG_MSG("Initializing Range Test Plugin -- Sender\n");
|
||||
return (5000); // Sending first message 5 seconds after initilization.
|
||||
} else {
|
||||
DEBUG_MSG("Initializing Range Test Plugin -- Receiver\n");
|
||||
return (500);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (radioConfig.preferences.range_test_plugin_sender) {
|
||||
// If sender
|
||||
DEBUG_MSG("Range Test Plugin - Sending heartbeat every %d ms\n", (senderHeartbeat));
|
||||
|
||||
DEBUG_MSG("gpsStatus->getLatitude() %d\n", gpsStatus->getLatitude());
|
||||
DEBUG_MSG("gpsStatus->getLongitude() %d\n", gpsStatus->getLongitude());
|
||||
DEBUG_MSG("gpsStatus->getHasLock() %d\n", gpsStatus->getHasLock());
|
||||
DEBUG_MSG("gpsStatus->getDOP() %d\n", gpsStatus->getDOP());
|
||||
DEBUG_MSG("gpsStatus->getHasLock() %d\n", gpsStatus->getHasLock());
|
||||
DEBUG_MSG("pref.fixed_position() %d\n", radioConfig.preferences.fixed_position);
|
||||
|
||||
rangeTestPluginRadio->sendPayload();
|
||||
return (senderHeartbeat);
|
||||
} else {
|
||||
// Otherwise, we're a receiver.
|
||||
|
||||
return (500);
|
||||
}
|
||||
// TBD
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("Range Test Plugin - Disabled\n");
|
||||
}
|
||||
|
||||
return (INT32_MAX);
|
||||
#endif
|
||||
}
|
||||
|
||||
MeshPacket *RangeTestPluginRadio::allocReply()
|
||||
{
|
||||
|
||||
auto reply = allocDataPacket(); // Allocate a packet for sending
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void RangeTestPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
MeshPacket *p = allocReply();
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
|
||||
p->want_ack = true;
|
||||
|
||||
packetSequence++;
|
||||
|
||||
static char heartbeatString[20];
|
||||
snprintf(heartbeatString, sizeof(heartbeatString), "seq %d", packetSequence);
|
||||
|
||||
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);
|
||||
|
||||
// TODO: Handle this better. We want to keep the phone awake otherwise it stops sending.
|
||||
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
|
||||
}
|
||||
|
||||
bool RangeTestPluginRadio::handleReceived(const MeshPacket &mp)
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
|
||||
if (radioConfig.preferences.range_test_plugin_enabled) {
|
||||
|
||||
auto &p = mp.decoded;
|
||||
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
|
||||
// nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
|
||||
|
||||
if (getFrom(&mp) != nodeDB.getNodeNum()) {
|
||||
|
||||
// DEBUG_MSG("* * Message came from the mesh\n");
|
||||
// Serial2.println("* * Message came from the mesh");
|
||||
// Serial2.printf("%s", p.payload.bytes);
|
||||
/*
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
NodeInfo *n = nodeDB.getNode(getFrom(&mp));
|
||||
|
||||
if (radioConfig.preferences.range_test_plugin_save) {
|
||||
appendFile(mp);
|
||||
}
|
||||
|
||||
DEBUG_MSG("-----------------------------------------\n");
|
||||
DEBUG_MSG("p.payload.bytes \"%s\"\n", p.payload.bytes);
|
||||
DEBUG_MSG("p.payload.size %d\n", p.payload.size);
|
||||
DEBUG_MSG("---- Received Packet:\n");
|
||||
DEBUG_MSG("mp.from %d\n", mp.from);
|
||||
DEBUG_MSG("mp.rx_snr %f\n", mp.rx_snr);
|
||||
DEBUG_MSG("mp.hop_limit %d\n", mp.hop_limit);
|
||||
// DEBUG_MSG("mp.decoded.position.latitude_i %d\n", mp.decoded.position.latitude_i); // Depricated
|
||||
// DEBUG_MSG("mp.decoded.position.longitude_i %d\n", mp.decoded.position.longitude_i); // Depricated
|
||||
DEBUG_MSG("---- Node Information of Received Packet (mp.from):\n");
|
||||
DEBUG_MSG("n->user.long_name %s\n", n->user.long_name);
|
||||
DEBUG_MSG("n->user.short_name %s\n", n->user.short_name);
|
||||
// DEBUG_MSG("n->user.macaddr %X\n", n->user.macaddr);
|
||||
DEBUG_MSG("n->has_position %d\n", n->has_position);
|
||||
DEBUG_MSG("n->position.latitude_i %d\n", n->position.latitude_i);
|
||||
DEBUG_MSG("n->position.longitude_i %d\n", n->position.longitude_i);
|
||||
DEBUG_MSG("n->position.battery_level %d\n", n->position.battery_level);
|
||||
DEBUG_MSG("---- Current device location information:\n");
|
||||
DEBUG_MSG("gpsStatus->getLatitude() %d\n", gpsStatus->getLatitude());
|
||||
DEBUG_MSG("gpsStatus->getLongitude() %d\n", gpsStatus->getLongitude());
|
||||
DEBUG_MSG("gpsStatus->getHasLock() %d\n", gpsStatus->getHasLock());
|
||||
DEBUG_MSG("gpsStatus->getDOP() %d\n", gpsStatus->getDOP());
|
||||
DEBUG_MSG("-----------------------------------------\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("Range Test Plugin Disabled\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return true; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
/// Ported from my old java code, returns distance in meters along the globe
|
||||
/// surface (by magic?)
|
||||
float RangeTestPluginRadio::latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b)
|
||||
{
|
||||
double pk = (180 / 3.14169);
|
||||
double a1 = lat_a / pk;
|
||||
double a2 = lng_a / pk;
|
||||
double b1 = lat_b / pk;
|
||||
double b2 = lng_b / pk;
|
||||
double cos_b1 = cos(b1);
|
||||
double cos_a1 = cos(a1);
|
||||
double t1 = cos_a1 * cos(a2) * cos_b1 * cos(b2);
|
||||
double t2 = cos_a1 * sin(a2) * cos_b1 * sin(b2);
|
||||
double t3 = sin(a1) * sin(b1);
|
||||
double tt = acos(t1 + t2 + t3);
|
||||
if (isnan(tt))
|
||||
tt = 0.0; // Must have been the same point?
|
||||
|
||||
return (float)(6366000 * tt);
|
||||
}
|
||||
|
||||
bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
|
||||
{
|
||||
auto &p = mp.decoded;
|
||||
|
||||
NodeInfo *n = nodeDB.getNode(getFrom(&mp));
|
||||
/*
|
||||
DEBUG_MSG("-----------------------------------------\n");
|
||||
DEBUG_MSG("p.payload.bytes \"%s\"\n", p.payload.bytes);
|
||||
DEBUG_MSG("p.payload.size %d\n", p.payload.size);
|
||||
DEBUG_MSG("---- Received Packet:\n");
|
||||
DEBUG_MSG("mp.from %d\n", mp.from);
|
||||
DEBUG_MSG("mp.rx_snr %f\n", mp.rx_snr);
|
||||
DEBUG_MSG("mp.hop_limit %d\n", mp.hop_limit);
|
||||
// DEBUG_MSG("mp.decoded.position.latitude_i %d\n", mp.decoded.position.latitude_i); // Depricated
|
||||
// DEBUG_MSG("mp.decoded.position.longitude_i %d\n", mp.decoded.position.longitude_i); // Depricated
|
||||
DEBUG_MSG("---- Node Information of Received Packet (mp.from):\n");
|
||||
DEBUG_MSG("n->user.long_name %s\n", n->user.long_name);
|
||||
DEBUG_MSG("n->user.short_name %s\n", n->user.short_name);
|
||||
DEBUG_MSG("n->user.macaddr %X\n", n->user.macaddr);
|
||||
DEBUG_MSG("n->has_position %d\n", n->has_position);
|
||||
DEBUG_MSG("n->position.latitude_i %d\n", n->position.latitude_i);
|
||||
DEBUG_MSG("n->position.longitude_i %d\n", n->position.longitude_i);
|
||||
DEBUG_MSG("n->position.battery_level %d\n", n->position.battery_level);
|
||||
DEBUG_MSG("---- Current device location information:\n");
|
||||
DEBUG_MSG("gpsStatus->getLatitude() %d\n", gpsStatus->getLatitude());
|
||||
DEBUG_MSG("gpsStatus->getLongitude() %d\n", gpsStatus->getLongitude());
|
||||
DEBUG_MSG("gpsStatus->getHasLock() %d\n", gpsStatus->getHasLock());
|
||||
DEBUG_MSG("gpsStatus->getDOP() %d\n", gpsStatus->getDOP());
|
||||
DEBUG_MSG("-----------------------------------------\n");
|
||||
*/
|
||||
if (!SPIFFS.begin(true)) {
|
||||
DEBUG_MSG("An Error has occurred while mounting SPIFFS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (SPIFFS.totalBytes() - SPIFFS.usedBytes() < 51200) {
|
||||
DEBUG_MSG("SPIFFS doesn't have enough free space. Abourting write.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the file doesn't exist, write the header.
|
||||
if (!SPIFFS.exists("/static/rangetest.csv")) {
|
||||
//--------- Write to file
|
||||
File fileToWrite = SPIFFS.open("/static/rangetest.csv", FILE_WRITE);
|
||||
|
||||
if (!fileToWrite) {
|
||||
DEBUG_MSG("There was an error opening the file for writing\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fileToWrite.println("time,from,sender name,sender lat,sender long,rx lat,rx long,rx snr,distance,payload")) {
|
||||
DEBUG_MSG("File was written\n");
|
||||
} else {
|
||||
DEBUG_MSG("File write failed\n");
|
||||
}
|
||||
|
||||
fileToWrite.close();
|
||||
}
|
||||
|
||||
//--------- Apend content to file
|
||||
File fileToAppend = SPIFFS.open("/static/rangetest.csv", FILE_APPEND);
|
||||
|
||||
if (!fileToAppend) {
|
||||
DEBUG_MSG("There was an error opening the file for appending\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
if (!gettimeofday(&tv, NULL)) {
|
||||
long hms = tv.tv_sec % SEC_PER_DAY;
|
||||
// hms += tz.tz_dsttime * SEC_PER_HOUR;
|
||||
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
|
||||
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
|
||||
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||
|
||||
// Tear apart hms into h:m:s
|
||||
int hour = hms / SEC_PER_HOUR;
|
||||
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
||||
|
||||
fileToAppend.printf("%02d:%02d:%02d,", hour, min, sec); // Time
|
||||
} else {
|
||||
fileToAppend.printf("??:??:??,"); // Time
|
||||
}
|
||||
|
||||
fileToAppend.printf("%d,", getFrom(&mp)); // From
|
||||
fileToAppend.printf("%s,", n->user.long_name); // Long Name
|
||||
fileToAppend.printf("%f,", n->position.latitude_i * 1e-7); // Sender Lat
|
||||
fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long
|
||||
fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat
|
||||
fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
|
||||
fileToAppend.printf("%f,", mp.rx_snr); // RX SNR
|
||||
|
||||
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,
|
||||
gpsStatus->getLatitude() * 1e-7, gpsStatus->getLongitude() * 1e-7);
|
||||
fileToAppend.printf("%f,", distance); // Distance in meters
|
||||
} else {
|
||||
fileToAppend.printf("0,");
|
||||
}
|
||||
|
||||
// TODO: If quotes are found in the payload, it has to be escaped.
|
||||
fileToAppend.printf("\"%s\"\n", p.payload.bytes);
|
||||
|
||||
fileToAppend.close();
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "SinglePortPlugin.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
class RangeTestPlugin : private concurrency::OSThread
|
||||
{
|
||||
bool firstTime = 1;
|
||||
|
||||
public:
|
||||
RangeTestPlugin();
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern RangeTestPlugin *rangeTestPlugin;
|
||||
|
||||
/*
|
||||
* Radio interface for RangeTestPlugin
|
||||
*
|
||||
*/
|
||||
class RangeTestPluginRadio : public SinglePortPlugin
|
||||
{
|
||||
uint32_t lastRxID;
|
||||
|
||||
public:
|
||||
RangeTestPluginRadio() : SinglePortPlugin("RangeTestPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
|
||||
|
||||
/**
|
||||
* Send our payload into the mesh
|
||||
*/
|
||||
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||
|
||||
/**
|
||||
* Append range test data to the file on the spiffs
|
||||
*/
|
||||
bool appendFile(const MeshPacket &mp);
|
||||
|
||||
/**
|
||||
* Kevin's magical calculation of two points to meters.
|
||||
*/
|
||||
float latLongToMeter(double lat_a, double lng_a, double lat_b, double lng_b);
|
||||
|
||||
protected:
|
||||
virtual MeshPacket *allocReply();
|
||||
|
||||
/** Called to handle a particular incoming message
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceived(const MeshPacket &mp);
|
||||
};
|
||||
|
||||
extern RangeTestPluginRadio *rangeTestPluginRadio;
|
||||
@@ -0,0 +1,324 @@
|
||||
#include "StoreForwardPlugin.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include <Arduino.h>
|
||||
#include <map>
|
||||
|
||||
#define STOREFORWARD_MAX_PACKETS 7500
|
||||
#define STOREFORWARD_SEND_HISTORY_SHORT 600
|
||||
|
||||
StoreForwardPlugin *storeForwardPlugin;
|
||||
StoreForwardPluginRadio *storeForwardPluginRadio;
|
||||
|
||||
StoreForwardPlugin::StoreForwardPlugin() : concurrency::OSThread("StoreForwardPlugin") {}
|
||||
|
||||
int32_t StoreForwardPlugin::runOnce()
|
||||
{
|
||||
|
||||
#ifndef NO_ESP32
|
||||
|
||||
/*
|
||||
Uncomment the preferences below if you want to use the plugin
|
||||
without having to configure it from the PythonAPI or WebUI.
|
||||
|
||||
attn @mc-hamster I moved this back inside the comment because I don't think it was intended to checkin. It was forcing all
|
||||
nodes to be running this and turning off is_router.
|
||||
|
||||
radioConfig.preferences.store_forward_plugin_enabled = 1;
|
||||
radioConfig.preferences.is_router = 0;
|
||||
*/
|
||||
|
||||
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
||||
|
||||
if (firstTime) {
|
||||
|
||||
firstTime = 0;
|
||||
|
||||
if (radioConfig.preferences.is_router) {
|
||||
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled as Router\n");
|
||||
// Router
|
||||
if (ESP.getPsramSize()) {
|
||||
if (ESP.getFreePsram() >= 2048 * 1024) {
|
||||
// Do the startup here
|
||||
storeForwardPluginRadio = new StoreForwardPluginRadio();
|
||||
|
||||
this->populatePSRAM();
|
||||
|
||||
// packetHistory[0].bytes;
|
||||
return (10 * 1000);
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("Device has less than 2M of PSRAM free. Aborting startup.\n");
|
||||
DEBUG_MSG("Store & Forward Plugin - Aborting Startup.\n");
|
||||
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("Device doesn't have PSRAM.\n");
|
||||
DEBUG_MSG("Store & Forward Plugin - Aborting Startup.\n");
|
||||
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled as Client\n");
|
||||
return (5 * 1000);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (radioConfig.preferences.is_router) {
|
||||
// Maybe some cleanup functions?
|
||||
this->sawNodeReport();
|
||||
this->historyReport();
|
||||
return (10 * 1000);
|
||||
} else {
|
||||
/*
|
||||
* If the plugin is turned on and is_router is not enabled, then we'll send a heartbeat every
|
||||
* few minutes.
|
||||
*/
|
||||
|
||||
DEBUG_MSG("Store & Forward Plugin - Sending heartbeat\n");
|
||||
|
||||
// storeForwardPluginRadio->sendPayloadHeartbeat();
|
||||
if(storeForwardPluginRadio)
|
||||
storeForwardPluginRadio->sendPayload();
|
||||
|
||||
return (1 * 60 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG_MSG("Store & Forward Plugin - Disabled\n");
|
||||
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
#endif
|
||||
return (INT32_MAX);
|
||||
}
|
||||
|
||||
void StoreForwardPlugin::populatePSRAM()
|
||||
{
|
||||
/*
|
||||
For PSRAM usage, see:
|
||||
https://learn.upesy.com/en/programmation/psram.html#psram-tab
|
||||
*/
|
||||
|
||||
DEBUG_MSG("Before PSRAM initilization\n");
|
||||
|
||||
DEBUG_MSG("Total heap: %d\n", ESP.getHeapSize());
|
||||
DEBUG_MSG("Free heap: %d\n", ESP.getFreeHeap());
|
||||
DEBUG_MSG("Total PSRAM: %d\n", ESP.getPsramSize());
|
||||
DEBUG_MSG("Free PSRAM: %d\n", ESP.getFreePsram());
|
||||
|
||||
// PacketHistoryStruct *packetHistory = (PacketHistoryStruct *)ps_calloc(STOREFORWARD_MAX_PACKETS,
|
||||
// sizeof(PacketHistoryStruct));
|
||||
this->packetHistory = (PacketHistoryStruct *)ps_calloc(STOREFORWARD_MAX_PACKETS, sizeof(PacketHistoryStruct));
|
||||
DEBUG_MSG("After PSRAM initilization\n");
|
||||
|
||||
DEBUG_MSG("Total heap: %d\n", ESP.getHeapSize());
|
||||
DEBUG_MSG("Free heap: %d\n", ESP.getFreeHeap());
|
||||
DEBUG_MSG("Total PSRAM: %d\n", ESP.getPsramSize());
|
||||
DEBUG_MSG("Free PSRAM: %d\n", ESP.getFreePsram());
|
||||
|
||||
DEBUG_MSG("packetHistory Size - %u", sizeof(packetHistory));
|
||||
}
|
||||
|
||||
// We saw a node.
|
||||
uint32_t StoreForwardPlugin::sawNode(uint32_t node)
|
||||
{
|
||||
|
||||
/*
|
||||
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("%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()
|
||||
{
|
||||
DEBUG_MSG("Iterating through the message history...\n");
|
||||
DEBUG_MSG("Message history contains %u records\n", this->packetHistoryCurrent);
|
||||
uint32_t startTimer = millis();
|
||||
for (int i = 0; i < this->packetHistoryCurrent; i++) {
|
||||
if (this->packetHistory[i].time) {
|
||||
// DEBUG_MSG("... time-%u to-0x%08x\n", this->packetHistory[i].time, this->packetHistory[i].to & 0xffffffff);
|
||||
}
|
||||
}
|
||||
DEBUG_MSG("StoreForwardPlugin::historyReport runtime - %u ms\n", millis() - startTimer);
|
||||
}
|
||||
void StoreForwardPlugin::historySend(uint32_t msAgo, uint32_t to)
|
||||
{
|
||||
for (int i = 0; i < this->packetHistoryCurrent; i++) {
|
||||
if (this->packetHistory[i].time) {
|
||||
// DEBUG_MSG("... time-%u to-0x%08x\n", this->packetHistory[i].time, this->packetHistory[i].to & 0xffffffff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StoreForwardPlugin::historyAdd(const MeshPacket *mp)
|
||||
{
|
||||
auto &p = mp;
|
||||
|
||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded);
|
||||
assert(numbytes <= MAX_RHPACKETLEN);
|
||||
|
||||
DEBUG_MSG("MP numbytes %u\n", numbytes);
|
||||
|
||||
// destination, source, bytes
|
||||
// memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||
memcpy(this->packetHistory[this->packetHistoryCurrent].bytes, bytes, MAX_RHPACKETLEN);
|
||||
this->packetHistory[this->packetHistoryCurrent].time = millis();
|
||||
this->packetHistory[this->packetHistoryCurrent].to = mp->to;
|
||||
this->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 *StoreForwardPluginRadio::allocReply()
|
||||
{
|
||||
auto reply = allocDataPacket(); // Allocate a packet for sending
|
||||
return reply; // attn @mc-hamster this code was commented out and was causing memory corruption
|
||||
}
|
||||
|
||||
void StoreForwardPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
/*
|
||||
MeshPacket *p = this->allocReply(); // attn @mc-hamster, I moved inside the commented block to prevent leaking memory
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
|
||||
p->want_ack = true;
|
||||
*/
|
||||
// static char heartbeatString[20];
|
||||
// snprintf(heartbeatString, sizeof(heartbeatString), "1");
|
||||
|
||||
// p->decoded.data.payload.size = strlen(heartbeatString); // You must specify how many bytes are in the reply
|
||||
// memcpy(p->decoded.data.payload.bytes, "1", 1);
|
||||
|
||||
// service.sendToMesh(p);
|
||||
}
|
||||
|
||||
void StoreForwardPluginRadio::sendPayloadHeartbeat(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
DEBUG_MSG("Sending S&F Heartbeat\n");
|
||||
MeshPacket *p = this->allocReply();
|
||||
p->to = dest;
|
||||
p->decoded.want_response = wantReplies;
|
||||
|
||||
service.sendToMesh(p);
|
||||
}
|
||||
|
||||
bool StoreForwardPluginRadio::handleReceived(const MeshPacket &mp)
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
if (mp.decoded.portnum == PortNum_UNKNOWN_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");
|
||||
|
||||
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_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 {
|
||||
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 {
|
||||
DEBUG_MSG("Store & Forward Plugin - Disabled\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return true; // Let others look at this message also if they want
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include "SinglePortPlugin.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
|
||||
struct PacketHistoryStruct {
|
||||
uint32_t time;
|
||||
uint32_t to;
|
||||
bool ack;
|
||||
uint8_t bytes[MAX_RHPACKETLEN];
|
||||
};
|
||||
|
||||
class StoreForwardPlugin : private concurrency::OSThread
|
||||
{
|
||||
bool firstTime = 1;
|
||||
|
||||
uint32_t receivedRecord[50][2] = {{0}};
|
||||
|
||||
PacketHistoryStruct *packetHistory;
|
||||
uint32_t packetHistoryCurrent = 0;
|
||||
|
||||
public:
|
||||
StoreForwardPlugin();
|
||||
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
uint32_t sawNode(uint32_t);
|
||||
void sawNodeReport();
|
||||
void historyAdd(const MeshPacket *mp);
|
||||
void historyReport();
|
||||
void historySend(uint32_t msAgo, uint32_t to);
|
||||
void populatePSRAM();
|
||||
|
||||
private:
|
||||
// Nothing here
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern StoreForwardPlugin *storeForwardPlugin;
|
||||
|
||||
/*
|
||||
* Radio interface for StoreForwardPlugin
|
||||
*
|
||||
*/
|
||||
class StoreForwardPluginRadio : public SinglePortPlugin
|
||||
{
|
||||
// uint32_t lastRxID;
|
||||
|
||||
public:
|
||||
StoreForwardPluginRadio() : SinglePortPlugin("StoreForwardPluginRadio", PortNum_STORE_FORWARD_APP) {}
|
||||
// StoreForwardPluginRadio() : SinglePortPlugin("StoreForwardPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
|
||||
|
||||
/**
|
||||
* Send our payload into the mesh
|
||||
*/
|
||||
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||
|
||||
/**
|
||||
* Send our payload into the mesh
|
||||
*/
|
||||
void sendPayloadHeartbeat(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
||||
|
||||
protected:
|
||||
virtual MeshPacket *allocReply();
|
||||
|
||||
virtual bool wantPortnum(PortNum p) { return true; };
|
||||
|
||||
/** Called to handle a particular incoming message
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceived(const MeshPacket &mp);
|
||||
};
|
||||
|
||||
extern StoreForwardPluginRadio *storeForwardPluginRadio;
|
||||
@@ -10,9 +10,6 @@ class CrossPlatformCryptoEngine : public CryptoEngine
|
||||
|
||||
CTRCommon *ctr = NULL;
|
||||
|
||||
/// How many bytes in our key
|
||||
uint8_t keySize = 0;
|
||||
|
||||
public:
|
||||
CrossPlatformCryptoEngine() {}
|
||||
|
||||
@@ -27,21 +24,21 @@ class CrossPlatformCryptoEngine : public CryptoEngine
|
||||
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
|
||||
* provided pointer)
|
||||
*/
|
||||
virtual void setKey(size_t numBytes, uint8_t *bytes)
|
||||
virtual void setKey(const CryptoKey &k)
|
||||
{
|
||||
keySize = numBytes;
|
||||
DEBUG_MSG("Installing AES%d key!\n", numBytes * 8);
|
||||
CryptoEngine::setKey(k);
|
||||
DEBUG_MSG("Installing AES%d key!\n", key.length * 8);
|
||||
if (ctr) {
|
||||
delete ctr;
|
||||
ctr = NULL;
|
||||
}
|
||||
if (numBytes != 0) {
|
||||
if (numBytes == 16)
|
||||
if (key.length != 0) {
|
||||
if (key.length == 16)
|
||||
ctr = new CTR<AES128>();
|
||||
else
|
||||
ctr = new CTR<AES256>();
|
||||
|
||||
ctr->setKey(bytes, numBytes);
|
||||
ctr->setKey(key.bytes, key.length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +49,7 @@ class CrossPlatformCryptoEngine : public CryptoEngine
|
||||
*/
|
||||
virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes)
|
||||
{
|
||||
if (keySize != 0) {
|
||||
if (key.length > 0) {
|
||||
uint8_t stream_block[16];
|
||||
static uint8_t scratch[MAX_BLOCKSIZE];
|
||||
size_t nc_off = 0;
|
||||
|
||||
Reference in New Issue
Block a user