Merge branch 'develop' into gps-toggle-final

This commit is contained in:
Thomas Göttgens
2022-12-16 20:58:53 +01:00
committed by GitHub
38 changed files with 967 additions and 464 deletions

47
arch/esp32/esp32s2.ini Normal file
View File

@@ -0,0 +1,47 @@
[esp32s2_base]
extends = arduino_base
platform = platformio/espressif32@^5.2.0
build_src_filter =
${arduino_base.build_src_filter} -<platform/nrf52/> -<platform/stm32wl> -<platform/rp2040> -<mesh/eth/> -<nimble/>
upload_speed = 961200
monitor_speed = 115200
debug_init_break = tbreak setup
monitor_filters = esp32_exception_decoder
board_build.filesystem = littlefs
# Remove -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL for low level BLE logging.
# See library directory for BLE logging possible values: .pio/libdeps/tbeam/NimBLE-Arduino/src/log_common/log_common.h
# This overrides the BLE logging default of LOG_LEVEL_INFO (1) from: .pio/libdeps/tbeam/NimBLE-Arduino/src/esp_nimble_cfg.h
build_flags =
${arduino_base.build_flags}
-Wall
-Wextra
-Isrc/platform/esp32
-std=c++11
-DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
-DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL
-DAXP_DEBUG_PORT=Serial
-DCONFIG_BT_NIMBLE_ENABLED
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
-DHAS_BLUETOOTH=0
-DDEBUG_HEAP
lib_deps =
${arduino_base.lib_deps}
${networking_base.lib_deps}
${environmental_base.lib_deps}
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
caveman99/ESP32 Codec2@^1.0.1
lib_ignore =
segger_rtt
ESP32 BLE Arduino
; customize the partition table
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
board_build.partitions = partition-table.csv

View File

