Release Develop to Master

This commit is contained in:
Thomas Göttgens
2022-12-28 15:31:04 +01:00
parent d0243d2cd0
commit 14be4ee9f0
49 changed files with 711 additions and 397 deletions

47
boards/tlora-t3s3-v1.json Normal file
View File

@@ -0,0 +1,47 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld"
},
"core": "esp32",
"extra_flags": [
"-DLILYGO_T3S3_V1",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "dio",
"hwids": [
[
"0X303A",
"0x1001"
]
],
"mcu": "esp32s3",
"variant": "tlora-t3s3-v1"
},
"connectivity": [
"wifi"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "LilyGo TLora-T3S3-V1",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 921600
},
"url": "http://www.lilygo.cn/",
"vendor": "LilyGo"
}

View File

@@ -65,7 +65,8 @@ lib_deps =
https://github.com/meshtastic/ArduinoThread.git#72921ac222eed6f526ba1682023cee290d9aa1b3 https://github.com/meshtastic/ArduinoThread.git#72921ac222eed6f526ba1682023cee290d9aa1b3
nanopb/Nanopb@^0.4.6 nanopb/Nanopb@^0.4.6
erriez/ErriezCRC32@^1.0.1 erriez/ErriezCRC32@^1.0.1
jgromes/RadioLib@^5.5.0 ; jgromes/RadioLib@^5.5.1
https://github.com/jgromes/RadioLib.git#395844922c5d88d5db0481a9c91479931172428d
; Used for the code analysis in PIO Home / Inspect ; Used for the code analysis in PIO Home / Inspect
check_tool = cppcheck check_tool = cppcheck
@@ -81,6 +82,7 @@ framework = arduino
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
mprograms/QMC5883LCompass@^1.1.1 mprograms/QMC5883LCompass@^1.1.1
end2endzone/NonBlockingRTTTL@^1.3.0
https://github.com/meshtastic/SparkFun_ATECCX08a_Arduino_Library.git#52b5282639d08a8cbd4b748363089eed6102dc76 https://github.com/meshtastic/SparkFun_ATECCX08a_Arduino_Library.git#52b5282639d08a8cbd4b748363089eed6102dc76
build_flags = ${env.build_flags} -Os -DRADIOLIB_SPI_PARANOID=0 build_flags = ${env.build_flags} -Os -DRADIOLIB_SPI_PARANOID=0

View File

@@ -327,9 +327,7 @@ void PowerFSM_setup()
powerFSM.add_timed_transition(&stateON, &stateDARK, getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL, "Screen-on timeout"); powerFSM.add_timed_transition(&stateON, &stateDARK, getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL, "Screen-on timeout");
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
// On most boards we use light-sleep to be our main state, but on NRF52 we just stay in DARK
State *lowPowerState = &stateLS; State *lowPowerState = &stateLS;
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally) // We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
// See: https://github.com/meshtastic/firmware/issues/1071 // See: https://github.com/meshtastic/firmware/issues/1071

View File

@@ -25,7 +25,7 @@ void consolePrintf(const char *format, ...)
#endif #endif
} }
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port) SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), concurrency::OSThread("SerialConsole")
{ {
assert(!console); assert(!console);
console = this; console = this;
@@ -46,6 +46,10 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port)
emitRebooted(); emitRebooted();
} }
int32_t SerialConsole::runOnce()
{
return runOncePart();
}
// For the serial port we can't really detect if any client is on the other side, so instead just look for recent messages // For the serial port we can't really detect if any client is on the other side, so instead just look for recent messages
bool SerialConsole::checkIsConnected() bool SerialConsole::checkIsConnected()

View File

