From 14be4ee9f050645dd7e6a4bcd6cdb92e1ed8969b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 28 Dec 2022 15:31:04 +0100 Subject: [PATCH] Release Develop to Master --- boards/tlora-t3s3-v1.json | 47 +++ platformio.ini | 4 +- src/PowerFSM.cpp | 2 - src/SerialConsole.cpp | 6 +- src/SerialConsole.h | 4 +- src/graphics/Screen.cpp | 12 +- src/main.cpp | 12 - src/mesh/FloodingRouter.cpp | 5 +- src/mesh/FloodingRouter.h | 2 +- src/mesh/MeshModule.cpp | 4 +- src/mesh/NodeDB.cpp | 20 +- src/mesh/PhoneAPI.cpp | 4 +- src/mesh/RadioInterface.cpp | 2 +- src/mesh/RadioLibRF95.h | 5 - src/mesh/ReliableRouter.cpp | 22 +- src/mesh/ReliableRouter.h | 2 +- src/mesh/Router.cpp | 4 +- src/mesh/Router.h | 2 +- src/mesh/SX128xInterface.cpp | 19 +- src/mesh/SX128xInterface.h | 2 - src/mesh/StreamAPI.cpp | 4 +- src/mesh/StreamAPI.h | 6 +- src/mesh/eth/ethServerAPI.cpp | 4 +- src/mesh/eth/ethServerAPI.h | 2 +- src/mesh/generated/mesh.pb.h | 2 + src/mesh/generated/storeforward.pb.h | 56 ++-- src/mesh/wifi/WiFiServerAPI.cpp | 4 +- src/mesh/wifi/WiFiServerAPI.h | 2 +- src/modules/AdminModule.cpp | 2 +- src/modules/CannedMessageModule.cpp | 9 +- src/modules/ExternalNotificationModule.cpp | 297 ++++++++++++------ src/modules/ExternalNotificationModule.h | 18 +- src/modules/Modules.cpp | 6 +- src/modules/NodeInfoModule.cpp | 2 +- src/modules/PositionModule.cpp | 2 +- src/modules/RemoteHardwareModule.cpp | 2 +- src/modules/RoutingModule.cpp | 2 +- src/modules/SerialModule.cpp | 63 ++-- src/modules/SerialModule.h | 9 +- .../Telemetry/EnvironmentTelemetry.cpp | 2 +- src/modules/TraceRouteModule.cpp | 8 +- src/modules/esp32/StoreForwardModule.cpp | 286 +++++++++-------- src/modules/esp32/StoreForwardModule.h | 35 ++- src/platform/esp32/architecture.h | 2 + src/platform/portduino/SimRadio.cpp | 2 +- variants/tlora_t3s3_v1/pins_arduino.h | 34 ++ variants/tlora_t3s3_v1/platformio.ini | 9 + variants/tlora_t3s3_v1/variant.h | 54 ++++ variants/tlora_v2_1_18/platformio.ini | 4 +- 49 files changed, 711 insertions(+), 397 deletions(-) create mode 100644 boards/tlora-t3s3-v1.json create mode 100644 variants/tlora_t3s3_v1/pins_arduino.h create mode 100644 variants/tlora_t3s3_v1/platformio.ini create mode 100644 variants/tlora_t3s3_v1/variant.h diff --git a/boards/tlora-t3s3-v1.json b/boards/tlora-t3s3-v1.json new file mode 100644 index 000000000..36a54ad2b --- /dev/null +++ b/boards/tlora-t3s3-v1.json @@ -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" +} \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 4461d1a5f..e58656dd2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -65,7 +65,8 @@ lib_deps = https://github.com/meshtastic/ArduinoThread.git#72921ac222eed6f526ba1682023cee290d9aa1b3 nanopb/Nanopb@^0.4.6 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 check_tool = cppcheck @@ -81,6 +82,7 @@ framework = arduino lib_deps = ${env.lib_deps} mprograms/QMC5883LCompass@^1.1.1 + end2endzone/NonBlockingRTTTL@^1.3.0 https://github.com/meshtastic/SparkFun_ATECCX08a_Arduino_Library.git#52b5282639d08a8cbd4b748363089eed6102dc76 build_flags = ${env.build_flags} -Os -DRADIOLIB_SPI_PARANOID=0 diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index e60056ccd..5bea8d796 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -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"); #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; - // 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 diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index b7854b2cf..e7355db29 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -25,7 +25,7 @@ void consolePrintf(const char *format, ...) #endif } -SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port) +SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), concurrency::OSThread("SerialConsole") { assert(!console); console = this; @@ -46,6 +46,10 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port) 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 bool SerialConsole::checkIsConnected() diff --git a/src/SerialConsole.h b/src/SerialConsole.h index e7b8af34f..d1f2abac8 100644 --- a/src/SerialConsole.h +++ b/src/SerialConsole.h @@ -6,7 +6,7 @@ * 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). */ -class SerialConsole : public StreamAPI, public RedirectablePrint +class SerialConsole : public StreamAPI, public RedirectablePrint, private concurrency::OSThread { public: SerialConsole(); @@ -24,6 +24,8 @@ class SerialConsole : public StreamAPI, public RedirectablePrint return RedirectablePrint::write(c); } + virtual int32_t runOnce() override; + protected: /// Check the current underlying physical link to see if the client is currently connected diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 8d3429454..7b34aee55 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -35,7 +35,7 @@ along with this program. If not, see . #include "mesh/Channels.h" #include "mesh/generated/deviceonly.pb.h" #include "modules/TextMessageModule.h" - +#include "modules/ExternalNotificationModule.h" #include "sleep.h" #include "target_specific.h" #include "utils.h" @@ -1071,7 +1071,13 @@ int32_t Screen::runOnce() handleSetOn(false); break; 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; case Cmd::START_BLUETOOTH_PIN_SCREEN: handleStartBluetoothPinScreen(cmd.bluetooth_pin); @@ -1400,7 +1406,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 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 if (moduleConfig.store_forward.enabled) { -#if 0 +#ifdef ARCH_ESP32 if (millis() - storeForwardModule->lastHeartbeat > (storeForwardModule->heartbeatInterval * 1200)) { //no heartbeat, overlap a bit #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); diff --git a/src/main.cpp b/src/main.cpp index c0fcaf96e..f999ac708 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -242,7 +242,6 @@ void setup() digitalWrite(PIN_3V3_EN, 1); #endif - // Currently only the tbeam has a PMU // PMU initialization needs to be placed before scanI2Cdevice 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 mqttInit(); #endif diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index 925735903..e21820305 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -17,7 +17,7 @@ ErrorCode FloodingRouter::send(MeshPacket *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 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 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 - } 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 (config.device.role != Config_DeviceConfig_Role_CLIENT_MUTE) { MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it diff --git a/src/mesh/FloodingRouter.h b/src/mesh/FloodingRouter.h index 7e6271fc0..01b51c9a2 100644 --- a/src/mesh/FloodingRouter.h +++ b/src/mesh/FloodingRouter.h @@ -51,7 +51,7 @@ class FloodingRouter : public Router, protected PacketHistory * Called immedately on receiption, before any further processing. * @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 diff --git a/src/mesh/MeshModule.cpp b/src/mesh/MeshModule.cpp index ca1fb5b50..8019cda1e 100644 --- a/src/mesh/MeshModule.cpp +++ b/src/mesh/MeshModule.cpp @@ -44,11 +44,11 @@ MeshPacket *MeshModule::allocAckNak(Routing_Error err, NodeNum to, PacketId idFr // auto p = allocDataProtobuf(c); MeshPacket *p = router->allocForSending(); 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->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->decoded.request_id = idFrom; p->channel = chIndex; diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 1e40b8d92..b9107515f 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -401,7 +401,7 @@ bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_ void NodeDB::loadFromDisk() { // 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 } else { 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 } else { 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 } else { 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 } else { 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"); } /** 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; #ifdef FSCom @@ -498,7 +498,7 @@ void NodeDB::saveChannelsToDisk() #ifdef FSCom FSCom.mkdir("/prefs"); #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 FSCom.mkdir("/prefs"); #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_network = 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) { @@ -541,7 +541,7 @@ void NodeDB::saveToDisk(int saveWhat) moduleConfig.has_serial = true; moduleConfig.has_store_forward = 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) { diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index ea08bf769..b50f93bf0 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -77,7 +77,7 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) // return (lastContactMsec != 0) && memset(&toRadioScratch, 0, sizeof(toRadioScratch)); - if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) { + if (pb_decode_from_bytes(buf, bufLength, &ToRadio_msg, &toRadioScratch)) { switch (toRadioScratch.which_payload_variant) { case ToRadio_packet_tag: return handleToRadioPacket(toRadioScratch.packet); @@ -291,7 +291,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) // Do we have a message from the mesh? if (fromRadioScratch.which_payload_variant != 0) { // 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); return numbytes; diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 4a40c6600..222e5d14b 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -175,7 +175,7 @@ uint32_t RadioInterface::getRetransmissionMsec(const MeshPacket *p) { assert(slotTimeMsec); // Better be non zero 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)); // 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); diff --git a/src/mesh/RadioLibRF95.h b/src/mesh/RadioLibRF95.h index ff8164ef2..7fbaff780 100644 --- a/src/mesh/RadioLibRF95.h +++ b/src/mesh/RadioLibRF95.h @@ -66,10 +66,5 @@ class RadioLibRF95: public SX1278 { // since default current limit for SX126x/127x in updated RadioLib is 60mA // use the previous value float currentLimit = 100; - -#ifndef RADIOLIB_GODMODE - private: -#endif - }; diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 7933a7920..22f3692b9 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -27,7 +27,7 @@ ErrorCode ReliableRouter::send(MeshPacket *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 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 // 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 - // heard one one adjacent node hear our packet that a) probably other adjacent nodes heard it and b) we can trust those - // nodes to reach our destination. Both of which might be incorrect. + // This "optimization", does save lots of airtime. For DMs, you also get a real ACK back + // from the intended recipient. auto key = GlobalPacketId(getFrom(p), p->id); auto old = findPendingPacket(key); if (old) { @@ -54,16 +53,11 @@ bool ReliableRouter::shouldFilterReceived(MeshPacket *p) } } - /* send acks for repeated packets that want acks and are destined for us - * this way if an ACK is dropped and a packet is resent we'll ACK the resent packet - * make sure wasSeenRecently _doesn't_ update - * finding the channel requires decoding the packet. */ - if (p->want_ack && (p->to == getNodeNum()) && wasSeenRecently(p, false) && !MeshModule::currentReply) { - 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()) { + /* Resend implicit ACKs for repeated packets (assuming the original packet was sent with HOP_RELIABLE) + * this way if an implicit ACK is dropped and a packet is resent we'll rebroadcast again. + * Resending real ACKs is omitted, as you might receive a packet multiple times due to flooding and + * flooding this ACK back to the original sender already adds redundancy. */ + 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 DEBUG_MSG("Resending implicit ack for a repeated floodmsg\n"); MeshPacket *tosend = packetPool.allocCopy(*p); diff --git a/src/mesh/ReliableRouter.h b/src/mesh/ReliableRouter.h index ff304cdd7..65f486e5b 100644 --- a/src/mesh/ReliableRouter.h +++ b/src/mesh/ReliableRouter.h @@ -96,7 +96,7 @@ class ReliableRouter : public FloodingRouter /** * 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. diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 5ce26f49a..66e21d21d 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -306,7 +306,7 @@ bool perhapsDecode(MeshPacket *p) // Take those raw bytes and convert them back into a well structured protobuf we can understand memset(&p->decoded, 0, sizeof(p->decoded)); - if (!pb_decode_from_bytes(bytes, rawSize, Data_fields, &p->decoded)) { + if (!pb_decode_from_bytes(bytes, rawSize, &Data_msg, &p->decoded)) { DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?)!\n"); } else if (p->decoded.portnum == PortNum_UNKNOWN_APP) { DEBUG_MSG("Invalid portnum (bad psk?)!\n"); @@ -360,7 +360,7 @@ Routing_Error perhapsEncode(MeshPacket *p) if (p->which_payload_variant == MeshPacket_decoded_tag) { static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union - size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), 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. // TODO: Allow modules to opt into compression. diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 3f079d92c..f7748bb2b 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -90,7 +90,7 @@ class Router : protected concurrency::OSThread * Called immedately on receiption, before any further processing. * @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 diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index 0a51d618a..d056ab8dd 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -1,5 +1,6 @@ #include "configuration.h" #include "SX128xInterface.h" +#include "mesh/NodeDB.h" #include "error.h" // Particular boards might define a different max power based on what their hardware can do @@ -50,6 +51,20 @@ bool SX128xInterface::init() // \todo Display actual typename of the adapter, not just `SX128x` 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("Bandwidth set to %f\n", bw); DEBUG_MSG("Power output set to %d\n", power); @@ -223,13 +238,9 @@ bool SX128xInterface::isChannelActive() template bool SX128xInterface::isActivelyReceiving() { -#ifdef RADIOLIB_GODMODE uint16_t irq = lora.getIrqStatus(); bool hasPreamble = (irq & RADIOLIB_SX128X_IRQ_HEADER_VALID); return hasPreamble; -#else - return isChannelActive(); -#endif } template diff --git a/src/mesh/SX128xInterface.h b/src/mesh/SX128xInterface.h index 5a6ed95f9..2010a65c4 100644 --- a/src/mesh/SX128xInterface.h +++ b/src/mesh/SX128xInterface.h @@ -27,9 +27,7 @@ class SX128xInterface : public RadioLibInterface /// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep. virtual bool sleep() override; -#ifdef RADIOLIB_GODMODE bool isIRQPending() override { return lora.getIrqStatus() != 0; } -#endif protected: diff --git a/src/mesh/StreamAPI.cpp b/src/mesh/StreamAPI.cpp index 0b959ddcb..57911cf77 100644 --- a/src/mesh/StreamAPI.cpp +++ b/src/mesh/StreamAPI.cpp @@ -6,7 +6,7 @@ #define START2 0xc3 #define HEADER_LEN 4 -int32_t StreamAPI::runOnce() +int32_t StreamAPI::runOncePart() { auto result = readStream(); writeStream(); @@ -115,7 +115,7 @@ void StreamAPI::emitRebooted() fromRadioScratch.rebooted = true; // 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 diff --git a/src/mesh/StreamAPI.h b/src/mesh/StreamAPI.h index 0f36ecb06..3196e96f8 100644 --- a/src/mesh/StreamAPI.h +++ b/src/mesh/StreamAPI.h @@ -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. */ -class StreamAPI : public PhoneAPI, protected concurrency::OSThread +class StreamAPI : public PhoneAPI { /** * The stream we read/write from @@ -42,13 +42,13 @@ class StreamAPI : public PhoneAPI, protected concurrency::OSThread uint32_t lastRxMsec = 0; 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 * phone. */ - virtual int32_t runOnce() override; + virtual int32_t runOncePart(); private: /** diff --git a/src/mesh/eth/ethServerAPI.cpp b/src/mesh/eth/ethServerAPI.cpp index bb7dd927d..3b3b6bbc8 100644 --- a/src/mesh/eth/ethServerAPI.cpp +++ b/src/mesh/eth/ethServerAPI.cpp @@ -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"); } @@ -42,7 +42,7 @@ bool ethServerAPI::checkIsConnected() int32_t ethServerAPI::runOnce() { if (client.connected()) { - return StreamAPI::runOnce(); + return StreamAPI::runOncePart(); } else { DEBUG_MSG("Client dropped connection, suspending API service\n"); enabled = false; // we no longer need to run diff --git a/src/mesh/eth/ethServerAPI.h b/src/mesh/eth/ethServerAPI.h index c92ab2f17..962841c80 100644 --- a/src/mesh/eth/ethServerAPI.h +++ b/src/mesh/eth/ethServerAPI.h @@ -7,7 +7,7 @@ * 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). */ -class ethServerAPI : public StreamAPI +class ethServerAPI : public StreamAPI, private concurrency::OSThread { private: EthernetClient client; diff --git a/src/mesh/generated/mesh.pb.h b/src/mesh/generated/mesh.pb.h index 6fa6c8d33..31e7817a0 100644 --- a/src/mesh/generated/mesh.pb.h +++ b/src/mesh/generated/mesh.pb.h @@ -54,6 +54,8 @@ typedef enum _HardwareModel { HardwareModel_NANO_G1 = 14, /* TODO: REPLACE */ 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 */ HardwareModel_STATION_G1 = 25, /* Less common/prototype boards listed here (needs one more byte over the air) */ diff --git a/src/mesh/generated/storeforward.pb.h b/src/mesh/generated/storeforward.pb.h index 625fb273c..0f0761949 100644 --- a/src/mesh/generated/storeforward.pb.h +++ b/src/mesh/generated/storeforward.pb.h @@ -10,8 +10,8 @@ #endif /* Enum definitions */ -/* 1 - 99 = From Router - 101 - 199 = From Client */ +/* 001 - 063 = From Router + 064 - 127 = From Client */ typedef enum _StoreAndForward_RequestResponse { /* Unset/unused */ StoreAndForward_RequestResponse_UNSET = 0, @@ -28,17 +28,19 @@ typedef enum _StoreAndForward_RequestResponse { StoreAndForward_RequestResponse_ROUTER_BUSY = 5, /* Router is responding to a request for history. */ StoreAndForward_RequestResponse_ROUTER_HISTORY = 6, + /* Router is responding to a request for stats. */ + StoreAndForward_RequestResponse_ROUTER_STATS = 7, /* Client is an in error state. */ - StoreAndForward_RequestResponse_CLIENT_ERROR = 101, + StoreAndForward_RequestResponse_CLIENT_ERROR = 64, /* 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. */ - StoreAndForward_RequestResponse_CLIENT_STATS = 103, + StoreAndForward_RequestResponse_CLIENT_STATS = 66, /* Client has requested the router respond. This can work as a "are you there" message. */ - StoreAndForward_RequestResponse_CLIENT_PING = 104, + StoreAndForward_RequestResponse_CLIENT_PING = 67, /* 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 */ StoreAndForward_RequestResponse_CLIENT_ABORT = 106 } StoreAndForward_RequestResponse; @@ -88,15 +90,17 @@ typedef struct _StoreAndForward_Heartbeat { typedef struct _StoreAndForward { /* TODO: REPLACE */ StoreAndForward_RequestResponse rr; - /* TODO: REPLACE */ - bool has_stats; - StoreAndForward_Statistics stats; - /* TODO: REPLACE */ - bool has_history; - StoreAndForward_History history; - /* TODO: REPLACE */ - bool has_heartbeat; - StoreAndForward_Heartbeat heartbeat; + pb_size_t which_variant; + union { + /* TODO: REPLACE */ + StoreAndForward_Statistics stats; + /* TODO: REPLACE */ + StoreAndForward_History history; + /* TODO: REPLACE */ + StoreAndForward_Heartbeat heartbeat; + /* Empty Payload */ + bool empty; + } variant; } StoreAndForward; @@ -116,11 +120,11 @@ extern "C" { /* 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_History_init_default {0, 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_History_init_zero {0, 0, 0} #define StoreAndForward_Heartbeat_init_zero {0, 0} @@ -144,18 +148,20 @@ extern "C" { #define StoreAndForward_stats_tag 2 #define StoreAndForward_history_tag 3 #define StoreAndForward_heartbeat_tag 4 +#define StoreAndForward_empty_tag 5 /* Struct field encoding specification for nanopb */ #define StoreAndForward_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, rr, 1) \ -X(a, STATIC, OPTIONAL, MESSAGE, stats, 2) \ -X(a, STATIC, OPTIONAL, MESSAGE, history, 3) \ -X(a, STATIC, OPTIONAL, MESSAGE, heartbeat, 4) +X(a, STATIC, ONEOF, MESSAGE, (variant,stats,variant.stats), 2) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,history,variant.history), 3) \ +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_DEFAULT NULL -#define StoreAndForward_stats_MSGTYPE StoreAndForward_Statistics -#define StoreAndForward_history_MSGTYPE StoreAndForward_History -#define StoreAndForward_heartbeat_MSGTYPE StoreAndForward_Heartbeat +#define StoreAndForward_variant_stats_MSGTYPE StoreAndForward_Statistics +#define StoreAndForward_variant_history_MSGTYPE StoreAndForward_History +#define StoreAndForward_variant_heartbeat_MSGTYPE StoreAndForward_Heartbeat #define StoreAndForward_Statistics_FIELDLIST(X, a) \ 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_History_size 18 #define StoreAndForward_Statistics_size 50 -#define StoreAndForward_size 88 +#define StoreAndForward_size 54 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/wifi/WiFiServerAPI.cpp b/src/mesh/wifi/WiFiServerAPI.cpp index 34a15f71b..78936176b 100644 --- a/src/mesh/wifi/WiFiServerAPI.cpp +++ b/src/mesh/wifi/WiFiServerAPI.cpp @@ -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"); } @@ -42,7 +42,7 @@ bool WiFiServerAPI::checkIsConnected() int32_t WiFiServerAPI::runOnce() { if (client.connected()) { - return StreamAPI::runOnce(); + return StreamAPI::runOncePart(); } else { DEBUG_MSG("Client dropped connection, suspending API service\n"); enabled = false; // we no longer need to run diff --git a/src/mesh/wifi/WiFiServerAPI.h b/src/mesh/wifi/WiFiServerAPI.h index 84a23be06..812408818 100644 --- a/src/mesh/wifi/WiFiServerAPI.h +++ b/src/mesh/wifi/WiFiServerAPI.h @@ -7,7 +7,7 @@ * 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). */ -class WiFiServerAPI : public StreamAPI +class WiFiServerAPI : public StreamAPI, private concurrency::OSThread { private: WiFiClient client; diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 0b617adea..2ed050f66 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -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 boundChannel = Channels::adminChannel; diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index d148768ad..4357ee2ca 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -45,8 +45,7 @@ CannedMessageModule *cannedMessageModule; // 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 saveProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, - const void *dest_struct); +extern bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct); CannedMessageModule::CannedMessageModule() : SinglePortModule("canned", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessageModule") @@ -480,7 +479,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st void CannedMessageModule::loadProtoForModule() { if (!loadProto(cannedMessagesConfigFile, CannedMessageModuleConfig_size, sizeof(cannedMessagesConfigFile), - CannedMessageModuleConfig_fields, &cannedMessageModuleConfig)) { + &CannedMessageModuleConfig_msg, &cannedMessageModuleConfig)) { installDefaultCannedMessageModuleConfig(); } } @@ -499,8 +498,8 @@ bool CannedMessageModule::saveProtoForModule() FS.mkdir("/prefs"); #endif - okay &= saveProto(cannedMessagesConfigFile, CannedMessageModuleConfig_size, sizeof(cannedMessageModuleConfig), - CannedMessageModuleConfig_fields, &cannedMessageModuleConfig); + okay &= saveProto(cannedMessagesConfigFile, CannedMessageModuleConfig_size, + &CannedMessageModuleConfig_msg, &cannedMessageModuleConfig); return okay; } diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 5a95ceec1..6c31fdabc 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -11,41 +11,9 @@ #define PIN_BUZZER false #endif -//#include - /* - Documentation: - https://github.com/meshtastic/firmware/blob/master/docs/software/modules/ExternalNotificationModule.md - - 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. - + https://meshtastic.org/docs/settings/moduleconfig/external-notification */ // Default configurations @@ -58,59 +26,113 @@ #define ASCII_BELL 0x07 -bool externalCurrentState = 0; -uint32_t externalTurnedOn = 0; +ExternalNotificationModule *externalNotificationModule; + +bool externalCurrentState[3] = {}; + +uint32_t externalTurnedOn[3] = {}; int32_t ExternalNotificationModule::runOnce() { - /* - Uncomment the preferences below if you want to use the module - without having to configure it from the PythonAPI or WebUI. - */ - - // moduleConfig.external_notification.enabled = 1; - // moduleConfig.external_notification.alert_message = 1; - - // moduleConfig.external_notification.active = 1; - // moduleConfig.external_notification.alert_bell = 1; - // moduleConfig.external_notification.output_ms = 1000; - // moduleConfig.external_notification.output = 13; - - if (externalCurrentState && !moduleConfig.external_notification.use_pwm) { + if (!moduleConfig.external_notification.enabled) { + return INT32_MAX; // we don't need this thread here... + } else { + if ((nagCycleCutoff < millis()) && !rtttl::isPlaying()) { + nagCycleCutoff = UINT32_MAX; + DEBUG_MSG("Turning off external notification: "); + for (int i = 0; i < 2; i++) { + if (getExternal(i)) { + setExternalOff(i); + externalTurnedOn[i] = 0; + DEBUG_MSG("%d ", i); + } + } + DEBUG_MSG("\n"); + return INT32_MAX; // save cycles till we're needed again + } // If 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 - : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < - millis()) { - DEBUG_MSG("Turning off external notification\n"); - setExternalOff(); + : EXT_NOTIFICATION_MODULE_OUTPUT_MS) < millis()) { + getExternal(0) ? setExternalOff(0) : setExternalOn(0); + } + 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; + } } -void ExternalNotificationModule::setExternalOn() +void ExternalNotificationModule::setExternalOn(uint8_t index) { - externalCurrentState = 1; - externalTurnedOn = millis(); + externalCurrentState[index] = 1; + externalTurnedOn[index] = millis(); - digitalWrite(output, - (moduleConfig.external_notification.active ? true : false)); + switch(index) { + 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, - (moduleConfig.external_notification.active ? false : true)); + switch(index) { + 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() : SinglePortModule("ExternalNotificationModule", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread( @@ -121,13 +143,18 @@ ExternalNotificationModule::ExternalNotificationModule() without having to configure it from the PythonAPI or WebUI. */ - // moduleConfig.external_notification.enabled = 1; - // moduleConfig.external_notification.alert_message = 1; + // moduleConfig.external_notification.enabled = true; + // 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.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) { @@ -137,19 +164,30 @@ ExternalNotificationModule::ExternalNotificationModule() ? moduleConfig.external_notification.output : EXT_NOTIFICATION_MODULE_OUTPUT; - if (!moduleConfig.external_notification.use_pwm) { - // Set the direction of a pin - DEBUG_MSG("Using Pin %i in digital mode\n", output); - pinMode(output, OUTPUT); - // Turn off the pin - setExternalOff(); - } 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); + // Set the direction of a pin + DEBUG_MSG("Using Pin %i in digital mode\n", output); + pinMode(output, OUTPUT); + setExternalOff(0); + externalTurnedOn[0] = 0; + if(moduleConfig.external_notification.output_vibra) { + DEBUG_MSG("Using Pin %i for vibra motor\n", moduleConfig.external_notification.output_vibra); + pinMode(moduleConfig.external_notification.output_vibra, OUTPUT); + setExternalOff(1); + externalTurnedOn[1] = 0; + } + if(moduleConfig.external_notification.output_buzzer) { + 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 { DEBUG_MSG("External Notification Module Disabled\n"); @@ -163,30 +201,91 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp) if (getFrom(&mp) != nodeDB.getNodeNum()) { - // TODO: This may be a problem if messages are sent in unicide, but I'm not sure if it will. - // Need to know if and how this could be a problem. + // Check if the message contains a bell character. Don't do this loop for every pin, just once. + 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) { - auto &p = mp.decoded; - DEBUG_MSG("externalNotificationModule - Notification Bell\n"); - for (int i = 0; i < p.payload.size; i++) { - if (p.payload.bytes[i] == ASCII_BELL) { - if (!moduleConfig.external_notification.use_pwm) { - setExternalOn(); - } else { - playBeep(); - } + if (containsBell) { + DEBUG_MSG("externalNotificationModule - Notification Bell\n"); + setExternalOn(0); + 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_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) { DEBUG_MSG("externalNotificationModule - Notification Module\n"); - if (!moduleConfig.external_notification.use_pwm) { - setExternalOn(); + setExternalOn(0); + if (moduleConfig.external_notification.nag_timeout) { + nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; } 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 { diff --git a/src/modules/ExternalNotificationModule.h b/src/modules/ExternalNotificationModule.h index b5ab64b3c..04de235cf 100644 --- a/src/modules/ExternalNotificationModule.h +++ b/src/modules/ExternalNotificationModule.h @@ -3,6 +3,7 @@ #include "SinglePortModule.h" #include "concurrency/OSThread.h" #include "configuration.h" +#include #include #include @@ -17,18 +18,23 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency: public: ExternalNotificationModule(); - void setExternalOn(); - void setExternalOff(); - void getExternal(); + uint32_t nagCycleCutoff = UINT32_MAX; + + 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: - // virtual MeshPacket *allocReply(); - /** 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 */ virtual ProcessMessage handleReceived(const MeshPacket &mp) override; virtual int32_t runOnce() override; }; + +extern ExternalNotificationModule *externalNotificationModule; diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index ece160ced..96a6c0582 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -24,7 +24,7 @@ #endif #if defined(ARCH_ESP32) || defined(ARCH_NRF52) #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" #endif #endif @@ -63,13 +63,13 @@ void setupModules() new DeviceTelemetryModule(); new EnvironmentTelemetryModule(); #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(); #endif #ifdef ARCH_ESP32 // Only run on an esp32 based device. audioModule = new AudioModule(); - new ExternalNotificationModule(); + externalNotificationModule = new ExternalNotificationModule(); storeForwardModule = new StoreForwardModule(); diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index c3d58fd51..1ce1acf33 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -51,7 +51,7 @@ MeshPacket *NodeInfoModule::allocReply() } 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 setIntervalFromNow(30 * 1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup) diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp index 5159de278..013f2ee44 100644 --- a/src/modules/PositionModule.cpp +++ b/src/modules/PositionModule.cpp @@ -10,7 +10,7 @@ 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 setIntervalFromNow(60 * 1000); // Send our initial position 60 seconds after we start (to give GPS time to setup) diff --git a/src/modules/RemoteHardwareModule.cpp b/src/modules/RemoteHardwareModule.cpp index 7a859c87a..26f1c4dba 100644 --- a/src/modules/RemoteHardwareModule.cpp +++ b/src/modules/RemoteHardwareModule.cpp @@ -47,7 +47,7 @@ static uint64_t digitalReads(uint64_t mask) } RemoteHardwareModule::RemoteHardwareModule() - : ProtobufModule("remotehardware", PortNum_REMOTE_HARDWARE_APP, HardwareMessage_fields), concurrency::OSThread( + : ProtobufModule("remotehardware", PortNum_REMOTE_HARDWARE_APP, &HardwareMessage_msg), concurrency::OSThread( "remotehardware") { } diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index 40919eef9..8c4eeb4ff 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -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 } -RoutingModule::RoutingModule() : ProtobufModule("routing", PortNum_ROUTING_APP, Routing_fields) +RoutingModule::RoutingModule() : ProtobufModule("routing", PortNum_ROUTING_APP, &Routing_msg) { isPromiscuous = true; encryptedOk = true; diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index 55ce7b75e..eb8f0986b 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -46,6 +46,8 @@ */ +#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2) + #define RXD2 16 #define TXD2 17 #define RX_BUFFER 128 @@ -53,13 +55,15 @@ #define BAUD 38400 #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; SerialModuleRadio *serialModuleRadio; -SerialModule::SerialModule() : concurrency::OSThread("SerialModule") {} +SerialModule::SerialModule() : StreamAPI(&Serial2), concurrency::OSThread("SerialModule") {} -char serialBytes[Constants_DATA_PAYLOAD_LEN]; -size_t serialPayloadSize; +char serialStringChar[Constants_DATA_PAYLOAD_LEN]; 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() { -#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 without having to configure it from the PythonAPI or WebUI. @@ -178,19 +188,32 @@ int32_t SerialModule::runOnce() firstTime = 0; + // in API mode send rebooted sequence + if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_PROTO) { + emitRebooted(); + } + } else { - // in NMEA mode send out GGA every 2 seconds, Don't read from Port - if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_NMEA) { + if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_PROTO) { + 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) { lastNmeaTime = millis(); printGGA(outbuf, nodeDB.getNode(myNodeInfo.my_node_num)->position); Serial2.printf("%s", outbuf); } } else { + String serialString; + while (Serial2.available()) { - serialPayloadSize = Serial2.readBytes(serialBytes, Constants_DATA_PAYLOAD_LEN); + serialString = Serial2.readString(); + serialString.toCharArray(serialStringChar, Constants_DATA_PAYLOAD_LEN); + serialModuleRadio->sendPayload(); + + DEBUG_MSG("Received: %s\n", serialStringChar); } } } @@ -201,9 +224,6 @@ int32_t SerialModule::runOnce() return INT32_MAX; } -#else - return INT32_MAX; -#endif } MeshPacket *SerialModuleRadio::allocReply() @@ -215,26 +235,25 @@ MeshPacket *SerialModuleRadio::allocReply() void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies) { - Channel *ch = (boundChannel != NULL) ? &channels.getByName(boundChannel) : NULL; MeshPacket *p = allocReply(); p->to = dest; - if (ch != NULL) { - p->channel = ch->index; - } p->decoded.want_response = wantReplies; p->want_ack = ACK; - p->decoded.payload.size = serialPayloadSize; // You must specify how many bytes are in the reply - memcpy(p->decoded.payload.bytes, serialBytes, p->decoded.payload.size); + p->decoded.payload.size = strlen(serialStringChar); // You must specify how many bytes are in the reply + memcpy(p->decoded.payload.bytes, serialStringChar, p->decoded.payload.size); service.sendToMesh(p); } 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.mode == ModuleConfig_SerialConfig_Serial_Mode_PROTO) { + // in API mode we don't care about stuff from radio. + return ProcessMessage::CONTINUE; + } auto &p = mp.decoded; // 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 || 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) { NodeInfo *node = nodeDB.getNode(getFrom(&mp)); String sender = (node && node->has_user) ? node->user.short_name : "???"; Serial2.println(); Serial2.printf("%s: %s", sender, p.payload.bytes); Serial2.println(); - } else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_PROTO) { - // TODO this needs to be implemented } else if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_NMEA) { // Decode the Payload some more Position scratch; Position *decoded = NULL; if (mp.which_payload_variant == MeshPacket_decoded_tag && mp.decoded.portnum == ourPortNum) { 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; } // send position packet as WPL to the serial port @@ -290,8 +307,6 @@ ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp) } else { DEBUG_MSG("Serial Module Disabled\n"); } - -#endif - return ProcessMessage::CONTINUE; // Let others look at this message also if they want } +#endif diff --git a/src/modules/SerialModule.h b/src/modules/SerialModule.h index 0130bbe2d..c853263b6 100644 --- a/src/modules/SerialModule.h +++ b/src/modules/SerialModule.h @@ -8,7 +8,9 @@ #include "Router.h" #include -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; unsigned long lastNmeaTime = millis(); @@ -19,6 +21,9 @@ class SerialModule : private concurrency::OSThread protected: 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; @@ -65,3 +70,5 @@ class SerialModuleRadio : public MeshModule }; extern SerialModuleRadio *serialModuleRadio; + +#endif diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 7668a1f19..11955f3f6 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -159,7 +159,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt const char *lastSender = getSenderShortName(*lastMeasurementPacket); 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->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error"); DEBUG_MSG("Unable to decode last packet"); diff --git a/src/modules/TraceRouteModule.cpp b/src/modules/TraceRouteModule.cpp index 1c5fd97d3..fcb234664 100644 --- a/src/modules/TraceRouteModule.cpp +++ b/src/modules/TraceRouteModule.cpp @@ -24,14 +24,14 @@ void TraceRouteModule::updateRoute(MeshPacket* p) RouteDiscovery scratch; RouteDiscovery *updated = NULL; 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; appendMyID(updated); printRoute(updated, p->from, NODENUM_BROADCAST); // Set updated route to the payload of the to be flooded packet - p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), RouteDiscovery_fields, updated); + 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 *updated = NULL; 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; 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; } diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/esp32/StoreForwardModule.cpp index 9d1c44c18..54a250d2a 100644 --- a/src/modules/esp32/StoreForwardModule.cpp +++ b/src/modules/esp32/StoreForwardModule.cpp @@ -22,41 +22,32 @@ int32_t StoreForwardModule::runOnce() if (this->busy) { // Only send packets if the channel is less than 25% utilized. if (airTime->channelUtilizationPercent() < polite_channel_util_percent) { - - // DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index); storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index); - if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) { - strcpy(this->routerMessage, "** S&F - Done"); - storeForwardModule->sendMessage(this->busyTo, this->routerMessage); - - // DEBUG_MSG("--- --- --- In busy loop - Done \n"); + // Tell the client we're done sending + StoreAndForward sf = StoreAndForward_init_zero; + sf.rr = StoreAndForward_RequestResponse_ROUTER_PING; + storeForwardModule->sendMessage(this->busyTo, sf); + DEBUG_MSG("*** S&F - Done. (ROUTER_PING)\n"); this->packetHistoryTXQueue_index = 0; this->busy = false; } else { this->packetHistoryTXQueue_index++; } - } 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(); - DEBUG_MSG("Sending heartbeat\n"); - - StoreAndForward sf; + DEBUG_MSG("*** Sending heartbeat\n"); + StoreAndForward sf = StoreAndForward_init_zero; sf.rr = StoreAndForward_RequestResponse_ROUTER_HEARTBEAT; - sf.has_heartbeat = true; - sf.heartbeat.period = 300; - sf.heartbeat.secondary = 0; // TODO we always have one primary router for now - - MeshPacket *p = allocDataProtobuf(sf); - p->to = NODENUM_BROADCAST; - p->decoded.want_response = false; - p->priority = MeshPacket_Priority_MIN; - service.sendToMesh(p); + sf.which_variant = StoreAndForward_heartbeat_tag; + sf.variant.heartbeat.period = 300; + sf.variant.heartbeat.secondary = 0; // TODO we always have one primary router for now + storeForwardModule->sendMessage(NODENUM_BROADCAST, sf); } return (this->packetTimeMax); } @@ -74,7 +65,7 @@ void StoreForwardModule::populatePSRAM() 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 = static_cast(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 */ uint32_t numberOfPackets = (this->records ? this->records : (((ESP.getFreePsram() / 3) * 2) / sizeof(PacketHistoryStruct))); + this->records = numberOfPackets; this->packetHistory = static_cast(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("numberOfPackets for packetHistory - %u\n", numberOfPackets); + 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); } -void StoreForwardModule::historyReport() -{ - DEBUG_MSG("Message history contains %u records\n", this->packetHistoryCurrent); -} - -/* - * - */ void StoreForwardModule::historySend(uint32_t msAgo, uint32_t to) { - - // uint32_t packetsSent = 0; - uint32_t queueSize = storeForwardModule->historyQueueCreate(msAgo, to); if (queueSize) { - snprintf(this->routerMessage, 80, "** S&F - Sending %u message(s)", queueSize); - storeForwardModule->sendMessage(to, this->routerMessage); - + DEBUG_MSG ("*** S&F - Sending %u message(s)\n", queueSize); this->busy = true; // runOnce() will pickup the next steps once busy = true. this->busyTo = to; - } else { - strcpy(this->routerMessage, "** S&F - No history to send"); - storeForwardModule->sendMessage(to, this->routerMessage); + DEBUG_MSG ("*** S&F - No history to send\n"); } + 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 packetHistoryTXQueueIndex = 0; - this->packetHistoryTXQueue_size = 0; 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)); */ 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 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 || ((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].to = this->packetHistory[i].to; this->packetHistoryTXQueue[this->packetHistoryTXQueue_size].from = this->packetHistory[i].from; 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); this->packetHistoryTXQueue_size++; - 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->packetHistoryTXQueue[packetHistoryTXQueueIndex].payload); + DEBUG_MSG("*** PacketHistoryStruct time=%d\n", this->packetHistory[i].time); + DEBUG_MSG("*** PacketHistoryStruct msg=%s\n", this->packetHistory[i].payload); } } } @@ -174,6 +153,7 @@ void StoreForwardModule::historyAdd(const MeshPacket &mp) memcpy(this->packetHistory[this->packetHistoryCurrent].payload, p.payload.bytes, Constants_DATA_PAYLOAD_LEN); this->packetHistoryCurrent++; + this->packetHistoryMax++; } MeshPacket *StoreForwardModule::allocReply() @@ -184,7 +164,7 @@ MeshPacket *StoreForwardModule::allocReply() 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(); p->to = dest; @@ -203,12 +183,14 @@ void StoreForwardModule::sendPayload(NodeNum dest, uint32_t packetHistory_index) 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->priority = MeshPacket_Priority_MIN; + // FIXME - Determine if the delayed packet is broadcast or delayed. For now, assume // everything is 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. // TODO: Make this configurable. p->want_ack = 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)); + p->decoded.want_response = false; 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) @@ -230,46 +236,28 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp) #ifdef ARCH_ESP32 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 if ((getFrom(&mp) != nodeDB.getNodeNum()) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) { if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) { - DEBUG_MSG("Packet came from - PortNum_TEXT_MESSAGE_APP\n"); - - 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); - } + storeForwardModule->historyAdd(mp); + DEBUG_MSG("*** S&F stored. Message history contains %u records now.\n", this->packetHistoryCurrent); } else if (mp.decoded.portnum == PortNum_STORE_FORWARD_APP) { - DEBUG_MSG("Packet came from an PortNum_STORE_FORWARD_APP port %u\n", mp.decoded.portnum); - - } else { - DEBUG_MSG("Packet came from an unknown port %u\n", mp.decoded.portnum); - } + auto &p = mp.decoded; + StoreAndForward scratch; + StoreAndForward *decoded = NULL; + 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; } - if (mp.decoded.portnum != PortNum_STORE_FORWARD_APP) { - 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); - + requests++; switch (p->rr) { case StoreAndForward_RequestResponse_CLIENT_ERROR: + case StoreAndForward_RequestResponse_CLIENT_ABORT: if(is_server) { - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_ERROR\n"); + // stop sending stuff, the client wants to abort or has another error + if ((this->busy) && (this->busyTo == getFrom(&mp))) { + DEBUG_MSG("*** Client in ERROR or ABORT requested\n"); + this->packetHistoryTXQueue_index = 0; + this->busy = false; + } } break; case StoreAndForward_RequestResponse_CLIENT_HISTORY: 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. if (this->busy) { - strcpy(this->routerMessage, "** S&F - Busy. Try again shortly."); - storeForwardModule->sendMessage(getFrom(&mp), this->routerMessage); + storeForwardModule->sendMessage(getFrom(&mp), StoreAndForward_RequestResponse_ROUTER_BUSY); + DEBUG_MSG("*** S&F - Busy. Try again shortly.\n"); } 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; case StoreAndForward_RequestResponse_CLIENT_PING: 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; case StoreAndForward_RequestResponse_CLIENT_PONG: 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; case StoreAndForward_RequestResponse_CLIENT_STATS: if(is_server) { - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_STATS\n"); - } - break; - - case StoreAndForward_RequestResponse_ROUTER_BUSY: - if(is_client) { - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_BUSY\n"); + DEBUG_MSG("*** Client Request to send STATS\n"); + if (this->busy) { + storeForwardModule->sendMessage(getFrom(&mp), StoreAndForward_RequestResponse_ROUTER_BUSY); + DEBUG_MSG("*** S&F - Busy. Try again shortly.\n"); + } else { + storeForwardModule->statsSend(getFrom(&mp)); + } } break; case StoreAndForward_RequestResponse_ROUTER_ERROR: + case StoreAndForward_RequestResponse_ROUTER_BUSY: if(is_client) { - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_ERROR\n"); + DEBUG_MSG("*** StoreAndForward_RequestResponse_ROUTER_BUSY\n"); + // retry in messages_saved * packetTimeMax ms + retry_delay = millis() + packetHistoryCurrent * packetTimeMax * (StoreAndForward_RequestResponse_ROUTER_ERROR ? 2 : 1); } break; + case StoreAndForward_RequestResponse_ROUTER_PONG: + // A router responded, this is equal to receiving a heartbeat case StoreAndForward_RequestResponse_ROUTER_HEARTBEAT: if(is_client) { - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_HEARTBEAT\n"); + // register heartbeat and interval + if (p->which_variant == StoreAndForward_heartbeat_tag) { + heartbeatInterval = p->variant.heartbeat.period; + } + lastHeartbeat = millis(); + DEBUG_MSG("*** StoreAndForward Heartbeat received\n"); } break; case StoreAndForward_RequestResponse_ROUTER_PING: 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; - case StoreAndForward_RequestResponse_ROUTER_PONG: + case StoreAndForward_RequestResponse_ROUTER_STATS: if(is_client) { - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PONG\n"); + DEBUG_MSG("*** Router Response STATS\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; default: assert(0); // unexpected state - FIXME, make an error code and reboot - } } - return true; // There's no need for others to look at this message. } @@ -398,7 +418,7 @@ StoreForwardModule::StoreForwardModule() // Router 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.getFreePsram() >= 1024 * 1024) { @@ -424,19 +444,19 @@ StoreForwardModule::StoreForwardModule() this->populatePSRAM(); is_server = true; } else { - DEBUG_MSG("Device has less than 1M of PSRAM free.\n"); - DEBUG_MSG("Store & Forward Module - disabling server.\n"); + DEBUG_MSG("*** Device has less than 1M of PSRAM free.\n"); + DEBUG_MSG("*** Store & Forward Module - disabling server.\n"); } } else { - DEBUG_MSG("Device doesn't have PSRAM.\n"); - DEBUG_MSG("Store & Forward Module - disabling server.\n"); + DEBUG_MSG("*** Device doesn't have PSRAM.\n"); + DEBUG_MSG("*** Store & Forward Module - disabling server.\n"); } // Client } if ((config.device.role == Config_DeviceConfig_Role_CLIENT) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) { is_client = true; - DEBUG_MSG("Initializing Store & Forward Module in Client mode\n"); + DEBUG_MSG("*** Initializing Store & Forward Module in Client mode\n"); } } #endif diff --git a/src/modules/esp32/StoreForwardModule.h b/src/modules/esp32/StoreForwardModule.h index 32d3cddc9..c9cb72a5b 100644 --- a/src/modules/esp32/StoreForwardModule.h +++ b/src/modules/esp32/StoreForwardModule.h @@ -24,10 +24,9 @@ class StoreForwardModule : private concurrency::OSThread, public ProtobufModule< uint32_t busyTo = 0; char routerMessage[Constants_DATA_PAYLOAD_LEN] = {0}; - uint32_t receivedRecord[50][2] = {{0}}; - PacketHistoryStruct *packetHistory = 0; uint32_t packetHistoryCurrent = 0; + uint32_t packetHistoryMax = 0; PacketHistoryStruct *packetHistoryTXQueue = 0; uint32_t packetHistoryTXQueue_size = 0; @@ -35,20 +34,21 @@ class StoreForwardModule : private concurrency::OSThread, public ProtobufModule< uint32_t packetTimeMax = 5000; - unsigned long lastHeartbeat = 0; - bool is_client = false; bool is_server = false; public: StoreForwardModule(); + unsigned long lastHeartbeat = 0; + uint32_t heartbeatInterval = 300; + /** 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. */ void historyAdd(const MeshPacket &mp); - void historyReport(); + void statsSend(uint32_t to); void historySend(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 */ 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; /* - Override the wantPortnum method. - */ - virtual bool wantPortnum(PortNum p) { return true; }; + -Override the wantPacket method. + */ + 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: void populatePSRAM(); @@ -73,6 +84,12 @@ class StoreForwardModule : private concurrency::OSThread, public ProtobufModule< uint32_t records = 0; // Calculated bool heartbeat = false; // No heartbeat. + // stats + uint32_t requests = 0; + uint32_t requests_history = 0; + + uint32_t retry_delay = 0; + protected: virtual int32_t runOnce() override; diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index f80008ca2..212df9f46 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -88,6 +88,8 @@ #define HW_VENDOR HardwareModel_HELTEC_V3 #elif defined(HELTEC_WSL_V3) #define HW_VENDOR HardwareModel_HELTEC_WSL_V3 +#elif defined(TLORA_T3S3_V1) + #define HW_VENDOR HardwareModel_TLORA_T3_S3 #endif // diff --git a/src/platform/portduino/SimRadio.cpp b/src/platform/portduino/SimRadio.cpp index b3af114e5..06427a6d5 100644 --- a/src/platform/portduino/SimRadio.cpp +++ b/src/platform/portduino/SimRadio.cpp @@ -201,7 +201,7 @@ void SimRadio::startSend(MeshPacket * txp) } else { 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; service.sendToPhone(p); // Sending back to simulator } diff --git a/variants/tlora_t3s3_v1/pins_arduino.h b/variants/tlora_t3s3_v1/pins_arduino.h new file mode 100644 index 000000000..9b8eba7c1 --- /dev/null +++ b/variants/tlora_t3s3_v1/pins_arduino.h @@ -0,0 +1,34 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#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 */ diff --git a/variants/tlora_t3s3_v1/platformio.ini b/variants/tlora_t3s3_v1/platformio.ini new file mode 100644 index 000000000..aa8189199 --- /dev/null +++ b/variants/tlora_t3s3_v1/platformio.ini @@ -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 \ No newline at end of file diff --git a/variants/tlora_t3s3_v1/variant.h b/variants/tlora_t3s3_v1/variant.h new file mode 100644 index 000000000..64874dac0 --- /dev/null +++ b/variants/tlora_t3s3_v1/variant.h @@ -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 \ No newline at end of file diff --git a/variants/tlora_v2_1_18/platformio.ini b/variants/tlora_v2_1_18/platformio.ini index 9c79fa259..c61b8a28c 100644 --- a/variants/tlora_v2_1_18/platformio.ini +++ b/variants/tlora_v2_1_18/platformio.ini @@ -5,7 +5,5 @@ lib_deps = ${esp32_base.lib_deps} 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 = - ${esp32_base.build_flags} -D TLORA_V2_1_18 -I variants/tlora_v2_1_18 -D RADIOLIB_GODMODE \ No newline at end of file + ${esp32_base.build_flags} -D TLORA_V2_1_18 -I variants/tlora_v2_1_18 \ No newline at end of file