@@ -1,6 +1,6 @@
{ {
"build": { "build": {
"arduino":{ "arduino": {
"ldscript": "esp32s3_out.ld" "ldscript": "esp32s3_out.ld"
}, },
"core": "esp32", "core": "esp32",
@@ -8,9 +8,7 @@
"-DBOARD_HAS_PSRAM", "-DBOARD_HAS_PSRAM",
"-DLILYGO_TBEAM_S3_CORE", "-DLILYGO_TBEAM_S3_CORE",
"-DARDUINO_USB_CDC_ON_BOOT=1", "-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_DFU_ON_BOOT=1", "-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MSC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1", "-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1" "-DARDUINO_EVENT_RUNNING_CORE=1"
], ],

View File

@@ -4,6 +4,7 @@
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "configuration.h" #include "configuration.h"
#include "graphics/Screen.h" #include "graphics/Screen.h"
#include "modules/ExternalNotificationModule.h"
#include "power.h" #include "power.h"
#include <OneButton.h> #include <OneButton.h>
@@ -115,6 +116,10 @@ class ButtonThread : public concurrency::OSThread
{ {
// DEBUG_MSG("press!\n"); // DEBUG_MSG("press!\n");
#ifdef BUTTON_PIN #ifdef BUTTON_PIN
// If a nag notification is running, stop it
if (externalNotificationModule->nagCycleCutoff != UINT32_MAX) {
externalNotificationModule->nagCycleCutoff = 0;
}
if ((BUTTON_PIN != moduleConfig.canned_message.inputbroker_pin_press) || if ((BUTTON_PIN != moduleConfig.canned_message.inputbroker_pin_press) ||
!moduleConfig.canned_message.enabled) { !moduleConfig.canned_message.enabled) {
powerFSM.trigger(EVENT_PRESS); powerFSM.trigger(EVENT_PRESS);

View File

@@ -182,6 +182,9 @@ Power::Power() : OSThread("Power")
{ {
statusHandler = {}; statusHandler = {};
low_voltage_counter = 0; low_voltage_counter = 0;
#ifdef DEBUG_HEAP
lastheap = ESP.getFreeHeap();
#endif
} }
bool Power::analogInit() bool Power::analogInit()
@@ -284,7 +287,10 @@ void Power::readPowerStatus()
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
newStatus.notifyObservers(&powerStatus2); newStatus.notifyObservers(&powerStatus2);
#ifdef DEBUG_HEAP #ifdef DEBUG_HEAP
DEBUG_MSG("Heap status: %d/%d bytes free, running %d threads\n", ESP.getFreeHeap(), ESP.getHeapSize(), concurrency::mainController.size(false)); if (lastheap != ESP.getFreeHeap()){
DEBUG_MSG("Heap status: %d/%d bytes free (%d), running %d threads\n", ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getFreeHeap() - lastheap , concurrency::mainController.size(false));
lastheap = ESP.getFreeHeap();
}
#endif #endif
// If we have a battery at all and it is less than 10% full, force deep sleep if we have more than 3 low readings in a row // If we have a battery at all and it is less than 10% full, force deep sleep if we have more than 3 low readings in a row
@@ -458,6 +464,9 @@ bool Power::axpChipInit()
// Set constant current charging current // Set constant current charging current
PMU->setChargerConstantCurr(XPOWERS_AXP192_CHG_CUR_450MA); PMU->setChargerConstantCurr(XPOWERS_AXP192_CHG_CUR_450MA);
//Set up the charging voltage
PMU->setChargeTargetVoltage(XPOWERS_AXP192_CHG_VOL_4V2);
} else if (PMU->getChipModel() == XPOWERS_AXP2101) { } else if (PMU->getChipModel() == XPOWERS_AXP2101) {
// t-beam s3 core // t-beam s3 core
@@ -510,6 +519,8 @@ bool Power::axpChipInit()
//Set the constant current charging current of AXP2101, temporarily use 500mA by default //Set the constant current charging current of AXP2101, temporarily use 500mA by default
PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA); PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA);
//Set up the charging voltage
PMU->setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V2);
} }
@@ -563,9 +574,6 @@ bool Power::axpChipInit()
DEBUG_MSG("=======================================================================\n"); DEBUG_MSG("=======================================================================\n");
//Set up the charging voltage, AXP2101/AXP192 4.2V gear is the same
// XPOWERS_AXP192_CHG_VOL_4V2 = XPOWERS_AXP2101_CHG_VOL_4V2
PMU->setChargeTargetVoltage(XPOWERS_AXP192_CHG_VOL_4V2);
// Set PMU shutdown voltage at 2.6V to maximize battery utilization // Set PMU shutdown voltage at 2.6V to maximize battery utilization
PMU->setSysPowerDownVoltage(2600); PMU->setSysPowerDownVoltage(2600);

View File

@@ -329,11 +329,8 @@ static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, i
display->drawString(64 + x, y, "Updating"); display->drawString(64 + x, y, "Updating");
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
if ((millis() / 1000) % 2) { display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . ."); display->drawStringMaxWidth(0 + x, 2 + y + FONT_HEIGHT_SMALL *2, x + display->getWidth(), "Please be patient and do not power off.");
} else {
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . ");
}
} }
/// Draw the last text message we received /// Draw the last text message we received
@@ -364,6 +361,9 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
{ {
displayedNodeNum = 0; // Not currently showing a node pane displayedNodeNum = 0; // Not currently showing a node pane
// the max length of this buffer is much longer than we can possibly print
static char tempBuf[237];
MeshPacket &mp = devicestate.rx_text_message; MeshPacket &mp = devicestate.rx_text_message;
NodeInfo *node = nodeDB.getNode(getFrom(&mp)); NodeInfo *node = nodeDB.getNode(getFrom(&mp));
// DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from, // DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from,
@@ -373,16 +373,14 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
// with the third parameter you can define the width after which words will // with the third parameter you can define the width after which words will
// be wrapped. Currently only spaces and "-" are allowed for wrapping // be wrapped. Currently only spaces and "-" are allowed for wrapping
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_MEDIUM);
String sender = (node && node->has_user) ? node->user.short_name : "???";
display->drawString(0 + x, 0 + y, sender);
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
// the max length of this buffer is much longer than we can possibly print display->setColor(BLACK);
static char tempBuf[96]; display->drawStringf(0 + x, 0 + y, tempBuf, "From: %s", (node && node->has_user) ? node->user.short_name : "???");
snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.payload.bytes); display->drawStringf(1 + x, 0 + y, tempBuf, "From: %s", (node && node->has_user) ? node->user.short_name : "???");
display->setColor(WHITE);
display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf); snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.payload.bytes);
display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf);
} }
/// Draw a series of fields in a column, wrapping to multiple colums if needed /// Draw a series of fields in a column, wrapping to multiple colums if needed
@@ -395,6 +393,10 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char *
int xo = x, yo = y; int xo = x, yo = y;
while (*f) { while (*f) {
display->drawString(xo, yo, *f); display->drawString(xo, yo, *f);
if (display->getColor() == BLACK)
display->drawString(xo + 1, yo, *f);
display->setColor(WHITE);
yo += FONT_HEIGHT_SMALL; yo += FONT_HEIGHT_SMALL;
if (yo > SCREEN_HEIGHT - FONT_HEIGHT_SMALL) { if (yo > SCREEN_HEIGHT - FONT_HEIGHT_SMALL) {
xo += SCREEN_WIDTH / 2; xo += SCREEN_WIDTH / 2;
@@ -465,6 +467,7 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *no
sprintf(usersString, "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal()); sprintf(usersString, "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal());
display->drawFastImage(x, y, 8, 8, imgUser); display->drawFastImage(x, y, 8, 8, imgUser);
display->drawString(x + 10, y - 2, usersString); display->drawString(x + 10, y - 2, usersString);
display->drawString(x + 11, y - 2, usersString);
} }
// Draw GPS status summary // Draw GPS status summary
@@ -473,15 +476,18 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus
if (config.position.fixed_position) { if (config.position.fixed_position) {
// GPS coordinates are currently fixed // GPS coordinates are currently fixed
display->drawString(x - 1, y - 2, "Fixed GPS"); display->drawString(x - 1, y - 2, "Fixed GPS");
display->drawString(x, y - 2, "Fixed GPS");
return; return;
} }
if (!gps->getIsConnected()) { if (!gps->getIsConnected()) {
display->drawString(x, y - 2, "No GPS"); display->drawString(x, y - 2, "No GPS");
display->drawString(x + 1, y - 2, "No GPS");
return; return;
} }
display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty); display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
if (!gps->getHasLock()) { if (!gps->getHasLock()) {
display->drawString(x + 8, y - 2, "No sats"); display->drawString(x + 8, y - 2, "No sats");
display->drawString(x + 9, y - 2, "No sats");
return; return;
} else { } else {
char satsString[3]; char satsString[3];
@@ -685,16 +691,16 @@ static uint16_t getCompassDiam(OLEDDisplay *display)
{ {
uint16_t diam = 0; uint16_t diam = 0;
// get the smaller of the 2 dimensions and subtract 20 // get the smaller of the 2 dimensions and subtract 20
if(display->getWidth() > display->getHeight()) { if(display->getWidth() > (display->getHeight() - FONT_HEIGHT_SMALL)) {
diam = display->getHeight(); diam = display->getHeight() - FONT_HEIGHT_SMALL;
// if 2/3 of the other size would be smaller, use that // if 2/3 of the other size would be smaller, use that
if (diam > (display->getWidth() * 2 / 3)) { if (diam > (display->getWidth() * 2 / 3)) {
diam = display->getWidth() * 2 / 3; diam = display->getWidth() * 2 / 3;
} }
} else { } else {
diam = display->getWidth(); diam = display->getWidth();
if (diam > (display->getHeight() * 2 / 3)) { if (diam > ((display->getHeight() - FONT_HEIGHT_SMALL) * 2 / 3)) {
diam = display->getHeight() * 2 / 3; diam = (display->getHeight() - FONT_HEIGHT_SMALL) * 2 / 3;
} }
} }
@@ -774,6 +780,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
// The coordinates define the left starting point of the text // The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
const char *username = node->has_user ? node->user.long_name : "Unknown Name"; const char *username = node->has_user ? node->user.long_name : "Unknown Name";
static char signalStr[20]; static char signalStr[20];
@@ -804,7 +812,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
const char *fields[] = {username, distStr, signalStr, lastStr, NULL}; const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
// coordinates for the center of the compass/circle // coordinates for the center of the compass/circle
int16_t compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5, compassY = y + SCREEN_HEIGHT / 2; int16_t compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5, compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - FONT_HEIGHT_SMALL) / 2;
bool hasNodeHeading = false; bool hasNodeHeading = false;
if (ourNode && hasPosition(ourNode)) { if (ourNode && hasPosition(ourNode)) {
@@ -847,6 +855,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?"); display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
display->drawCircle(compassX, compassY, getCompassDiam(display) / 2); display->drawCircle(compassX, compassY, getCompassDiam(display) / 2);
display->setColor(BLACK);
// Must be after distStr is populated // Must be after distStr is populated
drawColumns(display, x, y, fields); drawColumns(display, x, y, fields);
} }
@@ -1373,6 +1382,9 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// The coordinates define the left starting point of the text // The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
char channelStr[20]; char channelStr[20];
{ {
concurrency::LockGuard guard(&lock); concurrency::LockGuard guard(&lock);
@@ -1382,22 +1394,24 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// Display power status // Display power status
if (powerStatus->getHasBattery()) if (powerStatus->getHasBattery())
drawBattery(display, x, y + 2, imgBattery, powerStatus); drawBattery(display, x + 1, y + 3, imgBattery, powerStatus);
else if (powerStatus->knowsUSB()) else if (powerStatus->knowsUSB())
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower); display->drawFastImage(x + 1, y + 3, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
// Display nodes status // Display nodes status
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus); drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 3, nodeStatus);
// Display GPS status // Display GPS status
if (!config.position.gps_enabled){ if (!config.position.gps_enabled){
int16_t yPos = y + 2; int16_t yPos = y + 2;
#ifdef GPS_POWER_TOGGLE #ifdef GPS_POWER_TOGGLE
yPos = (y + 10 + FONT_HEIGHT_SMALL); yPos = (y + 10 + FONT_HEIGHT_SMALL);
#endif #endif
drawGPSpowerstat(display, x, yPos, gpsStatus); drawGPSpowerstat(display, x, yPos, gpsStatus);
} else { } else {
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus); drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 3, gpsStatus);
} }
display->setColor(WHITE);
// Draw the channel name // Draw the channel name
display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr); display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr);
// Draw our hardware ID to assist with bluetooth pairing // Draw our hardware ID to assist with bluetooth pairing
@@ -1428,15 +1442,24 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
// The coordinates define the left starting point of the text // The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
if (WiFi.status() != WL_CONNECTED) { if (WiFi.status() != WL_CONNECTED) {
display->drawString(x, y, String("WiFi: Not Connected")); display->drawString(x, y, String("WiFi: Not Connected"));
display->drawString(x + 1, y, String("WiFi: Not Connected"));
} else { } else {
display->drawString(x, y, String("WiFi: Connected")); display->drawString(x, y, String("WiFi: Connected"));
display->drawString(x + 1, y, String("WiFi: Connected"));
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())), y, display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())), y,
"RSSI " + String(WiFi.RSSI())); "RSSI " + String(WiFi.RSSI()));
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())) - 1, y,
"RSSI " + String(WiFi.RSSI()));
} }
display->setColor(WHITE);
/* /*
- WL_CONNECTED: assigned when connected to a WiFi network; - WL_CONNECTED: assigned when connected to a WiFi network;
- WL_NO_SSID_AVAIL: assigned when no SSID are available; - WL_NO_SSID_AVAIL: assigned when no SSID are available;
@@ -1545,6 +1568,9 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
// The coordinates define the left starting point of the text // The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
char batStr[20]; char batStr[20];
if (powerStatus->getHasBattery()) { if (powerStatus->getHasBattery()) {
int batV = powerStatus->getBatteryVoltageMv() / 1000; int batV = powerStatus->getBatteryVoltageMv() / 1000;
@@ -1555,9 +1581,11 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
// Line 1 // Line 1
display->drawString(x, y, batStr); display->drawString(x, y, batStr);
display->drawString(x + 1, y, batStr);
} else { } else {
// Line 1 // Line 1
display->drawString(x, y, String("USB")); display->drawString(x, y, String("USB"));
display->drawString(x + 1, y, String("USB"));
} }
auto mode = ""; auto mode = "";
@@ -1590,6 +1618,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
} }
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode); display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode);
// Line 2 // Line 2
uint32_t currentMillis = millis(); uint32_t currentMillis = millis();
@@ -1602,6 +1631,8 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
// minutes %= 60; // minutes %= 60;
// hours %= 24; // hours %= 24;
display->setColor(WHITE);
// Show uptime as days, hours, minutes OR seconds // Show uptime as days, hours, minutes OR seconds
String uptime; String uptime;
if (days >= 2) if (days >= 2)

View File

@@ -21,6 +21,7 @@ class Screen
void startBluetoothPinScreen(uint32_t pin) {} void startBluetoothPinScreen(uint32_t pin) {}
void stopBluetoothPinScreen() {} void stopBluetoothPinScreen() {}
void startRebootScreen() {} void startRebootScreen() {}
void startFirmwareUpdateScreen() {}
}; };
} }

View File

@@ -15,33 +15,3 @@ const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF
const uint8_t imgInfo[] PROGMEM = { 0xFF, 0x81, 0x81, 0xB5, 0xB5, 0x81, 0x81, 0xFF }; const uint8_t imgInfo[] PROGMEM = { 0xFF, 0x81, 0x81, 0xB5, 0xB5, 0x81, 0x81, 0xFF };
#include "img/icon.xbm" #include "img/icon.xbm"
// We now programmatically draw our compass
#if 0
const
#include "img/compass.xbm"
#endif
#if 0
const uint8_t activeSymbol[] PROGMEM = {
B00000000,
B00000000,
B00011000,
B00100100,
B01000010,
B01000010,
B00100100,
B00011000
};
const uint8_t inactiveSymbol[] PROGMEM = {
B00000000,
B00000000,
B00000000,
B00000000,
B00011000,
B00011000,
B00000000,
B00000000
};
#endif

View File

@@ -1,28 +0,0 @@
#define compass_width 48
#define compass_height 48
static char compass_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00,
0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00,
0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x01, 0x00,
0x00, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0x00,
0x00, 0xF8, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0xFC, 0x07, 0xE0, 0x3F, 0x00,
0x00, 0xFE, 0x01, 0x80, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00,
0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xFC, 0x01,
0x80, 0x1F, 0x00, 0x00, 0xF8, 0x01, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x01,
0xC0, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03,
0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03,
0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F, 0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F,
0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F, 0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F,
0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03,
0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, 0xC0, 0x0F, 0x00, 0x00, 0xF0, 0x03,
0x80, 0x0F, 0x00, 0x00, 0xF0, 0x01, 0x80, 0x1F, 0x00, 0x00, 0xF8, 0x01,
0x80, 0x3F, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00,
0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x01, 0x80, 0x7F, 0x00,
0x00, 0xFC, 0x07, 0xE0, 0x3F, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x1F, 0x00,
0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0x00,
0x00, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00,
0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00,
0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

View File

@@ -1,6 +0,0 @@
#define pin_width 13
#define pin_height 13
static char pin_bits[] = {
0x00, 0x00, 0xF0, 0x01, 0xF8, 0x03, 0xFC, 0x07, 0xBC, 0x07, 0xBC, 0x07,
0xFC, 0x07, 0xF8, 0x03, 0xF8, 0x03, 0xF0, 0x01, 0xE0, 0x00, 0xE0, 0x00,
0x00, 0x00, };

View File

@@ -41,6 +41,11 @@ void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c)
tosend->hop_limit--; // bump down the hop count tosend->hop_limit--; // bump down the hop count
// If it is a traceRoute request, update the route that it went via me
if (p->which_payload_variant == MeshPacket_decoded_tag && traceRouteModule->wantPacket(p)) {
traceRouteModule->updateRoute(tosend);
}
printPacket("Rebroadcasting received floodmsg to neighbors", p); printPacket("Rebroadcasting received floodmsg to neighbors", p);
// Note: we are careful to resend using the original senders node id // Note: we are careful to resend using the original senders node id
// We are careful not to call our hooked version of send() - because we don't want to check this again // We are careful not to call our hooked version of send() - because we don't want to check this again

View File

@@ -2,6 +2,7 @@
#include "PacketHistory.h" #include "PacketHistory.h"
#include "Router.h" #include "Router.h"
#include "modules/TraceRouteModule.h"
/** /**
* This is a mixin that extends Router with the ability to do Naive Flooding (in the standard mesh protocol sense) * This is a mixin that extends Router with the ability to do Naive Flooding (in the standard mesh protocol sense)

View File

@@ -215,9 +215,9 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.has_external_notification = true; moduleConfig.has_external_notification = true;
moduleConfig.has_canned_message = true; moduleConfig.has_canned_message = true;
strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(default_mqtt_address)); strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(moduleConfig.mqtt.address));
strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(default_mqtt_username)); strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(moduleConfig.mqtt.username));
strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(default_mqtt_password)); strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(moduleConfig.mqtt.password));
initModuleConfigIntervals(); initModuleConfigIntervals();
} }

View File

@@ -3,6 +3,7 @@
#include "NodeDB.h" #include "NodeDB.h"
#include "SPILock.h" #include "SPILock.h"
#include "configuration.h" #include "configuration.h"
#include "main.h"
#include "error.h" #include "error.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include <pb_decode.h> #include <pb_decode.h>
@@ -87,10 +88,8 @@ bool RadioLibInterface::canSendImmediately()
if (busyTx && (millis() - lastTxStart > 60000)) { if (busyTx && (millis() - lastTxStart > 60000)) {
DEBUG_MSG("Hardware Failure! busyTx for more than 60s\n"); DEBUG_MSG("Hardware Failure! busyTx for more than 60s\n");
RECORD_CRITICALERROR(CriticalErrorCode_TRANSMIT_FAILED); RECORD_CRITICALERROR(CriticalErrorCode_TRANSMIT_FAILED);
#ifdef ARCH_ESP32 // reboot in 5 seconds when this condition occurs.
if (busyTx && (millis() - lastTxStart > 65000)) // After 5s more, reboot rebootAtMsec = lastTxStart + 65000;
ESP.restart();
#endif
} }
if (busyRx) if (busyRx)
DEBUG_MSG("Can not send yet, busyRx\n"); DEBUG_MSG("Can not send yet, busyRx\n");
@@ -386,6 +385,7 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
int res = iface->startTransmit(radiobuf, numbytes); int res = iface->startTransmit(radiobuf, numbytes);
if (res != RADIOLIB_ERR_NONE) { if (res != RADIOLIB_ERR_NONE) {
DEBUG_MSG("startTransmit failed, error=%d\n", res);
RECORD_CRITICALERROR(CriticalErrorCode_RADIO_SPI_BUG); RECORD_CRITICALERROR(CriticalErrorCode_RADIO_SPI_BUG);
// This send failed, but make sure to 'complete' it properly // This send failed, but make sure to 'complete' it properly

View File

@@ -151,7 +151,7 @@ extern const pb_msgdesc_t LocalModuleConfig_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define LocalConfig_size 387 #define LocalConfig_size 387
#define LocalModuleConfig_size 358 #define LocalModuleConfig_size 376
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -93,6 +93,13 @@ typedef struct _ModuleConfig_ExternalNotificationConfig {
bool alert_message; bool alert_message;
bool alert_bell; bool alert_bell;
bool use_pwm; bool use_pwm;
uint8_t output_vibra;
uint8_t output_buzzer;
bool alert_message_vibra;
bool alert_message_buzzer;
bool alert_bell_vibra;
bool alert_bell_buzzer;
uint16_t nag_timeout;
} ModuleConfig_ExternalNotificationConfig; } ModuleConfig_ExternalNotificationConfig;
typedef struct _ModuleConfig_MQTTConfig { typedef struct _ModuleConfig_MQTTConfig {
@@ -187,7 +194,7 @@ extern "C" {
#define ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0} #define ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0}
#define ModuleConfig_AudioConfig_init_default {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_AudioConfig_init_default {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0}
#define ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} #define ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN}
#define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0}
#define ModuleConfig_RangeTestConfig_init_default {0, 0, 0} #define ModuleConfig_RangeTestConfig_init_default {0, 0, 0}
#define ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0} #define ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0}
@@ -196,7 +203,7 @@ extern "C" {
#define ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0} #define ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0}
#define ModuleConfig_AudioConfig_init_zero {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_AudioConfig_init_zero {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0}
#define ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} #define ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN}
#define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0}
#define ModuleConfig_RangeTestConfig_init_zero {0, 0, 0} #define ModuleConfig_RangeTestConfig_init_zero {0, 0, 0}
#define ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0} #define ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0}
@@ -228,6 +235,13 @@ extern "C" {
#define ModuleConfig_ExternalNotificationConfig_alert_message_tag 5 #define ModuleConfig_ExternalNotificationConfig_alert_message_tag 5
#define ModuleConfig_ExternalNotificationConfig_alert_bell_tag 6 #define ModuleConfig_ExternalNotificationConfig_alert_bell_tag 6
#define ModuleConfig_ExternalNotificationConfig_use_pwm_tag 7 #define ModuleConfig_ExternalNotificationConfig_use_pwm_tag 7
#define ModuleConfig_ExternalNotificationConfig_output_vibra_tag 8
#define ModuleConfig_ExternalNotificationConfig_output_buzzer_tag 9
#define ModuleConfig_ExternalNotificationConfig_alert_message_vibra_tag 10
#define ModuleConfig_ExternalNotificationConfig_alert_message_buzzer_tag 11
#define ModuleConfig_ExternalNotificationConfig_alert_bell_vibra_tag 12
#define ModuleConfig_ExternalNotificationConfig_alert_bell_buzzer_tag 13
#define ModuleConfig_ExternalNotificationConfig_nag_timeout_tag 14
#define ModuleConfig_MQTTConfig_enabled_tag 1 #define ModuleConfig_MQTTConfig_enabled_tag 1
#define ModuleConfig_MQTTConfig_address_tag 2 #define ModuleConfig_MQTTConfig_address_tag 2
#define ModuleConfig_MQTTConfig_username_tag 3 #define ModuleConfig_MQTTConfig_username_tag 3
@@ -323,7 +337,14 @@ X(a, STATIC, SINGULAR, UINT32, output, 3) \
X(a, STATIC, SINGULAR, BOOL, active, 4) \ X(a, STATIC, SINGULAR, BOOL, active, 4) \
X(a, STATIC, SINGULAR, BOOL, alert_message, 5) \ X(a, STATIC, SINGULAR, BOOL, alert_message, 5) \
X(a, STATIC, SINGULAR, BOOL, alert_bell, 6) \ X(a, STATIC, SINGULAR, BOOL, alert_bell, 6) \
X(a, STATIC, SINGULAR, BOOL, use_pwm, 7) X(a, STATIC, SINGULAR, BOOL, use_pwm, 7) \
X(a, STATIC, SINGULAR, UINT32, output_vibra, 8) \
X(a, STATIC, SINGULAR, UINT32, output_buzzer, 9) \
X(a, STATIC, SINGULAR, BOOL, alert_message_vibra, 10) \
X(a, STATIC, SINGULAR, BOOL, alert_message_buzzer, 11) \
X(a, STATIC, SINGULAR, BOOL, alert_bell_vibra, 12) \
X(a, STATIC, SINGULAR, BOOL, alert_bell_buzzer, 13) \
X(a, STATIC, SINGULAR, UINT32, nag_timeout, 14)
#define ModuleConfig_ExternalNotificationConfig_CALLBACK NULL #define ModuleConfig_ExternalNotificationConfig_CALLBACK NULL
#define ModuleConfig_ExternalNotificationConfig_DEFAULT NULL #define ModuleConfig_ExternalNotificationConfig_DEFAULT NULL
@@ -391,7 +412,7 @@ extern const pb_msgdesc_t ModuleConfig_CannedMessageConfig_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define ModuleConfig_AudioConfig_size 19 #define ModuleConfig_AudioConfig_size 19
#define ModuleConfig_CannedMessageConfig_size 49 #define ModuleConfig_CannedMessageConfig_size 49
#define ModuleConfig_ExternalNotificationConfig_size 22 #define ModuleConfig_ExternalNotificationConfig_size 40
#define ModuleConfig_MQTTConfig_size 169 #define ModuleConfig_MQTTConfig_size 169
#define ModuleConfig_RangeTestConfig_size 10 #define ModuleConfig_RangeTestConfig_size 10
#define ModuleConfig_SerialConfig_size 26 #define ModuleConfig_SerialConfig_size 26

View File

@@ -82,6 +82,9 @@ typedef enum _PortNum {
Maintained by GitHub user GUVWAF. Maintained by GitHub user GUVWAF.
Project files at https://github.com/GUVWAF/Meshtasticator */ Project files at https://github.com/GUVWAF/Meshtasticator */
PortNum_SIMULATOR_APP = 69, PortNum_SIMULATOR_APP = 69,
/* Provides a traceroute functionality to show the route a packet towards
a certain destination would take on the mesh. */
PortNum_TRACEROUTE_APP = 70,
/* Private applications should use portnums >= 256. /* Private applications should use portnums >= 256.
To simplify initial development and testing you can use "PRIVATE_APP" To simplify initial development and testing you can use "PRIVATE_APP"
in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) */ in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) */