@@ -6,7 +6,7 @@
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs * Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs). * (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
*/ */
class SerialConsole : public StreamAPI, public RedirectablePrint class SerialConsole : public StreamAPI, public RedirectablePrint, private concurrency::OSThread
{ {
public: public:
SerialConsole(); SerialConsole();
@@ -24,6 +24,8 @@ class SerialConsole : public StreamAPI, public RedirectablePrint
return RedirectablePrint::write(c); return RedirectablePrint::write(c);
} }
virtual int32_t runOnce() override;
protected: protected:
/// Check the current underlying physical link to see if the client is currently connected /// Check the current underlying physical link to see if the client is currently connected

View File

@@ -35,7 +35,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mesh/Channels.h" #include "mesh/Channels.h"
#include "mesh/generated/deviceonly.pb.h" #include "mesh/generated/deviceonly.pb.h"
#include "modules/TextMessageModule.h" #include "modules/TextMessageModule.h"
#include "modules/ExternalNotificationModule.h"
#include "sleep.h" #include "sleep.h"
#include "target_specific.h" #include "target_specific.h"
#include "utils.h" #include "utils.h"
@@ -1071,7 +1071,13 @@ int32_t Screen::runOnce()
handleSetOn(false); handleSetOn(false);
break; break;
case Cmd::ON_PRESS: case Cmd::ON_PRESS:
handleOnPress(); // If a nag notification is running, stop it
if (externalNotificationModule->nagCycleCutoff != UINT32_MAX) {
externalNotificationModule->stopNow();
} else {
// Don't advance the screen if we just wanted to switch off the nag notification
handleOnPress();
}
break; break;
case Cmd::START_BLUETOOTH_PIN_SCREEN: case Cmd::START_BLUETOOTH_PIN_SCREEN:
handleStartBluetoothPinScreen(cmd.bluetooth_pin); handleStartBluetoothPinScreen(cmd.bluetooth_pin);
@@ -1400,7 +1406,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
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. Either prefix with Info or S&F Logo // Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo
if (moduleConfig.store_forward.enabled) { if (moduleConfig.store_forward.enabled) {
#if 0 #ifdef ARCH_ESP32
if (millis() - storeForwardModule->lastHeartbeat > (storeForwardModule->heartbeatInterval * 1200)) { //no heartbeat, overlap a bit if (millis() - storeForwardModule->lastHeartbeat > (storeForwardModule->heartbeatInterval * 1200)) { //no heartbeat, overlap a bit
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) #if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8, imgQuestionL1); display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8, imgQuestionL1);

View File

@@ -242,7 +242,6 @@ void setup()
digitalWrite(PIN_3V3_EN, 1); digitalWrite(PIN_3V3_EN, 1);
#endif #endif
// Currently only the tbeam has a PMU // Currently only the tbeam has a PMU
// PMU initialization needs to be placed before scanI2Cdevice // PMU initialization needs to be placed before scanI2Cdevice
power = new Power(); power = new Power();
@@ -462,17 +461,6 @@ if((config.lora.region == Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLo
} }
} }
if((config.lora.region != Config_LoRaConfig_RegionCode_LORA_24) && (rIf->wideLora())){
DEBUG_MSG("Warning: Radio chip only supports 2.4GHz LoRa. Adjusting Region.\n");
config.lora.region = Config_LoRaConfig_RegionCode_LORA_24;
nodeDB.saveToDisk(SEGMENT_CONFIG);
if(!rIf->reconfigure()) {
DEBUG_MSG("Reconfigure failed, rebooting\n");
screen->startRebootScreen();
rebootAtMsec = millis() + 5000;
}
}
#if HAS_WIFI || HAS_ETHERNET #if HAS_WIFI || HAS_ETHERNET
mqttInit(); mqttInit();
#endif #endif

View File

@@ -17,7 +17,7 @@ ErrorCode FloodingRouter::send(MeshPacket *p)
return Router::send(p); return Router::send(p);
} }
bool FloodingRouter::shouldFilterReceived(MeshPacket *p) bool FloodingRouter::shouldFilterReceived(const MeshPacket *p)
{ {
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
printPacket("Ignoring incoming msg, because we've already seen it", p); printPacket("Ignoring incoming msg, because we've already seen it", p);
@@ -34,7 +34,8 @@ void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c)
// do not flood direct message that is ACKed // do not flood direct message that is ACKed
DEBUG_MSG("Receiving an ACK not for me, but don't need to rebroadcast this direct message anymore.\n"); DEBUG_MSG("Receiving an ACK not for me, but don't need to rebroadcast this direct message anymore.\n");
Router::cancelSending(p->to, p->decoded.request_id); // cancel rebroadcast for this DM Router::cancelSending(p->to, p->decoded.request_id); // cancel rebroadcast for this DM
} else if ((p->to != getNodeNum()) && (p->hop_limit > 0) && (getFrom(p) != getNodeNum())) { }
if ((p->to != getNodeNum()) && (p->hop_limit > 0) && (getFrom(p) != getNodeNum())) {
if (p->id != 0) { if (p->id != 0) {
if (config.device.role != Config_DeviceConfig_Role_CLIENT_MUTE) { if (config.device.role != Config_DeviceConfig_Role_CLIENT_MUTE) {
MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it

View File

@@ -51,7 +51,7 @@ class FloodingRouter : public Router, protected PacketHistory
* Called immedately on receiption, before any further processing. * Called immedately on receiption, before any further processing.
* @return true to abandon the packet * @return true to abandon the packet
*/ */
virtual bool shouldFilterReceived(MeshPacket *p) override; virtual bool shouldFilterReceived(const MeshPacket *p) override;
/** /**
* Look for broadcasts we need to rebroadcast * Look for broadcasts we need to rebroadcast

View File

@@ -44,11 +44,11 @@ MeshPacket *MeshModule::allocAckNak(Routing_Error err, NodeNum to, PacketId idFr
// auto p = allocDataProtobuf(c); // auto p = allocDataProtobuf(c);
MeshPacket *p = router->allocForSending(); MeshPacket *p = router->allocForSending();
p->decoded.portnum = PortNum_ROUTING_APP; p->decoded.portnum = PortNum_ROUTING_APP;
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), Routing_fields, &c); p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &Routing_msg, &c);
p->priority = MeshPacket_Priority_ACK; p->priority = MeshPacket_Priority_ACK;
p->hop_limit = 0; // Assume just immediate neighbors for now p->hop_limit = config.lora.hop_limit; // Flood ACK back to original sender
p->to = to; p->to = to;
p->decoded.request_id = idFrom; p->decoded.request_id = idFrom;
p->channel = chIndex; p->channel = chIndex;

View File

@@ -401,7 +401,7 @@ bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_
void NodeDB::loadFromDisk() void NodeDB::loadFromDisk()
{ {
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM // static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
if (!loadProto(prefFileName, DeviceState_size, sizeof(devicestate), DeviceState_fields, &devicestate)) { if (!loadProto(prefFileName, DeviceState_size, sizeof(DeviceState), &DeviceState_msg, &devicestate)) {
installDefaultDeviceState(); // Our in RAM copy might now be corrupt installDefaultDeviceState(); // Our in RAM copy might now be corrupt
} else { } else {
if (devicestate.version < DEVICESTATE_MIN_VER) { if (devicestate.version < DEVICESTATE_MIN_VER) {
@@ -412,7 +412,7 @@ void NodeDB::loadFromDisk()
} }
} }
if (!loadProto(configFileName, LocalConfig_size, sizeof(LocalConfig), LocalConfig_fields, &config)) { if (!loadProto(configFileName, LocalConfig_size, sizeof(LocalConfig), &LocalConfig_msg, &config)) {
installDefaultConfig(); // Our in RAM copy might now be corrupt installDefaultConfig(); // Our in RAM copy might now be corrupt
} else { } else {
if (config.version < DEVICESTATE_MIN_VER) { if (config.version < DEVICESTATE_MIN_VER) {
@@ -423,7 +423,7 @@ void NodeDB::loadFromDisk()
} }
} }
if (!loadProto(moduleConfigFileName, LocalModuleConfig_size, sizeof(LocalModuleConfig), LocalModuleConfig_fields, &moduleConfig)) { if (!loadProto(moduleConfigFileName, LocalModuleConfig_size, sizeof(LocalModuleConfig), &LocalModuleConfig_msg, &moduleConfig)) {
installDefaultModuleConfig(); // Our in RAM copy might now be corrupt installDefaultModuleConfig(); // Our in RAM copy might now be corrupt
} else { } else {
if (moduleConfig.version < DEVICESTATE_MIN_VER) { if (moduleConfig.version < DEVICESTATE_MIN_VER) {
@@ -434,7 +434,7 @@ void NodeDB::loadFromDisk()
} }
} }
if (!loadProto(channelFileName, ChannelFile_size, sizeof(ChannelFile), ChannelFile_fields, &channelFile)) { if (!loadProto(channelFileName, ChannelFile_size, sizeof(ChannelFile), &ChannelFile_msg, &channelFile)) {
installDefaultChannels(); // Our in RAM copy might now be corrupt installDefaultChannels(); // Our in RAM copy might now be corrupt
} else { } else {
if (channelFile.version < DEVICESTATE_MIN_VER) { if (channelFile.version < DEVICESTATE_MIN_VER) {
@@ -445,12 +445,12 @@ void NodeDB::loadFromDisk()
} }
} }
if (loadProto(oemConfigFile, OEMStore_size, sizeof(OEMStore), OEMStore_fields, &oemStore)) if (loadProto(oemConfigFile, OEMStore_size, sizeof(OEMStore), &OEMStore_msg, &oemStore))
DEBUG_MSG("Loaded OEMStore\n"); DEBUG_MSG("Loaded OEMStore\n");
} }
/** Save a protobuf from a file, return true for success */ /** Save a protobuf from a file, return true for success */
bool saveProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, const void *dest_struct) bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct)
{ {
bool okay = false; bool okay = false;
#ifdef FSCom #ifdef FSCom
@@ -498,7 +498,7 @@ void NodeDB::saveChannelsToDisk()
#ifdef FSCom #ifdef FSCom
FSCom.mkdir("/prefs"); FSCom.mkdir("/prefs");
#endif #endif
saveProto(channelFileName, ChannelFile_size, sizeof(channelFile), ChannelFile_fields, &channelFile); saveProto(channelFileName, ChannelFile_size, &ChannelFile_msg, &channelFile);
} }
} }
@@ -508,7 +508,7 @@ void NodeDB::saveDeviceStateToDisk()
#ifdef FSCom #ifdef FSCom
FSCom.mkdir("/prefs"); FSCom.mkdir("/prefs");
#endif #endif
saveProto(prefFileName, DeviceState_size, sizeof(devicestate), DeviceState_fields, &devicestate); saveProto(prefFileName, DeviceState_size, &DeviceState_msg, &devicestate);
} }
} }
@@ -530,7 +530,7 @@ void NodeDB::saveToDisk(int saveWhat)
config.has_power = true; config.has_power = true;
config.has_network = true; config.has_network = true;
config.has_bluetooth = true; config.has_bluetooth = true;
saveProto(configFileName, LocalConfig_size, sizeof(config), LocalConfig_fields, &config); saveProto(configFileName, LocalConfig_size, &LocalConfig_msg, &config);
} }
if (saveWhat & SEGMENT_MODULECONFIG) { if (saveWhat & SEGMENT_MODULECONFIG) {
@@ -541,7 +541,7 @@ void NodeDB::saveToDisk(int saveWhat)
moduleConfig.has_serial = true; moduleConfig.has_serial = true;
moduleConfig.has_store_forward = true; moduleConfig.has_store_forward = true;
moduleConfig.has_telemetry = true; moduleConfig.has_telemetry = true;
saveProto(moduleConfigFileName, LocalModuleConfig_size, sizeof(moduleConfig), LocalModuleConfig_fields, &moduleConfig); saveProto(moduleConfigFileName, LocalModuleConfig_size, &LocalModuleConfig_msg, &moduleConfig);
} }
if (saveWhat & SEGMENT_CHANNELS) { if (saveWhat & SEGMENT_CHANNELS) {

View File

@@ -77,7 +77,7 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
// return (lastContactMsec != 0) && // return (lastContactMsec != 0) &&
memset(&toRadioScratch, 0, sizeof(toRadioScratch)); memset(&toRadioScratch, 0, sizeof(toRadioScratch));
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) { if (pb_decode_from_bytes(buf, bufLength, &ToRadio_msg, &toRadioScratch)) {
switch (toRadioScratch.which_payload_variant) { switch (toRadioScratch.which_payload_variant) {
case ToRadio_packet_tag: case ToRadio_packet_tag:
return handleToRadioPacket(toRadioScratch.packet); return handleToRadioPacket(toRadioScratch.packet);
@@ -291,7 +291,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
// Do we have a message from the mesh? // Do we have a message from the mesh?
if (fromRadioScratch.which_payload_variant != 0) { if (fromRadioScratch.which_payload_variant != 0) {
// Encapsulate as a FromRadio packet // Encapsulate as a FromRadio packet
size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch); size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, &FromRadio_msg, &fromRadioScratch);
DEBUG_MSG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payload_variant, numbytes); DEBUG_MSG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payload_variant, numbytes);
return numbytes; return numbytes;

View File

@@ -175,7 +175,7 @@ uint32_t RadioInterface::getRetransmissionMsec(const MeshPacket *p)
{ {
assert(slotTimeMsec); // Better be non zero assert(slotTimeMsec); // Better be non zero
static uint8_t bytes[MAX_RHPACKETLEN]; static uint8_t bytes[MAX_RHPACKETLEN];
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded); size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &Data_msg, &p->decoded);
uint32_t packetAirtime = getPacketTime(numbytes + sizeof(PacketHeader)); uint32_t packetAirtime = getPacketTime(numbytes + sizeof(PacketHeader));
// Make sure enough time has elapsed for this packet to be sent and an ACK is received. // Make sure enough time has elapsed for this packet to be sent and an ACK is received.
// DEBUG_MSG("Waiting for flooding message with airtime %d and slotTime is %d\n", packetAirtime, slotTimeMsec); // DEBUG_MSG("Waiting for flooding message with airtime %d and slotTime is %d\n", packetAirtime, slotTimeMsec);

View File

@@ -66,10 +66,5 @@ class RadioLibRF95: public SX1278 {
// since default current limit for SX126x/127x in updated RadioLib is 60mA // since default current limit for SX126x/127x in updated RadioLib is 60mA
// use the previous value // use the previous value
float currentLimit = 100; float currentLimit = 100;
#ifndef RADIOLIB_GODMODE
private:
#endif
}; };

View File

@@ -27,7 +27,7 @@ ErrorCode ReliableRouter::send(MeshPacket *p)
return FloodingRouter::send(p); return FloodingRouter::send(p);
} }
bool ReliableRouter::shouldFilterReceived(MeshPacket *p) bool ReliableRouter::shouldFilterReceived(const MeshPacket *p)
{ {
// Note: do not use getFrom() here, because we want to ignore messages sent from phone // Note: do not use getFrom() here, because we want to ignore messages sent from phone
if (p->from == getNodeNum()) { if (p->from == getNodeNum()) {
@@ -37,9 +37,8 @@ bool ReliableRouter::shouldFilterReceived(MeshPacket *p)
// If this is the first time we saw this, cancel any retransmissions we have queued up and generate an internal ack for // If this is the first time we saw this, cancel any retransmissions we have queued up and generate an internal ack for
// the original sending process. // the original sending process.
// FIXME - we might want to turn off this "optimization", it does save lots of airtime but it assumes that once we've // This "optimization", does save lots of airtime. For DMs, you also get a real ACK back
// heard one one adjacent node hear our packet that a) probably other adjacent nodes heard it and b) we can trust those // from the intended recipient.
// nodes to reach our destination. Both of which might be incorrect.
auto key = GlobalPacketId(getFrom(p), p->id); auto key = GlobalPacketId(getFrom(p), p->id);
auto old = findPendingPacket(key); auto old = findPendingPacket(key);
if (old) { if (old) {
@@ -54,16 +53,11 @@ bool ReliableRouter::shouldFilterReceived(MeshPacket *p)
} }
} }
/* send acks for repeated packets that want acks and are destined for us /* Resend implicit ACKs for repeated packets (assuming the original packet was sent with HOP_RELIABLE)
* this way if an ACK is dropped and a packet is resent we'll ACK the resent packet * this way if an implicit ACK is dropped and a packet is resent we'll rebroadcast again.
* make sure wasSeenRecently _doesn't_ update * Resending real ACKs is omitted, as you might receive a packet multiple times due to flooding and
* finding the channel requires decoding the packet. */ * flooding this ACK back to the original sender already adds redundancy. */
if (p->want_ack && (p->to == getNodeNum()) && wasSeenRecently(p, false) && !MeshModule::currentReply) { if (wasSeenRecently(p, false) && p->hop_limit == HOP_RELIABLE && !MeshModule::currentReply && p->to != nodeDB.getNodeNum()) {
if (perhapsDecode(p)) {
sendAckNak(Routing_Error_NONE, getFrom(p), p->id, p->channel);
DEBUG_MSG("acking a repeated want_ack packet\n");
}
} else if (wasSeenRecently(p, false) && p->hop_limit == HOP_RELIABLE && !MeshModule::currentReply && p->to != nodeDB.getNodeNum()) {
// retransmission on broadcast has hop_limit still equal to HOP_RELIABLE // retransmission on broadcast has hop_limit still equal to HOP_RELIABLE
DEBUG_MSG("Resending implicit ack for a repeated floodmsg\n"); DEBUG_MSG("Resending implicit ack for a repeated floodmsg\n");
MeshPacket *tosend = packetPool.allocCopy(*p); MeshPacket *tosend = packetPool.allocCopy(*p);

View File

@@ -96,7 +96,7 @@ class ReliableRouter : public FloodingRouter
/** /**
* We hook this method so we can see packets before FloodingRouter says they should be discarded * We hook this method so we can see packets before FloodingRouter says they should be discarded
*/ */
virtual bool shouldFilterReceived(MeshPacket *p) override; virtual bool shouldFilterReceived(const MeshPacket *p) override;
/** /**
* Add p to the list of packets to retransmit occasionally. We will free it once we stop retransmitting. * Add p to the list of packets to retransmit occasionally. We will free it once we stop retransmitting.

View File

@@ -306,7 +306,7 @@ bool perhapsDecode(MeshPacket *p)
// Take those raw bytes and convert them back into a well structured protobuf we can understand // Take those raw bytes and convert them back into a well structured protobuf we can understand
memset(&p->decoded, 0, sizeof(p->decoded)); memset(&p->decoded, 0, sizeof(p->decoded));
if (!pb_decode_from_bytes(bytes, rawSize, Data_fields, &p->decoded)) { if (!pb_decode_from_bytes(bytes, rawSize, &Data_msg, &p->decoded)) {
DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?)!\n"); DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?)!\n");
} else if (p->decoded.portnum == PortNum_UNKNOWN_APP) { } else if (p->decoded.portnum == PortNum_UNKNOWN_APP) {
DEBUG_MSG("Invalid portnum (bad psk?)!\n"); DEBUG_MSG("Invalid portnum (bad psk?)!\n");
@@ -360,7 +360,7 @@ Routing_Error perhapsEncode(MeshPacket *p)
if (p->which_payload_variant == MeshPacket_decoded_tag) { if (p->which_payload_variant == MeshPacket_decoded_tag) {
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded); size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &Data_msg, &p->decoded);
// Only allow encryption on the text message app. // Only allow encryption on the text message app.
// TODO: Allow modules to opt into compression. // TODO: Allow modules to opt into compression.

View File

@@ -90,7 +90,7 @@ class Router : protected concurrency::OSThread
* Called immedately on receiption, before any further processing. * Called immedately on receiption, before any further processing.
* @return true to abandon the packet * @return true to abandon the packet
*/ */
virtual bool shouldFilterReceived(MeshPacket *p) { return false; } virtual bool shouldFilterReceived(const MeshPacket *p) { return false; }
/** /**
* Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to * Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to

View File

@@ -1,5 +1,6 @@
#include "configuration.h" #include "configuration.h"
#include "SX128xInterface.h" #include "SX128xInterface.h"
#include "mesh/NodeDB.h"
#include "error.h" #include "error.h"
// Particular boards might define a different max power based on what their hardware can do // Particular boards might define a different max power based on what their hardware can do
@@ -50,6 +51,20 @@ bool SX128xInterface<T>::init()
// \todo Display actual typename of the adapter, not just `SX128x` // \todo Display actual typename of the adapter, not just `SX128x`
DEBUG_MSG("SX128x init result %d\n", res); DEBUG_MSG("SX128x init result %d\n", res);
if((config.lora.region != Config_LoRaConfig_RegionCode_LORA_24) && (res == RADIOLIB_ERR_INVALID_FREQUENCY)) {
DEBUG_MSG("Warning: Radio chip only supports 2.4GHz LoRa. Adjusting Region and rebooting.\n");
config.lora.region = Config_LoRaConfig_RegionCode_LORA_24;
nodeDB.saveToDisk(SEGMENT_CONFIG);
delay(2000);
#if defined(ARCH_ESP32)
ESP.restart();
#elif defined(ARCH_NRF52)
NVIC_SystemReset();
#else
DEBUG_MSG("FIXME implement reboot for this platform. Skipping for now.\n");
#endif
}
DEBUG_MSG("Frequency set to %f\n", getFreq()); DEBUG_MSG("Frequency set to %f\n", getFreq());
DEBUG_MSG("Bandwidth set to %f\n", bw); DEBUG_MSG("Bandwidth set to %f\n", bw);
DEBUG_MSG("Power output set to %d\n", power); DEBUG_MSG("Power output set to %d\n", power);
@@ -223,13 +238,9 @@ bool SX128xInterface<T>::isChannelActive()
template<typename T> template<typename T>
bool SX128xInterface<T>::isActivelyReceiving() bool SX128xInterface<T>::isActivelyReceiving()
{ {
#ifdef RADIOLIB_GODMODE
uint16_t irq = lora.getIrqStatus(); uint16_t irq = lora.getIrqStatus();
bool hasPreamble = (irq & RADIOLIB_SX128X_IRQ_HEADER_VALID); bool hasPreamble = (irq & RADIOLIB_SX128X_IRQ_HEADER_VALID);
return hasPreamble; return hasPreamble;
#else
return isChannelActive();
#endif
} }
template<typename T> template<typename T>

View File

@@ -27,9 +27,7 @@ class SX128xInterface : public RadioLibInterface
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep. /// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
virtual bool sleep() override; virtual bool sleep() override;
#ifdef RADIOLIB_GODMODE
bool isIRQPending() override { return lora.getIrqStatus() != 0; } bool isIRQPending() override { return lora.getIrqStatus() != 0; }
#endif
protected: protected:

View File

@@ -6,7 +6,7 @@
#define START2 0xc3 #define START2 0xc3
#define HEADER_LEN 4 #define HEADER_LEN 4
int32_t StreamAPI::runOnce() int32_t StreamAPI::runOncePart()
{ {
auto result = readStream(); auto result = readStream();
writeStream(); writeStream();
@@ -115,7 +115,7 @@ void StreamAPI::emitRebooted()
fromRadioScratch.rebooted = true; fromRadioScratch.rebooted = true;
// DEBUG_MSG("Emitting reboot packet for serial shell\n"); // DEBUG_MSG("Emitting reboot packet for serial shell\n");
emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, FromRadio_size, FromRadio_fields, &fromRadioScratch)); emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, FromRadio_size, &FromRadio_msg, &fromRadioScratch));
} }
/// Hookable to find out when connection changes /// Hookable to find out when connection changes

View File

@@ -28,7 +28,7 @@ valid utf8 encoding. This makes it a bit easier to start a device outputting reg
after it has received a valid packet from the PC, turn off unencoded debug printing and switch to this packet encoding. after it has received a valid packet from the PC, turn off unencoded debug printing and switch to this packet encoding.
*/ */
class StreamAPI : public PhoneAPI, protected concurrency::OSThread class StreamAPI : public PhoneAPI
{ {
/** /**
* The stream we read/write from * The stream we read/write from
@@ -42,13 +42,13 @@ class StreamAPI : public PhoneAPI, protected concurrency::OSThread
uint32_t lastRxMsec = 0; uint32_t lastRxMsec = 0;
public: public:
StreamAPI(Stream *_stream) : concurrency::OSThread("StreamAPI"), stream(_stream) {} StreamAPI(Stream *_stream) : stream(_stream) {}
/** /**
* Currently we require frequent invocation from loop() to check for arrived serial packets and to send new packets to the * Currently we require frequent invocation from loop() to check for arrived serial packets and to send new packets to the
* phone. * phone.
*/ */
virtual int32_t runOnce() override; virtual int32_t runOncePart();
private: private:
/** /**

View File

@@ -14,7 +14,7 @@ void initApiServer(int port)
} }
} }
ethServerAPI::ethServerAPI(EthernetClient &_client) : StreamAPI(&client), client(_client) ethServerAPI::ethServerAPI(EthernetClient &_client) : StreamAPI(&client), concurrency::OSThread("ethServerAPI"), client(_client)
{ {
DEBUG_MSG("Incoming ethernet connection\n"); DEBUG_MSG("Incoming ethernet connection\n");
} }
@@ -42,7 +42,7 @@ bool ethServerAPI::checkIsConnected()
int32_t ethServerAPI::runOnce() int32_t ethServerAPI::runOnce()
{ {
if (client.connected()) { if (client.connected()) {
return StreamAPI::runOnce(); return StreamAPI::runOncePart();
} else { } else {
DEBUG_MSG("Client dropped connection, suspending API service\n"); DEBUG_MSG("Client dropped connection, suspending API service\n");
enabled = false; // we no longer need to run enabled = false; // we no longer need to run

View File

@@ -7,7 +7,7 @@
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs * Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs). * (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
*/ */
class ethServerAPI : public StreamAPI class ethServerAPI : public StreamAPI, private concurrency::OSThread
{ {
private: private:
EthernetClient client; EthernetClient client;

View File

@@ -54,6 +54,8 @@ typedef enum _HardwareModel {
HardwareModel_NANO_G1 = 14, HardwareModel_NANO_G1 = 14,
/* TODO: REPLACE */ /* TODO: REPLACE */
HardwareModel_TLORA_V2_1_1P8 = 15, HardwareModel_TLORA_V2_1_1P8 = 15,
/* TODO: REPLACE */
HardwareModel_TLORA_T3_S3 = 16,
/* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */ /* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */
HardwareModel_STATION_G1 = 25, HardwareModel_STATION_G1 = 25,
/* Less common/prototype boards listed here (needs one more byte over the air) */ /* Less common/prototype boards listed here (needs one more byte over the air) */

View File

@@ -10,8 +10,8 @@
#endif #endif
/* Enum definitions */ /* Enum definitions */
/* 1 - 99 = From Router /* 001 - 063 = From Router
101 - 199 = From Client */ 064 - 127 = From Client */
typedef enum _StoreAndForward_RequestResponse { typedef enum _StoreAndForward_RequestResponse {
/* Unset/unused */ /* Unset/unused */
StoreAndForward_RequestResponse_UNSET = 0, StoreAndForward_RequestResponse_UNSET = 0,
@@ -28,17 +28,19 @@ typedef enum _StoreAndForward_RequestResponse {
StoreAndForward_RequestResponse_ROUTER_BUSY = 5, StoreAndForward_RequestResponse_ROUTER_BUSY = 5,
/* Router is responding to a request for history. */ /* Router is responding to a request for history. */
StoreAndForward_RequestResponse_ROUTER_HISTORY = 6, StoreAndForward_RequestResponse_ROUTER_HISTORY = 6,
/* Router is responding to a request for stats. */
StoreAndForward_RequestResponse_ROUTER_STATS = 7,
/* Client is an in error state. */ /* Client is an in error state. */
StoreAndForward_RequestResponse_CLIENT_ERROR = 101, StoreAndForward_RequestResponse_CLIENT_ERROR = 64,
/* Client has requested a replay from the router. */ /* Client has requested a replay from the router. */
StoreAndForward_RequestResponse_CLIENT_HISTORY = 102, StoreAndForward_RequestResponse_CLIENT_HISTORY = 65,
/* Client has requested stats from the router. */ /* Client has requested stats from the router. */
StoreAndForward_RequestResponse_CLIENT_STATS = 103, StoreAndForward_RequestResponse_CLIENT_STATS = 66,
/* Client has requested the router respond. This can work as a /* Client has requested the router respond. This can work as a
"are you there" message. */ "are you there" message. */
StoreAndForward_RequestResponse_CLIENT_PING = 104, StoreAndForward_RequestResponse_CLIENT_PING = 67,
/* The response to a "Ping" */ /* The response to a "Ping" */
StoreAndForward_RequestResponse_CLIENT_PONG = 105, StoreAndForward_RequestResponse_CLIENT_PONG = 68,
/* Client has requested that the router abort processing the client's request */ /* Client has requested that the router abort processing the client's request */
StoreAndForward_RequestResponse_CLIENT_ABORT = 106 StoreAndForward_RequestResponse_CLIENT_ABORT = 106
} StoreAndForward_RequestResponse; } StoreAndForward_RequestResponse;
@@ -88,15 +90,17 @@ typedef struct _StoreAndForward_Heartbeat {
typedef struct _StoreAndForward { typedef struct _StoreAndForward {
/* TODO: REPLACE */ /* TODO: REPLACE */
StoreAndForward_RequestResponse rr; StoreAndForward_RequestResponse rr;
/* TODO: REPLACE */ pb_size_t which_variant;
bool has_stats; union {
StoreAndForward_Statistics stats; /* TODO: REPLACE */
/* TODO: REPLACE */ StoreAndForward_Statistics stats;
bool has_history; /* TODO: REPLACE */
StoreAndForward_History history; StoreAndForward_History history;
/* TODO: REPLACE */ /* TODO: REPLACE */
bool has_heartbeat; StoreAndForward_Heartbeat heartbeat;
StoreAndForward_Heartbeat heartbeat; /* Empty Payload */
bool empty;
} variant;
} StoreAndForward; } StoreAndForward;
@@ -116,11 +120,11 @@ extern "C" {
/* Initializer values for message structs */ /* Initializer values for message structs */
#define StoreAndForward_init_default {_StoreAndForward_RequestResponse_MIN, false, StoreAndForward_Statistics_init_default, false, StoreAndForward_History_init_default, false, StoreAndForward_Heartbeat_init_default} #define StoreAndForward_init_default {_StoreAndForward_RequestResponse_MIN, 0, {StoreAndForward_Statistics_init_default}}
#define StoreAndForward_Statistics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0} #define StoreAndForward_Statistics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define StoreAndForward_History_init_default {0, 0, 0} #define StoreAndForward_History_init_default {0, 0, 0}
#define StoreAndForward_Heartbeat_init_default {0, 0} #define StoreAndForward_Heartbeat_init_default {0, 0}
#define StoreAndForward_init_zero {_StoreAndForward_RequestResponse_MIN, false, StoreAndForward_Statistics_init_zero, false, StoreAndForward_History_init_zero, false, StoreAndForward_Heartbeat_init_zero} #define StoreAndForward_init_zero {_StoreAndForward_RequestResponse_MIN, 0, {StoreAndForward_Statistics_init_zero}}
#define StoreAndForward_Statistics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0} #define StoreAndForward_Statistics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define StoreAndForward_History_init_zero {0, 0, 0} #define StoreAndForward_History_init_zero {0, 0, 0}
#define StoreAndForward_Heartbeat_init_zero {0, 0} #define StoreAndForward_Heartbeat_init_zero {0, 0}
@@ -144,18 +148,20 @@ extern "C" {
#define StoreAndForward_stats_tag 2 #define StoreAndForward_stats_tag 2
#define StoreAndForward_history_tag 3 #define StoreAndForward_history_tag 3
#define StoreAndForward_heartbeat_tag 4 #define StoreAndForward_heartbeat_tag 4
#define StoreAndForward_empty_tag 5
/* Struct field encoding specification for nanopb */ /* Struct field encoding specification for nanopb */
#define StoreAndForward_FIELDLIST(X, a) \ #define StoreAndForward_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, rr, 1) \ X(a, STATIC, SINGULAR, UENUM, rr, 1) \
X(a, STATIC, OPTIONAL, MESSAGE, stats, 2) \ X(a, STATIC, ONEOF, MESSAGE, (variant,stats,variant.stats), 2) \
X(a, STATIC, OPTIONAL, MESSAGE, history, 3) \ X(a, STATIC, ONEOF, MESSAGE, (variant,history,variant.history), 3) \
X(a, STATIC, OPTIONAL, MESSAGE, heartbeat, 4) X(a, STATIC, ONEOF, MESSAGE, (variant,heartbeat,variant.heartbeat), 4) \
X(a, STATIC, ONEOF, BOOL, (variant,empty,variant.empty), 5)
#define StoreAndForward_CALLBACK NULL #define StoreAndForward_CALLBACK NULL
#define StoreAndForward_DEFAULT NULL #define StoreAndForward_DEFAULT NULL
#define StoreAndForward_stats_MSGTYPE StoreAndForward_Statistics #define StoreAndForward_variant_stats_MSGTYPE StoreAndForward_Statistics
#define StoreAndForward_history_MSGTYPE StoreAndForward_History #define StoreAndForward_variant_history_MSGTYPE StoreAndForward_History
#define StoreAndForward_heartbeat_MSGTYPE StoreAndForward_Heartbeat #define StoreAndForward_variant_heartbeat_MSGTYPE StoreAndForward_Heartbeat
#define StoreAndForward_Statistics_FIELDLIST(X, a) \ #define StoreAndForward_Statistics_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, messages_total, 1) \ X(a, STATIC, SINGULAR, UINT32, messages_total, 1) \
@@ -198,7 +204,7 @@ extern const pb_msgdesc_t StoreAndForward_Heartbeat_msg;
#define StoreAndForward_Heartbeat_size 12 #define StoreAndForward_Heartbeat_size 12
#define StoreAndForward_History_size 18 #define StoreAndForward_History_size 18
#define StoreAndForward_Statistics_size 50 #define StoreAndForward_Statistics_size 50
#define StoreAndForward_size 88 #define StoreAndForward_size 54
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -14,7 +14,7 @@ void initApiServer(int port)
} }
} }
WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : StreamAPI(&client), client(_client) WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : StreamAPI(&client), concurrency::OSThread("WiFiServerAPI"), client(_client)
{ {
DEBUG_MSG("Incoming wifi connection\n"); DEBUG_MSG("Incoming wifi connection\n");
} }
@@ -42,7 +42,7 @@ bool WiFiServerAPI::checkIsConnected()
int32_t WiFiServerAPI::runOnce() int32_t WiFiServerAPI::runOnce()
{ {
if (client.connected()) { if (client.connected()) {
return StreamAPI::runOnce(); return StreamAPI::runOncePart();
} else { } else {
DEBUG_MSG("Client dropped connection, suspending API service\n"); DEBUG_MSG("Client dropped connection, suspending API service\n");
enabled = false; // we no longer need to run enabled = false; // we no longer need to run

View File

@@ -7,7 +7,7 @@
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs * Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
* (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs). * (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs).
*/ */
class WiFiServerAPI : public StreamAPI class WiFiServerAPI : public StreamAPI, private concurrency::OSThread
{ {
private: private:
WiFiClient client; WiFiClient client;

View File

@@ -515,7 +515,7 @@ void AdminModule::saveChanges(int saveWhat, bool shouldReboot)
} }
} }
AdminModule::AdminModule() : ProtobufModule("Admin", PortNum_ADMIN_APP, AdminMessage_fields) AdminModule::AdminModule() : ProtobufModule("Admin", PortNum_ADMIN_APP, &AdminMessage_msg)
{ {
// restrict to the admin channel for rx // restrict to the admin channel for rx
boundChannel = Channels::adminChannel; boundChannel = Channels::adminChannel;

View File

@@ -45,8 +45,7 @@ CannedMessageModule *cannedMessageModule;
// TODO: move it into NodeDB.h! // TODO: move it into NodeDB.h!
extern bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct); extern bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct);
extern bool saveProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, extern bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct);
const void *dest_struct);
CannedMessageModule::CannedMessageModule() CannedMessageModule::CannedMessageModule()
: SinglePortModule("canned", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessageModule") : SinglePortModule("canned", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessageModule")
@@ -480,7 +479,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
void CannedMessageModule::loadProtoForModule() void CannedMessageModule::loadProtoForModule()
{ {
if (!loadProto(cannedMessagesConfigFile, CannedMessageModuleConfig_size, sizeof(cannedMessagesConfigFile), if (!loadProto(cannedMessagesConfigFile, CannedMessageModuleConfig_size, sizeof(cannedMessagesConfigFile),
CannedMessageModuleConfig_fields, &cannedMessageModuleConfig)) { &CannedMessageModuleConfig_msg, &cannedMessageModuleConfig)) {
installDefaultCannedMessageModuleConfig(); installDefaultCannedMessageModuleConfig();
} }
} }
@@ -499,8 +498,8 @@ bool CannedMessageModule::saveProtoForModule()
FS.mkdir("/prefs"); FS.mkdir("/prefs");
#endif #endif
okay &= saveProto(cannedMessagesConfigFile, CannedMessageModuleConfig_size, sizeof(cannedMessageModuleConfig), okay &= saveProto(cannedMessagesConfigFile, CannedMessageModuleConfig_size,
CannedMessageModuleConfig_fields, &cannedMessageModuleConfig); &CannedMessageModuleConfig_msg, &cannedMessageModuleConfig);
return okay; return okay;
} }

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,59 +26,113 @@
#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.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 {
*/ if ((nagCycleCutoff < millis()) && !rtttl::isPlaying()) {
nagCycleCutoff = UINT32_MAX;
// moduleConfig.external_notification.enabled = 1; DEBUG_MSG("Turning off external notification: ");
// moduleConfig.external_notification.alert_message = 1; for (int i = 0; i < 2; i++) {
if (getExternal(i)) {
// moduleConfig.external_notification.active = 1; setExternalOff(i);
// moduleConfig.external_notification.alert_bell = 1; externalTurnedOn[i] = 0;
// moduleConfig.external_notification.output_ms = 1000; DEBUG_MSG("%d ", i);
// moduleConfig.external_notification.output = 13; }
}
if (externalCurrentState && !moduleConfig.external_notification.use_pwm) { DEBUG_MSG("\n");
return INT32_MAX; // save cycles till we're needed again
}
// 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);
}
}
// now let the PWM buzzer play
if (moduleConfig.external_notification.use_pwm) {
if (rtttl::isPlaying()) {
rtttl::play();
} else if (nagCycleCutoff >= millis()) {
// start the song again if we have time left
rtttl::begin(config.device.buzzer_gpio, pwmRingtone);
}
} }
}
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];
}
void ExternalNotificationModule::stopNow() {
rtttl::stop();
nagCycleCutoff = 1; // small value
setIntervalFromNow(0);
}
ExternalNotificationModule::ExternalNotificationModule() ExternalNotificationModule::ExternalNotificationModule()
: SinglePortModule("ExternalNotificationModule", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread( : SinglePortModule("ExternalNotificationModule", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread(
@@ -121,13 +143,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) {
@@ -137,19 +164,30 @@ ExternalNotificationModule::ExternalNotificationModule()
? moduleConfig.external_notification.output ? moduleConfig.external_notification.output
: EXT_NOTIFICATION_MODULE_OUTPUT; : EXT_NOTIFICATION_MODULE_OUTPUT;
if (!moduleConfig.external_notification.use_pwm) { // 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); setExternalOff(0);
// Turn off the pin externalTurnedOn[0] = 0;
setExternalOff(); if(moduleConfig.external_notification.output_vibra) {
} else { DEBUG_MSG("Using Pin %i for vibra motor\n", moduleConfig.external_notification.output_vibra);
config.device.buzzer_gpio = config.device.buzzer_gpio pinMode(moduleConfig.external_notification.output_vibra, OUTPUT);
? config.device.buzzer_gpio setExternalOff(1);
: PIN_BUZZER; externalTurnedOn[1] = 0;
}
// in PWM Mode we force the buzzer pin if it is set if(moduleConfig.external_notification.output_buzzer) {
DEBUG_MSG("Using Pin %i in PWM mode\n", config.device.buzzer_gpio); if (!moduleConfig.external_notification.use_pwm) {
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 {
config.device.buzzer_gpio = config.device.buzzer_gpio
? config.device.buzzer_gpio
: PIN_BUZZER;
// in PWM Mode we force the buzzer pin if it is set
DEBUG_MSG("Using Pin %i in PWM mode\n", config.device.buzzer_gpio);
}
} }
} else { } else {
DEBUG_MSG("External Notification Module Disabled\n"); DEBUG_MSG("External Notification Module Disabled\n");
@@ -163,30 +201,91 @@ 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++) { setExternalOn(0);
if (p.payload.bytes[i] == ASCII_BELL) { if (moduleConfig.external_notification.nag_timeout) {
if (!moduleConfig.external_notification.use_pwm) { nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
setExternalOn(); } else {
} else { nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
playBeep(); }
} }
}
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");
if (!moduleConfig.external_notification.use_pwm) {
setExternalOn(2);
} else {
rtttl::begin(config.device.buzzer_gpio, pwmRingtone);
}
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) { if (moduleConfig.external_notification.alert_message) {
DEBUG_MSG("externalNotificationModule - Notification Module\n"); DEBUG_MSG("externalNotificationModule - Notification Module\n");
if (!moduleConfig.external_notification.use_pwm) { setExternalOn(0);
setExternalOn(); if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else { } else {
playBeep(); nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
} }
} }
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");
if (!moduleConfig.external_notification.use_pwm) {
setExternalOn(2);
} else {
rtttl::begin(config.device.buzzer_gpio, pwmRingtone);
}
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

@@ -3,6 +3,7 @@
#include "SinglePortModule.h" #include "SinglePortModule.h"
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "configuration.h" #include "configuration.h"
#include <NonBlockingRtttl.h>
#include <Arduino.h> #include <Arduino.h>
#include <functional> #include <functional>
@@ -17,18 +18,23 @@ 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);
void stopNow();
char pwmRingtone[Constants_DATA_PAYLOAD_LEN] = "a:d=8,o=5,b=125:4d#6,a#,2d#6,16p,g#,4a#,4d#.,p,16g,16a#,d#6,a#,f6,2d#6,16p,c#.6,16c6,16a#,g#.,2a#";
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

@@ -24,7 +24,7 @@
#endif #endif
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) #if defined(ARCH_ESP32) || defined(ARCH_NRF52)
#include "modules/ExternalNotificationModule.h" #include "modules/ExternalNotificationModule.h"
#if !defined(TTGO_T_ECHO) #if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2)
#include "modules/SerialModule.h" #include "modules/SerialModule.h"
#endif #endif
#endif #endif
@@ -63,13 +63,13 @@ void setupModules()
new DeviceTelemetryModule(); new DeviceTelemetryModule();
new EnvironmentTelemetryModule(); new EnvironmentTelemetryModule();
#endif #endif
#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)
new SerialModule(); new SerialModule();
#endif #endif
#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

@@ -51,7 +51,7 @@ MeshPacket *NodeInfoModule::allocReply()
} }
NodeInfoModule::NodeInfoModule() NodeInfoModule::NodeInfoModule()
: ProtobufModule("nodeinfo", PortNum_NODEINFO_APP, User_fields), concurrency::OSThread("NodeInfoModule") : ProtobufModule("nodeinfo", PortNum_NODEINFO_APP, &User_msg), concurrency::OSThread("NodeInfoModule")
{ {
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
setIntervalFromNow(30 * 1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup) setIntervalFromNow(30 * 1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup)

View File

@@ -10,7 +10,7 @@
PositionModule *positionModule; PositionModule *positionModule;
PositionModule::PositionModule() PositionModule::PositionModule()
: ProtobufModule("position", PortNum_POSITION_APP, Position_fields), concurrency::OSThread("PositionModule") : ProtobufModule("position", PortNum_POSITION_APP, &Position_msg), concurrency::OSThread("PositionModule")
{ {
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
setIntervalFromNow(60 * 1000); // Send our initial position 60 seconds after we start (to give GPS time to setup) setIntervalFromNow(60 * 1000); // Send our initial position 60 seconds after we start (to give GPS time to setup)

View File

@@ -47,7 +47,7 @@ static uint64_t digitalReads(uint64_t mask)
} }
RemoteHardwareModule::RemoteHardwareModule() RemoteHardwareModule::RemoteHardwareModule()
: ProtobufModule("remotehardware", PortNum_REMOTE_HARDWARE_APP, HardwareMessage_fields), concurrency::OSThread( : ProtobufModule("remotehardware", PortNum_REMOTE_HARDWARE_APP, &HardwareMessage_msg), concurrency::OSThread(
"remotehardware") "remotehardware")
{ {
} }

View File

@@ -41,7 +41,7 @@ void RoutingModule::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, C
router->sendLocal(p); // we sometimes send directly to the local node router->sendLocal(p); // we sometimes send directly to the local node
} }
RoutingModule::RoutingModule() : ProtobufModule("routing", PortNum_ROUTING_APP, Routing_fields) RoutingModule::RoutingModule() : ProtobufModule("routing", PortNum_ROUTING_APP, &Routing_msg)
{ {
isPromiscuous = true; isPromiscuous = true;
encryptedOk = true; encryptedOk = true;

View File

@@ -46,6 +46,8 @@
*/ */
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2)
#define RXD2 16 #define RXD2 16
#define TXD2 17 #define TXD2 17
#define RX_BUFFER 128 #define RX_BUFFER 128
@@ -53,13 +55,15 @@
#define BAUD 38400 #define BAUD 38400
#define ACK 1 #define ACK 1
// API: Defaulting to the formerly removed phone_timeout_secs value of 15 minutes
#define SERIAL_CONNECTION_TIMEOUT (15 * 60) * 1000UL
SerialModule *serialModule; SerialModule *serialModule;
SerialModuleRadio *serialModuleRadio; SerialModuleRadio *serialModuleRadio;
SerialModule::SerialModule() : concurrency::OSThread("SerialModule") {} SerialModule::SerialModule() : StreamAPI(&Serial2), concurrency::OSThread("SerialModule") {}
char serialBytes[Constants_DATA_PAYLOAD_LEN]; char serialStringChar[Constants_DATA_PAYLOAD_LEN];
size_t serialPayloadSize;
SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio") SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio")
{ {
@@ -80,9 +84,15 @@ SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio")
} }
} }
// For the serial2 port we can't really detect if any client is on the other side, so instead just look for recent messages
bool SerialModule::checkIsConnected()
{
uint32_t now = millis();
return (now - lastContactMsec) < SERIAL_CONNECTION_TIMEOUT;
}
int32_t SerialModule::runOnce() int32_t SerialModule::runOnce()
{ {
#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.
@@ -178,19 +188,32 @@ int32_t SerialModule::runOnce()
firstTime = 0; firstTime = 0;
// in API mode send rebooted sequence
if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_PROTO) {
emitRebooted();
}
} else { } else {
// in NMEA mode send out GGA every 2 seconds, Don't read from Port if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_PROTO) {
if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_NMEA) { return runOncePart();
} else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_NMEA) {
// in NMEA mode send out GGA every 2 seconds, Don't read from Port
if (millis() - lastNmeaTime > 2000) { if (millis() - lastNmeaTime > 2000) {
lastNmeaTime = millis(); lastNmeaTime = millis();
printGGA(outbuf, nodeDB.getNode(myNodeInfo.my_node_num)->position); printGGA(outbuf, nodeDB.getNode(myNodeInfo.my_node_num)->position);
Serial2.printf("%s", outbuf); Serial2.printf("%s", outbuf);
} }
} else { } else {
String serialString;
while (Serial2.available()) { while (Serial2.available()) {
serialPayloadSize = Serial2.readBytes(serialBytes, Constants_DATA_PAYLOAD_LEN); serialString = Serial2.readString();
serialString.toCharArray(serialStringChar, Constants_DATA_PAYLOAD_LEN);
serialModuleRadio->sendPayload(); serialModuleRadio->sendPayload();
DEBUG_MSG("Received: %s\n", serialStringChar);
} }
} }
} }
@@ -201,9 +224,6 @@ int32_t SerialModule::runOnce()
return INT32_MAX; return INT32_MAX;
} }
#else
return INT32_MAX;
#endif
} }
MeshPacket *SerialModuleRadio::allocReply() MeshPacket *SerialModuleRadio::allocReply()
@@ -215,26 +235,25 @@ MeshPacket *SerialModuleRadio::allocReply()
void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies) void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
{ {
Channel *ch = (boundChannel != NULL) ? &channels.getByName(boundChannel) : NULL;
MeshPacket *p = allocReply(); MeshPacket *p = allocReply();
p->to = dest; p->to = dest;
if (ch != NULL) {
p->channel = ch->index;
}
p->decoded.want_response = wantReplies; p->decoded.want_response = wantReplies;
p->want_ack = ACK; p->want_ack = ACK;
p->decoded.payload.size = serialPayloadSize; // You must specify how many bytes are in the reply p->decoded.payload.size = strlen(serialStringChar); // You must specify how many bytes are in the reply
memcpy(p->decoded.payload.bytes, serialBytes, p->decoded.payload.size); memcpy(p->decoded.payload.bytes, serialStringChar, p->decoded.payload.size);
service.sendToMesh(p); service.sendToMesh(p);
} }
ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp) ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp)
{ {
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2)
if (moduleConfig.serial.enabled) { if (moduleConfig.serial.enabled) {
if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_PROTO) {
// in API mode we don't care about stuff from radio.
return ProcessMessage::CONTINUE;
}
auto &p = mp.decoded; auto &p = mp.decoded;
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n", // DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
@@ -262,22 +281,20 @@ 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.write(p.payload.bytes, p.payload.size); Serial2.printf("%s", p.payload.bytes);
} else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG) { } else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG) {
NodeInfo *node = nodeDB.getNode(getFrom(&mp)); NodeInfo *node = nodeDB.getNode(getFrom(&mp));
String sender = (node && node->has_user) ? node->user.short_name : "???"; String sender = (node && node->has_user) ? node->user.short_name : "???";
Serial2.println(); Serial2.println();
Serial2.printf("%s: %s", sender, p.payload.bytes); Serial2.printf("%s: %s", sender, p.payload.bytes);
Serial2.println(); Serial2.println();
} else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_PROTO) {
// 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) {
// Decode the Payload some more // Decode the Payload some more
Position scratch; Position scratch;
Position *decoded = NULL; Position *decoded = NULL;
if (mp.which_payload_variant == MeshPacket_decoded_tag && mp.decoded.portnum == ourPortNum) { if (mp.which_payload_variant == MeshPacket_decoded_tag && mp.decoded.portnum == ourPortNum) {
memset(&scratch, 0, sizeof(scratch)); memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, Position_fields, &scratch)) { if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &Position_msg, &scratch)) {
decoded = &scratch; decoded = &scratch;
} }
// send position packet as WPL to the serial port // send position packet as WPL to the serial port
@@ -290,8 +307,6 @@ ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp)
} else { } else {
DEBUG_MSG("Serial Module Disabled\n"); DEBUG_MSG("Serial Module Disabled\n");
} }
#endif
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
} }
#endif

View File

@@ -8,7 +8,9 @@
#include "Router.h" #include "Router.h"
#include <functional> #include <functional>
class SerialModule : private concurrency::OSThread #if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2)
class SerialModule : public StreamAPI, private concurrency::OSThread
{ {
bool firstTime = 1; bool firstTime = 1;
unsigned long lastNmeaTime = millis(); unsigned long lastNmeaTime = millis();
@@ -19,6 +21,9 @@ class SerialModule : private concurrency::OSThread
protected: protected:
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
/// Check the current underlying physical link to see if the client is currently connected
virtual bool checkIsConnected() override;
}; };
extern SerialModule *serialModule; extern SerialModule *serialModule;
@@ -65,3 +70,5 @@ class SerialModuleRadio : public MeshModule
}; };
extern SerialModuleRadio *serialModuleRadio; extern SerialModuleRadio *serialModuleRadio;
#endif

View File

@@ -159,7 +159,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
const char *lastSender = getSenderShortName(*lastMeasurementPacket); const char *lastSender = getSenderShortName(*lastMeasurementPacket);
auto &p = lastMeasurementPacket->decoded; auto &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, Telemetry_fields, &lastMeasurement)) { if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &Telemetry_msg, &lastMeasurement)) {
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error"); display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error");
DEBUG_MSG("Unable to decode last packet"); DEBUG_MSG("Unable to decode last packet");