View File

@@ -69,10 +69,10 @@ typedef struct _EnvironmentMetrics {
/* Types of Measurements the telemetry module is equipped to handle */ /* Types of Measurements the telemetry module is equipped to handle */
typedef struct _Telemetry { typedef struct _Telemetry {
/* This is usually not sent over the mesh (to save space), but it is sent /* This is usually not sent over the mesh (to save space), but it is sent
from the phone so that the local device can set its RTC If it is sent over from the phone so that the local device can set its RTC If it is sent over
the mesh (because there are devices on the mesh without GPS), it will only the mesh (because there are devices on the mesh without GPS), it will only
be sent by devices which has a hardware GPS clock (IE Mobile Phone). be sent by devices which has a hardware GPS clock (IE Mobile Phone).
seconds since 1970 */ seconds since 1970 */
uint32_t time; uint32_t time;
pb_size_t which_variant; pb_size_t which_variant;

View File

@@ -1,7 +1,7 @@
#include "mesh/http/WiFiAPClient.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "RTC.h" #include "RTC.h"
#include "concurrency/Periodic.h" #include "concurrency/Periodic.h"
#include "mesh/http/WiFiAPClient.h"
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
#include "mesh/http/WebServer.h" #include "mesh/http/WebServer.h"
@@ -37,9 +37,9 @@ bool APStartupComplete = 0;
unsigned long lastrun_ntp = 0; unsigned long lastrun_ntp = 0;
static bool needReconnect = true; // If we create our reconnector, run it once at the beginning bool needReconnect = true; // If we create our reconnector, run it once at the beginning
static Periodic *wifiReconnect; Periodic *wifiReconnect;
static int32_t reconnectWiFi() static int32_t reconnectWiFi()
{ {
@@ -56,29 +56,15 @@ static int32_t reconnectWiFi()
// Make sure we clear old connection credentials // Make sure we clear old connection credentials
WiFi.disconnect(false, true); WiFi.disconnect(false, true);
DEBUG_MSG("... Reconnecting to WiFi access point %s\n",wifiName); DEBUG_MSG("Reconnecting to WiFi access point %s\n",wifiName);
int n = WiFi.scanNetworks();
if (n > 0) {
for (int i = 0; i < n; ++i) {
DEBUG_MSG("Found WiFi network %s, signal strength %d\n", WiFi.SSID(i).c_str(), WiFi.RSSI(i));
yield();
}
WiFi.mode(WIFI_MODE_STA);
WiFi.begin(wifiName, wifiPsw);
} else {
DEBUG_MSG("No networks found during site survey. Rebooting MCU...\n");
screen->startRebootScreen();
rebootAtMsec = millis() + 5000;
}
WiFi.mode(WIFI_MODE_STA);
WiFi.begin(wifiName, wifiPsw);
} }
#ifndef DISABLE_NTP #ifndef DISABLE_NTP
if (WiFi.isConnected() && (((millis() - lastrun_ntp) > 43200000) || (lastrun_ntp == 0))) { // every 12 hours if (WiFi.isConnected() && (((millis() - lastrun_ntp) > 43200000) || (lastrun_ntp == 0))) { // every 12 hours
DEBUG_MSG("Updating NTP time\n"); DEBUG_MSG("Updating NTP time from %s\n",config.network.ntp_server);
if (timeClient.update()) { if (timeClient.update()) {
DEBUG_MSG("NTP Request Success - Setting RTCQualityNTP if needed\n"); DEBUG_MSG("NTP Request Success - Setting RTCQualityNTP if needed\n");
@@ -129,7 +115,7 @@ static void onNetworkConnected()
{ {
if (!APStartupComplete) { if (!APStartupComplete) {
// Start web server // Start web server
DEBUG_MSG("... Starting network services\n"); DEBUG_MSG("Starting network services\n");
// start mdns // start mdns
if (!MDNS.begin("Meshtastic")) { if (!MDNS.begin("Meshtastic")) {
@@ -168,6 +154,8 @@ bool initWifi()
createSSLCert(); createSSLCert();
esp_wifi_set_storage(WIFI_STORAGE_RAM); // Disable flash storage for WiFi credentials
if (!*wifiPsw) // Treat empty password as no password if (!*wifiPsw) // Treat empty password as no password
wifiPsw = NULL; wifiPsw = NULL;
@@ -194,7 +182,7 @@ bool initWifi()
WiFi.onEvent( WiFi.onEvent(
[](WiFiEvent_t event, WiFiEventInfo_t info) { [](WiFiEvent_t event, WiFiEventInfo_t info) {
Serial.print("\nWiFi lost connection. Reason: "); Serial.print("WiFi lost connection. Reason: ");
Serial.println(info.wifi_sta_disconnected.reason); Serial.println(info.wifi_sta_disconnected.reason);
/* /*
@@ -221,91 +209,137 @@ bool initWifi()
// Called by the Espressif SDK to // Called by the Espressif SDK to
static void WiFiEvent(WiFiEvent_t event) static void WiFiEvent(WiFiEvent_t event)
{ {
DEBUG_MSG("************ [WiFi-event] event: %d ************\n", event); DEBUG_MSG("WiFi-Event %d: ", event);
switch (event) { switch (event) {
case SYSTEM_EVENT_WIFI_READY: case ARDUINO_EVENT_WIFI_READY:
DEBUG_MSG("WiFi interface ready\n"); DEBUG_MSG("WiFi interface ready\n");
break; break;
case SYSTEM_EVENT_SCAN_DONE: case ARDUINO_EVENT_WIFI_SCAN_DONE:
DEBUG_MSG("Completed scan for access points\n"); DEBUG_MSG("Completed scan for access points\n");
break; break;
case SYSTEM_EVENT_STA_START: case ARDUINO_EVENT_WIFI_STA_START:
DEBUG_MSG("WiFi station started\n"); DEBUG_MSG("WiFi station started\n");
break; break;
case SYSTEM_EVENT_STA_STOP: case ARDUINO_EVENT_WIFI_STA_STOP:
DEBUG_MSG("WiFi station stopped\n"); DEBUG_MSG("WiFi station stopped\n");
break; break;
case SYSTEM_EVENT_STA_CONNECTED: case ARDUINO_EVENT_WIFI_STA_CONNECTED:
DEBUG_MSG("Connected to access point\n"); DEBUG_MSG("Connected to access point\n");
break; break;
case SYSTEM_EVENT_STA_DISCONNECTED: case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
DEBUG_MSG("Disconnected from WiFi access point\n"); DEBUG_MSG("Disconnected from WiFi access point\n");
WiFi.disconnect(false, true); WiFi.disconnect(false, true);
needReconnect = true; needReconnect = true;
wifiReconnect->setIntervalFromNow(1000); wifiReconnect->setIntervalFromNow(1000);
break; break;
case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:
DEBUG_MSG("Authentication mode of access point has changed\n"); DEBUG_MSG("Authentication mode of access point has changed\n");
break; break;
case SYSTEM_EVENT_STA_GOT_IP: case ARDUINO_EVENT_WIFI_STA_GOT_IP:
DEBUG_MSG("Obtained IP address: "); DEBUG_MSG("Obtained IP address: ");
Serial.println(WiFi.localIP()); Serial.println(WiFi.localIP());
onNetworkConnected(); onNetworkConnected();
break; break;
case SYSTEM_EVENT_STA_LOST_IP: case ARDUINO_EVENT_WIFI_STA_GOT_IP6:
DEBUG_MSG("Obtained IP6 address: ");
Serial.println(WiFi.localIPv6());
break;
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
DEBUG_MSG("Lost IP address and IP address is reset to 0\n"); DEBUG_MSG("Lost IP address and IP address is reset to 0\n");
WiFi.disconnect(false, true); WiFi.disconnect(false, true);
needReconnect = true; needReconnect = true;
wifiReconnect->setIntervalFromNow(1000); wifiReconnect->setIntervalFromNow(1000);
break; break;
case SYSTEM_EVENT_STA_WPS_ER_SUCCESS: case ARDUINO_EVENT_WPS_ER_SUCCESS:
DEBUG_MSG("WiFi Protected Setup (WPS): succeeded in enrollee mode\n"); DEBUG_MSG("WiFi Protected Setup (WPS): succeeded in enrollee mode\n");
break; break;
case SYSTEM_EVENT_STA_WPS_ER_FAILED: case ARDUINO_EVENT_WPS_ER_FAILED:
DEBUG_MSG("WiFi Protected Setup (WPS): failed in enrollee mode\n"); DEBUG_MSG("WiFi Protected Setup (WPS): failed in enrollee mode\n");
break; break;
case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT: case ARDUINO_EVENT_WPS_ER_TIMEOUT:
DEBUG_MSG("WiFi Protected Setup (WPS): timeout in enrollee mode\n"); DEBUG_MSG("WiFi Protected Setup (WPS): timeout in enrollee mode\n");
break; break;
case SYSTEM_EVENT_STA_WPS_ER_PIN: case ARDUINO_EVENT_WPS_ER_PIN:
DEBUG_MSG("WiFi Protected Setup (WPS): pin code in enrollee mode\n"); DEBUG_MSG("WiFi Protected Setup (WPS): pin code in enrollee mode\n");
break; break;
case SYSTEM_EVENT_AP_START: case ARDUINO_EVENT_WPS_ER_PBC_OVERLAP:
DEBUG_MSG("WiFi Protected Setup (WPS): push button overlap in enrollee mode\n");
break;
case ARDUINO_EVENT_WIFI_AP_START:
DEBUG_MSG("WiFi access point started\n"); DEBUG_MSG("WiFi access point started\n");
break; break;
case SYSTEM_EVENT_AP_STOP: case ARDUINO_EVENT_WIFI_AP_STOP:
DEBUG_MSG("WiFi access point stopped\n"); DEBUG_MSG("WiFi access point stopped\n");
break; break;
case SYSTEM_EVENT_AP_STACONNECTED: case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
DEBUG_MSG("Client connected\n"); DEBUG_MSG("Client connected\n");
break; break;
case SYSTEM_EVENT_AP_STADISCONNECTED: case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
DEBUG_MSG("Client disconnected\n"); DEBUG_MSG("Client disconnected\n");
break; break;
case SYSTEM_EVENT_AP_STAIPASSIGNED: case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED:
DEBUG_MSG("Assigned IP address to client\n"); DEBUG_MSG("Assigned IP address to client\n");
break; break;
case SYSTEM_EVENT_AP_PROBEREQRECVED: case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED:
DEBUG_MSG("Received probe request\n"); DEBUG_MSG("Received probe request\n");
break; break;
case SYSTEM_EVENT_GOT_IP6: case ARDUINO_EVENT_WIFI_AP_GOT_IP6:
DEBUG_MSG("IPv6 is preferred\n"); DEBUG_MSG("IPv6 is preferred\n");
break; break;
case SYSTEM_EVENT_ETH_START: case ARDUINO_EVENT_WIFI_FTM_REPORT:
DEBUG_MSG("Fast Transition Management report\n");
break;
case ARDUINO_EVENT_ETH_START:
DEBUG_MSG("Ethernet started\n"); DEBUG_MSG("Ethernet started\n");
break; break;
case SYSTEM_EVENT_ETH_STOP: case ARDUINO_EVENT_ETH_STOP:
DEBUG_MSG("Ethernet stopped\n"); DEBUG_MSG("Ethernet stopped\n");
break; break;
case SYSTEM_EVENT_ETH_CONNECTED: case ARDUINO_EVENT_ETH_CONNECTED:
DEBUG_MSG("Ethernet connected\n"); DEBUG_MSG("Ethernet connected\n");
break; break;
case SYSTEM_EVENT_ETH_DISCONNECTED: case ARDUINO_EVENT_ETH_DISCONNECTED:
DEBUG_MSG("Ethernet disconnected\n"); DEBUG_MSG("Ethernet disconnected\n");
break; break;
case SYSTEM_EVENT_ETH_GOT_IP: case ARDUINO_EVENT_ETH_GOT_IP:
DEBUG_MSG("Obtained IP address (SYSTEM_EVENT_ETH_GOT_IP)\n"); DEBUG_MSG("Obtained IP address (ARDUINO_EVENT_ETH_GOT_IP)\n");
break;
case ARDUINO_EVENT_ETH_GOT_IP6:
DEBUG_MSG("Obtained IP6 address (ARDUINO_EVENT_ETH_GOT_IP6)\n");
break;
case ARDUINO_EVENT_SC_SCAN_DONE:
DEBUG_MSG("SmartConfig: Scan done\n");
break;
case ARDUINO_EVENT_SC_FOUND_CHANNEL:
DEBUG_MSG("SmartConfig: Found channel\n");
break;
case ARDUINO_EVENT_SC_GOT_SSID_PSWD:
DEBUG_MSG("SmartConfig: Got SSID and password\n");
break;
case ARDUINO_EVENT_SC_SEND_ACK_DONE:
DEBUG_MSG("SmartConfig: Send ACK done\n");
break;
case ARDUINO_EVENT_PROV_INIT:
DEBUG_MSG("Provisioning: Init\n");
break;
case ARDUINO_EVENT_PROV_DEINIT:
DEBUG_MSG("Provisioning: Stopped\n");
break;
case ARDUINO_EVENT_PROV_START:
DEBUG_MSG("Provisioning: Started\n");
break;
case ARDUINO_EVENT_PROV_END:
DEBUG_MSG("Provisioning: End\n");
break;
case ARDUINO_EVENT_PROV_CRED_RECV:
DEBUG_MSG("Provisioning: Credentials received\n");
break;
case ARDUINO_EVENT_PROV_CRED_FAIL:
DEBUG_MSG("Provisioning: Credentials failed\n");
break;
case ARDUINO_EVENT_PROV_CRED_SUCCESS:
DEBUG_MSG("Provisioning: Credentials success\n");
break; break;
default: default:
break; break;

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "configuration.h" #include "configuration.h"
#include "concurrency/Periodic.h"
#include <Arduino.h> #include <Arduino.h>
#include <functional> #include <functional>
@@ -8,6 +9,9 @@
#include <WiFi.h> #include <WiFi.h>
#endif #endif
extern bool needReconnect;
extern concurrency::Periodic *wifiReconnect;
/// @return true if wifi is now in use /// @return true if wifi is now in use
bool initWifi(); bool initWifi();

View File

@@ -112,12 +112,15 @@ bool AdminModule::handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *r)
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
if (BleOta::getOtaAppVersion().isEmpty()) { if (BleOta::getOtaAppVersion().isEmpty()) {
DEBUG_MSG("No OTA firmware available, scheduling regular reboot in %d seconds\n", s); DEBUG_MSG("No OTA firmware available, scheduling regular reboot in %d seconds\n", s);
screen->startRebootScreen();
}else{ }else{
screen->startFirmwareUpdateScreen();
BleOta::switchToOtaApp(); BleOta::switchToOtaApp();
DEBUG_MSG("Rebooting to OTA in %d seconds\n", s); DEBUG_MSG("Rebooting to OTA in %d seconds\n", s);
} }
#else #else
DEBUG_MSG("Not on ESP32, scheduling regular reboot in %d seconds\n", s); DEBUG_MSG("Not on ESP32, scheduling regular reboot in %d seconds\n", s);
screen->startRebootScreen();
#endif #endif
rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000); rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000);
break; break;

View File

@@ -11,41 +11,9 @@
#define PIN_BUZZER false #define PIN_BUZZER false
#endif #endif
//#include <assert.h>
/* /*
Documentation: Documentation:
https://github.com/meshtastic/firmware/blob/master/docs/software/modules/ExternalNotificationModule.md https://meshtastic.org/docs/settings/moduleconfig/external-notification
This module supports:
https://github.com/meshtastic/firmware/issues/654
Quick reference:
moduleConfig.external_notification.enabled
0 = Disabled (Default)
1 = Enabled
moduleConfig.external_notification.active
0 = Active Low (Default)
1 = Active High
moduleConfig.external_notification.alert_message
0 = Disabled (Default)
1 = Alert when a text message comes
moduleConfig.external_notification.alert_bell
0 = Disabled (Default)
1 = Alert when the bell character is received
moduleConfig.external_notification.output
GPIO of the output. (Default = 13)
moduleConfig.external_notification.output_ms
Amount of time in ms for the alert. Default is 1000.
*/ */
// Default configurations // Default configurations
@@ -58,56 +26,97 @@
#define ASCII_BELL 0x07 #define ASCII_BELL 0x07
bool externalCurrentState = 0; ExternalNotificationModule *externalNotificationModule;
uint32_t externalTurnedOn = 0;
bool externalCurrentState[3] = {};
uint32_t externalTurnedOn[3] = {};
int32_t ExternalNotificationModule::runOnce() int32_t ExternalNotificationModule::runOnce()
{ {
/* if (moduleConfig.external_notification.use_pwm || !moduleConfig.external_notification.enabled) {
Uncomment the preferences below if you want to use the module return INT32_MAX; // we don't need this thread here...
without having to configure it from the PythonAPI or WebUI. } else {
*/
// moduleConfig.external_notification.enabled = 1;
// moduleConfig.external_notification.alert_message = 1;
// moduleConfig.external_notification.active = 1;
// moduleConfig.external_notification.alert_bell = 1;
// moduleConfig.external_notification.output_ms = 1000;
// moduleConfig.external_notification.output = 13;
if (externalCurrentState && !moduleConfig.external_notification.use_pwm) {
// If the output is turned on, turn it back off after the given period of time. // If the output is turned on, turn it back off after the given period of time.
if (externalTurnedOn + (moduleConfig.external_notification.output_ms if (nagCycleCutoff != UINT32_MAX) {
if (externalTurnedOn[0] + (moduleConfig.external_notification.output_ms
? moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms
: EXT_NOTIFICATION_MODULE_OUTPUT_MS) < : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) {
millis()) { getExternal(0) ? setExternalOff(0) : setExternalOn(0);
DEBUG_MSG("Turning off external notification\n"); }
setExternalOff(); if (externalTurnedOn[1] + (moduleConfig.external_notification.output_ms
? moduleConfig.external_notification.output_ms
: EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) {
getExternal(1) ? setExternalOff(1) : setExternalOn(1);
}
if (externalTurnedOn[2] + (moduleConfig.external_notification.output_ms
? moduleConfig.external_notification.output_ms
: EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) {
getExternal(2) ? setExternalOff(2) : setExternalOn(2);
}
}
if (nagCycleCutoff < millis()) {
nagCycleCutoff = UINT32_MAX;
DEBUG_MSG("Turning off external notification: ");
for (int i = 0; i < 2; i++) {
if (getExternal(i)) {
setExternalOff(i);
externalTurnedOn[i] = 0;
DEBUG_MSG("%d ", i);
}
}
DEBUG_MSG("\n");
return INT32_MAX; // save cycles till we're needed again
} }
}
if (moduleConfig.external_notification.use_pwm)
return INT32_MAX; // we don't need this thread here...
else
return 25; return 25;
}
} }
void ExternalNotificationModule::setExternalOn() void ExternalNotificationModule::setExternalOn(uint8_t index)
{ {
externalCurrentState = 1; externalCurrentState[index] = 1;
externalTurnedOn = millis(); externalTurnedOn[index] = millis();
digitalWrite(output, switch(index) {
(moduleConfig.external_notification.active ? true : false)); case 1:
if(moduleConfig.external_notification.output_vibra)
digitalWrite(moduleConfig.external_notification.output_vibra, true);
break;
case 2:
if(moduleConfig.external_notification.output_buzzer)
digitalWrite(moduleConfig.external_notification.output_buzzer, true);
break;
default:
digitalWrite(output, (moduleConfig.external_notification.active ? true : false));
break;
}
} }
void ExternalNotificationModule::setExternalOff() void ExternalNotificationModule::setExternalOff(uint8_t index)
{ {
externalCurrentState = 0; externalCurrentState[index] = 0;
externalTurnedOn[index] = millis();
digitalWrite(output, switch(index) {
(moduleConfig.external_notification.active ? false : true)); case 1:
if(moduleConfig.external_notification.output_vibra)
digitalWrite(moduleConfig.external_notification.output_vibra, false);
break;
case 2:
if(moduleConfig.external_notification.output_buzzer)
digitalWrite(moduleConfig.external_notification.output_buzzer, false);
break;
default:
digitalWrite(output, (moduleConfig.external_notification.active ? false : true));
break;
}
}
bool ExternalNotificationModule::getExternal(uint8_t index)
{
return externalCurrentState[index];
} }
// -------- // --------
@@ -121,13 +130,18 @@ ExternalNotificationModule::ExternalNotificationModule()
without having to configure it from the PythonAPI or WebUI. without having to configure it from the PythonAPI or WebUI.
*/ */
// moduleConfig.external_notification.enabled = 1; // moduleConfig.external_notification.enabled = true;
// moduleConfig.external_notification.alert_message = 1; // moduleConfig.external_notification.alert_message = true;
// moduleConfig.external_notification.alert_message_buzzer = true;
// moduleConfig.external_notification.alert_message_vibra = true;
// moduleConfig.external_notification.active = 1; // moduleConfig.external_notification.active = true;
// moduleConfig.external_notification.alert_bell = 1; // moduleConfig.external_notification.alert_bell = 1;
// moduleConfig.external_notification.output_ms = 1000; // moduleConfig.external_notification.output_ms = 1000;
// moduleConfig.external_notification.output = 13; // moduleConfig.external_notification.output = 4; // RAK4631 IO4
// moduleConfig.external_notification.output_buzzer = 10; // RAK4631 IO6
// moduleConfig.external_notification.output_vibra = 28; // RAK4631 IO7
// moduleConfig.external_notification.nag_timeout = 300;
if (moduleConfig.external_notification.enabled) { if (moduleConfig.external_notification.enabled) {
@@ -141,8 +155,20 @@ ExternalNotificationModule::ExternalNotificationModule()
// Set the direction of a pin // Set the direction of a pin
DEBUG_MSG("Using Pin %i in digital mode\n", output); DEBUG_MSG("Using Pin %i in digital mode\n", output);
pinMode(output, OUTPUT); pinMode(output, OUTPUT);
// Turn off the pin setExternalOff(0);
setExternalOff(); externalTurnedOn[0] = 0;
if(moduleConfig.external_notification.output_vibra) {
DEBUG_MSG("Using Pin %i for vibra motor\n", moduleConfig.external_notification.output_vibra);
pinMode(moduleConfig.external_notification.output_vibra, OUTPUT);
setExternalOff(1);
externalTurnedOn[1] = 0;
}
if(moduleConfig.external_notification.output_buzzer) {
DEBUG_MSG("Using Pin %i for buzzer\n", moduleConfig.external_notification.output_buzzer);
pinMode(moduleConfig.external_notification.output_buzzer, OUTPUT);
setExternalOff(2);
externalTurnedOn[2] = 0;
}
} else { } else {
config.device.buzzer_gpio = config.device.buzzer_gpio config.device.buzzer_gpio = config.device.buzzer_gpio
? config.device.buzzer_gpio ? config.device.buzzer_gpio
@@ -163,17 +189,53 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp)
if (getFrom(&mp) != 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. // Check if the message contains a bell character. Don't do this loop for every pin, just once.
// Need to know if and how this could be a problem. auto &p = mp.decoded;
bool containsBell = false;
for (int i = 0; i < p.payload.size; i++) {
if (p.payload.bytes[i] == ASCII_BELL) {
containsBell = true;
}
}
if (moduleConfig.external_notification.alert_bell) { if (moduleConfig.external_notification.alert_bell) {
auto &p = mp.decoded; if (containsBell) {
DEBUG_MSG("externalNotificationModule - Notification Bell\n"); DEBUG_MSG("externalNotificationModule - Notification Bell\n");
for (int i = 0; i < p.payload.size; i++) { if (!moduleConfig.external_notification.use_pwm) {
if (p.payload.bytes[i] == ASCII_BELL) { setExternalOn(0);
if (!moduleConfig.external_notification.use_pwm) { if (moduleConfig.external_notification.nag_timeout) {
setExternalOn(); nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else { } else {
playBeep(); nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
}
// run_once now
} else {
playBeep();
}
}
}
if (!moduleConfig.external_notification.use_pwm) {
if (moduleConfig.external_notification.alert_bell_vibra) {
if (containsBell) {
DEBUG_MSG("externalNotificationModule - Notification Bell (Vibra)\n");
setExternalOn(1);
if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else {
nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
}
}
}
if (moduleConfig.external_notification.alert_bell_buzzer) {
if (containsBell) {
DEBUG_MSG("externalNotificationModule - Notification Bell (Buzzer)\n");
setExternalOn(2);
if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else {
nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
} }
} }
} }
@@ -182,11 +244,39 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp)
if (moduleConfig.external_notification.alert_message) { if (moduleConfig.external_notification.alert_message) {
DEBUG_MSG("externalNotificationModule - Notification Module\n"); DEBUG_MSG("externalNotificationModule - Notification Module\n");
if (!moduleConfig.external_notification.use_pwm) { if (!moduleConfig.external_notification.use_pwm) {
setExternalOn(); setExternalOn(0);
if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else {
nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
}
} else { } else {
playBeep(); playBeep();
} }
} }
if (!moduleConfig.external_notification.use_pwm) {
if (moduleConfig.external_notification.alert_message_vibra) {
DEBUG_MSG("externalNotificationModule - Notification Module (Vibra)\n");
setExternalOn(1);
if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else {
nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
}
}
if (moduleConfig.external_notification.alert_message_buzzer) {
DEBUG_MSG("externalNotificationModule - Notification Module (Buzzer)\n");
setExternalOn(2);
if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else {
nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
}
}
}
setIntervalFromNow(0); // run once so we know if we should do something
} }
} else { } else {

View File

@@ -17,18 +17,19 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
public: public:
ExternalNotificationModule(); ExternalNotificationModule();
void setExternalOn(); uint32_t nagCycleCutoff = UINT32_MAX;
void setExternalOff();
void getExternal(); void setExternalOn(uint8_t index = 0);
void setExternalOff(uint8_t index = 0);
bool getExternal(uint8_t index = 0);
protected: protected:
// virtual MeshPacket *allocReply();
/** Called to handle a particular incoming message /** Called to handle a particular incoming message
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for it @return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for it
*/ */
virtual ProcessMessage handleReceived(const MeshPacket &mp) override; virtual ProcessMessage handleReceived(const MeshPacket &mp) override;
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
}; };
extern ExternalNotificationModule *externalNotificationModule;

View File

@@ -11,6 +11,7 @@
#include "modules/ReplyModule.h" #include "modules/ReplyModule.h"
#include "modules/RoutingModule.h" #include "modules/RoutingModule.h"
#include "modules/TextMessageModule.h" #include "modules/TextMessageModule.h"
#include "modules/TraceRouteModule.h"
#include "modules/WaypointModule.h" #include "modules/WaypointModule.h"
#if HAS_TELEMETRY #if HAS_TELEMETRY
#include "modules/Telemetry/DeviceTelemetry.h" #include "modules/Telemetry/DeviceTelemetry.h"
@@ -40,6 +41,7 @@ void setupModules()
positionModule = new PositionModule(); positionModule = new PositionModule();
waypointModule = new WaypointModule(); waypointModule = new WaypointModule();
textMessageModule = new TextMessageModule(); textMessageModule = new TextMessageModule();
traceRouteModule = new TraceRouteModule();
// Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance // Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance
// to a global variable. // to a global variable.
@@ -67,7 +69,7 @@ void setupModules()
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
// Only run on an esp32 based device. // Only run on an esp32 based device.
audioModule = new AudioModule(); audioModule = new AudioModule();
new ExternalNotificationModule(); externalNotificationModule = new ExternalNotificationModule();
storeForwardModule = new StoreForwardModule(); storeForwardModule = new StoreForwardModule();

View File

@@ -82,7 +82,7 @@ SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio")
int32_t SerialModule::runOnce() int32_t SerialModule::runOnce()
{ {
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) #if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2)
/* /*
Uncomment the preferences below if you want to use the module Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI. without having to configure it from the PythonAPI or WebUI.
@@ -214,7 +214,6 @@ int32_t SerialModule::runOnce()
MeshPacket *SerialModuleRadio::allocReply() MeshPacket *SerialModuleRadio::allocReply()
{ {
auto reply = allocDataPacket(); // Allocate a packet for sending auto reply = allocDataPacket(); // Allocate a packet for sending
return reply; return reply;
@@ -236,7 +235,7 @@ void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp) ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp)
{ {
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) #if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2)
if (moduleConfig.serial.enabled) { if (moduleConfig.serial.enabled) {
auto &p = mp.decoded; auto &p = mp.decoded;
@@ -266,7 +265,12 @@ ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp)
if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_DEFAULT || if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_DEFAULT ||
moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_SIMPLE) { moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_SIMPLE) {
Serial2.printf("%s", p.payload.bytes); Serial2.printf("%s", p.payload.bytes);
} else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG) {
NodeInfo *node = nodeDB.getNode(getFrom(&mp));
String sender = (node && node->has_user) ? node->user.short_name : "???";
Serial2.println();
Serial2.printf("%s: %s", sender, p.payload.bytes);
Serial2.println();
} else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_PROTO) { } else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_PROTO) {
// TODO this needs to be implemented // TODO this needs to be implemented
} else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_NMEA) { } else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_NMEA) {

View File

@@ -0,0 +1,86 @@
#include "TraceRouteModule.h"
#include "MeshService.h"
#include "FloodingRouter.h"
TraceRouteModule *traceRouteModule;
bool TraceRouteModule::handleReceivedProtobuf(const MeshPacket &mp, RouteDiscovery *r)
{
// Only handle a response
if (mp.decoded.request_id) {
printRoute(r, mp.to, mp.from);
}
return false; // let it be handled by RoutingModule
}
void TraceRouteModule::updateRoute(MeshPacket* p)
{
auto &incoming = p->decoded;
// Only append an ID for the request (one way)
if (!incoming.request_id) {
RouteDiscovery scratch;
RouteDiscovery *updated = NULL;
memset(&scratch, 0, sizeof(scratch));
pb_decode_from_bytes(incoming.payload.bytes, incoming.payload.size, RouteDiscovery_fields, &scratch);
updated = &scratch;
appendMyID(updated);
printRoute(updated, p->from, NODENUM_BROADCAST);
// Set updated route to the payload of the to be flooded packet
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), RouteDiscovery_fields, updated);
}
}
void TraceRouteModule::appendMyID(RouteDiscovery* updated)
{
// Length of route array can normally not be exceeded due to the max. hop_limit of 7
if (updated->route_count < sizeof(updated->route)/sizeof(updated->route[0])) {
updated->route[updated->route_count] = myNodeInfo.my_node_num;
updated->route_count += 1;
} else {
DEBUG_MSG("WARNING: Route exceeded maximum hop limit, are you bridging networks?\n");
}
}
void TraceRouteModule::printRoute(RouteDiscovery* r, uint32_t origin, uint32_t dest)
{
DEBUG_MSG("Route traced:\n");
DEBUG_MSG("0x%x --> ", origin);
for (uint8_t i=0; i<r->route_count; i++) {
DEBUG_MSG("0x%x --> ", r->route[i]);
}
if (dest != NODENUM_BROADCAST) DEBUG_MSG("0x%x\n", dest); else DEBUG_MSG("...\n");
}
MeshPacket* TraceRouteModule::allocReply()
{
assert(currentRequest);
// Copy the payload of the current request
auto req = *currentRequest;
auto &p = req.decoded;
RouteDiscovery scratch;
RouteDiscovery *updated = NULL;
memset(&scratch, 0, sizeof(scratch));
pb_decode_from_bytes(p.payload.bytes, p.payload.size, RouteDiscovery_fields, &scratch);
updated = &scratch;
printRoute(updated, req.from, req.to);
// Create a MeshPacket with this payload and set it as the reply
MeshPacket* reply = allocDataProtobuf(*updated);
return reply;
}
TraceRouteModule::TraceRouteModule() : ProtobufModule("traceroute", PortNum_TRACEROUTE_APP, RouteDiscovery_fields) {
ourPortNum = PortNum_TRACEROUTE_APP;
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include "ProtobufModule.h"
/**
* A module that traces the route to a certain destination node
*/
class TraceRouteModule : public ProtobufModule<RouteDiscovery>
{
public:
TraceRouteModule();
// Let FloodingRouter call updateRoute upon rebroadcasting a TraceRoute request
friend class FloodingRouter;
protected:
bool handleReceivedProtobuf(const MeshPacket &mp, RouteDiscovery *r) override;
virtual MeshPacket *allocReply() override;
/* Call before rebroadcasting a RouteDiscovery payload in order to update
the route array containing the IDs of nodes this packet went through */
void updateRoute(MeshPacket* p);
private:
// Call to add your ID to the route array of a RouteDiscovery message
void appendMyID(RouteDiscovery *r);
/* Call to print the route array of a RouteDiscovery message.
Set origin to where the request came from.
Set dest to the ID of its destination, or NODENUM_BROADCAST if it has not yet arrived there. */
void printRoute(RouteDiscovery* r, uint32_t origin, uint32_t dest);
};
extern TraceRouteModule *traceRouteModule;

View File

@@ -10,6 +10,10 @@
#include <assert.h> #include <assert.h>
#ifdef OLED_RU
#include "graphics/fonts/OLEDDisplayFontsRU.h"
#endif
/* /*
AudioModule AudioModule
A interface to send raw codec2 audio data over the mesh network. Based on the example code from the ESP32_codec2 project. A interface to send raw codec2 audio data over the mesh network. Based on the example code from the ESP32_codec2 project.
@@ -41,48 +45,72 @@ AudioModule *audioModule;
#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR(x) #define YIELD_FROM_ISR(x) portYIELD_FROM_ISR(x)
#endif #endif
//int16_t 1KHz sine test tone #if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS)
int16_t Sine1KHz[8] = { -21210 , -30000, -21210, 0 , 21210 , 30000 , 21210, 0 }; // The screen is bigger so use bigger fonts
int Sine1KHz_index = 0; #define FONT_SMALL ArialMT_Plain_16
#define FONT_MEDIUM ArialMT_Plain_24
#define FONT_LARGE ArialMT_Plain_24
#else
#ifdef OLED_RU
#define FONT_SMALL ArialMT_Plain_10_RU
#else
#define FONT_SMALL ArialMT_Plain_10
#endif
#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)
#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE)
void run_codec2(void* parameter) void run_codec2(void* parameter)
{ {
// 4 bytes of header in each frame Kennung hex c0 de c2 plus the bitrate // 4 bytes of header in each frame hex c0 de c2 plus the bitrate
memcpy(audioModule->tx_encode_frame,&audioModule->tx_header,sizeof(audioModule->tx_header)); memcpy(audioModule->tx_encode_frame,&audioModule->tx_header,sizeof(audioModule->tx_header));
DEBUG_MSG("Starting codec2 task\n");
while (true) { while (true) {
uint32_t tcount = ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(10000)); uint32_t tcount = ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(10000));
if (tcount != 0) { if (tcount != 0) {
if (audioModule->radio_state == RadioState::tx) { if (audioModule->radio_state == RadioState::tx) {
// Apply the TX filter
for (int i = 0; i < audioModule->adc_buffer_size; i++) for (int i = 0; i < audioModule->adc_buffer_size; i++)
audioModule->speech[i] = (int16_t)hp_filter.Update((float)audioModule->speech[i]); audioModule->speech[i] = (int16_t)hp_filter.Update((float)audioModule->speech[i]);
// Encode the audio
codec2_encode(audioModule->codec2, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index, audioModule->speech); codec2_encode(audioModule->codec2, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index, audioModule->speech);
//increment the pointer where the encoded frame must be saved
audioModule->tx_encode_frame_index += audioModule->encode_codec_size; audioModule->tx_encode_frame_index += audioModule->encode_codec_size;
//If it this is reached we have a ready trasnmission frame
if (audioModule->tx_encode_frame_index == (audioModule->encode_frame_size + sizeof(audioModule->tx_header))) if (audioModule->tx_encode_frame_index == (audioModule->encode_frame_size + sizeof(audioModule->tx_header)))
{ {
//Transmit it DEBUG_MSG("Sending %d codec2 bytes\n", audioModule->encode_frame_size);
DEBUG_MSG("♪♫♪ Sending %d codec2 bytes\n", audioModule->encode_frame_size);
audioModule->sendPayload(); audioModule->sendPayload();
audioModule->tx_encode_frame_index = sizeof(audioModule->tx_header); audioModule->tx_encode_frame_index = sizeof(audioModule->tx_header);
} }
} }
if (audioModule->radio_state == RadioState::rx) { if (audioModule->radio_state == RadioState::rx) {
//Make a cycle to get each codec2 frame from the received frame size_t bytesOut = 0;
for (int i = 0; i < audioModule->rx_encode_frame_index; i += audioModule->encode_codec_size) if (memcmp(audioModule->rx_encode_frame, &audioModule->tx_header, sizeof(audioModule->tx_header)) == 0) {
{ for (int i = 4; i < audioModule->rx_encode_frame_index; i += audioModule->encode_codec_size)
//Decode the codec2 frame {
codec2_decode(audioModule->codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i); codec2_decode(audioModule->codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i);
size_t bytesOut = 0; i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500));
i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500)); }
} else {
// if the buffer header does not match our own codec, make a temp decoding setup.
CODEC2* tmp_codec2 = codec2_create(audioModule->rx_encode_frame[3]);
codec2_set_lpc_post_filter(tmp_codec2, 1, 0, 0.8, 0.2);
int tmp_encode_codec_size = (codec2_bits_per_frame(tmp_codec2) + 7) / 8;
int tmp_adc_buffer_size = codec2_samples_per_frame(tmp_codec2);
for (int i = 4; i < audioModule->rx_encode_frame_index; i += tmp_encode_codec_size)
{
codec2_decode(tmp_codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i);
i2s_write(I2S_PORT, &audioModule->output_buffer, tmp_adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500));
}
codec2_destroy(tmp_codec2);
} }
} }
} }
@@ -91,8 +119,15 @@ void run_codec2(void* parameter)
AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule")
{ {
// moduleConfig.audio.codec2_enabled = true;
// moduleConfig.audio.i2s_ws = 13;
// moduleConfig.audio.i2s_sd = 15;
// moduleConfig.audio.i2s_din = 22;
// moduleConfig.audio.i2s_sck = 14;
// moduleConfig.audio.ptt_pin = 39;
if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) {
DEBUG_MSG("♪♫♪ Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); DEBUG_MSG("Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1);
codec2 = codec2_create((moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); codec2 = codec2_create((moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1);
memcpy(tx_header.magic,c2_magic,sizeof(c2_magic)); memcpy(tx_header.magic,c2_magic,sizeof(c2_magic));
tx_header.mode = (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1; tx_header.mode = (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1;
@@ -104,7 +139,31 @@ AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP),
DEBUG_MSG(" using %d frames of %d bytes for a total payload length of %d bytes\n", encode_frame_num, encode_codec_size, encode_frame_size); DEBUG_MSG(" using %d frames of %d bytes for a total payload length of %d bytes\n", encode_frame_num, encode_codec_size, encode_frame_size);
xTaskCreate(&run_codec2, "codec2_task", 30000, NULL, 5, &codec2HandlerTask); xTaskCreate(&run_codec2, "codec2_task", 30000, NULL, 5, &codec2HandlerTask);
} else { } else {
DEBUG_MSG("♪♫♪ Codec2 disabled (AudioModule %d, Region %s, permitted %d)\n", moduleConfig.audio.codec2_enabled, myRegion->name, myRegion->audioPermitted); DEBUG_MSG("Codec2 disabled (AudioModule %d, Region %s, permitted %d)\n", moduleConfig.audio.codec2_enabled, myRegion->name, myRegion->audioPermitted);
}
}
void AudioModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
displayedNodeNum = 0; // Not currently showing a node pane
char buffer[50];
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
display->drawStringf(0 + x, 0 + y, buffer, "Codec2 Mode %d Audio", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1);
display->setColor(WHITE);
display->setFont(FONT_LARGE);
display->setTextAlignment(TEXT_ALIGN_CENTER);
switch (radio_state) {
case RadioState::tx:
display->drawString(display->getWidth() / 2 + x, (display->getHeight() - FONT_HEIGHT_SMALL) / 2 + y, "PTT");
break;
default:
display->drawString(display->getWidth() / 2 + x, (display->getHeight() - FONT_HEIGHT_SMALL) / 2 + y, "Receive");
break;
} }
} }
@@ -114,7 +173,7 @@ int32_t AudioModule::runOnce()
esp_err_t res; esp_err_t res;
if (firstTime) { if (firstTime) {
// Set up I2S Processor configuration. This will produce 16bit samples at 8 kHz instead of 12 from the ADC // Set up I2S Processor configuration. This will produce 16bit samples at 8 kHz instead of 12 from the ADC
DEBUG_MSG("♪♫♪ Initializing I2S SD: %d DIN: %d WS: %d SCK:%d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck); DEBUG_MSG("Initializing I2S SD: %d DIN: %d WS: %d SCK: %d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck);
i2s_config_t i2s_config = { i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | (moduleConfig.audio.i2s_sd ? I2S_MODE_RX : 0) | (moduleConfig.audio.i2s_din ? I2S_MODE_TX : 0)), .mode = (i2s_mode_t)(I2S_MODE_MASTER | (moduleConfig.audio.i2s_sd ? I2S_MODE_RX : 0) | (moduleConfig.audio.i2s_din ? I2S_MODE_TX : 0)),
.sample_rate = 8000, .sample_rate = 8000,
@@ -130,7 +189,7 @@ int32_t AudioModule::runOnce()
}; };
res = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); res = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
if(res != ESP_OK) if(res != ESP_OK)
DEBUG_MSG("♪♫♪ Failed to install I2S driver: %d\n", res); DEBUG_MSG("Failed to install I2S driver: %d\n", res);
const i2s_pin_config_t pin_config = { const i2s_pin_config_t pin_config = {
.bck_io_num = moduleConfig.audio.i2s_sck, .bck_io_num = moduleConfig.audio.i2s_sck,
@@ -140,36 +199,41 @@ int32_t AudioModule::runOnce()
}; };
res = i2s_set_pin(I2S_PORT, &pin_config); res = i2s_set_pin(I2S_PORT, &pin_config);
if(res != ESP_OK) if(res != ESP_OK)
DEBUG_MSG("♪♫♪ Failed to set I2S pin config: %d\n", res); DEBUG_MSG("Failed to set I2S pin config: %d\n", res);
res = i2s_start(I2S_PORT); res = i2s_start(I2S_PORT);
if(res != ESP_OK) if(res != ESP_OK)
DEBUG_MSG("♪♫♪ Failed to start I2S: %d\n", res); DEBUG_MSG("Failed to start I2S: %d\n", res);
radio_state = RadioState::rx; radio_state = RadioState::rx;
// Configure PTT input // Configure PTT input
DEBUG_MSG("♪♫♪ Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); DEBUG_MSG("Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN);
pinMode(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN, INPUT); pinMode(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN, INPUT);
firstTime = false; firstTime = false;
} else { } else {
UIFrameEvent e = {false, true};
// Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive. // Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive.
if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) { if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) {
if (radio_state == RadioState::rx) { if (radio_state == RadioState::rx) {
DEBUG_MSG("♪♫♪ PTT pressed, switching to TX\n"); DEBUG_MSG("PTT pressed, switching to TX\n");
radio_state = RadioState::tx; radio_state = RadioState::tx;
e.frameChanged = true;
this->notifyObservers(&e);
} }
} else { } else {
if (radio_state == RadioState::tx) { if (radio_state == RadioState::tx) {
DEBUG_MSG("PTT released, switching to RX\n");
if (tx_encode_frame_index > sizeof(tx_header)) { if (tx_encode_frame_index > sizeof(tx_header)) {
// Send the incomplete frame // Send the incomplete frame
DEBUG_MSG("♪♫♪ Sending %d codec2 bytes (incomplete)\n", tx_encode_frame_index); DEBUG_MSG("Sending %d codec2 bytes (incomplete)\n", tx_encode_frame_index);
sendPayload(); sendPayload();
} }
DEBUG_MSG("♪♫♪ PTT released, switching to RX\n");
tx_encode_frame_index = sizeof(tx_header); tx_encode_frame_index = sizeof(tx_header);
radio_state = RadioState::rx; radio_state = RadioState::rx;
e.frameChanged = true;
this->notifyObservers(&e);
} }
} }
if (radio_state == RadioState::tx) { if (radio_state == RadioState::tx) {
@@ -194,7 +258,7 @@ int32_t AudioModule::runOnce()
} }
return 100; return 100;
} else { } else {
DEBUG_MSG("♪♫♪ Audio Module Disabled\n"); DEBUG_MSG("Audio Module Disabled\n");
return INT32_MAX; return INT32_MAX;
} }
@@ -202,17 +266,25 @@ int32_t AudioModule::runOnce()
MeshPacket *AudioModule::allocReply() MeshPacket *AudioModule::allocReply()
{ {
auto reply = allocDataPacket(); // Allocate a packet for sending auto reply = allocDataPacket();
return reply; return reply;
} }
bool AudioModule::shouldDraw()
{
if (!moduleConfig.audio.codec2_enabled) {
return false;
}
return (radio_state == RadioState::tx);
}
void AudioModule::sendPayload(NodeNum dest, bool wantReplies) void AudioModule::sendPayload(NodeNum dest, bool wantReplies)
{ {
MeshPacket *p = allocReply(); MeshPacket *p = allocReply();
p->to = dest; p->to = dest;
p->decoded.want_response = wantReplies; p->decoded.want_response = wantReplies;
p->want_ack = false; // Audio is shoot&forget. TODO: Is this really suppressing retransmissions? p->want_ack = false; // Audio is shoot&forget. No need to wait for ACKs.
p->priority = MeshPacket_Priority_MAX; // Audio is important, because realtime p->priority = MeshPacket_Priority_MAX; // Audio is important, because realtime
p->decoded.payload.size = tx_encode_frame_index; p->decoded.payload.size = tx_encode_frame_index;

View File

@@ -10,6 +10,8 @@
#include <functional> #include <functional>
#include <codec2.h> #include <codec2.h>
#include <ButterworthFilter.h> #include <ButterworthFilter.h>
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
enum RadioState { standby, rx, tx }; enum RadioState { standby, rx, tx };
@@ -28,7 +30,7 @@ struct c2_header {
#define AUDIO_MODULE_RX_BUFFER 128 #define AUDIO_MODULE_RX_BUFFER 128
#define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 #define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700
class AudioModule : public SinglePortModule, private concurrency::OSThread class AudioModule : public SinglePortModule, public Observable<const UIFrameEvent *>, private concurrency::OSThread
{ {
public: public:
unsigned char rx_encode_frame[Constants_DATA_PAYLOAD_LEN] = {}; unsigned char rx_encode_frame[Constants_DATA_PAYLOAD_LEN] = {};
@@ -50,6 +52,8 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread
AudioModule(); AudioModule();
bool shouldDraw();
/** /**
* Send our payload into the mesh * Send our payload into the mesh
*/ */
@@ -63,6 +67,15 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread
virtual MeshPacket *allocReply() override; virtual MeshPacket *allocReply() override;
virtual bool wantUIFrame() override { return this->shouldDraw(); }
virtual Observable<const UIFrameEvent *>* getUIFrameObservable() override { return this; }
#if !HAS_SCREEN
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
#else
virtual void drawFrame(
OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
#endif
/** Called to handle a particular incoming message /** Called to handle a particular incoming message
* @return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for it * @return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for it
*/ */

View File

@@ -16,52 +16,50 @@ StoreForwardModule *storeForwardModule;
int32_t StoreForwardModule::runOnce() int32_t StoreForwardModule::runOnce()
{ {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
if (moduleConfig.store_forward.enabled && is_server) {
// Send out the message queue.
if (this->busy) {
// Only send packets if the channel is less than 25% utilized.
if (airTime->channelUtilizationPercent() < polite_channel_util_percent) {
if (moduleConfig.store_forward.enabled) { // DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index);
storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
if (config.device.role == Config_DeviceConfig_Role_ROUTER) { if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
strcpy(this->routerMessage, "** S&F - Done");
// Send out the message queue. storeForwardModule->sendMessage(this->busyTo, this->routerMessage);
if (this->busy) {
// Only send packets if the channel is less than 25% utilized.
if (airTime->channelUtilizationPercent() < polite_channel_util_percent) {
// DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index);
storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
strcpy(this->routerMessage, "** S&F - Done");
storeForwardModule->sendMessage(this->busyTo, this->routerMessage);
// DEBUG_MSG("--- --- --- In busy loop - Done \n");
this->packetHistoryTXQueue_index = 0;
this->busy = false;
} else {
this->packetHistoryTXQueue_index++;
}
// DEBUG_MSG("--- --- --- In busy loop - Done \n");
this->packetHistoryTXQueue_index = 0;
this->busy = false;
} else { } else {
DEBUG_MSG("Channel utilization is too high. Skipping this opportunity to send and will retry later.\n"); this->packetHistoryTXQueue_index++;
} }
} else {
DEBUG_MSG("Channel utilization is too high. Retrying later.\n");
} }
DEBUG_MSG("SF myNodeInfo.bitrate = %f bytes / sec\n", myNodeInfo.bitrate); DEBUG_MSG("SF bitrate = %f bytes / sec\n", myNodeInfo.bitrate);
return (this->packetTimeMax); } else if (millis() - lastHeartbeat > 300000) {
} else { lastHeartbeat = millis();
DEBUG_MSG("Store & Forward Module - Disabled (is_router = false)\n"); DEBUG_MSG("Sending heartbeat\n");
StoreAndForward sf;
sf.rr = StoreAndForward_RequestResponse_ROUTER_HEARTBEAT;
sf.has_heartbeat = true;
sf.heartbeat.period = 300;
sf.heartbeat.secondary = 0; // TODO we always have one primary router for now
return (INT32_MAX); MeshPacket *p = allocDataProtobuf(sf);
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = MeshPacket_Priority_MIN;
service.sendToMesh(p);
} }
return (this->packetTimeMax);
} else {
DEBUG_MSG("Store & Forward Module - Disabled\n");
return (INT32_MAX);
} }
#endif #endif
return (INT32_MAX); return (INT32_MAX);
} }
@@ -76,12 +74,7 @@ void StoreForwardModule::populatePSRAM()
https://learn.upesy.com/en/programmation/psram.html#psram-tab https://learn.upesy.com/en/programmation/psram.html#psram-tab
*/ */
DEBUG_MSG("Before PSRAM initilization:\n"); DEBUG_MSG("Before PSRAM initilization: heap %d/%d PSRAM %d/%d\n", ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getFreePsram(), ESP.getPsramSize());
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());
this->packetHistoryTXQueue = this->packetHistoryTXQueue =
static_cast<PacketHistoryStruct *>(ps_calloc(this->historyReturnMax, sizeof(PacketHistoryStruct))); static_cast<PacketHistoryStruct *>(ps_calloc(this->historyReturnMax, sizeof(PacketHistoryStruct)));
@@ -93,19 +86,12 @@ void StoreForwardModule::populatePSRAM()
this->packetHistory = static_cast<PacketHistoryStruct *>(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct))); this->packetHistory = static_cast<PacketHistoryStruct *>(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct)));
DEBUG_MSG("After PSRAM initilization:\n"); DEBUG_MSG("After PSRAM initilization: heap %d/%d PSRAM %d/%d\n", ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getFreePsram(), ESP.getPsramSize());
DEBUG_MSG("numberOfPackets for packetHistory - %u\n", numberOfPackets);
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("Store and Forward Stats:\n");
DEBUG_MSG(" numberOfPackets for packetHistory - %u\n", numberOfPackets);
} }
void StoreForwardModule::historyReport() void StoreForwardModule::historyReport()
{ {
DEBUG_MSG("Iterating through the message history...\n");
DEBUG_MSG("Message history contains %u records\n", this->packetHistoryCurrent); DEBUG_MSG("Message history contains %u records\n", this->packetHistoryCurrent);
} }
@@ -246,8 +232,8 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp)
DEBUG_MSG("--- S&F Received something\n"); DEBUG_MSG("--- S&F Received something\n");
// The router node should not be sending messages as a client. // The router node should not be sending messages as a client. Unless he is a ROUTER_CLIENT
if (getFrom(&mp) != nodeDB.getNodeNum()) { if ((getFrom(&mp) != nodeDB.getNodeNum()) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) {
if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) { if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) {
DEBUG_MSG("Packet came from - PortNum_TEXT_MESSAGE_APP\n"); DEBUG_MSG("Packet came from - PortNum_TEXT_MESSAGE_APP\n");
@@ -264,6 +250,7 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp)
} else { } else {
storeForwardModule->historySend(1000 * 60, getFrom(&mp)); storeForwardModule->historySend(1000 * 60, getFrom(&mp));
} }
} else if ((p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && (p.payload.bytes[2] == 'm') && } else if ((p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && (p.payload.bytes[2] == 'm') &&
(p.payload.bytes[3] == 0x00)) { (p.payload.bytes[3] == 0x00)) {
strlcpy(this->routerMessage, strlcpy(this->routerMessage,
@@ -278,14 +265,12 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp)
} }
} else if (mp.decoded.portnum == PortNum_STORE_FORWARD_APP) { } else if (mp.decoded.portnum == PortNum_STORE_FORWARD_APP) {
DEBUG_MSG("Packet came from an PortNum_STORE_FORWARD_APP port %u\n", mp.decoded.portnum);
} else { } else {
DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum); DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum);
} }
} }
} else {
DEBUG_MSG("Store & Forward Module - Disabled\n");
} }
#endif #endif
@@ -293,92 +278,107 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp)
return ProcessMessage::CONTINUE; // Let others look at this message also if they want return ProcessMessage::CONTINUE; // Let others look at this message also if they want
} }
ProcessMessage StoreForwardModule::handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p) bool StoreForwardModule::handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p)
{ {
if (!moduleConfig.store_forward.enabled) { if (!moduleConfig.store_forward.enabled) {
// If this module is not enabled in any capacity, don't handle the packet, and allow other modules to consume // If this module is not enabled in any capacity, don't handle the packet, and allow other modules to consume
return ProcessMessage::CONTINUE; return false;
} }
if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) { if (mp.decoded.portnum != PortNum_STORE_FORWARD_APP) {
DEBUG_MSG("Packet came from an PortNum_TEXT_MESSAGE_APP port %u\n", mp.decoded.portnum); DEBUG_MSG("Packet came from port %u\n", mp.decoded.portnum);
return ProcessMessage::CONTINUE; return false;
} else if (mp.decoded.portnum == PortNum_STORE_FORWARD_APP) {
DEBUG_MSG("Packet came from an PortNum_STORE_FORWARD_APP port %u\n", mp.decoded.portnum);
} else { } else {
DEBUG_MSG("Packet came from an UNKNOWN port %u\n", mp.decoded.portnum); DEBUG_MSG("Packet came from PortNum_STORE_FORWARD_APP port %u\n", mp.decoded.portnum);
return ProcessMessage::CONTINUE;
}
switch (p->rr) { switch (p->rr) {
case StoreAndForward_RequestResponse_CLIENT_ERROR: case StoreAndForward_RequestResponse_CLIENT_ERROR:
// Do nothing if(is_server) {
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_ERROR\n"); // Do nothing
break; DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_ERROR\n");
}
break;
case StoreAndForward_RequestResponse_CLIENT_HISTORY: case StoreAndForward_RequestResponse_CLIENT_HISTORY:
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_HISTORY\n"); if(is_server) {
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_HISTORY\n");
// Send the last 60 minutes of messages.
if (this->busy) {
strcpy(this->routerMessage, "** S&F - Busy. Try again shortly.");
storeForwardModule->sendMessage(getFrom(&mp), this->routerMessage);
} else {
storeForwardModule->historySend(1000 * 60, getFrom(&mp));
}
}
break;
// Send the last 60 minutes of messages. case StoreAndForward_RequestResponse_CLIENT_PING:
if (this->busy) { if(is_server) {
strcpy(this->routerMessage, "** S&F - Busy. Try again shortly."); // Do nothing
storeForwardModule->sendMessage(getFrom(&mp), this->routerMessage); DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PING\n");
} else { }
storeForwardModule->historySend(1000 * 60, getFrom(&mp)); break;
case StoreAndForward_RequestResponse_CLIENT_PONG:
if(is_server) {
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PONG\n");
}
break;
case StoreAndForward_RequestResponse_CLIENT_STATS:
if(is_server) {
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_STATS\n");
}
break;
case StoreAndForward_RequestResponse_ROUTER_BUSY:
if(is_client) {
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_BUSY\n");
}
break;
case StoreAndForward_RequestResponse_ROUTER_ERROR:
if(is_client) {
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_ERROR\n");
}
break;
case StoreAndForward_RequestResponse_ROUTER_HEARTBEAT:
if(is_client) {
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_HEARTBEAT\n");
}
break;
case StoreAndForward_RequestResponse_ROUTER_PING:
if(is_client) {
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PING\n");
}
break;
case StoreAndForward_RequestResponse_ROUTER_PONG:
if(is_client) {
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PONG\n");
}
break;
default:
assert(0); // unexpected state - FIXME, make an error code and reboot
} }
break;
case StoreAndForward_RequestResponse_CLIENT_PING:
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PING\n");
break;
case StoreAndForward_RequestResponse_CLIENT_PONG:
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PONG\n");
break;
case StoreAndForward_RequestResponse_CLIENT_STATS:
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_STATS\n");
break;
case StoreAndForward_RequestResponse_ROUTER_BUSY:
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_BUSY\n");
break;
case StoreAndForward_RequestResponse_ROUTER_ERROR:
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_ERROR\n");
break;
case StoreAndForward_RequestResponse_ROUTER_HEARTBEAT:
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_HEARTBEAT\n");
break;
case StoreAndForward_RequestResponse_ROUTER_PING:
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PING\n");
break;
case StoreAndForward_RequestResponse_ROUTER_PONG:
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PONG\n");
break;
default:
assert(0); // unexpected state - FIXME, make an error code and reboot
} }
return ProcessMessage::STOP; // There's no need for others to look at this message. return true; // There's no need for others to look at this message.
} }
StoreForwardModule::StoreForwardModule() StoreForwardModule::StoreForwardModule()
: SinglePortModule("StoreForwardModule", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("StoreForwardModule") : concurrency::OSThread("StoreForwardModule"), ProtobufModule("StoreForward", PortNum_STORE_FORWARD_APP, &StoreAndForward_msg)
{ {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
@@ -397,9 +397,9 @@ StoreForwardModule::StoreForwardModule()
if (moduleConfig.store_forward.enabled) { if (moduleConfig.store_forward.enabled) {
// Router // Router
if (config.device.role == Config_DeviceConfig_Role_ROUTER) { if ((config.device.role == Config_DeviceConfig_Role_ROUTER) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) {
DEBUG_MSG("Initializing Store & Forward Module - Enabled as Router\n"); DEBUG_MSG("Initializing Store & Forward Module in Router mode\n");
if (ESP.getPsramSize()) { if (ESP.getPsramSize() > 0) {
if (ESP.getFreePsram() >= 1024 * 1024) { if (ESP.getFreePsram() >= 1024 * 1024) {
// Do the startup here // Do the startup here
@@ -416,26 +416,27 @@ StoreForwardModule::StoreForwardModule()
if (moduleConfig.store_forward.records) if (moduleConfig.store_forward.records)
this->records = moduleConfig.store_forward.records; this->records = moduleConfig.store_forward.records;
// Maximum number of records to store in memory // send heartbeat advertising?
if (moduleConfig.store_forward.heartbeat) if (moduleConfig.store_forward.heartbeat)
this->heartbeat = moduleConfig.store_forward.heartbeat; this->heartbeat = moduleConfig.store_forward.heartbeat;
// Popupate PSRAM with our data structures. // Popupate PSRAM with our data structures.
this->populatePSRAM(); this->populatePSRAM();
is_server = true;
} else { } else {
DEBUG_MSG("Device has less than 1M of PSRAM free. Aborting startup.\n"); DEBUG_MSG("Device has less than 1M of PSRAM free.\n");
DEBUG_MSG("Store & Forward Module - Aborting Startup.\n"); DEBUG_MSG("Store & Forward Module - disabling server.\n");
} }
} else { } else {
DEBUG_MSG("Device doesn't have PSRAM.\n"); DEBUG_MSG("Device doesn't have PSRAM.\n");
DEBUG_MSG("Store & Forward Module - Aborting Startup.\n"); DEBUG_MSG("Store & Forward Module - disabling server.\n");
} }
// Client // Client
} else { }
DEBUG_MSG("Initializing Store & Forward Module - Enabled as Client\n"); if ((config.device.role == Config_DeviceConfig_Role_CLIENT) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) {
is_client = true;
DEBUG_MSG("Initializing Store & Forward Module in Client mode\n");
} }
} }
#endif #endif

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "SinglePortModule.h" #include "ProtobufModule.h"
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "mesh/generated/storeforward.pb.h" #include "mesh/generated/storeforward.pb.h"
@@ -18,9 +18,8 @@ struct PacketHistoryStruct {
pb_size_t payload_size; pb_size_t payload_size;
}; };
class StoreForwardModule : public SinglePortModule, private concurrency::OSThread class StoreForwardModule : private concurrency::OSThread, public ProtobufModule<StoreAndForward>
{ {
// bool firstTime = 1;
bool busy = 0; bool busy = 0;
uint32_t busyTo = 0; uint32_t busyTo = 0;
char routerMessage[Constants_DATA_PAYLOAD_LEN] = {0}; char routerMessage[Constants_DATA_PAYLOAD_LEN] = {0};
@@ -34,7 +33,12 @@ class StoreForwardModule : public SinglePortModule, private concurrency::OSThrea
uint32_t packetHistoryTXQueue_size = 0; uint32_t packetHistoryTXQueue_size = 0;
uint32_t packetHistoryTXQueue_index = 0; uint32_t packetHistoryTXQueue_index = 0;
uint32_t packetTimeMax = 2000; uint32_t packetTimeMax = 5000;
unsigned long lastHeartbeat = 0;
bool is_client = false;
bool is_server = false;
public: public:
StoreForwardModule(); StoreForwardModule();
@@ -78,7 +82,7 @@ class StoreForwardModule : public SinglePortModule, private concurrency::OSThrea
it it
*/ */
virtual ProcessMessage handleReceived(const MeshPacket &mp) override; virtual ProcessMessage handleReceived(const MeshPacket &mp) override;
virtual ProcessMessage handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p); virtual bool handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p);
}; };