View File

@@ -24,14 +24,14 @@ void TraceRouteModule::updateRoute(MeshPacket* p)
RouteDiscovery scratch; RouteDiscovery scratch;
RouteDiscovery *updated = NULL; RouteDiscovery *updated = NULL;
memset(&scratch, 0, sizeof(scratch)); memset(&scratch, 0, sizeof(scratch));
pb_decode_from_bytes(incoming.payload.bytes, incoming.payload.size, RouteDiscovery_fields, &scratch); pb_decode_from_bytes(incoming.payload.bytes, incoming.payload.size, &RouteDiscovery_msg, &scratch);
updated = &scratch; updated = &scratch;
appendMyID(updated); appendMyID(updated);
printRoute(updated, p->from, NODENUM_BROADCAST); printRoute(updated, p->from, NODENUM_BROADCAST);
// Set updated route to the payload of the to be flooded packet // 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); p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &RouteDiscovery_msg, updated);
} }
} }
@@ -69,7 +69,7 @@ MeshPacket* TraceRouteModule::allocReply()
RouteDiscovery scratch; RouteDiscovery scratch;
RouteDiscovery *updated = NULL; RouteDiscovery *updated = NULL;
memset(&scratch, 0, sizeof(scratch)); memset(&scratch, 0, sizeof(scratch));
pb_decode_from_bytes(p.payload.bytes, p.payload.size, RouteDiscovery_fields, &scratch); pb_decode_from_bytes(p.payload.bytes, p.payload.size, &RouteDiscovery_msg, &scratch);
updated = &scratch; updated = &scratch;
printRoute(updated, req.from, req.to); printRoute(updated, req.from, req.to);
@@ -81,6 +81,6 @@ MeshPacket* TraceRouteModule::allocReply()
} }
TraceRouteModule::TraceRouteModule() : ProtobufModule("traceroute", PortNum_TRACEROUTE_APP, RouteDiscovery_fields) { TraceRouteModule::TraceRouteModule() : ProtobufModule("traceroute", PortNum_TRACEROUTE_APP, &RouteDiscovery_msg) {
ourPortNum = PortNum_TRACEROUTE_APP; ourPortNum = PortNum_TRACEROUTE_APP;
} }

View File

@@ -22,41 +22,32 @@ int32_t StoreForwardModule::runOnce()
if (this->busy) { if (this->busy) {
// Only send packets if the channel is less than 25% utilized. // Only send packets if the channel is less than 25% utilized.
if (airTime->channelUtilizationPercent() < polite_channel_util_percent) { 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); storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) { if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
strcpy(this->routerMessage, "** S&F - Done"); // Tell the client we're done sending
storeForwardModule->sendMessage(this->busyTo, this->routerMessage); StoreAndForward sf = StoreAndForward_init_zero;
sf.rr = StoreAndForward_RequestResponse_ROUTER_PING;
// DEBUG_MSG("--- --- --- In busy loop - Done \n"); storeForwardModule->sendMessage(this->busyTo, sf);
DEBUG_MSG("*** S&F - Done. (ROUTER_PING)\n");
this->packetHistoryTXQueue_index = 0; this->packetHistoryTXQueue_index = 0;
this->busy = false; this->busy = false;
} else { } else {
this->packetHistoryTXQueue_index++; this->packetHistoryTXQueue_index++;
} }
} else { } else {
DEBUG_MSG("Channel utilization is too high. Retrying later.\n"); DEBUG_MSG("*** Channel utilization is too high. Retrying later.\n");
} }
DEBUG_MSG("SF bitrate = %f bytes / sec\n", myNodeInfo.bitrate); DEBUG_MSG("*** SF bitrate = %f bytes / sec\n", myNodeInfo.bitrate);
} else if (millis() - lastHeartbeat > 300000) { } else if ((millis() - lastHeartbeat > (heartbeatInterval * 1000)) && (airTime->channelUtilizationPercent() < polite_channel_util_percent)) {
lastHeartbeat = millis(); lastHeartbeat = millis();
DEBUG_MSG("Sending heartbeat\n"); DEBUG_MSG("*** Sending heartbeat\n");
StoreAndForward sf = StoreAndForward_init_zero;
StoreAndForward sf;
sf.rr = StoreAndForward_RequestResponse_ROUTER_HEARTBEAT; sf.rr = StoreAndForward_RequestResponse_ROUTER_HEARTBEAT;
sf.has_heartbeat = true; sf.which_variant = StoreAndForward_heartbeat_tag;
sf.heartbeat.period = 300; sf.variant.heartbeat.period = 300;
sf.heartbeat.secondary = 0; // TODO we always have one primary router for now sf.variant.heartbeat.secondary = 0; // TODO we always have one primary router for now
storeForwardModule->sendMessage(NODENUM_BROADCAST, sf);
MeshPacket *p = allocDataProtobuf(sf);
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = MeshPacket_Priority_MIN;
service.sendToMesh(p);
} }
return (this->packetTimeMax); return (this->packetTimeMax);
} }
@@ -74,7 +65,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: heap %d/%d PSRAM %d/%d\n", ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getFreePsram(), ESP.getPsramSize()); DEBUG_MSG("*** Before PSRAM initilization: heap %d/%d PSRAM %d/%d\n", ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getFreePsram(), ESP.getPsramSize());
this->packetHistoryTXQueue = this->packetHistoryTXQueue =
static_cast<PacketHistoryStruct *>(ps_calloc(this->historyReturnMax, sizeof(PacketHistoryStruct))); static_cast<PacketHistoryStruct *>(ps_calloc(this->historyReturnMax, sizeof(PacketHistoryStruct)));
@@ -83,46 +74,36 @@ void StoreForwardModule::populatePSRAM()
Note: This needs to be done after every thing that would use PSRAM Note: This needs to be done after every thing that would use PSRAM
*/ */
uint32_t numberOfPackets = (this->records ? this->records : (((ESP.getFreePsram() / 3) * 2) / sizeof(PacketHistoryStruct))); uint32_t numberOfPackets = (this->records ? this->records : (((ESP.getFreePsram() / 3) * 2) / sizeof(PacketHistoryStruct)));
this->records = numberOfPackets;
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: heap %d/%d PSRAM %d/%d\n", ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getFreePsram(), ESP.getPsramSize()); 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("*** numberOfPackets for packetHistory - %u\n", numberOfPackets);
} }
void StoreForwardModule::historyReport()
{
DEBUG_MSG("Message history contains %u records\n", this->packetHistoryCurrent);
}
/*
*
*/
void StoreForwardModule::historySend(uint32_t msAgo, uint32_t to) void StoreForwardModule::historySend(uint32_t msAgo, uint32_t to)
{ {
// uint32_t packetsSent = 0;
uint32_t queueSize = storeForwardModule->historyQueueCreate(msAgo, to); uint32_t queueSize = storeForwardModule->historyQueueCreate(msAgo, to);
if (queueSize) { if (queueSize) {
snprintf(this->routerMessage, 80, "** S&F - Sending %u message(s)", queueSize); DEBUG_MSG ("*** S&F - Sending %u message(s)\n", queueSize);
storeForwardModule->sendMessage(to, this->routerMessage);
this->busy = true; // runOnce() will pickup the next steps once busy = true. this->busy = true; // runOnce() will pickup the next steps once busy = true.
this->busyTo = to; this->busyTo = to;
} else { } else {
strcpy(this->routerMessage, "** S&F - No history to send"); DEBUG_MSG ("*** S&F - No history to send\n");
storeForwardModule->sendMessage(to, this->routerMessage);
} }
StoreAndForward sf = StoreAndForward_init_zero;
sf.rr = StoreAndForward_RequestResponse_ROUTER_HISTORY;
sf.which_variant = StoreAndForward_history_tag;
sf.variant.history.history_messages = queueSize;
sf.variant.history.window = msAgo;
storeForwardModule->sendMessage(to, sf);
} }
uint32_t StoreForwardModule::historyQueueCreate(uint32_t msAgo, uint32_t to) uint32_t StoreForwardModule::historyQueueCreate(uint32_t msAgo, uint32_t to)
{ {
// uint32_t packetHistoryTXQueueIndex = 0;
this->packetHistoryTXQueue_size = 0; this->packetHistoryTXQueue_size = 0;
for (int i = 0; i < this->packetHistoryCurrent; i++) { for (int i = 0; i < this->packetHistoryCurrent; i++) {
@@ -133,7 +114,7 @@ uint32_t StoreForwardModule::historyQueueCreate(uint32_t msAgo, uint32_t to)
DEBUG_MSG("SF historyQueueCreate - math %d\n", (millis() - msAgo)); DEBUG_MSG("SF historyQueueCreate - math %d\n", (millis() - msAgo));
*/ */
if (this->packetHistory[i].time && (this->packetHistory[i].time < (millis() - msAgo))) { if (this->packetHistory[i].time && (this->packetHistory[i].time < (millis() - msAgo))) {
DEBUG_MSG("SF historyQueueCreate - Time matches - ok\n"); DEBUG_MSG("*** SF historyQueueCreate - Time matches - ok\n");
/* /*
Copy the messages that were received by the router in the last msAgo Copy the messages that were received by the router in the last msAgo
to the packetHistoryTXQueue structure. to the packetHistoryTXQueue structure.
@@ -144,7 +125,6 @@ uint32_t StoreForwardModule::historyQueueCreate(uint32_t msAgo, uint32_t to)
if ((this->packetHistory[i].to & NODENUM_BROADCAST) == NODENUM_BROADCAST || if ((this->packetHistory[i].to & NODENUM_BROADCAST) == NODENUM_BROADCAST ||
((this->packetHistory[i].to & NODENUM_BROADCAST) == to)) { ((this->packetHistory[i].to & NODENUM_BROADCAST) == to)) {
this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].time = this->packetHistory[i].time; this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].time = this->packetHistory[i].time;
this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].time = this->packetHistory[i].time;
this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].to = this->packetHistory[i].to; this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].to = this->packetHistory[i].to;
this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].from = this->packetHistory[i].from; this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].from = this->packetHistory[i].from;
this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].channel = this->packetHistory[i].channel; this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].channel = this->packetHistory[i].channel;
@@ -153,9 +133,8 @@ uint32_t StoreForwardModule::historyQueueCreate(uint32_t msAgo, uint32_t to)
Constants_DATA_PAYLOAD_LEN); Constants_DATA_PAYLOAD_LEN);
this->packetHistoryTXQueue_size++; this->packetHistoryTXQueue_size++;
DEBUG_MSG("PacketHistoryStruct time=%d\n", this->packetHistory[i].time); DEBUG_MSG("*** PacketHistoryStruct time=%d\n", this->packetHistory[i].time);
DEBUG_MSG("PacketHistoryStruct msg=%.*s\n", this->packetHistory[i].payload); DEBUG_MSG("*** PacketHistoryStruct msg=%s\n", this->packetHistory[i].payload);
// DEBUG_MSG("PacketHistoryStruct msg=%.*s\n", this->packetHistoryTXQueue[packetHistoryTXQueueIndex].payload);
} }
} }
} }
@@ -174,6 +153,7 @@ void StoreForwardModule::historyAdd(const MeshPacket &mp)
memcpy(this->packetHistory[this->packetHistoryCurrent].payload, p.payload.bytes, Constants_DATA_PAYLOAD_LEN); memcpy(this->packetHistory[this->packetHistoryCurrent].payload, p.payload.bytes, Constants_DATA_PAYLOAD_LEN);
this->packetHistoryCurrent++; this->packetHistoryCurrent++;
this->packetHistoryMax++;
} }
MeshPacket *StoreForwardModule::allocReply() MeshPacket *StoreForwardModule::allocReply()
@@ -184,7 +164,7 @@ MeshPacket *StoreForwardModule::allocReply()
void StoreForwardModule::sendPayload(NodeNum dest, uint32_t packetHistory_index) void StoreForwardModule::sendPayload(NodeNum dest, uint32_t packetHistory_index)
{ {
DEBUG_MSG("Sending S&F Payload\n"); DEBUG_MSG("*** Sending S&F Payload\n");
MeshPacket *p = allocReply(); MeshPacket *p = allocReply();
p->to = dest; p->to = dest;
@@ -203,12 +183,14 @@ void StoreForwardModule::sendPayload(NodeNum dest, uint32_t packetHistory_index)
service.sendToMesh(p); service.sendToMesh(p);
} }
void StoreForwardModule::sendMessage(NodeNum dest, char *str) void StoreForwardModule::sendMessage(NodeNum dest, StoreAndForward &payload)
{ {
MeshPacket *p = allocReply(); MeshPacket *p = allocDataProtobuf(payload);
p->to = dest; p->to = dest;
p->priority = MeshPacket_Priority_MIN;
// FIXME - Determine if the delayed packet is broadcast or delayed. For now, assume // FIXME - Determine if the delayed packet is broadcast or delayed. For now, assume
// everything is broadcast. // everything is broadcast.
p->delayed = MeshPacket_Delayed_DELAYED_BROADCAST; p->delayed = MeshPacket_Delayed_DELAYED_BROADCAST;
@@ -216,13 +198,37 @@ void StoreForwardModule::sendMessage(NodeNum dest, char *str)
// Let's assume that if the router received the S&F request that the client is in range. // Let's assume that if the router received the S&F request that the client is in range.
// TODO: Make this configurable. // TODO: Make this configurable.
p->want_ack = false; p->want_ack = false;
p->decoded.want_response = false;
p->decoded.payload.size = strlen(str); // You must specify how many bytes are in the reply
memcpy(p->decoded.payload.bytes, str, strlen(str));
service.sendToMesh(p); service.sendToMesh(p);
}
// HardwareMessage_init_default void StoreForwardModule::sendMessage(NodeNum dest, StoreAndForward_RequestResponse rr)
{
// Craft an empty response, save some bytes in flash
StoreAndForward sf = StoreAndForward_init_zero;
sf.rr = rr;
storeForwardModule->sendMessage(dest, sf);
}
void StoreForwardModule::statsSend(uint32_t to)
{
StoreAndForward sf = StoreAndForward_init_zero;
sf.rr = StoreAndForward_RequestResponse_ROUTER_STATS;
sf.which_variant = StoreAndForward_stats_tag;
sf.variant.stats.messages_total = this->packetHistoryMax;
sf.variant.stats.messages_saved = this->packetHistoryCurrent;
sf.variant.stats.messages_max = this->records;
sf.variant.stats.up_time = millis() / 1000;
sf.variant.stats.requests = this->requests;
sf.variant.stats.requests_history = this->requests_history;
sf.variant.stats.heartbeat = this->heartbeat;
sf.variant.stats.return_max = this->historyReturnMax;
sf.variant.stats.return_window = this->historyReturnWindow;
DEBUG_MSG("*** Sending S&F Stats\n");
storeForwardModule->sendMessage(to, sf);
} }
ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp) ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp)
@@ -230,46 +236,28 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp)
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
if (moduleConfig.store_forward.enabled) { if (moduleConfig.store_forward.enabled) {
DEBUG_MSG("--- S&F Received something\n");
// The router node should not be sending messages as a client. Unless he is a ROUTER_CLIENT // The router node should not be sending messages as a client. Unless he is a ROUTER_CLIENT
if ((getFrom(&mp) != nodeDB.getNodeNum()) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) { 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"); storeForwardModule->historyAdd(mp);
DEBUG_MSG("*** S&F stored. Message history contains %u records now.\n", this->packetHistoryCurrent);
auto &p = mp.decoded;
if ((p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && (p.payload.bytes[2] == 0x00)) {
DEBUG_MSG("--- --- --- Request to send\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));
}
} else if ((p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && (p.payload.bytes[2] == 'm') &&
(p.payload.bytes[3] == 0x00)) {
strlcpy(this->routerMessage,
"01234567890123456789012345678901234567890123456789012345678901234567890123456789"
"01234567890123456789012345678901234567890123456789012345678901234567890123456789"
"01234567890123456789012345678901234567890123456789012345678901234567890123456",
sizeof(this->routerMessage));
storeForwardModule->sendMessage(getFrom(&mp), this->routerMessage);
} else {
storeForwardModule->historyAdd(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); auto &p = mp.decoded;
StoreAndForward scratch;
} else { StoreAndForward *decoded = NULL;
DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum); if (mp.which_payload_variant == MeshPacket_decoded_tag) {
} if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &StoreAndForward_msg, &scratch)) {
decoded = &scratch;
} else {
DEBUG_MSG("Error decoding protobuf module!\n");
// if we can't decode it, nobody can process it!
return ProcessMessage::STOP;
}
return handleReceivedProtobuf(mp, decoded) ? ProcessMessage::STOP : ProcessMessage::CONTINUE;
}
} // all others are irrelevant
} }
} }
@@ -285,95 +273,127 @@ bool StoreForwardModule::handleReceivedProtobuf(const MeshPacket &mp, StoreAndFo
return false; return false;
} }
if (mp.decoded.portnum != PortNum_STORE_FORWARD_APP) { requests++;
DEBUG_MSG("Packet came from port %u\n", mp.decoded.portnum);
return false;
} else {
DEBUG_MSG("Packet came from PortNum_STORE_FORWARD_APP port %u\n", mp.decoded.portnum);
switch (p->rr) { switch (p->rr) {
case StoreAndForward_RequestResponse_CLIENT_ERROR: case StoreAndForward_RequestResponse_CLIENT_ERROR:
case StoreAndForward_RequestResponse_CLIENT_ABORT:
if(is_server) { if(is_server) {
// Do nothing // stop sending stuff, the client wants to abort or has another error
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_ERROR\n"); if ((this->busy) && (this->busyTo == getFrom(&mp))) {
DEBUG_MSG("*** Client in ERROR or ABORT requested\n");
this->packetHistoryTXQueue_index = 0;
this->busy = false;
}
} }
break; break;
case StoreAndForward_RequestResponse_CLIENT_HISTORY: case StoreAndForward_RequestResponse_CLIENT_HISTORY:
if(is_server) { if(is_server) {
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_HISTORY\n"); requests_history++;
DEBUG_MSG("*** Client Request to send HISTORY\n");
// Send the last 60 minutes of messages. // Send the last 60 minutes of messages.
if (this->busy) { if (this->busy) {
strcpy(this->routerMessage, "** S&F - Busy. Try again shortly."); storeForwardModule->sendMessage(getFrom(&mp), StoreAndForward_RequestResponse_ROUTER_BUSY);
storeForwardModule->sendMessage(getFrom(&mp), this->routerMessage); DEBUG_MSG("*** S&F - Busy. Try again shortly.\n");
} else { } else {
storeForwardModule->historySend(1000 * 60, getFrom(&mp)); if ((p->which_variant == StoreAndForward_history_tag) && (p->variant.history.window > 0)){
storeForwardModule->historySend(p->variant.history.window * 60000, getFrom(&mp)); // window is in minutes
} else {
storeForwardModule->historySend(historyReturnWindow * 60000, getFrom(&mp)); // defaults to 4 hours
}
} }
} }
break; break;
case StoreAndForward_RequestResponse_CLIENT_PING: case StoreAndForward_RequestResponse_CLIENT_PING:
if(is_server) { if(is_server) {
// Do nothing DEBUG_MSG("*** StoreAndForward_RequestResponse_CLIENT_PING\n");
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PING\n"); // respond with a ROUTER PONG
storeForwardModule->sendMessage(getFrom(&mp), StoreAndForward_RequestResponse_ROUTER_PONG);
} }
break; break;
case StoreAndForward_RequestResponse_CLIENT_PONG: case StoreAndForward_RequestResponse_CLIENT_PONG:
if(is_server) { if(is_server) {
// Do nothing DEBUG_MSG("*** StoreAndForward_RequestResponse_CLIENT_PONG\n");
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PONG\n"); // The Client is alive, update NodeDB
nodeDB.updateFrom(mp);
} }
break; break;
case StoreAndForward_RequestResponse_CLIENT_STATS: case StoreAndForward_RequestResponse_CLIENT_STATS:
if(is_server) { if(is_server) {
// Do nothing DEBUG_MSG("*** Client Request to send STATS\n");
DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_STATS\n"); if (this->busy) {
} storeForwardModule->sendMessage(getFrom(&mp), StoreAndForward_RequestResponse_ROUTER_BUSY);
break; DEBUG_MSG("*** S&F - Busy. Try again shortly.\n");
} else {
case StoreAndForward_RequestResponse_ROUTER_BUSY: storeForwardModule->statsSend(getFrom(&mp));
if(is_client) { }
// Do nothing
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_BUSY\n");
} }
break; break;
case StoreAndForward_RequestResponse_ROUTER_ERROR: case StoreAndForward_RequestResponse_ROUTER_ERROR:
case StoreAndForward_RequestResponse_ROUTER_BUSY:
if(is_client) { if(is_client) {
// Do nothing DEBUG_MSG("*** StoreAndForward_RequestResponse_ROUTER_BUSY\n");
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_ERROR\n"); // retry in messages_saved * packetTimeMax ms
retry_delay = millis() + packetHistoryCurrent * packetTimeMax * (StoreAndForward_RequestResponse_ROUTER_ERROR ? 2 : 1);
} }
break; break;
case StoreAndForward_RequestResponse_ROUTER_PONG:
// A router responded, this is equal to receiving a heartbeat
case StoreAndForward_RequestResponse_ROUTER_HEARTBEAT: case StoreAndForward_RequestResponse_ROUTER_HEARTBEAT:
if(is_client) { if(is_client) {
// Do nothing // register heartbeat and interval
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_HEARTBEAT\n"); if (p->which_variant == StoreAndForward_heartbeat_tag) {
heartbeatInterval = p->variant.heartbeat.period;
}
lastHeartbeat = millis();
DEBUG_MSG("*** StoreAndForward Heartbeat received\n");
} }
break; break;
case StoreAndForward_RequestResponse_ROUTER_PING: case StoreAndForward_RequestResponse_ROUTER_PING:
if(is_client) { if(is_client) {
// Do nothing DEBUG_MSG("*** StoreAndForward_RequestResponse_ROUTER_PING\n");
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PING\n"); // respond with a CLIENT PONG
storeForwardModule->sendMessage(getFrom(&mp), StoreAndForward_RequestResponse_CLIENT_PONG);
} }
break; break;
case StoreAndForward_RequestResponse_ROUTER_PONG: case StoreAndForward_RequestResponse_ROUTER_STATS:
if(is_client) { if(is_client) {
// Do nothing DEBUG_MSG("*** Router Response STATS\n");
DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PONG\n"); // These fields only have informational purpose on a client. Fill them to consume later.
if (p->which_variant == StoreAndForward_stats_tag) {
this->packetHistoryMax = p->variant.stats.messages_total;
this->packetHistoryCurrent = p->variant.stats.messages_saved;
this->records = p->variant.stats.messages_max;
this->requests = p->variant.stats.requests;
this->requests_history = p->variant.stats.requests_history;
this->heartbeat = p->variant.stats.heartbeat;
this->historyReturnMax = p->variant.stats.return_max;
this->historyReturnWindow = p->variant.stats.return_window;
}
}
break;
case StoreAndForward_RequestResponse_ROUTER_HISTORY:
if(is_client) {
// These fields only have informational purpose on a client. Fill them to consume later.
if (p->which_variant == StoreAndForward_history_tag) {
this->historyReturnWindow = p->variant.history.window / 60000;
DEBUG_MSG("*** Router Response HISTORY - Sending %d messages from last %d minutes\n", p->variant.history.history_messages, this->historyReturnWindow);
}
} }
break; break;
default: default:
assert(0); // unexpected state - FIXME, make an error code and reboot assert(0); // unexpected state - FIXME, make an error code and reboot
}
} }
return true; // There's no need for others to look at this message. return true; // There's no need for others to look at this message.
} }
@@ -398,7 +418,7 @@ StoreForwardModule::StoreForwardModule()
// Router // Router
if ((config.device.role == Config_DeviceConfig_Role_ROUTER) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) { if ((config.device.role == Config_DeviceConfig_Role_ROUTER) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) {
DEBUG_MSG("Initializing Store & Forward Module in Router mode\n"); DEBUG_MSG("*** Initializing Store & Forward Module in Router mode\n");
if (ESP.getPsramSize() > 0) { if (ESP.getPsramSize() > 0) {
if (ESP.getFreePsram() >= 1024 * 1024) { if (ESP.getFreePsram() >= 1024 * 1024) {
@@ -424,19 +444,19 @@ StoreForwardModule::StoreForwardModule()
this->populatePSRAM(); this->populatePSRAM();
is_server = true; is_server = true;
} else { } else {
DEBUG_MSG("Device has less than 1M of PSRAM free.\n"); DEBUG_MSG("*** Device has less than 1M of PSRAM free.\n");
DEBUG_MSG("Store & Forward Module - disabling server.\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 - disabling server.\n"); DEBUG_MSG("*** Store & Forward Module - disabling server.\n");
} }
// Client // Client
} }
if ((config.device.role == Config_DeviceConfig_Role_CLIENT) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) { if ((config.device.role == Config_DeviceConfig_Role_CLIENT) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) {
is_client = true; is_client = true;
DEBUG_MSG("Initializing Store & Forward Module in Client mode\n"); DEBUG_MSG("*** Initializing Store & Forward Module in Client mode\n");
} }
} }
#endif #endif

View File

@@ -24,10 +24,9 @@ class StoreForwardModule : private concurrency::OSThread, public ProtobufModule<
uint32_t busyTo = 0; uint32_t busyTo = 0;
char routerMessage[Constants_DATA_PAYLOAD_LEN] = {0}; char routerMessage[Constants_DATA_PAYLOAD_LEN] = {0};
uint32_t receivedRecord[50][2] = {{0}};
PacketHistoryStruct *packetHistory = 0; PacketHistoryStruct *packetHistory = 0;
uint32_t packetHistoryCurrent = 0; uint32_t packetHistoryCurrent = 0;
uint32_t packetHistoryMax = 0;
PacketHistoryStruct *packetHistoryTXQueue = 0; PacketHistoryStruct *packetHistoryTXQueue = 0;
uint32_t packetHistoryTXQueue_size = 0; uint32_t packetHistoryTXQueue_size = 0;
@@ -35,20 +34,21 @@ class StoreForwardModule : private concurrency::OSThread, public ProtobufModule<
uint32_t packetTimeMax = 5000; uint32_t packetTimeMax = 5000;
unsigned long lastHeartbeat = 0;
bool is_client = false; bool is_client = false;
bool is_server = false; bool is_server = false;
public: public:
StoreForwardModule(); StoreForwardModule();
unsigned long lastHeartbeat = 0;
uint32_t heartbeatInterval = 300;
/** /**
Update our local reference of when we last saw that node. Update our local reference of when we last saw that node.
@return 0 if we have never seen that node before otherwise return the last time we saw the node. @return 0 if we have never seen that node before otherwise return the last time we saw the node.
*/ */
void historyAdd(const MeshPacket &mp); void historyAdd(const MeshPacket &mp);
void historyReport(); void statsSend(uint32_t to);
void historySend(uint32_t msAgo, uint32_t to); void historySend(uint32_t msAgo, uint32_t to);
uint32_t historyQueueCreate(uint32_t msAgo, uint32_t to); uint32_t historyQueueCreate(uint32_t msAgo, uint32_t to);
@@ -57,12 +57,23 @@ class StoreForwardModule : private concurrency::OSThread, public ProtobufModule<
* Send our payload into the mesh * Send our payload into the mesh
*/ */
void sendPayload(NodeNum dest = NODENUM_BROADCAST, uint32_t packetHistory_index = 0); void sendPayload(NodeNum dest = NODENUM_BROADCAST, uint32_t packetHistory_index = 0);
void sendMessage(NodeNum dest, char *str); void sendMessage(NodeNum dest, StoreAndForward &payload);
void sendMessage(NodeNum dest, StoreAndForward_RequestResponse rr);
virtual MeshPacket *allocReply() override; virtual MeshPacket *allocReply() override;
/* /*
Override the wantPortnum method. -Override the wantPacket method.
*/ */
virtual bool wantPortnum(PortNum p) { return true; }; virtual bool wantPacket(const MeshPacket *p) override
{
switch(p->decoded.portnum) {
case PortNum_TEXT_MESSAGE_APP:
case PortNum_STORE_FORWARD_APP:
return true;
default:
return false;
}
}
private: private:
void populatePSRAM(); void populatePSRAM();
@@ -73,6 +84,12 @@ class StoreForwardModule : private concurrency::OSThread, public ProtobufModule<
uint32_t records = 0; // Calculated uint32_t records = 0; // Calculated
bool heartbeat = false; // No heartbeat. bool heartbeat = false; // No heartbeat.
// stats
uint32_t requests = 0;
uint32_t requests_history = 0;
uint32_t retry_delay = 0;
protected: protected:
virtual int32_t runOnce() override; virtual int32_t runOnce() override;

View File

@@ -88,6 +88,8 @@
#define HW_VENDOR HardwareModel_HELTEC_V3 #define HW_VENDOR HardwareModel_HELTEC_V3
#elif defined(HELTEC_WSL_V3) #elif defined(HELTEC_WSL_V3)
#define HW_VENDOR HardwareModel_HELTEC_WSL_V3 #define HW_VENDOR HardwareModel_HELTEC_WSL_V3
#elif defined(TLORA_T3S3_V1)
#define HW_VENDOR HardwareModel_TLORA_T3_S3
#endif #endif
// //

View File

@@ -201,7 +201,7 @@ void SimRadio::startSend(MeshPacket * txp)
} else { } else {
DEBUG_MSG("Payload size is larger than compressed message allows! Sending empty payload.\n"); DEBUG_MSG("Payload size is larger than compressed message allows! Sending empty payload.\n");
} }
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), Compressed_fields, &c); p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &Compressed_msg, &c);
p->decoded.portnum = PortNum_SIMULATOR_APP; p->decoded.portnum = PortNum_SIMULATOR_APP;
service.sendToPhone(p); // Sending back to simulator service.sendToPhone(p); // Sending back to simulator
} }