View File

@@ -7,6 +7,7 @@
#include "mesh/Router.h" #include "mesh/Router.h"
#include "mesh/generated/mqtt.pb.h" #include "mesh/generated/mqtt.pb.h"
#include "mesh/generated/telemetry.pb.h" #include "mesh/generated/telemetry.pb.h"
#include "mesh/http/WiFiAPClient.h"
#include "sleep.h" #include "sleep.h"
#if HAS_WIFI #if HAS_WIFI
#include <WiFi.h> #include <WiFi.h>
@@ -20,6 +21,10 @@ String statusTopic = "msh/2/stat/";
String cryptTopic = "msh/2/c/"; // msh/2/c/CHANNELID/NODEID String cryptTopic = "msh/2/c/"; // msh/2/c/CHANNELID/NODEID
String jsonTopic = "msh/2/json/"; // msh/2/json/CHANNELID/NODEID String jsonTopic = "msh/2/json/"; // msh/2/json/CHANNELID/NODEID
static MemoryDynamic<ServiceEnvelope> staticMqttPool;
Allocator<ServiceEnvelope> &mqttPool = staticMqttPool;
void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length) void MQTT::mqttCallback(char *topic, byte *payload, unsigned int length)
{ {
mqtt->onPublish(topic, payload, length); mqtt->onPublish(topic, payload, length);
@@ -61,7 +66,27 @@ void MQTT::onPublish(char *topic, byte *payload, unsigned int length)
} else { } else {
DEBUG_MSG("JSON Ignoring downlink message we originally sent.\n"); DEBUG_MSG("JSON Ignoring downlink message we originally sent.\n");
} }
} else { } else if ((json.find("sender") != json.end()) && (json.find("payload") != json.end()) && (json.find("type") != json.end()) && json["type"]->IsString() && (json["type"]->AsString().compare("sendposition") == 0)) {
//invent the "sendposition" type for a valid envelope
if (json["payload"]->IsObject() && json["type"]->IsString() && (json["sender"]->AsString().compare(owner.id) != 0)) {
JSONObject posit;
posit=json["payload"]->AsObject(); //get nested JSON Position
Position pos =Position_init_default;
pos.latitude_i=posit["latitude_i"]->AsNumber();
pos.longitude_i=posit["longitude_i"]->AsNumber();
pos.altitude=posit["altitude"]->AsNumber();
pos.time=posit["time"]->AsNumber();
// construct protobuf data packet using POSITION, send it to the mesh
MeshPacket *p = router->allocForSending();
p->decoded.portnum = PortNum_POSITION_APP;
p->decoded.payload.size=pb_encode_to_bytes(p->decoded.payload.bytes,sizeof(p->decoded.payload.bytes),Position_fields, &pos); //make the Data protobuf from position
service.sendToMesh(p, RX_SRC_LOCAL);
} else {
DEBUG_MSG("JSON Ignoring downlink message we originally sent.\n");
}
} else{
DEBUG_MSG("JSON Received payload on MQTT but not a valid envelope\n"); DEBUG_MSG("JSON Received payload on MQTT but not a valid envelope\n");
} }
} else { } else {
@@ -101,7 +126,7 @@ void mqttInit()
new MQTT(); new MQTT();
} }
MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient) MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient), mqttQueue(MAX_MQTT_QUEUE)
{ {
assert(!mqtt); assert(!mqtt);
mqtt = this; mqtt = this;
@@ -148,14 +173,23 @@ void MQTT::reconnect()
DEBUG_MSG("MQTT connected\n"); DEBUG_MSG("MQTT connected\n");
enabled = true; // Start running background process again enabled = true; // Start running background process again
runASAP = true; runASAP = true;
reconnectCount = 0;
/// FIXME, include more information in the status text /// FIXME, include more information in the status text
bool ok = pubSub.publish(myStatus.c_str(), "online", true); bool ok = pubSub.publish(myStatus.c_str(), "online", true);
DEBUG_MSG("published %d\n", ok); DEBUG_MSG("published %d\n", ok);
sendSubscriptions(); sendSubscriptions();
} else } else {
DEBUG_MSG("Failed to contact MQTT server...\n"); DEBUG_MSG("Failed to contact MQTT server (%d/10)...\n",reconnectCount);
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
if (reconnectCount > 9) {
needReconnect = true;
wifiReconnect->setIntervalFromNow(1000);
}
#endif
reconnectCount++;
}
} }
} }
@@ -211,8 +245,35 @@ int32_t MQTT::runOnce()
if (wantConnection) { if (wantConnection) {
reconnect(); reconnect();
// If we succeeded, start reading rapidly, else try again in 30 seconds (TCP connections are EXPENSIVE so try rarely) // If we succeeded, empty the queue one by one and start reading rapidly, else try again in 30 seconds (TCP connections are EXPENSIVE so try rarely)
return pubSub.connected() ? 20 : 30000; if (pubSub.connected()) {
if (!mqttQueue.isEmpty()) {
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
ServiceEnvelope *env = mqttQueue.dequeuePtr(0);
static uint8_t bytes[MeshPacket_size + 64];
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), ServiceEnvelope_fields, env);
String topic = cryptTopic + env->channel_id + "/" + owner.id;
DEBUG_MSG("publish %s, %u bytes from queue\n", topic.c_str(), numBytes);
pubSub.publish(topic.c_str(), bytes, numBytes, false);
if (moduleConfig.mqtt.json_enabled) {
// handle json topic
auto jsonString = this->downstreamPacketToJson(env->packet);
if (jsonString.length() != 0) {
String topicJson = jsonTopic + env->channel_id + "/" + owner.id;
DEBUG_MSG("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), jsonString.c_str());
pubSub.publish(topicJson.c_str(), jsonString.c_str(), false);
}
}
mqttPool.release(env);
}
return 20;
} else {
return 30000;
}
} else } else
return 5000; // If we don't want connection now, check again in 5 secs return 5000; // If we don't want connection now, check again in 5 secs
} else { } else {
@@ -231,33 +292,48 @@ void MQTT::onSend(const MeshPacket &mp, ChannelIndex chIndex)
{ {
auto &ch = channels.getByIndex(chIndex); auto &ch = channels.getByIndex(chIndex);
// don't bother sending if not connected... if (ch.settings.uplink_enabled) {
if (pubSub.connected() && ch.settings.uplink_enabled) {
const char *channelId = channels.getGlobalId(chIndex); // FIXME, for now we just use the human name for the channel const char *channelId = channels.getGlobalId(chIndex); // FIXME, for now we just use the human name for the channel
ServiceEnvelope env = ServiceEnvelope_init_default; ServiceEnvelope *env = mqttPool.allocZeroed();
env.channel_id = (char *)channelId; env->channel_id = (char *)channelId;
env.gateway_id = owner.id; env->gateway_id = owner.id;
env.packet = (MeshPacket *)&mp; env->packet = (MeshPacket *)&mp;
// FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets // don't bother sending if not connected...
static uint8_t bytes[MeshPacket_size + 64]; if (pubSub.connected()) {
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), ServiceEnvelope_fields, &env);
String topic = cryptTopic + channelId + "/" + owner.id; // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets
DEBUG_MSG("publish %s, %u bytes\n", topic.c_str(), numBytes); static uint8_t bytes[MeshPacket_size + 64];
size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), ServiceEnvelope_fields, env);
pubSub.publish(topic.c_str(), bytes, numBytes, false); String topic = cryptTopic + channelId + "/" + owner.id;
DEBUG_MSG("publish %s, %u bytes\n", topic.c_str(), numBytes);
if (moduleConfig.mqtt.json_enabled) { pubSub.publish(topic.c_str(), bytes, numBytes, false);
// handle json topic
auto jsonString = this->downstreamPacketToJson((MeshPacket *)&mp); if (moduleConfig.mqtt.json_enabled) {
if (jsonString.length() != 0) { // handle json topic
String topicJson = jsonTopic + channelId + "/" + owner.id; auto jsonString = this->downstreamPacketToJson((MeshPacket *)&mp);
DEBUG_MSG("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), jsonString.c_str()); if (jsonString.length() != 0) {
pubSub.publish(topicJson.c_str(), jsonString.c_str(), false); String topicJson = jsonTopic + channelId + "/" + owner.id;
DEBUG_MSG("JSON publish message to %s, %u bytes: %s\n", topicJson.c_str(), jsonString.length(), jsonString.c_str());
pubSub.publish(topicJson.c_str(), jsonString.c_str(), false);
}
} }
} else {
DEBUG_MSG("MQTT not connected, queueing packet\n");
if (mqttQueue.numFree() == 0) {
DEBUG_MSG("NOTE: MQTT queue is full, discarding oldest\n");
ServiceEnvelope *d = mqttQueue.dequeuePtr(0);
if (d)
mqttPool.release(d);
}
// make a copy of serviceEnvelope and queue it
ServiceEnvelope *copied = mqttPool.allocCopy(*env);
assert(mqttQueue.enqueue(copied, 0));
} }
mqttPool.release(env);
} }
} }