View File

@@ -0,0 +1,34 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
#define USB_VID 0x303a
#define USB_PID 0x1001
#define EXTERNAL_NUM_INTERRUPTS 46
#define NUM_DIGITAL_PINS 48
#define NUM_ANALOG_INPUTS 20
#define analogInputToDigitalPin(p) (((p)<20)?(analogChannelToDigitalPin(p)):-1)
#define digitalPinToInterrupt(p) (((p)<48)?(p):-1)
#define digitalPinHasPWM(p) (p < 46)
// The default Wire will be mapped to PMU and RTC
static const uint8_t SDA = 18;
static const uint8_t SCL = 17;
// Default SPI will be mapped to Radio
static const uint8_t SS = 7;
static const uint8_t MOSI = 6;
static const uint8_t MISO = 3;
static const uint8_t SCK = 5;
#define SPI_MOSI (11)
#define SPI_SCK (14)
#define SPI_MISO (2)
#define SPI_CS (13)
#define SDCARD_CS SPI_CS
#endif /* Pins_Arduino_h */

View File

@@ -0,0 +1,9 @@
[env:tlora-t3s3-v1]
extends = esp32s3_base
board = tlora-t3s3-v1
lib_deps =
${esp32_base.lib_deps}
caveman99/ESP32 Codec2@^1.0.1
build_flags =
${esp32_base.build_flags} -D TLORA_T3S3_V1 -I variants/tlora_t3s3_v1