View File

@@ -4,6 +4,7 @@
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "mesh/Channels.h" #include "mesh/Channels.h"
#include "mesh/generated/mqtt.pb.h"
#include <PubSubClient.h> #include <PubSubClient.h>
#if HAS_WIFI #if HAS_WIFI
#include <WiFiClient.h> #include <WiFiClient.h>
@@ -12,6 +13,8 @@
#include <EthernetClient.h> #include <EthernetClient.h>
#endif #endif
#define MAX_MQTT_QUEUE 32
/** /**
* Our wrapper/singleton for sending/receiving MQTT "udp" packets. This object isolates the MQTT protocol implementation from * Our wrapper/singleton for sending/receiving MQTT "udp" packets. This object isolates the MQTT protocol implementation from
* the two components that use it: MQTTPlugin and MQTTSimInterface. * the two components that use it: MQTTPlugin and MQTTSimInterface.
@@ -52,6 +55,10 @@ class MQTT : private concurrency::OSThread
bool connected(); bool connected();
protected: protected:
PointerQueue<ServiceEnvelope> mqttQueue;
int reconnectCount = 0;
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
private: private:

View File

@@ -3,7 +3,9 @@
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "main.h" #include "main.h"
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
#include "nimble/NimbleBluetooth.h" #include "nimble/NimbleBluetooth.h"
#endif
#include "BleOta.h" #include "BleOta.h"
#include "mesh/http/WiFiAPClient.h" #include "mesh/http/WiFiAPClient.h"
@@ -16,13 +18,9 @@
#include <nvs_flash.h> #include <nvs_flash.h>
#include "soc/rtc.h" #include "soc/rtc.h"
#if !defined(CONFIG_IDF_TARGET_ESP32S2)
NimbleBluetooth *nimbleBluetooth; NimbleBluetooth *nimbleBluetooth;
void getMacAddr(uint8_t *dmac)
{
assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
}
void setBluetoothEnable(bool on) { void setBluetoothEnable(bool on) {
if (!isWifiAvailable() && config.bluetooth.enabled == true) { if (!isWifiAvailable() && config.bluetooth.enabled == true) {
@@ -36,6 +34,15 @@ void setBluetoothEnable(bool on) {
} }
} }
} }
#else
void setBluetoothEnable(bool on) { }
void updateBatteryLevel(uint8_t level) { }
#endif
void getMacAddr(uint8_t *dmac)
{
assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
}
#ifdef HAS_32768HZ #ifdef HAS_32768HZ
#define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk) #define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk)

View File

@@ -40,6 +40,9 @@ class Power : private concurrency::OSThread
private: private:
uint8_t low_voltage_counter; uint8_t low_voltage_counter;
#ifdef DEBUG_HEAP
uint32_t lastheap;
#endif
}; };
extern Power *power; extern Power *power;

View File

@@ -332,6 +332,8 @@ void enableModemSleep()
#if CONFIG_IDF_TARGET_ESP32S3 #if CONFIG_IDF_TARGET_ESP32S3
esp32_config.max_freq_mhz = CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ; esp32_config.max_freq_mhz = CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ;
#elif CONFIG_IDF_TARGET_ESP32S2
esp32_config.max_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ;
#else #else
esp32_config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; esp32_config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
#endif #endif

View File

@@ -169,6 +169,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
IO4 <-> P0.04 (Arduino GPIO number 4) IO4 <-> P0.04 (Arduino GPIO number 4)
IO5 <-> P0.09 (Arduino GPIO number 9) IO5 <-> P0.09 (Arduino GPIO number 9)
IO6 <-> P0.10 (Arduino GPIO number 10) IO6 <-> P0.10 (Arduino GPIO number 10)
IO7 <-> P0.28 (Arduino GPIO number 28)
SW1 <-> P0.01 (Arduino GPIO number 1) SW1 <-> P0.01 (Arduino GPIO number 1)
A0 <-> P0.04/AIN2 (Arduino Analog A2 A0 <-> P0.04/AIN2 (Arduino Analog A2
A1 <-> P0.31/AIN7 (Arduino Analog A7 A1 <-> P0.31/AIN7 (Arduino Analog A7

View File

@@ -169,6 +169,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
IO4 <-> P0.04 (Arduino GPIO number 4) IO4 <-> P0.04 (Arduino GPIO number 4)
IO5 <-> P0.09 (Arduino GPIO number 9) IO5 <-> P0.09 (Arduino GPIO number 9)
IO6 <-> P0.10 (Arduino GPIO number 10) IO6 <-> P0.10 (Arduino GPIO number 10)
IO7 <-> P0.28 (Arduino GPIO number 28)
SW1 <-> P0.01 (Arduino GPIO number 1) SW1 <-> P0.01 (Arduino GPIO number 1)
A0 <-> P0.04/AIN2 (Arduino Analog A2 A0 <-> P0.04/AIN2 (Arduino Analog A2
A1 <-> P0.31/AIN7 (Arduino Analog A7 A1 <-> P0.31/AIN7 (Arduino Analog A7