View File

@@ -0,0 +1,54 @@
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define HAS_SDCARD
#define SDCARD_USE_SPI1
#define USE_SSD1306
#define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
// ratio of voltage divider = 2.0 (R42=100k, R43=100k)
#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage.
#define I2C_SDA 18 // I2C pins for this board
#define I2C_SCL 17
#define LED_PIN 37 // If defined we will blink this LED
#define BUTTON_PIN 0 // If defined, this will be used for user button presses,
#define BUTTON_NEED_PULLUP
// TTGO uses a common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if
// not found then probe for SX1262
#define USE_RF95 // RFM95/SX127x
#define USE_SX1262
#define USE_SX1280
#define RF95_SCK 5
#define RF95_MISO 3
#define RF95_MOSI 6
#define RF95_NSS 7
#define LORA_RESET 8
#define LORA_DIO0 9
#define LORA_DIO1 9
#define LORA_DIO2 33 // SX1262 BUSY
#define LORA_DIO3 34 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled
#ifdef USE_SX1262
#define SX126X_CS RF95_NSS // FIXME - we really should define LORA_CS instead
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY 36
#define SX126X_RESET LORA_RESET
#define SX126X_RXEN 21
#define SX126X_TXEN 10
#endif
#ifdef USE_SX1280
#define SX128X_CS RF95_NSS
#define SX128X_DIO1 LORA_DIO1
#define SX128X_BUSY 36
#define SX128X_RESET LORA_RESET
#define SX128X_RXEN 21
#define SX128X_TXEN 10
#endif

View File

@@ -5,7 +5,5 @@ lib_deps =
${esp32_base.lib_deps} ${esp32_base.lib_deps}
caveman99/ESP32 Codec2@^1.0.1 caveman99/ESP32 Codec2@^1.0.1
; the RADIOLIB_GODMODE flag can be removed once the next version of RadioLib is released. (>5.5.0)
build_flags = build_flags =
${esp32_base.build_flags} -D TLORA_V2_1_18 -I variants/tlora_v2_1_18 -D RADIOLIB_GODMODE ${esp32_base.build_flags} -D TLORA_V2_1_18 -I variants/tlora_v2_1_18