From 4de557b4db38f75ded25a71b2dd81c0a0f344b77 Mon Sep 17 00:00:00 2001 From: Mykhailo Lesyk Date: Wed, 21 Dec 2022 01:35:19 -0800 Subject: [PATCH 01/52] [modules][serial] fix simple module ability to send --- src/mesh/Channels.cpp | 11 +++++++++++ src/mesh/Channels.h | 3 +++ src/modules/SerialModule.cpp | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 9ae211e6e..e7f1cb3ed 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -196,6 +196,17 @@ Channel &Channels::getByIndex(ChannelIndex chIndex) return *ch; } +Channel &Channels::getByName(const char* chName) +{ + for (ChannelIndex i = 0; i < getNumChannels(); i++) { + if (strcasecmp(channelFile.channels[i].settings.name, chName) == 0) { + return channelFile.channels[i]; + } + } + + return getByIndex(getPrimaryIndex()); +} + void Channels::setChannel(const Channel &c) { Channel &old = getByIndex(c.index); diff --git a/src/mesh/Channels.h b/src/mesh/Channels.h index ebf08d32c..f66848577 100644 --- a/src/mesh/Channels.h +++ b/src/mesh/Channels.h @@ -40,6 +40,9 @@ class Channels /** Return the Channel for a specified index */ Channel &getByIndex(ChannelIndex chIndex); + /** Return the Channel for a specified name, return primary if not found */ + Channel &getByName(const char* chName); + /** Using the index inside the channel, update the specified channel's settings and role. If this channel is being promoted * to be primary, force all other channels to be secondary. */ diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index aaaf3b144..116690d74 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -221,8 +221,12 @@ 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; From 6a5dd26907fcaed2f562a7acd5ec5d485b4823b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 21 Dec 2022 12:24:38 +0100 Subject: [PATCH 02/52] need to trigger new CI run --- src/mesh/Channels.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/Channels.h b/src/mesh/Channels.h index f66848577..79e96b7f6 100644 --- a/src/mesh/Channels.h +++ b/src/mesh/Channels.h @@ -40,7 +40,7 @@ class Channels /** Return the Channel for a specified index */ Channel &getByIndex(ChannelIndex chIndex); - /** Return the Channel for a specified name, return primary if not found */ + /** Return the Channel for a specified name, return primary if not found. */ Channel &getByName(const char* chName); /** Using the index inside the channel, update the specified channel's settings and role. If this channel is being promoted From bd2bfd682235c8b0e4df46b2586fc8c2a8c25757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sun, 27 Nov 2022 14:03:50 +0100 Subject: [PATCH 03/52] update board definition, update copy/paste errors, fix SX1280. --- src/main.cpp | 26 ++++++++++++++++++- src/mesh/InterfacesTemplates.cpp | 3 --- src/mesh/RadioInterface.h | 2 ++ src/mesh/SX1280Interface.cpp | 4 --- src/mesh/SX1280Interface.h | 3 --- src/mesh/SX128xInterface.cpp | 44 +++++++++++++++++--------------- src/mesh/SX128xInterface.h | 12 ++++----- variants/tlora_v2_1_16/variant.h | 5 ---- variants/tlora_v2_1_18/variant.h | 12 ++------- 9 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 98f22ecf1..692837ad8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -387,7 +387,7 @@ void setup() } #endif -#if defined(USE_SX1280) && !defined(ARCH_PORTDUINO) +#if defined(USE_SX1280) if (!rIf) { rIf = new SX1280Interface(SX128X_CS, SX128X_DIO1, SX128X_RESET, SX128X_BUSY, SPI); if (!rIf->init()) { @@ -452,6 +452,30 @@ void setup() } #endif +// check if the radio chip matches the selected region + +if((config.lora.region == Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())){ + DEBUG_MSG("Warning: Radio chip does not support 2.4GHz LoRa. Reverting to unset.\n"); + config.lora.region = Config_LoRaConfig_RegionCode_UNSET; + nodeDB.saveToDisk(SEGMENT_CONFIG); + if(!rIf->reconfigure()) { + DEBUG_MSG("Reconfigure failed, rebooting\n"); + screen->startRebootScreen(); + rebootAtMsec = millis() + 5000; + } +} + +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/InterfacesTemplates.cpp b/src/mesh/InterfacesTemplates.cpp index 9602525b5..0d2246428 100644 --- a/src/mesh/InterfacesTemplates.cpp +++ b/src/mesh/InterfacesTemplates.cpp @@ -7,7 +7,4 @@ template class SX126xInterface; template class SX126xInterface; template class SX126xInterface; - -#if defined(RADIOLIB_GODMODE) template class SX128xInterface; -#endif \ No newline at end of file diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index e9f725c89..437f294a4 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -97,6 +97,8 @@ class RadioInterface */ virtual bool canSleep() { return true; } + virtual bool wideLora() { return false; } + /// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep. virtual bool sleep() { return true; } diff --git a/src/mesh/SX1280Interface.cpp b/src/mesh/SX1280Interface.cpp index 97a3febe3..7fc6b45e1 100644 --- a/src/mesh/SX1280Interface.cpp +++ b/src/mesh/SX1280Interface.cpp @@ -2,12 +2,8 @@ #include "SX1280Interface.h" #include "error.h" -#if defined(RADIOLIB_GODMODE) - SX1280Interface::SX1280Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi) : SX128xInterface(cs, irq, rst, busy, spi) { } - -#endif \ No newline at end of file diff --git a/src/mesh/SX1280Interface.h b/src/mesh/SX1280Interface.h index a9661501a..190ca3cf4 100644 --- a/src/mesh/SX1280Interface.h +++ b/src/mesh/SX1280Interface.h @@ -6,12 +6,9 @@ * Our adapter for SX1280 radios */ -#if defined(RADIOLIB_GODMODE) class SX1280Interface : public SX128xInterface { public: SX1280Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi); }; - -#endif \ No newline at end of file diff --git a/src/mesh/SX128xInterface.cpp b/src/mesh/SX128xInterface.cpp index 36eb0bb94..0a51d618a 100644 --- a/src/mesh/SX128xInterface.cpp +++ b/src/mesh/SX128xInterface.cpp @@ -2,8 +2,6 @@ #include "SX128xInterface.h" #include "error.h" -#if defined(RADIOLIB_GODMODE) - // Particular boards might define a different max power based on what their hardware can do #ifndef SX128X_MAX_POWER #define SX128X_MAX_POWER 13 @@ -27,11 +25,11 @@ bool SX128xInterface::init() pinMode(SX128X_POWER_EN, OUTPUT); #endif -#ifdef SX128X_RXEN // set not rx or tx mode +#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode digitalWrite(SX128X_RXEN, LOW); // Set low before becoming an output pinMode(SX128X_RXEN, OUTPUT); #endif -#ifdef SX128X_TXEN +#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC) digitalWrite(SX128X_TXEN, LOW); pinMode(SX128X_TXEN, OUTPUT); #endif @@ -46,6 +44,8 @@ bool SX128xInterface::init() limitPower(); + preambleLength = 12; // 12 is the default for this chip, 32 does not RX at all + int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength); // \todo Display actual typename of the adapter, not just `SX128x` DEBUG_MSG("SX128x init result %d\n", res); @@ -54,12 +54,6 @@ bool SX128xInterface::init() DEBUG_MSG("Bandwidth set to %f\n", bw); DEBUG_MSG("Power output set to %d\n", power); -#ifdef SX128X_TXEN - // lora.begin sets Dio2 as RF switch control, which is not true if we are manually controlling RX and TX - if (res == RADIOLIB_ERR_NONE) - res = lora.setDio2AsRfSwitch(true); -#endif - if (res == RADIOLIB_ERR_NONE) res = lora.setCRC(2); @@ -122,18 +116,28 @@ void INTERRUPT_ATTR SX128xInterface::disableInterrupt() lora.clearDio1Action(); } +template +bool SX128xInterface::wideLora() +{ + return true; +} + template void SX128xInterface::setStandby() { checkNotification(); // handle any pending interrupts before we force standby int err = lora.standby(); + + if (err != RADIOLIB_ERR_NONE) + DEBUG_MSG("SX128x standby failed with error %d\n", err); + assert(err == RADIOLIB_ERR_NONE); -#ifdef SX128X_RXEN // we have RXEN/TXEN control - turn off RX and TX power +#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn off RX and TX power digitalWrite(SX128X_RXEN, LOW); #endif -#ifdef SX128X_TXEN +#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC) digitalWrite(SX128X_TXEN, LOW); #endif @@ -158,10 +162,10 @@ void SX128xInterface::addReceiveMetadata(MeshPacket *mp) template void SX128xInterface::configHardwareForSend() { -#ifdef SX128X_TXEN // we have RXEN/TXEN control - turn on TX power / off RX power +#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn on TX power / off RX power digitalWrite(SX128X_TXEN, HIGH); #endif -#ifdef SX128X_RXEN +#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) digitalWrite(SX128X_RXEN, LOW); #endif @@ -180,10 +184,10 @@ void SX128xInterface::startReceive() setStandby(); -#ifdef SX128X_RXEN // we have RXEN/TXEN control - turn on RX power / off TX power +#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn on RX power / off TX power digitalWrite(SX128X_RXEN, HIGH); #endif -#ifdef SX128X_TXEN +#if defined(SX128X_TXEN) && (SX128X_TXEN != RADIOLIB_NC) digitalWrite(SX128X_TXEN, LOW); #endif @@ -219,11 +223,13 @@ bool SX128xInterface::isChannelActive() template bool SX128xInterface::isActivelyReceiving() { - // return isChannelActive(); - +#ifdef RADIOLIB_GODMODE uint16_t irq = lora.getIrqStatus(); bool hasPreamble = (irq & RADIOLIB_SX128X_IRQ_HEADER_VALID); return hasPreamble; +#else + return isChannelActive(); +#endif } template @@ -248,5 +254,3 @@ bool SX128xInterface::sleep() return true; } - -#endif \ No newline at end of file diff --git a/src/mesh/SX128xInterface.h b/src/mesh/SX128xInterface.h index 9994ce850..5a6ed95f9 100644 --- a/src/mesh/SX128xInterface.h +++ b/src/mesh/SX128xInterface.h @@ -1,7 +1,5 @@ #pragma once -#if defined(RADIOLIB_GODMODE) - #include "RadioLibInterface.h" /** @@ -19,6 +17,8 @@ class SX128xInterface : public RadioLibInterface /// \return true if initialisation succeeded. virtual bool init() override; + virtual bool wideLora() override; + /// Apply any radio provisioning changes /// Make sure the Driver is properly configured before calling init(). /// \return true if initialisation succeeded. @@ -27,9 +27,11 @@ class SX128xInterface : public RadioLibInterface /// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep. virtual bool sleep() override; - protected: +#ifdef RADIOLIB_GODMODE + bool isIRQPending() override { return lora.getIrqStatus() != 0; } +#endif - float currentLimit = 140; // Higher OCP limit for SX128x PA + protected: /** * Specific module instance @@ -71,5 +73,3 @@ class SX128xInterface : public RadioLibInterface private: }; - -#endif \ No newline at end of file diff --git a/variants/tlora_v2_1_16/variant.h b/variants/tlora_v2_1_16/variant.h index a58b10b81..14175f48b 100644 --- a/variants/tlora_v2_1_16/variant.h +++ b/variants/tlora_v2_1_16/variant.h @@ -3,8 +3,6 @@ #define GPS_RX_PIN 15 // per @der_bear on the forum, 36 is incorrect for this board type and 15 is a better pick #define GPS_TX_PIN 13 -#define EXT_NOTIFY_OUT 2 // Default pin to use for Ext Notify Module. - #define BATTERY_PIN 35 // 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. @@ -12,9 +10,6 @@ #define I2C_SDA 21 // I2C pins for this board #define I2C_SCL 22 -// #define RESET_OLED 16 // If defined, this pin will be used to reset the display controller. Crashes on newer ESP-IDF and not needed per schematic - -#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost #define LED_PIN 25 // If defined we will blink this LED #define BUTTON_PIN 12 // If defined, this will be used for user button presses, diff --git a/variants/tlora_v2_1_18/variant.h b/variants/tlora_v2_1_18/variant.h index dd94847be..95d699767 100644 --- a/variants/tlora_v2_1_18/variant.h +++ b/variants/tlora_v2_1_18/variant.h @@ -1,9 +1,5 @@ #undef GPS_RX_PIN #undef GPS_TX_PIN -#define GPS_RX_PIN 15 // per @der_bear on the forum, 36 is incorrect for this board type and 15 is a better pick -#define GPS_TX_PIN 13 - -#define EXT_NOTIFY_OUT 2 // Default pin to use for Ext Notify Module. #define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage // ratio of voltage divider = 2.0 (R42=100k, R43=100k) @@ -20,11 +16,7 @@ #define USE_SX1280 #define LORA_RESET 23 -#define SX128X_CS 18 // FIXME - we really should define LORA_CS instead +#define SX128X_CS 18 #define SX128X_DIO1 26 -#define SX128X_DIO2 33 #define SX128X_BUSY 32 -#define SX128X_RESET LORA_RESET -#define SX128X_E22 // Not really an E22 but TTGO seems to be trying to clone that -// Internally the TTGO module hooks the SX1280-DIO2 in to control the TX/RX switch (which is the default for the sx1280interface -// code) +#define SX128X_RESET LORA_RESET \ No newline at end of file From efc3f4c0eec18448dd447956c99ad40811855882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Mon, 28 Nov 2022 21:58:30 +0100 Subject: [PATCH 04/52] remove a few DSR Router bits for S&F Module --- src/main.cpp | 3 --- src/mesh/ReliableRouter.cpp | 6 ++---- src/mesh/ReliableRouter.h | 6 ------ src/mesh/Router.cpp | 1 - src/sleep.cpp | 2 +- 5 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 692837ad8..c0fcaf96e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,8 +8,6 @@ #include "configuration.h" #include "error.h" #include "power.h" -// #include "rom/rtc.h" -//#include "DSRRouter.h" #include "ReliableRouter.h" // #include "debug.h" #include "FSCommon.h" @@ -217,7 +215,6 @@ void setup() fsInit(); - // router = new DSRRouter(); router = new ReliableRouter(); #ifdef I2C_SDA1 diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 2807178b8..7933a7920 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -90,8 +90,7 @@ void ReliableRouter::sniffReceived(const MeshPacket *p, const Routing *c) { NodeNum ourNode = getNodeNum(); - if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability - // - not DSR routing) + if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability) if (p->want_ack) { if (MeshModule::currentReply) DEBUG_MSG("Some other module has replied to this message, no need for a 2nd ack\n"); @@ -200,8 +199,7 @@ int32_t ReliableRouter::doRetransmissions() DEBUG_MSG("Reliable send failed, returning a nak for fr=0x%x,to=0x%x,id=0x%x\n", p.packet->from, p.packet->to, p.packet->id); sendAckNak(Routing_Error_MAX_RETRANSMIT, getFrom(p.packet), p.packet->id, p.packet->channel); - // Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived - which - // allows the DSR version to still be able to look at the PendingPacket + // Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived stopRetransmission(it->first); stillValid = false; // just deleted it } else { diff --git a/src/mesh/ReliableRouter.h b/src/mesh/ReliableRouter.h index 3f89dcbd6..ff304cdd7 100644 --- a/src/mesh/ReliableRouter.h +++ b/src/mesh/ReliableRouter.h @@ -38,12 +38,6 @@ struct PendingPacket { /** Starts at NUM_RETRANSMISSIONS -1(normally 3) and counts down. Once zero it will be removed from the list */ uint8_t numRetransmissions = 0; - /** True if we have started trying to find a route - for DSR usage - * While trying to find a route we don't actually send the data packet. We just leave it here pending until - * we have a route or we've failed to find one. - */ - bool wantRoute = false; - PendingPacket() {} explicit PendingPacket(MeshPacket *p); }; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index e0746bdd9..6b6a03a3a 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -21,7 +21,6 @@ extern "C" { * DONE: Implement basic interface and use it elsewhere in app * Add naive flooding mixin (& drop duplicate rx broadcasts), add tools for sending broadcasts with incrementing sequence #s * Add an optional adjacent node only 'send with ack' mixin. If we timeout waiting for the ack, call handleAckTimeout(packet) - * Add DSR mixin * **/ diff --git a/src/sleep.cpp b/src/sleep.cpp index 390ab7f65..39ee43ebc 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -237,7 +237,7 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r // We want RTC peripherals to stay on esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); -#ifdef BUTTON_NEED_PULLUP +#if defined(BUTTON_PIN) && defined(BUTTON_NEED_PULLUP) gpio_pullup_en((gpio_num_t)BUTTON_PIN); #endif From f5120a29ec2d24a6ab30dbd82065bc77e41e75f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 29 Nov 2022 11:22:18 +0100 Subject: [PATCH 05/52] WIP: audio module still does not work, but enabled for all regions where audio is permitted. # Conflicts: # variants/tlora_v2_1_18/platformio.ini --- arch/esp32/esp32.ini | 3 +- arch/esp32/esp32s3.ini | 1 + src/modules/Modules.cpp | 4 - src/modules/esp32/AudioModule.cpp | 221 +++++++++++++++++------------- src/modules/esp32/AudioModule.h | 59 +++++--- 5 files changed, 167 insertions(+), 121 deletions(-) diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index 730b78942..70654e8ec 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -33,7 +33,8 @@ lib_deps = ${environmental_base.lib_deps} https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2 h2zero/NimBLE-Arduino@^1.4.0 - https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 + https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 + caveman99/ESP32 Codec2@^1.0.1 lib_ignore = segger_rtt diff --git a/arch/esp32/esp32s3.ini b/arch/esp32/esp32s3.ini index 0c2d7d8f1..b05772344 100644 --- a/arch/esp32/esp32s3.ini +++ b/arch/esp32/esp32s3.ini @@ -34,6 +34,7 @@ lib_deps = https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2 h2zero/NimBLE-Arduino@^1.4.0 https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 + caveman99/ESP32 Codec2@^1.0.1 lib_ignore = segger_rtt diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index d24fa8f26..b97e965e1 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -19,10 +19,8 @@ #ifdef ARCH_ESP32 #include "modules/esp32/RangeTestModule.h" #include "modules/esp32/StoreForwardModule.h" -#ifdef USE_SX1280 #include "modules/esp32/AudioModule.h" #endif -#endif #if defined(ARCH_ESP32) || defined(ARCH_NRF52) #include "modules/ExternalNotificationModule.h" #if !defined(TTGO_T_ECHO) @@ -68,9 +66,7 @@ void setupModules() #endif #ifdef ARCH_ESP32 // Only run on an esp32 based device. -#ifdef USE_SX1280 new AudioModule(); -#endif new ExternalNotificationModule(); storeForwardModule = new StoreForwardModule(); diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index c37a1b2a8..0f95c306a 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -6,6 +6,8 @@ #include "Router.h" #include "FSCommon.h" +#include +#include #include /* @@ -30,7 +32,7 @@ KNOWN PROBLEMS * Until the module is initilized by the startup sequence, the amp_pin pin is in a floating - state. This may produce a bit of "noise". + radio_state. This may produce a bit of "noise". * Will not work on NRF and the Linux device targets. */ @@ -40,12 +42,20 @@ #define AUDIO_MODULE_RX_BUFFER 128 #define AUDIO_MODULE_DATA_MAX Constants_DATA_PAYLOAD_LEN -#define AUDIO_MODULE_MODE 7 // 700B -#define AUDIO_MODULE_ACK 1 +#define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 -#if defined(ARCH_ESP32) && defined(USE_SX1280) +#if defined(ARCH_ESP32) AudioModule *audioModule; +Codec2Thread *codec2Thread; + +FastAudioFIFO audio_fifo; +uint16_t adc_buffer[ADC_BUFFER_SIZE] = {}; +uint16_t adc_buffer_index = 0; +portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; +int16_t speech[ADC_BUFFER_SIZE] = {}; +volatile RadioState radio_state = RadioState::tx; +adc1_channel_t mic_chan = (adc1_channel_t)0; ButterworthFilter hp_filter(240, 8000, ButterworthFilter::ButterworthFilter::Highpass, 1); @@ -55,55 +65,22 @@ int Sine1KHz_index = 0; uint8_t rx_raw_audio_value = 127; -AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") { - audio_fifo.init(); +int IRAM_ATTR local_adc1_read(int channel) { + uint16_t adc_value; + SENS.sar_meas_start1.sar1_en_pad = (1 << channel); // only one channel is selected + while (SENS.sar_slave_addr1.meas_status != 0); + SENS.sar_meas_start1.meas1_start_sar = 0; + SENS.sar_meas_start1.meas1_start_sar = 1; + while (SENS.sar_meas_start1.meas1_done_sar == 0); + adc_value = SENS.sar_meas_start1.meas1_data_sar; + return adc_value; } -void AudioModule::run_codec2() +IRAM_ATTR void am_onTimer() { - if (state == State::tx) - { - for (int i = 0; i < ADC_BUFFER_SIZE; i++) - speech[i] = (int16_t)hp_filter.Update((float)speech[i]); - - codec2_encode(codec2_state, tx_encode_frame + tx_encode_frame_index, speech); - - //increment the pointer where the encoded frame must be saved - tx_encode_frame_index += 8; - - //If it is the 5th time then we have a ready trasnmission frame - if (tx_encode_frame_index == ENCODE_FRAME_SIZE) - { - tx_encode_frame_index = 0; - //Transmit it - sendPayload(); - } - } - if (state == State::rx) //Receiving - { - //Make a cycle to get each codec2 frame from the received frame - for (int i = 0; i < ENCODE_FRAME_SIZE; i += 8) - { - //Decode the codec2 frame - codec2_decode(codec2_state, output_buffer, rx_encode_frame + i); - - // Add to the audio buffer the 320 samples resulting of the decode of the codec2 frame. - for (int g = 0; g < ADC_BUFFER_SIZE; g++) - audio_fifo.put(output_buffer[g]); - } - } - state = State::standby; -} - -void AudioModule::handleInterrupt() -{ - audioModule->onTimer(); -} - -void AudioModule::onTimer() -{ - if (state == State::tx) { - adc_buffer[adc_buffer_index++] = (16 * adc1_get_raw(mic_chan)) - 32768; + portENTER_CRITICAL_ISR(&timerMux); //Enter crital code without interruptions + if (radio_state == RadioState::tx) { + adc_buffer[adc_buffer_index++] = (16 * local_adc1_read(mic_chan)) - 32768; //If you want to test with a 1KHz tone, comment the line above and descomment the three lines below @@ -113,39 +90,96 @@ void AudioModule::onTimer() if (adc_buffer_index == ADC_BUFFER_SIZE) { adc_buffer_index = 0; + DEBUG_MSG("--- memcpy\n"); memcpy((void*)speech, (void*)adc_buffer, 2 * ADC_BUFFER_SIZE); - audioModule->setIntervalFromNow(0); // process buffer immediately + // Notify codec2 task that the buffer is ready. + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + DEBUG_MSG("--- notifyFromISR\n"); + codec2Thread->notifyFromISR(&xHigherPriorityTaskWoken, RadioState::tx, true); + if (xHigherPriorityTaskWoken) + portYIELD_FROM_ISR(); } - } else if (state == State::rx) { + } else if (radio_state == RadioState::rx) { int16_t v; //Get a value from audio_fifo and convert it to 0 - 255 to play it in the ADC - //If none value is available the DAC will play the last one that was read, that's - //why the rx_raw_audio_value variable is a global one. if (audio_fifo.get(&v)) rx_raw_audio_value = (uint8_t)((v + 32768) / 256); - - //Play dacWrite(moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP, rx_raw_audio_value); } + portEXIT_CRITICAL_ISR(&timerMux); // exit critical code +} + +Codec2Thread::Codec2Thread() : concurrency::NotifiedWorkerThread("Codec2Thread") { + if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { + DEBUG_MSG("--- Setting up codec2 in mode %u\n", moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE); + codec2_state = codec2_create(moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE); + codec2_set_lpc_post_filter(codec2_state, 1, 0, 0.8, 0.2); + } else { + DEBUG_MSG("--- Codec2 disabled\n"); + } +} + +AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") { + audio_fifo.init(); + new Codec2Thread(); +} + +void Codec2Thread::onNotify(uint32_t notification) +{ + switch (notification) { + case RadioState::tx: + for (int i = 0; i < ADC_BUFFER_SIZE; i++) + speech[i] = (int16_t)hp_filter.Update((float)speech[i]); + + codec2_encode(codec2_state, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index, speech); + + //increment the pointer where the encoded frame must be saved + audioModule->tx_encode_frame_index += 8; + + //If it is the 5th time then we have a ready trasnmission frame + if (audioModule->tx_encode_frame_index == ENCODE_FRAME_SIZE) + { + audioModule->tx_encode_frame_index = 0; + //Transmit it + audioModule->sendPayload(); + } + break; + case RadioState::rx: + //Make a cycle to get each codec2 frame from the received frame + for (int i = 0; i < ENCODE_FRAME_SIZE; i += ENCODE_CODEC2_SIZE) + { + //Decode the codec2 frame + codec2_decode(codec2_state, output_buffer, audioModule->rx_encode_frame + i); + + // Add to the audio buffer the 320 samples resulting of the decode of the codec2 frame. + for (int g = 0; g < ADC_BUFFER_SIZE; g++) + audio_fifo.put(output_buffer[g]); + } + break; + default: + assert(0); // We expected to receive a valid notification from the ISR + break; + } } int32_t AudioModule::runOnce() { - if (moduleConfig.audio.codec2_enabled) { - + if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { if (firstTime) { - - DEBUG_MSG("Initializing ADC on Channel %u\n", moduleConfig.audio.mic_chan ? moduleConfig.audio.mic_chan : AMIC); + DEBUG_MSG("--- Initializing ADC on Channel %u\n", moduleConfig.audio.mic_chan ? moduleConfig.audio.mic_chan : AMIC); mic_chan = moduleConfig.audio.mic_chan ? (adc1_channel_t)(int)moduleConfig.audio.mic_chan : (adc1_channel_t)AMIC; adc1_config_width(ADC_WIDTH_12Bit); - adc1_config_channel_atten(mic_chan, ADC_ATTEN_DB_6); + adc1_config_channel_atten(mic_chan, ADC_ATTEN_DB_6); + adc1_get_raw(mic_chan); + + radio_state = RadioState::rx; // Start a timer at 8kHz to sample the ADC and play the audio on the DAC. uint32_t cpufreq = getCpuFrequencyMhz(); - switch (cpufreq){ + switch (cpufreq){ case 160: adcTimer = timerBegin(3, 1000, true); // 160 MHz / 1000 = 160KHz break; @@ -160,48 +194,44 @@ int32_t AudioModule::runOnce() adcTimer = timerBegin(3, 500, true); // 80 MHz / 500 = 160KHz break; } - timerAttachInterrupt(adcTimer, &AudioModule::handleInterrupt, true); - timerAlarmWrite(adcTimer, 20, true); // Interrupts when counter == 20, 8.000 times a second - timerAlarmEnable(adcTimer); + DEBUG_MSG("--- Timer CPU Frequency: %u MHz\n", cpufreq); + timerAttachInterrupt(adcTimer, &am_onTimer, false); + timerAlarmWrite(adcTimer, 20, true); // Interrupts when counter == 20, 8.000 times a second + timerAlarmEnable(adcTimer); - DEBUG_MSG("Initializing DAC on Pin %u\n", moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP); - DEBUG_MSG("Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); + DEBUG_MSG("--- Initializing DAC on Pin %u\n", moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP); + DEBUG_MSG("--- Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); // Configure PTT input - pinMode(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN, INPUT_PULLUP); + pinMode(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN, INPUT); - state = State::rx; - - DEBUG_MSG("Setting up codec2 in mode %u\n", moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE); - - codec2_state = codec2_create(moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE); - codec2_set_lpc_post_filter(codec2_state, 1, 0, 0.8, 0.2); - - firstTime = 0; + firstTime = false; } else { - // Check if we have a PTT press - if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == LOW) { - // PTT pressed, recording - state = State::tx; - } - if (state != State::standby) { - run_codec2(); + // Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive. + if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) { + if (radio_state == RadioState::rx) { + DEBUG_MSG("--- PTT pressed, switching to TX\n"); + radio_state = RadioState::tx; + } + } else { + if (radio_state == RadioState::tx) { + DEBUG_MSG("--- PTT released, switching to RX\n"); + radio_state = RadioState::rx; + } } + } - return 100; } else { - DEBUG_MSG("Audio Module Disabled\n"); - + DEBUG_MSG("--- Audio Module Disabled\n"); return INT32_MAX; } + } MeshPacket *AudioModule::allocReply() { - auto reply = allocDataPacket(); // Allocate a packet for sending - return reply; } @@ -211,7 +241,8 @@ void AudioModule::sendPayload(NodeNum dest, bool wantReplies) p->to = dest; p->decoded.want_response = wantReplies; - p->want_ack = AUDIO_MODULE_ACK; + p->want_ack = false; // Audio is shoot&forget. TODO: Is this really suppressing retransmissions? + p->priority = MeshPacket_Priority_MAX; // Audio is important, because realtime p->decoded.payload.size = ENCODE_FRAME_SIZE; memcpy(p->decoded.payload.bytes, tx_encode_frame, p->decoded.payload.size); @@ -221,16 +252,18 @@ void AudioModule::sendPayload(NodeNum dest, bool wantReplies) ProcessMessage AudioModule::handleReceived(const MeshPacket &mp) { - if (moduleConfig.audio.codec2_enabled) { + if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { auto &p = mp.decoded; if (getFrom(&mp) != nodeDB.getNodeNum()) { if (p.payload.size == ENCODE_FRAME_SIZE) { memcpy(rx_encode_frame, p.payload.bytes, p.payload.size); - state = State::rx; - audioModule->setIntervalFromNow(0); - run_codec2(); + radio_state = RadioState::rx; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + codec2Thread->notifyFromISR(&xHigherPriorityTaskWoken, RadioState::rx, true); + if (xHigherPriorityTaskWoken) + portYIELD_FROM_ISR(); } else { - DEBUG_MSG("Invalid payload size %u != %u\n", p.payload.size, ENCODE_FRAME_SIZE); + DEBUG_MSG("--- Invalid payload size %u != %u\n", p.payload.size, ENCODE_FRAME_SIZE); } } } diff --git a/src/modules/esp32/AudioModule.h b/src/modules/esp32/AudioModule.h index d82af4d43..c83160cfb 100644 --- a/src/modules/esp32/AudioModule.h +++ b/src/modules/esp32/AudioModule.h @@ -7,38 +7,45 @@ #include #include #include -#if defined(ARCH_ESP32) && defined(USE_SX1280) +#if defined(ARCH_ESP32) #include #include #include #endif #define ADC_BUFFER_SIZE 320 // 40ms of voice in 8KHz sampling frequency -#define ENCODE_FRAME_SIZE 40 // 5 codec2 frames of 8 bytes each +#define ENCODE_CODEC2_SIZE 8 +#define ENCODE_FRAME_SIZE (ENCODE_CODEC2_SIZE * 5) // 5 codec2 frames of 8 bytes each + +class Codec2Thread : public concurrency::NotifiedWorkerThread +{ +#if defined(ARCH_ESP32) + struct CODEC2* codec2_state = NULL; + int16_t output_buffer[ADC_BUFFER_SIZE] = {}; + + public: + Codec2Thread(); + + protected: + virtual void onNotify(uint32_t notification) override; +#endif +}; class AudioModule : public SinglePortModule, private concurrency::OSThread { -#if defined(ARCH_ESP32) && defined(USE_SX1280) - bool firstTime = 1; +#if defined(ARCH_ESP32) + bool firstTime = true; hw_timer_t* adcTimer = NULL; - uint16_t adc_buffer[ADC_BUFFER_SIZE] = {}; - int16_t speech[ADC_BUFFER_SIZE] = {}; - int16_t output_buffer[ADC_BUFFER_SIZE] = {}; - unsigned char rx_encode_frame[ENCODE_FRAME_SIZE] = {}; - unsigned char tx_encode_frame[ENCODE_FRAME_SIZE] = {}; - int tx_encode_frame_index = 0; + FastAudioFIFO audio_fifo; uint16_t adc_buffer_index = 0; - adc1_channel_t mic_chan = (adc1_channel_t)0; - struct CODEC2* codec2_state = NULL; - enum State - { - standby, rx, tx - }; - volatile State state = State::tx; public: + unsigned char rx_encode_frame[ENCODE_FRAME_SIZE] = {}; + unsigned char tx_encode_frame[ENCODE_FRAME_SIZE] = {}; + int tx_encode_frame_index = 0; + AudioModule(); /** @@ -49,11 +56,7 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread protected: virtual int32_t runOnce() override; - static void handleInterrupt(); - - void onTimer(); - - void run_codec2(); + // void run_codec2(); virtual MeshPacket *allocReply() override; @@ -65,4 +68,16 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread }; extern AudioModule *audioModule; +extern Codec2Thread *codec2Thread; + +extern FastAudioFIFO audio_fifo; +extern uint16_t adc_buffer[ADC_BUFFER_SIZE]; +extern uint16_t adc_buffer_index; +extern portMUX_TYPE timerMux; +extern int16_t speech[ADC_BUFFER_SIZE]; +enum RadioState { standby, rx, tx }; +extern volatile RadioState radio_state; +extern adc1_channel_t mic_chan; + +IRAM_ATTR void am_onTimer(); From 80d0b63c3af409422fa2557a7d30da551a858ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 29 Nov 2022 14:35:46 +0100 Subject: [PATCH 06/52] hopefully fix compilation errors --- src/modules/esp32/AudioModule.cpp | 16 ++++++++++++++-- src/modules/esp32/AudioModule.h | 12 ++---------- variants/heltec_v3/platformio.ini | 2 +- variants/heltec_wsl_v3/platformio.ini | 2 +- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 0f95c306a..959366dec 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -1,4 +1,6 @@ + #include "configuration.h" +#if defined(ARCH_ESP32) #include "AudioModule.h" #include "MeshService.h" #include "NodeDB.h" @@ -44,8 +46,6 @@ #define AUDIO_MODULE_DATA_MAX Constants_DATA_PAYLOAD_LEN #define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 -#if defined(ARCH_ESP32) - AudioModule *audioModule; Codec2Thread *codec2Thread; @@ -67,12 +67,21 @@ uint8_t rx_raw_audio_value = 127; int IRAM_ATTR local_adc1_read(int channel) { uint16_t adc_value; +#if CONFIG_IDF_TARGET_ESP32S3 + SENS.sar_meas1_ctrl2.sar1_en_pad = (1 << channel); // only one channel is selected + while (SENS.sar_slave_addr1.meas_status != 0); + SENS.sar_meas1_ctrl2.meas1_start_sar = 0; + SENS.sar_meas1_ctrl2.meas1_start_sar = 1; + while (SENS.sar_meas1_ctrl2.meas1_done_sar == 0); + adc_value = SENS.sar_meas1_ctrl2.meas1_data_sar; +#else SENS.sar_meas_start1.sar1_en_pad = (1 << channel); // only one channel is selected while (SENS.sar_slave_addr1.meas_status != 0); SENS.sar_meas_start1.meas1_start_sar = 0; SENS.sar_meas_start1.meas1_start_sar = 1; while (SENS.sar_meas_start1.meas1_done_sar == 0); adc_value = SENS.sar_meas_start1.meas1_data_sar; +#endif return adc_value; } @@ -106,7 +115,10 @@ IRAM_ATTR void am_onTimer() //Get a value from audio_fifo and convert it to 0 - 255 to play it in the ADC if (audio_fifo.get(&v)) rx_raw_audio_value = (uint8_t)((v + 32768) / 256); + // comment out for now, S3 does not have Hardware-DAC. Consider I2S instead. +#if !CONFIG_IDF_TARGET_ESP32S3 dacWrite(moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP, rx_raw_audio_value); +#endif } portEXIT_CRITICAL_ISR(&timerMux); // exit critical code } diff --git a/src/modules/esp32/AudioModule.h b/src/modules/esp32/AudioModule.h index c83160cfb..16705a141 100644 --- a/src/modules/esp32/AudioModule.h +++ b/src/modules/esp32/AudioModule.h @@ -3,15 +3,14 @@ #include "SinglePortModule.h" #include "concurrency/OSThread.h" #include "configuration.h" +#if defined(ARCH_ESP32) #include "NodeDB.h" #include #include #include -#if defined(ARCH_ESP32) #include #include #include -#endif #define ADC_BUFFER_SIZE 320 // 40ms of voice in 8KHz sampling frequency #define ENCODE_CODEC2_SIZE 8 @@ -19,7 +18,6 @@ class Codec2Thread : public concurrency::NotifiedWorkerThread { -#if defined(ARCH_ESP32) struct CODEC2* codec2_state = NULL; int16_t output_buffer[ADC_BUFFER_SIZE] = {}; @@ -28,16 +26,12 @@ class Codec2Thread : public concurrency::NotifiedWorkerThread protected: virtual void onNotify(uint32_t notification) override; -#endif }; class AudioModule : public SinglePortModule, private concurrency::OSThread { -#if defined(ARCH_ESP32) bool firstTime = true; hw_timer_t* adcTimer = NULL; - - FastAudioFIFO audio_fifo; uint16_t adc_buffer_index = 0; @@ -64,13 +58,11 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread * @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; -#endif }; extern AudioModule *audioModule; extern Codec2Thread *codec2Thread; -extern FastAudioFIFO audio_fifo; extern uint16_t adc_buffer[ADC_BUFFER_SIZE]; extern uint16_t adc_buffer_index; extern portMUX_TYPE timerMux; @@ -80,4 +72,4 @@ extern volatile RadioState radio_state; extern adc1_channel_t mic_chan; IRAM_ATTR void am_onTimer(); - +#endif \ No newline at end of file diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 6cd75bed8..372f42d64 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -1,6 +1,6 @@ [env:heltec-v3] platform = https://github.com/Baptou88/platform-espressif32.git -extends = esp32_base +extends = esp32s3_base board = heltec_wifi_lora_32_V3 # Temporary: https://community.platformio.org/t/heltec-esp32-lora-v3-board-support/30406/2 platform_packages = diff --git a/variants/heltec_wsl_v3/platformio.ini b/variants/heltec_wsl_v3/platformio.ini index 8854b1a44..336e44936 100644 --- a/variants/heltec_wsl_v3/platformio.ini +++ b/variants/heltec_wsl_v3/platformio.ini @@ -1,6 +1,6 @@ [env:heltec-wsl-v3] platform = https://github.com/Baptou88/platform-espressif32.git -extends = esp32_base +extends = esp32s3_base board = heltec_wifi_lora_32_V3 # Temporary: https://community.platformio.org/t/heltec-esp32-lora-v3-board-support/30406/2 platform_packages = From 8cbf292373b51546c2845ce2d2c7dde9009f09b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 29 Nov 2022 17:19:10 +0100 Subject: [PATCH 07/52] WIP: add digital audio. Needs a proto change, so checking in generated files for now. # Conflicts: # src/mesh/generated/localonly.pb.h # src/mesh/generated/module_config.pb.h --- src/mesh/generated/localonly.pb.h | 2 +- src/mesh/generated/module_config.pb.h | 75 +++++++----------- src/modules/esp32/AudioModule.cpp | 110 ++++++++++++++++++-------- src/modules/esp32/AudioModule.h | 1 + 4 files changed, 109 insertions(+), 79 deletions(-) diff --git a/src/mesh/generated/localonly.pb.h b/src/mesh/generated/localonly.pb.h index c154a98db..44d923839 100644 --- a/src/mesh/generated/localonly.pb.h +++ b/src/mesh/generated/localonly.pb.h @@ -151,7 +151,7 @@ extern const pb_msgdesc_t LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define LocalConfig_size 387 -#define LocalModuleConfig_size 376 +#define LocalModuleConfig_size 385 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/module_config.pb.h b/src/mesh/generated/module_config.pb.h index 47f410a5c..3747452c7 100644 --- a/src/mesh/generated/module_config.pb.h +++ b/src/mesh/generated/module_config.pb.h @@ -63,12 +63,14 @@ typedef enum _ModuleConfig_CannedMessageConfig_InputEventChar { /* Struct definitions */ typedef struct _ModuleConfig_AudioConfig { bool codec2_enabled; - uint8_t ptt_pin; + uint32_t mic_chan; + uint32_t amp_pin; + uint32_t ptt_pin; ModuleConfig_AudioConfig_Audio_Baud bitrate; - uint8_t i2s_ws; - uint8_t i2s_sd; - uint8_t i2s_din; - uint8_t i2s_sck; + uint32_t i2s_ws; + uint32_t i2s_sd; + uint32_t i2s_din; + uint32_t i2s_sck; } ModuleConfig_AudioConfig; typedef struct _ModuleConfig_CannedMessageConfig { @@ -93,13 +95,6 @@ typedef struct _ModuleConfig_ExternalNotificationConfig { bool alert_message; bool alert_bell; bool use_pwm; - uint8_t output_vibra; - uint8_t output_buzzer; - bool alert_message_vibra; - bool alert_message_buzzer; - bool alert_bell_vibra; - bool alert_bell_buzzer; - uint16_t nag_timeout; } ModuleConfig_ExternalNotificationConfig; typedef struct _ModuleConfig_MQTTConfig { @@ -192,18 +187,18 @@ extern "C" { /* Initializer values for message structs */ #define ModuleConfig_init_default {0, {ModuleConfig_MQTTConfig_init_default}} #define ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0} -#define ModuleConfig_AudioConfig_init_default {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} +#define ModuleConfig_AudioConfig_init_default {0, 0, 0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} -#define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0} #define ModuleConfig_RangeTestConfig_init_default {0, 0, 0} #define ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0} #define ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0} #define ModuleConfig_init_zero {0, {ModuleConfig_MQTTConfig_init_zero}} #define ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0} -#define ModuleConfig_AudioConfig_init_zero {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} +#define ModuleConfig_AudioConfig_init_zero {0, 0, 0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} -#define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +#define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0} #define ModuleConfig_RangeTestConfig_init_zero {0, 0, 0} #define ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0} @@ -211,12 +206,14 @@ extern "C" { /* Field tags (for use in manual encoding/decoding) */ #define ModuleConfig_AudioConfig_codec2_enabled_tag 1 -#define ModuleConfig_AudioConfig_ptt_pin_tag 2 -#define ModuleConfig_AudioConfig_bitrate_tag 3 -#define ModuleConfig_AudioConfig_i2s_ws_tag 4 -#define ModuleConfig_AudioConfig_i2s_sd_tag 5 -#define ModuleConfig_AudioConfig_i2s_din_tag 6 -#define ModuleConfig_AudioConfig_i2s_sck_tag 7 +#define ModuleConfig_AudioConfig_mic_chan_tag 2 +#define ModuleConfig_AudioConfig_amp_pin_tag 3 +#define ModuleConfig_AudioConfig_ptt_pin_tag 4 +#define ModuleConfig_AudioConfig_bitrate_tag 5 +#define ModuleConfig_AudioConfig_i2s_ws_tag 6 +#define ModuleConfig_AudioConfig_i2s_sd_tag 7 +#define ModuleConfig_AudioConfig_i2s_din_tag 8 +#define ModuleConfig_AudioConfig_i2s_sck_tag 9 #define ModuleConfig_CannedMessageConfig_rotary1_enabled_tag 1 #define ModuleConfig_CannedMessageConfig_inputbroker_pin_a_tag 2 #define ModuleConfig_CannedMessageConfig_inputbroker_pin_b_tag 3 @@ -235,13 +232,6 @@ extern "C" { #define ModuleConfig_ExternalNotificationConfig_alert_message_tag 5 #define ModuleConfig_ExternalNotificationConfig_alert_bell_tag 6 #define ModuleConfig_ExternalNotificationConfig_use_pwm_tag 7 -#define ModuleConfig_ExternalNotificationConfig_output_vibra_tag 8 -#define ModuleConfig_ExternalNotificationConfig_output_buzzer_tag 9 -#define ModuleConfig_ExternalNotificationConfig_alert_message_vibra_tag 10 -#define ModuleConfig_ExternalNotificationConfig_alert_message_buzzer_tag 11 -#define ModuleConfig_ExternalNotificationConfig_alert_bell_vibra_tag 12 -#define ModuleConfig_ExternalNotificationConfig_alert_bell_buzzer_tag 13 -#define ModuleConfig_ExternalNotificationConfig_nag_timeout_tag 14 #define ModuleConfig_MQTTConfig_enabled_tag 1 #define ModuleConfig_MQTTConfig_address_tag 2 #define ModuleConfig_MQTTConfig_username_tag 3 @@ -310,12 +300,14 @@ X(a, STATIC, SINGULAR, BOOL, json_enabled, 6) #define ModuleConfig_AudioConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, BOOL, codec2_enabled, 1) \ -X(a, STATIC, SINGULAR, UINT32, ptt_pin, 2) \ -X(a, STATIC, SINGULAR, UENUM, bitrate, 3) \ -X(a, STATIC, SINGULAR, UINT32, i2s_ws, 4) \ -X(a, STATIC, SINGULAR, UINT32, i2s_sd, 5) \ -X(a, STATIC, SINGULAR, UINT32, i2s_din, 6) \ -X(a, STATIC, SINGULAR, UINT32, i2s_sck, 7) +X(a, STATIC, SINGULAR, UINT32, mic_chan, 2) \ +X(a, STATIC, SINGULAR, UINT32, amp_pin, 3) \ +X(a, STATIC, SINGULAR, UINT32, ptt_pin, 4) \ +X(a, STATIC, SINGULAR, UENUM, bitrate, 5) \ +X(a, STATIC, SINGULAR, UINT32, i2s_ws, 6) \ +X(a, STATIC, SINGULAR, UINT32, i2s_sd, 7) \ +X(a, STATIC, SINGULAR, UINT32, i2s_din, 8) \ +X(a, STATIC, SINGULAR, UINT32, i2s_sck, 9) #define ModuleConfig_AudioConfig_CALLBACK NULL #define ModuleConfig_AudioConfig_DEFAULT NULL @@ -337,14 +329,7 @@ X(a, STATIC, SINGULAR, UINT32, output, 3) \ X(a, STATIC, SINGULAR, BOOL, active, 4) \ X(a, STATIC, SINGULAR, BOOL, alert_message, 5) \ X(a, STATIC, SINGULAR, BOOL, alert_bell, 6) \ -X(a, STATIC, SINGULAR, BOOL, use_pwm, 7) \ -X(a, STATIC, SINGULAR, UINT32, output_vibra, 8) \ -X(a, STATIC, SINGULAR, UINT32, output_buzzer, 9) \ -X(a, STATIC, SINGULAR, BOOL, alert_message_vibra, 10) \ -X(a, STATIC, SINGULAR, BOOL, alert_message_buzzer, 11) \ -X(a, STATIC, SINGULAR, BOOL, alert_bell_vibra, 12) \ -X(a, STATIC, SINGULAR, BOOL, alert_bell_buzzer, 13) \ -X(a, STATIC, SINGULAR, UINT32, nag_timeout, 14) +X(a, STATIC, SINGULAR, BOOL, use_pwm, 7) #define ModuleConfig_ExternalNotificationConfig_CALLBACK NULL #define ModuleConfig_ExternalNotificationConfig_DEFAULT NULL @@ -410,9 +395,9 @@ extern const pb_msgdesc_t ModuleConfig_CannedMessageConfig_msg; #define ModuleConfig_CannedMessageConfig_fields &ModuleConfig_CannedMessageConfig_msg /* Maximum encoded size of messages (where known) */ -#define ModuleConfig_AudioConfig_size 19 +#define ModuleConfig_AudioConfig_size 46 #define ModuleConfig_CannedMessageConfig_size 49 -#define ModuleConfig_ExternalNotificationConfig_size 40 +#define ModuleConfig_ExternalNotificationConfig_size 22 #define ModuleConfig_MQTTConfig_size 169 #define ModuleConfig_RangeTestConfig_size 10 #define ModuleConfig_SerialConfig_size 26 diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 959366dec..0c02251b0 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -42,6 +42,14 @@ #define AAMP 14 #define PTT_PIN 39 +// #define I2S_WS 13 +// #define I2S_SD 15 +// #define I2S_SIN 2 +// #define I2S_SCK 14 + +// Use I2S Processor 0 +#define I2S_PORT I2S_NUM_0 + #define AUDIO_MODULE_RX_BUFFER 128 #define AUDIO_MODULE_DATA_MAX Constants_DATA_PAYLOAD_LEN #define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 @@ -88,7 +96,7 @@ int IRAM_ATTR local_adc1_read(int channel) { IRAM_ATTR void am_onTimer() { portENTER_CRITICAL_ISR(&timerMux); //Enter crital code without interruptions - if (radio_state == RadioState::tx) { + if ((radio_state == RadioState::tx) && (!moduleConfig.audio.i2s_sd)) { adc_buffer[adc_buffer_index++] = (16 * local_adc1_read(mic_chan)) - 32768; //If you want to test with a 1KHz tone, comment the line above and descomment the three lines below @@ -108,15 +116,15 @@ IRAM_ATTR void am_onTimer() if (xHigherPriorityTaskWoken) portYIELD_FROM_ISR(); } - } else if (radio_state == RadioState::rx) { - + } else if ((radio_state == RadioState::rx) && (!moduleConfig.audio.i2s_din) { + // ESP32-S3 does not have DAC support +#if !defined(CONFIG_IDF_TARGET_ESP32S3) int16_t v; //Get a value from audio_fifo and convert it to 0 - 255 to play it in the ADC if (audio_fifo.get(&v)) rx_raw_audio_value = (uint8_t)((v + 32768) / 256); - // comment out for now, S3 does not have Hardware-DAC. Consider I2S instead. -#if !CONFIG_IDF_TARGET_ESP32S3 + dacWrite(moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP, rx_raw_audio_value); #endif } @@ -180,41 +188,77 @@ int32_t AudioModule::runOnce() { if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { if (firstTime) { - DEBUG_MSG("--- Initializing ADC on Channel %u\n", moduleConfig.audio.mic_chan ? moduleConfig.audio.mic_chan : AMIC); + // if we have I2S_SD defined, take samples from digital mic. I2S_DIN means digital output to amp. + if (moduleConfig.audio.i2s_sd || moduleConfig.audio.i2s_din) { + // Set up I2S Processor configuration. This will produce 16bit samples instead of 12 from the ADC + DEBUG_MSG("--- Initializing I2S for input\n"); + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | (moduleConfig.audio.i2s_sd ? I2S_MODE_RX : 0) | (moduleConfig.audio.i2s_din ? I2S_MODE_TX : 0)), + .sample_rate = 8000, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), + .intr_alloc_flags = 0, + .dma_buf_count = 8, + .dma_buf_len = ADC_BUFFER_SIZE, + .use_apll = false, + .tx_desc_auto_clear = true, + .fixed_mclk = 0 + }; + i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); - mic_chan = moduleConfig.audio.mic_chan ? (adc1_channel_t)(int)moduleConfig.audio.mic_chan : (adc1_channel_t)AMIC; - adc1_config_width(ADC_WIDTH_12Bit); - adc1_config_channel_atten(mic_chan, ADC_ATTEN_DB_6); - adc1_get_raw(mic_chan); + const i2s_pin_config_t pin_config = { + .bck_io_num = moduleConfig.audio.i2s_sck, + .ws_io_num = moduleConfig.audio.i2s_ws, + .data_out_num = moduleConfig.audio.i2s_din ? moduleConfig.audio.i2s_din : I2S_PIN_NO_CHANGE, + .data_in_num = moduleConfig.audio.i2s_sd ? moduleConfig.audio.i2s_sd : I2S_PIN_NO_CHANGE + }; + i2s_set_pin(I2S_PORT, &pin_config); + + i2s_start(I2S_PORT); + } + + if (!moduleConfig.audio.i2s_sd) { + DEBUG_MSG("--- Initializing ADC on Channel %u\n", moduleConfig.audio.mic_chan ? moduleConfig.audio.mic_chan : AMIC); + mic_chan = moduleConfig.audio.mic_chan ? (adc1_channel_t)(int)moduleConfig.audio.mic_chan : (adc1_channel_t)AMIC; + adc1_config_width(ADC_WIDTH_12Bit); + adc1_config_channel_atten(mic_chan, ADC_ATTEN_DB_6); + adc1_get_raw(mic_chan); + } radio_state = RadioState::rx; - // Start a timer at 8kHz to sample the ADC and play the audio on the DAC. - uint32_t cpufreq = getCpuFrequencyMhz(); - switch (cpufreq){ - case 160: - adcTimer = timerBegin(3, 1000, true); // 160 MHz / 1000 = 160KHz - break; - case 240: - adcTimer = timerBegin(3, 1500, true); // 240 MHz / 1500 = 160KHz - break; - case 320: - adcTimer = timerBegin(3, 2000, true); // 320 MHz / 2000 = 160KHz - break; - case 80: - default: - adcTimer = timerBegin(3, 500, true); // 80 MHz / 500 = 160KHz - break; + if ((!moduleConfig.audio.i2s_sd) || (!moduleConfig.audio.i2s_din)) { + // Start a timer at 8kHz to sample the ADC and play the audio on the DAC, but only if we have analogue audio to process + uint32_t cpufreq = getCpuFrequencyMhz(); + switch (cpufreq){ + case 160: + adcTimer = timerBegin(3, 1000, true); // 160 MHz / 1000 = 160KHz + break; + case 240: + adcTimer = timerBegin(3, 1500, true); // 240 MHz / 1500 = 160KHz + break; + case 320: + adcTimer = timerBegin(3, 2000, true); // 320 MHz / 2000 = 160KHz + break; + case 80: + default: + adcTimer = timerBegin(3, 500, true); // 80 MHz / 500 = 160KHz + break; + } + DEBUG_MSG("--- Timer CPU Frequency: %u MHz\n", cpufreq); + timerAttachInterrupt(adcTimer, &am_onTimer, false); + timerAlarmWrite(adcTimer, 20, true); // Interrupts when counter == 20, 8.000 times a second + timerAlarmEnable(adcTimer); } - DEBUG_MSG("--- Timer CPU Frequency: %u MHz\n", cpufreq); - timerAttachInterrupt(adcTimer, &am_onTimer, false); - timerAlarmWrite(adcTimer, 20, true); // Interrupts when counter == 20, 8.000 times a second - timerAlarmEnable(adcTimer); - - DEBUG_MSG("--- Initializing DAC on Pin %u\n", moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP); - DEBUG_MSG("--- Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); + // setup analogue DAC only if we don't use I2S for output. This is not available on ESP32-S3 +#if !defined(CONFIG_IDF_TARGET_ESP32S3) + if (moduleConfig.audio.i2s_din) + DEBUG_MSG("--- Initializing DAC on Pin %u\n", moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP); +#endif // Configure PTT input + DEBUG_MSG("--- Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); pinMode(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN, INPUT); firstTime = false; diff --git a/src/modules/esp32/AudioModule.h b/src/modules/esp32/AudioModule.h index 16705a141..b30e82bc2 100644 --- a/src/modules/esp32/AudioModule.h +++ b/src/modules/esp32/AudioModule.h @@ -6,6 +6,7 @@ #if defined(ARCH_ESP32) #include "NodeDB.h" #include +#include #include #include #include From 8b58eaac204b658c244b9152dd81077f14d6d57b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 29 Nov 2022 17:45:57 +0100 Subject: [PATCH 08/52] fix compile # Conflicts: # protobufs --- src/mesh/generated/localonly.pb.h | 2 +- src/mesh/generated/module_config.pb.h | 16 ++++++++-------- src/modules/esp32/AudioModule.cpp | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/mesh/generated/localonly.pb.h b/src/mesh/generated/localonly.pb.h index 44d923839..b8c8b27ef 100644 --- a/src/mesh/generated/localonly.pb.h +++ b/src/mesh/generated/localonly.pb.h @@ -151,7 +151,7 @@ extern const pb_msgdesc_t LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define LocalConfig_size 387 -#define LocalModuleConfig_size 385 +#define LocalModuleConfig_size 364 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/module_config.pb.h b/src/mesh/generated/module_config.pb.h index 3747452c7..189eb2ca5 100644 --- a/src/mesh/generated/module_config.pb.h +++ b/src/mesh/generated/module_config.pb.h @@ -63,14 +63,14 @@ typedef enum _ModuleConfig_CannedMessageConfig_InputEventChar { /* Struct definitions */ typedef struct _ModuleConfig_AudioConfig { bool codec2_enabled; - uint32_t mic_chan; - uint32_t amp_pin; - uint32_t ptt_pin; + uint8_t mic_chan; + uint8_t amp_pin; + uint8_t ptt_pin; ModuleConfig_AudioConfig_Audio_Baud bitrate; - uint32_t i2s_ws; - uint32_t i2s_sd; - uint32_t i2s_din; - uint32_t i2s_sck; + uint8_t i2s_ws; + uint8_t i2s_sd; + uint8_t i2s_din; + uint8_t i2s_sck; } ModuleConfig_AudioConfig; typedef struct _ModuleConfig_CannedMessageConfig { @@ -395,7 +395,7 @@ extern const pb_msgdesc_t ModuleConfig_CannedMessageConfig_msg; #define ModuleConfig_CannedMessageConfig_fields &ModuleConfig_CannedMessageConfig_msg /* Maximum encoded size of messages (where known) */ -#define ModuleConfig_AudioConfig_size 46 +#define ModuleConfig_AudioConfig_size 25 #define ModuleConfig_CannedMessageConfig_size 49 #define ModuleConfig_ExternalNotificationConfig_size 22 #define ModuleConfig_MQTTConfig_size 169 diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 0c02251b0..55677c0a9 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -116,7 +116,7 @@ IRAM_ATTR void am_onTimer() if (xHigherPriorityTaskWoken) portYIELD_FROM_ISR(); } - } else if ((radio_state == RadioState::rx) && (!moduleConfig.audio.i2s_din) { + } else if ((radio_state == RadioState::rx) && (!moduleConfig.audio.i2s_din)) { // ESP32-S3 does not have DAC support #if !defined(CONFIG_IDF_TARGET_ESP32S3) int16_t v; @@ -197,7 +197,7 @@ int32_t AudioModule::runOnce() .sample_rate = 8000, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), + .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S), .intr_alloc_flags = 0, .dma_buf_count = 8, .dma_buf_len = ADC_BUFFER_SIZE, From 0e6285edf2d8f981ea36f568df9f793729e52e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 30 Nov 2022 09:52:28 +0100 Subject: [PATCH 09/52] add temp code for heap debugging. Disable -DDEBUG_HEAP for release builds. DEBUG_MSG output only for now. --- platformio.ini | 1 + src/Power.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/platformio.ini b/platformio.ini index dbddf7535..792f2f8b1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -55,6 +55,7 @@ build_flags = -Wno-missing-field-initializers -DRADIOLIB_EXCLUDE_MORSE -DRADIOLIB_EXCLUDE_RTTY -DRADIOLIB_EXCLUDE_SSTV + -DDEBUG_HEAP monitor_speed = 115200 diff --git a/src/Power.cpp b/src/Power.cpp index ec86adc94..83eceeadf 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -283,6 +283,9 @@ void Power::readPowerStatus() DEBUG_MSG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(), powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); newStatus.notifyObservers(&powerStatus2); +#ifdef DEBUG_HEAP + DEBUG_MSG("Heap status: %d/%d bytes free\n", ESP.getFreeHeap(), ESP.getHeapSize()); +#endif // If we have a battery at all and it is less than 10% full, force deep sleep if we have more than 3 low readings in a row // Supect fluctuating voltage on the RAK4631 to force it to deep sleep even if battery is at 85% after only a few days From ab6b6514cbc7c729c3c57f08140e2e6b16e0cbcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 30 Nov 2022 10:04:54 +0100 Subject: [PATCH 10/52] this define is arch specific --- arch/esp32/esp32.ini | 1 + arch/esp32/esp32s3.ini | 5 +++-- platformio.ini | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index 70654e8ec..0011cc39f 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -26,6 +26,7 @@ build_flags = -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 -DCONFIG_BT_NIMBLE_MAX_CCCDS=20 -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING + -DDEBUG_HEAP lib_deps = ${arduino_base.lib_deps} diff --git a/arch/esp32/esp32s3.ini b/arch/esp32/esp32s3.ini index b05772344..72b176999 100644 --- a/arch/esp32/esp32s3.ini +++ b/arch/esp32/esp32s3.ini @@ -23,9 +23,10 @@ build_flags = -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL -DAXP_DEBUG_PORT=Serial -DCONFIG_BT_NIMBLE_ENABLED - -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 + -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 -DCONFIG_BT_NIMBLE_MAX_CCCDS=20 - -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING + -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING + -DDEBUG_HEAP lib_deps = ${arduino_base.lib_deps} diff --git a/platformio.ini b/platformio.ini index 792f2f8b1..dbddf7535 100644 --- a/platformio.ini +++ b/platformio.ini @@ -55,7 +55,6 @@ build_flags = -Wno-missing-field-initializers -DRADIOLIB_EXCLUDE_MORSE -DRADIOLIB_EXCLUDE_RTTY -DRADIOLIB_EXCLUDE_SSTV - -DDEBUG_HEAP monitor_speed = 115200 From 7d1b6f63b53d5253e54bba6119a1f1e8924d5654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Thu, 1 Dec 2022 17:47:04 +0100 Subject: [PATCH 11/52] Definition cleanup and AudioModule WIP --- arch/esp32/esp32s3.ini | 2 +- src/mesh/RadioInterface.h | 1 - src/mesh/RadioLibInterface.h | 3 +- src/modules/esp32/AudioModule.cpp | 102 ++++++++++++++++++++---------- src/modules/esp32/AudioModule.h | 16 +++-- src/platform/portduino/SimRadio.h | 1 - 6 files changed, 81 insertions(+), 44 deletions(-) diff --git a/arch/esp32/esp32s3.ini b/arch/esp32/esp32s3.ini index 72b176999..b276ceff9 100644 --- a/arch/esp32/esp32s3.ini +++ b/arch/esp32/esp32s3.ini @@ -35,7 +35,7 @@ lib_deps = https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2 h2zero/NimBLE-Arduino@^1.4.0 https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 - caveman99/ESP32 Codec2@^1.0.1 + caveman99/ESP32 Codec2@^1.0.1 lib_ignore = segger_rtt diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 437f294a4..f50c0ae77 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -1,6 +1,5 @@ #pragma once -#include "../concurrency/NotifiedWorkerThread.h" #include "MemoryPool.h" #include "MeshTypes.h" #include "Observer.h" diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index f368cf83e..16495c2f4 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -1,10 +1,9 @@ #pragma once -#include "../concurrency/OSThread.h" +#include "concurrency/NotifiedWorkerThread.h" #include "RadioInterface.h" #include "MeshPacketQueue.h" -#define RADIOLIB_EXCLUDE_HTTP #include // ESP32 has special rules about ISR code diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 55677c0a9..7ff46bf10 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -42,6 +42,13 @@ #define AAMP 14 #define PTT_PIN 39 +#ifdef ARCH_ESP32 +// ESP32 doesn't use that flag +#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR() +#else +#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR(x) +#endif + // #define I2S_WS 13 // #define I2S_SD 15 // #define I2S_SIN 2 @@ -51,7 +58,6 @@ #define I2S_PORT I2S_NUM_0 #define AUDIO_MODULE_RX_BUFFER 128 -#define AUDIO_MODULE_DATA_MAX Constants_DATA_PAYLOAD_LEN #define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 AudioModule *audioModule; @@ -107,11 +113,11 @@ IRAM_ATTR void am_onTimer() if (adc_buffer_index == ADC_BUFFER_SIZE) { adc_buffer_index = 0; - DEBUG_MSG("--- memcpy\n"); + DEBUG_MSG("♪♫♪ memcpy\n"); memcpy((void*)speech, (void*)adc_buffer, 2 * ADC_BUFFER_SIZE); // Notify codec2 task that the buffer is ready. BaseType_t xHigherPriorityTaskWoken = pdFALSE; - DEBUG_MSG("--- notifyFromISR\n"); + DEBUG_MSG("♪♫♪ notifyFromISR\n"); codec2Thread->notifyFromISR(&xHigherPriorityTaskWoken, RadioState::tx, true); if (xHigherPriorityTaskWoken) portYIELD_FROM_ISR(); @@ -133,42 +139,51 @@ IRAM_ATTR void am_onTimer() Codec2Thread::Codec2Thread() : concurrency::NotifiedWorkerThread("Codec2Thread") { if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { - DEBUG_MSG("--- Setting up codec2 in mode %u\n", moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE); - codec2_state = codec2_create(moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE); + DEBUG_MSG("♪♫♪ Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); + codec2_state = codec2_create((moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); codec2_set_lpc_post_filter(codec2_state, 1, 0, 0.8, 0.2); + encode_codec_size = (codec2_bits_per_frame(codec2_state) + 7) / 8; + encode_frame_num = Constants_DATA_PAYLOAD_LEN / encode_codec_size; + encode_frame_size = encode_frame_num * encode_codec_size; // max 237 bytes + DEBUG_MSG(" using %d frames of %d bytes for a total payload length of %d bytes\n", encode_frame_num, encode_codec_size, encode_frame_size); } else { - DEBUG_MSG("--- Codec2 disabled\n"); + DEBUG_MSG("♪♫♪ Codec2 disabled (AudioModule %d, Region %s, permitted %d)\n", moduleConfig.audio.codec2_enabled, myRegion->name, myRegion->audioPermitted); } } AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") { audio_fifo.init(); new Codec2Thread(); + //debug + moduleConfig.audio.i2s_ws = 13; + moduleConfig.audio.i2s_sd = 15; + moduleConfig.audio.i2s_din = 2; + moduleConfig.audio.i2s_sck = 14; } -void Codec2Thread::onNotify(uint32_t notification) +void IRAM_ATTR Codec2Thread::onNotify(uint32_t notification) { switch (notification) { case RadioState::tx: for (int i = 0; i < ADC_BUFFER_SIZE; i++) speech[i] = (int16_t)hp_filter.Update((float)speech[i]); - codec2_encode(codec2_state, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index, speech); + codec2_encode(codec2_state, audioModule->tx_encode_frame + tx_encode_frame_index, speech); //increment the pointer where the encoded frame must be saved - audioModule->tx_encode_frame_index += 8; + tx_encode_frame_index += encode_codec_size; - //If it is the 5th time then we have a ready trasnmission frame - if (audioModule->tx_encode_frame_index == ENCODE_FRAME_SIZE) + //If it this is reached we have a ready trasnmission frame + if (tx_encode_frame_index == encode_frame_size) { - audioModule->tx_encode_frame_index = 0; + tx_encode_frame_index = 0; //Transmit it audioModule->sendPayload(); } break; case RadioState::rx: //Make a cycle to get each codec2 frame from the received frame - for (int i = 0; i < ENCODE_FRAME_SIZE; i += ENCODE_CODEC2_SIZE) + for (int i = 0; i < encode_frame_size; i += encode_codec_size) { //Decode the codec2 frame codec2_decode(codec2_state, output_buffer, audioModule->rx_encode_frame + i); @@ -187,25 +202,28 @@ void Codec2Thread::onNotify(uint32_t notification) int32_t AudioModule::runOnce() { if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { + esp_err_t res; if (firstTime) { // if we have I2S_SD defined, take samples from digital mic. I2S_DIN means digital output to amp. if (moduleConfig.audio.i2s_sd || moduleConfig.audio.i2s_din) { - // Set up I2S Processor configuration. This will produce 16bit samples instead of 12 from the ADC - DEBUG_MSG("--- Initializing I2S for input\n"); + // Set up I2S Processor configuration. This will produce 16bit samples at 8 kHz instead of 12 from the ADC + DEBUG_MSG("♪♫♪ Initializing I2S SD: %d DIN: %d WS: %d SCK:%d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck); i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | (moduleConfig.audio.i2s_sd ? I2S_MODE_RX : 0) | (moduleConfig.audio.i2s_din ? I2S_MODE_TX : 0)), .sample_rate = 8000, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S), - .intr_alloc_flags = 0, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = 8, - .dma_buf_len = ADC_BUFFER_SIZE, + .dma_buf_len = ADC_BUFFER_SIZE, // 320 * 2 bytes .use_apll = false, .tx_desc_auto_clear = true, .fixed_mclk = 0 }; - i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); + res = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); + if(res != ESP_OK) + DEBUG_MSG("♪♫♪ Failed to install I2S driver: %d\n", res); const i2s_pin_config_t pin_config = { .bck_io_num = moduleConfig.audio.i2s_sck, @@ -213,13 +231,18 @@ int32_t AudioModule::runOnce() .data_out_num = moduleConfig.audio.i2s_din ? moduleConfig.audio.i2s_din : I2S_PIN_NO_CHANGE, .data_in_num = moduleConfig.audio.i2s_sd ? moduleConfig.audio.i2s_sd : I2S_PIN_NO_CHANGE }; - i2s_set_pin(I2S_PORT, &pin_config); + res = i2s_set_pin(I2S_PORT, &pin_config); + if(res != ESP_OK) + DEBUG_MSG("♪♫♪ Failed to set I2S pin config: %d\n", res); - i2s_start(I2S_PORT); + res = i2s_start(I2S_PORT); + if(res != ESP_OK) + DEBUG_MSG("♪♫♪ Failed to start I2S: %d\n", res); } if (!moduleConfig.audio.i2s_sd) { - DEBUG_MSG("--- Initializing ADC on Channel %u\n", moduleConfig.audio.mic_chan ? moduleConfig.audio.mic_chan : AMIC); + // Set up ADC if we don't have a digital microphone. + DEBUG_MSG("♪♫♪ Initializing ADC on Channel %u\n", moduleConfig.audio.mic_chan ? moduleConfig.audio.mic_chan : AMIC); mic_chan = moduleConfig.audio.mic_chan ? (adc1_channel_t)(int)moduleConfig.audio.mic_chan : (adc1_channel_t)AMIC; adc1_config_width(ADC_WIDTH_12Bit); adc1_config_channel_atten(mic_chan, ADC_ATTEN_DB_6); @@ -246,7 +269,7 @@ int32_t AudioModule::runOnce() adcTimer = timerBegin(3, 500, true); // 80 MHz / 500 = 160KHz break; } - DEBUG_MSG("--- Timer CPU Frequency: %u MHz\n", cpufreq); + DEBUG_MSG("♪♫♪ Timer CPU Frequency: %u MHz\n", cpufreq); timerAttachInterrupt(adcTimer, &am_onTimer, false); timerAlarmWrite(adcTimer, 20, true); // Interrupts when counter == 20, 8.000 times a second timerAlarmEnable(adcTimer); @@ -255,10 +278,10 @@ int32_t AudioModule::runOnce() // setup analogue DAC only if we don't use I2S for output. This is not available on ESP32-S3 #if !defined(CONFIG_IDF_TARGET_ESP32S3) if (moduleConfig.audio.i2s_din) - DEBUG_MSG("--- Initializing DAC on Pin %u\n", moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP); + DEBUG_MSG("♪♫♪ Initializing DAC on Pin %u\n", moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP); #endif // Configure PTT input - DEBUG_MSG("--- Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); + DEBUG_MSG("♪♫♪ Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); pinMode(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN, INPUT); firstTime = false; @@ -266,20 +289,35 @@ int32_t AudioModule::runOnce() // Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive. if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) { if (radio_state == RadioState::rx) { - DEBUG_MSG("--- PTT pressed, switching to TX\n"); + DEBUG_MSG("♪♫♪ PTT pressed, switching to TX\n"); radio_state = RadioState::tx; } } else { if (radio_state == RadioState::tx) { - DEBUG_MSG("--- PTT released, switching to RX\n"); + DEBUG_MSG("♪♫♪ PTT released, switching to RX\n"); radio_state = RadioState::rx; } } - + if ((radio_state == RadioState::tx) && moduleConfig.audio.i2s_sd) { + // Get I2S data from the microphone and place in data buffer + size_t bytesIn = 0; + res = i2s_read(I2S_PORT, &adc_buffer + adc_buffer_index, ADC_BUFFER_SIZE - adc_buffer_index, &bytesIn, pdMS_TO_TICKS(40)); // wait 40ms for audio to arrive. + + if (res == ESP_OK) { + adc_buffer_index += bytesIn; + if (adc_buffer_index == ADC_BUFFER_SIZE) { + adc_buffer_index = 0; + DEBUG_MSG("♪♫♪ We have a full buffer, process it\n"); + memcpy((void*)speech, (void*)adc_buffer, 2 * ADC_BUFFER_SIZE); + // Notify codec2 task that the buffer is ready. + codec2Thread->notify(RadioState::tx, true); + } + } + } } return 100; } else { - DEBUG_MSG("--- Audio Module Disabled\n"); + DEBUG_MSG("♪♫♪ Audio Module Disabled\n"); return INT32_MAX; } @@ -300,7 +338,7 @@ void AudioModule::sendPayload(NodeNum dest, bool wantReplies) p->want_ack = false; // Audio is shoot&forget. TODO: Is this really suppressing retransmissions? p->priority = MeshPacket_Priority_MAX; // Audio is important, because realtime - p->decoded.payload.size = ENCODE_FRAME_SIZE; + p->decoded.payload.size = codec2Thread->get_encode_frame_size(); memcpy(p->decoded.payload.bytes, tx_encode_frame, p->decoded.payload.size); service.sendToMesh(p); @@ -311,7 +349,7 @@ ProcessMessage AudioModule::handleReceived(const MeshPacket &mp) if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { auto &p = mp.decoded; if (getFrom(&mp) != nodeDB.getNodeNum()) { - if (p.payload.size == ENCODE_FRAME_SIZE) { + if (p.payload.size == codec2Thread->get_encode_frame_size()) { memcpy(rx_encode_frame, p.payload.bytes, p.payload.size); radio_state = RadioState::rx; BaseType_t xHigherPriorityTaskWoken = pdFALSE; @@ -319,7 +357,7 @@ ProcessMessage AudioModule::handleReceived(const MeshPacket &mp) if (xHigherPriorityTaskWoken) portYIELD_FROM_ISR(); } else { - DEBUG_MSG("--- Invalid payload size %u != %u\n", p.payload.size, ENCODE_FRAME_SIZE); + DEBUG_MSG("♪♫♪ Invalid payload size %u != %u\n", p.payload.size, codec2Thread->get_encode_frame_size()); } } } diff --git a/src/modules/esp32/AudioModule.h b/src/modules/esp32/AudioModule.h index b30e82bc2..47eba55ee 100644 --- a/src/modules/esp32/AudioModule.h +++ b/src/modules/esp32/AudioModule.h @@ -1,7 +1,7 @@ #pragma once #include "SinglePortModule.h" -#include "concurrency/OSThread.h" +#include "concurrency/NotifiedWorkerThread.h" #include "configuration.h" #if defined(ARCH_ESP32) #include "NodeDB.h" @@ -14,8 +14,6 @@ #include #define ADC_BUFFER_SIZE 320 // 40ms of voice in 8KHz sampling frequency -#define ENCODE_CODEC2_SIZE 8 -#define ENCODE_FRAME_SIZE (ENCODE_CODEC2_SIZE * 5) // 5 codec2 frames of 8 bytes each class Codec2Thread : public concurrency::NotifiedWorkerThread { @@ -25,7 +23,13 @@ class Codec2Thread : public concurrency::NotifiedWorkerThread public: Codec2Thread(); + int get_encode_frame_size() { return encode_frame_size; }; + protected: + int tx_encode_frame_index = 0; + int encode_codec_size = 0; + int encode_frame_num = 0; + int encode_frame_size = 0; virtual void onNotify(uint32_t notification) override; }; @@ -35,11 +39,9 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread hw_timer_t* adcTimer = NULL; uint16_t adc_buffer_index = 0; - public: - unsigned char rx_encode_frame[ENCODE_FRAME_SIZE] = {}; - unsigned char tx_encode_frame[ENCODE_FRAME_SIZE] = {}; - int tx_encode_frame_index = 0; + unsigned char rx_encode_frame[Constants_DATA_PAYLOAD_LEN] = {}; + unsigned char tx_encode_frame[Constants_DATA_PAYLOAD_LEN] = {}; AudioModule(); diff --git a/src/platform/portduino/SimRadio.h b/src/platform/portduino/SimRadio.h index dad419c62..a71cf22f8 100644 --- a/src/platform/portduino/SimRadio.h +++ b/src/platform/portduino/SimRadio.h @@ -4,7 +4,6 @@ #include "MeshPacketQueue.h" #include "wifi/WiFiServerAPI.h" -#define RADIOLIB_EXCLUDE_HTTP #include class SimRadio : public RadioInterface From a0c1e9cdc6facdc6038cda733f150aa7e1195cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Fri, 2 Dec 2022 00:30:31 +0100 Subject: [PATCH 12/52] Still WIP, but first working version of audio. I2S works good, analogue not so much. --- src/modules/Modules.cpp | 2 +- src/modules/esp32/AudioModule.cpp | 262 +++++++++++++++--------------- src/modules/esp32/AudioModule.h | 57 +++---- 3 files changed, 154 insertions(+), 167 deletions(-) diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index b97e965e1..b0b923863 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -66,7 +66,7 @@ void setupModules() #endif #ifdef ARCH_ESP32 // Only run on an esp32 based device. - new AudioModule(); + audioModule = new AudioModule(); new ExternalNotificationModule(); storeForwardModule = new StoreForwardModule(); diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 7ff46bf10..a47f47bd8 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -23,25 +23,36 @@ Basic Usage: 1) Enable the module by setting audio.codec2_enabled to 1. - 2) Set the pins (audio.mic_pin / audio.amp_pin) for your preferred microphone and amplifier GPIO pins. - On tbeam, recommend to use: + 2a) Set the pins for the I2S interface if you want to use digital audio. Recommended on TLora is I2S_WS 13/I2S_SD 15/I2S_SIN 2/I2S_SCK 14 + 2b) Set the pins (audio.mic_pin / audio.amp_pin) for your preferred microphone and amplifier GPIO pins if you want to use analog audio. + This is rather heavy on the CPU and not recommended. + On tbeam, best use: audio.mic_chan 6 (GPIO 34) audio.amp_pin 14 audio.ptt_pin 39 - 3) Set audio.timeout to the amount of time to wait before we consider - your voice stream as "done". - 4) Set audio.bitrate to the desired codec2 rate (CODEC2_3200, CODEC2_2400, CODEC2_1600, CODEC2_1400, CODEC2_1300, CODEC2_1200, CODEC2_700, CODEC2_700B) + 3) Set audio.bitrate to the desired codec2 rate (CODEC2_3200, CODEC2_2400, CODEC2_1600, CODEC2_1400, CODEC2_1300, CODEC2_1200, CODEC2_700, CODEC2_700B) KNOWN PROBLEMS - * Until the module is initilized by the startup sequence, the amp_pin pin is in a floating - radio_state. This may produce a bit of "noise". - * Will not work on NRF and the Linux device targets. + * Until the module is initilized by the startup sequence, the amp_pin is in a floating + state. This may produce a bit of "noise". + * Will not work on NRF and the Linux device targets (yet?). */ #define AMIC 6 #define AAMP 14 #define PTT_PIN 39 +ButterworthFilter hp_filter(240, 8000, ButterworthFilter::ButterworthFilter::Highpass, 1); + +// Use I2S Processor 0 +#define I2S_PORT I2S_NUM_0 + +#define AUDIO_MODULE_RX_BUFFER 128 +#define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 + +TaskHandle_t codec2HandlerTask; +AudioModule *audioModule; + #ifdef ARCH_ESP32 // ESP32 doesn't use that flag #define YIELD_FROM_ISR(x) portYIELD_FROM_ISR() @@ -49,36 +60,13 @@ #define YIELD_FROM_ISR(x) portYIELD_FROM_ISR(x) #endif -// #define I2S_WS 13 -// #define I2S_SD 15 -// #define I2S_SIN 2 -// #define I2S_SCK 14 - -// Use I2S Processor 0 -#define I2S_PORT I2S_NUM_0 - -#define AUDIO_MODULE_RX_BUFFER 128 -#define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 - -AudioModule *audioModule; -Codec2Thread *codec2Thread; - -FastAudioFIFO audio_fifo; -uint16_t adc_buffer[ADC_BUFFER_SIZE] = {}; -uint16_t adc_buffer_index = 0; portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; -int16_t speech[ADC_BUFFER_SIZE] = {}; -volatile RadioState radio_state = RadioState::tx; -adc1_channel_t mic_chan = (adc1_channel_t)0; - -ButterworthFilter hp_filter(240, 8000, ButterworthFilter::ButterworthFilter::Highpass, 1); +hw_timer_t* adcTimer = NULL; //int16_t 1KHz sine test tone int16_t Sine1KHz[8] = { -21210 , -30000, -21210, 0 , 21210 , 30000 , 21210, 0 }; int Sine1KHz_index = 0; -uint8_t rx_raw_audio_value = 127; - int IRAM_ATTR local_adc1_read(int channel) { uint16_t adc_value; #if CONFIG_IDF_TARGET_ESP32S3 @@ -102,8 +90,8 @@ int IRAM_ATTR local_adc1_read(int channel) { IRAM_ATTR void am_onTimer() { portENTER_CRITICAL_ISR(&timerMux); //Enter crital code without interruptions - if ((radio_state == RadioState::tx) && (!moduleConfig.audio.i2s_sd)) { - adc_buffer[adc_buffer_index++] = (16 * local_adc1_read(mic_chan)) - 32768; + if ((audioModule->radio_state == RadioState::tx) && (!moduleConfig.audio.i2s_sd)) { + audioModule->adc_buffer[audioModule->adc_buffer_index++] = (16 * local_adc1_read(audioModule->mic_chan)) - 32768; //If you want to test with a 1KHz tone, comment the line above and descomment the three lines below @@ -111,91 +99,90 @@ IRAM_ATTR void am_onTimer() // if (Sine1KHz_index >= 8) // Sine1KHz_index = 0; - if (adc_buffer_index == ADC_BUFFER_SIZE) { - adc_buffer_index = 0; - DEBUG_MSG("♪♫♪ memcpy\n"); - memcpy((void*)speech, (void*)adc_buffer, 2 * ADC_BUFFER_SIZE); - // Notify codec2 task that the buffer is ready. + if (audioModule->adc_buffer_index == audioModule->adc_buffer_size) { + audioModule->adc_buffer_index = 0; + memcpy((void*)audioModule->speech, (void*)audioModule->adc_buffer, 2 * audioModule->adc_buffer_size); BaseType_t xHigherPriorityTaskWoken = pdFALSE; - DEBUG_MSG("♪♫♪ notifyFromISR\n"); - codec2Thread->notifyFromISR(&xHigherPriorityTaskWoken, RadioState::tx, true); - if (xHigherPriorityTaskWoken) - portYIELD_FROM_ISR(); + vTaskNotifyGiveFromISR(codec2HandlerTask, &xHigherPriorityTaskWoken); + if (xHigherPriorityTaskWoken == pdTRUE) + YIELD_FROM_ISR(xHigherPriorityTaskWoken); } - } else if ((radio_state == RadioState::rx) && (!moduleConfig.audio.i2s_din)) { + } else if ((audioModule->radio_state == RadioState::rx) && (!moduleConfig.audio.i2s_din)) { // ESP32-S3 does not have DAC support #if !defined(CONFIG_IDF_TARGET_ESP32S3) - int16_t v; //Get a value from audio_fifo and convert it to 0 - 255 to play it in the ADC - if (audio_fifo.get(&v)) - rx_raw_audio_value = (uint8_t)((v + 32768) / 256); + if (audioModule->fifo.get(&audioModule->sample)) + audioModule->rx_raw_audio_value = (uint8_t)((audioModule->sample + 32768) / 256); - dacWrite(moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP, rx_raw_audio_value); + dacWrite(moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP, audioModule->rx_raw_audio_value); #endif } portEXIT_CRITICAL_ISR(&timerMux); // exit critical code } -Codec2Thread::Codec2Thread() : concurrency::NotifiedWorkerThread("Codec2Thread") { - if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { - DEBUG_MSG("♪♫♪ Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); - codec2_state = codec2_create((moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); - codec2_set_lpc_post_filter(codec2_state, 1, 0, 0.8, 0.2); - encode_codec_size = (codec2_bits_per_frame(codec2_state) + 7) / 8; - encode_frame_num = Constants_DATA_PAYLOAD_LEN / encode_codec_size; - encode_frame_size = encode_frame_num * encode_codec_size; // max 237 bytes - DEBUG_MSG(" using %d frames of %d bytes for a total payload length of %d bytes\n", encode_frame_num, encode_codec_size, encode_frame_size); - } else { - DEBUG_MSG("♪♫♪ Codec2 disabled (AudioModule %d, Region %s, permitted %d)\n", moduleConfig.audio.codec2_enabled, myRegion->name, myRegion->audioPermitted); +void run_codec2(void* parameter) +{ + while (true) { + uint32_t tcount = ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(10000)); + + if (tcount != 0) { + if (audioModule->radio_state == RadioState::tx) { + + for (int i = 0; i < audioModule->adc_buffer_size; i++) + audioModule->speech[i] = (int16_t)hp_filter.Update((float)audioModule->speech[i]); + + codec2_encode(audioModule->codec2, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index, audioModule->speech); + + //increment the pointer where the encoded frame must be saved + audioModule->tx_encode_frame_index += audioModule->encode_codec_size; + + //If it this is reached we have a ready trasnmission frame + if (audioModule->tx_encode_frame_index == audioModule->encode_frame_size) + { + //Transmit it + DEBUG_MSG("♪♫♪ Sending %d bytes\n", audioModule->encode_frame_size); + audioModule->sendPayload(); + audioModule->tx_encode_frame_index = 0; + } + } + if (audioModule->radio_state == RadioState::rx) { + //Make a cycle to get each codec2 frame from the received frame + for (int i = 0; i < audioModule->rx_encode_frame_index; i += audioModule->encode_codec_size) + { + //Decode the codec2 frame + codec2_decode(audioModule->codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i); + + if (moduleConfig.audio.i2s_din) { + size_t bytesOut = 0; + + i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(40)); + } else { + //Put the decoded audio in the fifo + for (int j = 0; j < audioModule->adc_buffer_size; j++) + audioModule->fifo.put(audioModule->output_buffer[j]); + } + } + } + } } } -AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") { - audio_fifo.init(); - new Codec2Thread(); - //debug - moduleConfig.audio.i2s_ws = 13; - moduleConfig.audio.i2s_sd = 15; - moduleConfig.audio.i2s_din = 2; - moduleConfig.audio.i2s_sck = 14; -} - -void IRAM_ATTR Codec2Thread::onNotify(uint32_t notification) +AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") { - switch (notification) { - case RadioState::tx: - for (int i = 0; i < ADC_BUFFER_SIZE; i++) - speech[i] = (int16_t)hp_filter.Update((float)speech[i]); - - codec2_encode(codec2_state, audioModule->tx_encode_frame + tx_encode_frame_index, speech); - - //increment the pointer where the encoded frame must be saved - tx_encode_frame_index += encode_codec_size; - - //If it this is reached we have a ready trasnmission frame - if (tx_encode_frame_index == encode_frame_size) - { - tx_encode_frame_index = 0; - //Transmit it - audioModule->sendPayload(); - } - break; - case RadioState::rx: - //Make a cycle to get each codec2 frame from the received frame - for (int i = 0; i < encode_frame_size; i += encode_codec_size) - { - //Decode the codec2 frame - codec2_decode(codec2_state, output_buffer, audioModule->rx_encode_frame + i); - - // Add to the audio buffer the 320 samples resulting of the decode of the codec2 frame. - for (int g = 0; g < ADC_BUFFER_SIZE; g++) - audio_fifo.put(output_buffer[g]); - } - break; - default: - assert(0); // We expected to receive a valid notification from the ISR - break; + if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { + fifo.init(); + DEBUG_MSG("♪♫♪ Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); + codec2 = codec2_create((moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); + codec2_set_lpc_post_filter(codec2, 1, 0, 0.8, 0.2); + encode_codec_size = (codec2_bits_per_frame(codec2) + 7) / 8; + encode_frame_num = Constants_DATA_PAYLOAD_LEN / encode_codec_size; + encode_frame_size = encode_frame_num * encode_codec_size; // max 237 bytes + adc_buffer_size = codec2_samples_per_frame(codec2); + DEBUG_MSG(" using %d frames of %d bytes for a total payload length of %d bytes\n", encode_frame_num, encode_codec_size, encode_frame_size); + xTaskCreate(&run_codec2, "codec2_task", 30000, NULL, 5, &codec2HandlerTask); + } else { + DEBUG_MSG("♪♫♪ Codec2 disabled (AudioModule %d, Region %s, permitted %d)\n", moduleConfig.audio.codec2_enabled, myRegion->name, myRegion->audioPermitted); } } @@ -214,9 +201,9 @@ int32_t AudioModule::runOnce() .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S), - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .intr_alloc_flags = 0, .dma_buf_count = 8, - .dma_buf_len = ADC_BUFFER_SIZE, // 320 * 2 bytes + .dma_buf_len = adc_buffer_size, // 320 * 2 bytes .use_apll = false, .tx_desc_auto_clear = true, .fixed_mclk = 0 @@ -239,9 +226,20 @@ int32_t AudioModule::runOnce() if(res != ESP_OK) DEBUG_MSG("♪♫♪ Failed to start I2S: %d\n", res); } + + // setup analogue DAC only if we don't use I2S for output. This is not available on ESP32-S3 +#if !defined(CONFIG_IDF_TARGET_ESP32S3) + if (!moduleConfig.audio.i2s_din) + DEBUG_MSG("♪♫♪ Initializing DAC on Pin %u\n", moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP); +#else + if (!moduleConfig.audio.i2s_din) { + DEBUG_MSG("♪♫♪ ESP32-S3 does not support DAC. Audio Module Disabled.\n"); + return INT32_MAX; + } +#endif if (!moduleConfig.audio.i2s_sd) { - // Set up ADC if we don't have a digital microphone. + // Set up ADC if we don't have a digital microphone. DEBUG_MSG("♪♫♪ Initializing ADC on Channel %u\n", moduleConfig.audio.mic_chan ? moduleConfig.audio.mic_chan : AMIC); mic_chan = moduleConfig.audio.mic_chan ? (adc1_channel_t)(int)moduleConfig.audio.mic_chan : (adc1_channel_t)AMIC; adc1_config_width(ADC_WIDTH_12Bit); @@ -249,8 +247,6 @@ int32_t AudioModule::runOnce() adc1_get_raw(mic_chan); } - radio_state = RadioState::rx; - if ((!moduleConfig.audio.i2s_sd) || (!moduleConfig.audio.i2s_din)) { // Start a timer at 8kHz to sample the ADC and play the audio on the DAC, but only if we have analogue audio to process uint32_t cpufreq = getCpuFrequencyMhz(); @@ -275,11 +271,8 @@ int32_t AudioModule::runOnce() timerAlarmEnable(adcTimer); } - // setup analogue DAC only if we don't use I2S for output. This is not available on ESP32-S3 -#if !defined(CONFIG_IDF_TARGET_ESP32S3) - if (moduleConfig.audio.i2s_din) - DEBUG_MSG("♪♫♪ Initializing DAC on Pin %u\n", moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP); -#endif + radio_state = RadioState::rx; + // Configure PTT input DEBUG_MSG("♪♫♪ Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); pinMode(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN, INPUT); @@ -294,23 +287,32 @@ int32_t AudioModule::runOnce() } } else { if (radio_state == RadioState::tx) { - DEBUG_MSG("♪♫♪ PTT released, switching to RX\n"); + if (tx_encode_frame_index > 0) { + // Send the incomplete frame + DEBUG_MSG("♪♫♪ Sending %d bytes (incomplete)\n", tx_encode_frame_index); + sendPayload(); + } + DEBUG_MSG("♪♫♪ PTT released, switching to RX\n"); + tx_encode_frame_index = 0; radio_state = RadioState::rx; } } if ((radio_state == RadioState::tx) && moduleConfig.audio.i2s_sd) { // Get I2S data from the microphone and place in data buffer size_t bytesIn = 0; - res = i2s_read(I2S_PORT, &adc_buffer + adc_buffer_index, ADC_BUFFER_SIZE - adc_buffer_index, &bytesIn, pdMS_TO_TICKS(40)); // wait 40ms for audio to arrive. + res = i2s_read(I2S_PORT, adc_buffer + adc_buffer_index, adc_buffer_size - adc_buffer_index, &bytesIn, pdMS_TO_TICKS(40)); // wait 40ms for audio to arrive. if (res == ESP_OK) { adc_buffer_index += bytesIn; - if (adc_buffer_index == ADC_BUFFER_SIZE) { + if (adc_buffer_index == adc_buffer_size) { adc_buffer_index = 0; - DEBUG_MSG("♪♫♪ We have a full buffer, process it\n"); - memcpy((void*)speech, (void*)adc_buffer, 2 * ADC_BUFFER_SIZE); - // Notify codec2 task that the buffer is ready. - codec2Thread->notify(RadioState::tx, true); + memcpy((void*)speech, (void*)adc_buffer, 2 * adc_buffer_size); + // Notify run_codec2 task that the buffer is ready. + radio_state = RadioState::tx; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + vTaskNotifyGiveFromISR(codec2HandlerTask, &xHigherPriorityTaskWoken); + if (xHigherPriorityTaskWoken == pdTRUE) + YIELD_FROM_ISR(xHigherPriorityTaskWoken); } } } @@ -338,7 +340,7 @@ void AudioModule::sendPayload(NodeNum dest, bool wantReplies) p->want_ack = false; // Audio is shoot&forget. TODO: Is this really suppressing retransmissions? p->priority = MeshPacket_Priority_MAX; // Audio is important, because realtime - p->decoded.payload.size = codec2Thread->get_encode_frame_size(); + p->decoded.payload.size = tx_encode_frame_index; memcpy(p->decoded.payload.bytes, tx_encode_frame, p->decoded.payload.size); service.sendToMesh(p); @@ -349,16 +351,14 @@ ProcessMessage AudioModule::handleReceived(const MeshPacket &mp) if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { auto &p = mp.decoded; if (getFrom(&mp) != nodeDB.getNodeNum()) { - if (p.payload.size == codec2Thread->get_encode_frame_size()) { - memcpy(rx_encode_frame, p.payload.bytes, p.payload.size); - radio_state = RadioState::rx; - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - codec2Thread->notifyFromISR(&xHigherPriorityTaskWoken, RadioState::rx, true); - if (xHigherPriorityTaskWoken) - portYIELD_FROM_ISR(); - } else { - DEBUG_MSG("♪♫♪ Invalid payload size %u != %u\n", p.payload.size, codec2Thread->get_encode_frame_size()); - } + memcpy(rx_encode_frame, p.payload.bytes, p.payload.size); + radio_state = RadioState::rx; + rx_encode_frame_index = p.payload.size; + // Notify run_codec2 task that the buffer is ready. + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + vTaskNotifyGiveFromISR(codec2HandlerTask, &xHigherPriorityTaskWoken); + if (xHigherPriorityTaskWoken == pdTRUE) + YIELD_FROM_ISR(xHigherPriorityTaskWoken); } } diff --git a/src/modules/esp32/AudioModule.h b/src/modules/esp32/AudioModule.h index 47eba55ee..c81c5c310 100644 --- a/src/modules/esp32/AudioModule.h +++ b/src/modules/esp32/AudioModule.h @@ -7,41 +7,36 @@ #include "NodeDB.h" #include #include -#include +// #include #include #include #include #include -#define ADC_BUFFER_SIZE 320 // 40ms of voice in 8KHz sampling frequency +#define ADC_BUFFER_SIZE_MAX 320 -class Codec2Thread : public concurrency::NotifiedWorkerThread -{ - struct CODEC2* codec2_state = NULL; - int16_t output_buffer[ADC_BUFFER_SIZE] = {}; - - public: - Codec2Thread(); - - int get_encode_frame_size() { return encode_frame_size; }; - - protected: - int tx_encode_frame_index = 0; - int encode_codec_size = 0; - int encode_frame_num = 0; - int encode_frame_size = 0; - virtual void onNotify(uint32_t notification) override; -}; +enum RadioState { standby, rx, tx }; class AudioModule : public SinglePortModule, private concurrency::OSThread { - bool firstTime = true; - hw_timer_t* adcTimer = NULL; - uint16_t adc_buffer_index = 0; - public: unsigned char rx_encode_frame[Constants_DATA_PAYLOAD_LEN] = {}; unsigned char tx_encode_frame[Constants_DATA_PAYLOAD_LEN] = {}; + int16_t speech[ADC_BUFFER_SIZE_MAX] = {}; + int16_t output_buffer[ADC_BUFFER_SIZE_MAX] = {}; + uint16_t adc_buffer[ADC_BUFFER_SIZE_MAX] = {}; + int adc_buffer_size = 0; + uint16_t adc_buffer_index = 0; + int tx_encode_frame_index = 0; + int rx_encode_frame_index = 0; + int encode_codec_size = 0; + int encode_frame_size = 0; + volatile RadioState radio_state = RadioState::rx; + FastAudioFIFO fifo; + struct CODEC2* codec2 = NULL; + int16_t sample; + adc1_channel_t mic_chan = (adc1_channel_t)0; + uint8_t rx_raw_audio_value = 127; AudioModule(); @@ -51,9 +46,11 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); protected: - virtual int32_t runOnce() override; + int encode_frame_num = 0; + bool firstTime = true; - // void run_codec2(); + + virtual int32_t runOnce() override; virtual MeshPacket *allocReply() override; @@ -64,15 +61,5 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread }; extern AudioModule *audioModule; -extern Codec2Thread *codec2Thread; -extern uint16_t adc_buffer[ADC_BUFFER_SIZE]; -extern uint16_t adc_buffer_index; -extern portMUX_TYPE timerMux; -extern int16_t speech[ADC_BUFFER_SIZE]; -enum RadioState { standby, rx, tx }; -extern volatile RadioState radio_state; -extern adc1_channel_t mic_chan; - -IRAM_ATTR void am_onTimer(); #endif \ No newline at end of file From feb7181767927c2c7da6fedb7aaf1e601435cdd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Fri, 2 Dec 2022 12:20:21 +0100 Subject: [PATCH 13/52] debug print thread count. max_threads is 32 --- src/Power.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Power.cpp b/src/Power.cpp index 83eceeadf..be44c0d49 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -284,7 +284,7 @@ void Power::readPowerStatus() powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); newStatus.notifyObservers(&powerStatus2); #ifdef DEBUG_HEAP - DEBUG_MSG("Heap status: %d/%d bytes free\n", ESP.getFreeHeap(), ESP.getHeapSize()); + DEBUG_MSG("Heap status: %d/%d bytes free, running %d threads\n", ESP.getFreeHeap(), ESP.getHeapSize(), concurrency::mainController.size(false)); #endif // If we have a battery at all and it is less than 10% full, force deep sleep if we have more than 3 low readings in a row From a0f5e44967bfbd0a436b261a25977c227b6924ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Fri, 2 Dec 2022 12:58:26 +0100 Subject: [PATCH 14/52] Audio Module is finished for regression tests. --- src/mesh/generated/localonly.pb.h | 2 +- src/mesh/generated/module_config.pb.h | 36 ++--- src/modules/esp32/AudioModule.cpp | 202 +++++--------------------- src/modules/esp32/AudioModule.h | 9 +- 4 files changed, 58 insertions(+), 191 deletions(-) diff --git a/src/mesh/generated/localonly.pb.h b/src/mesh/generated/localonly.pb.h index b8c8b27ef..596bb7b9a 100644 --- a/src/mesh/generated/localonly.pb.h +++ b/src/mesh/generated/localonly.pb.h @@ -151,7 +151,7 @@ extern const pb_msgdesc_t LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define LocalConfig_size 387 -#define LocalModuleConfig_size 364 +#define LocalModuleConfig_size 358 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/module_config.pb.h b/src/mesh/generated/module_config.pb.h index 189eb2ca5..4b2e29f17 100644 --- a/src/mesh/generated/module_config.pb.h +++ b/src/mesh/generated/module_config.pb.h @@ -63,8 +63,6 @@ typedef enum _ModuleConfig_CannedMessageConfig_InputEventChar { /* Struct definitions */ typedef struct _ModuleConfig_AudioConfig { bool codec2_enabled; - uint8_t mic_chan; - uint8_t amp_pin; uint8_t ptt_pin; ModuleConfig_AudioConfig_Audio_Baud bitrate; uint8_t i2s_ws; @@ -187,7 +185,7 @@ extern "C" { /* Initializer values for message structs */ #define ModuleConfig_init_default {0, {ModuleConfig_MQTTConfig_init_default}} #define ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0} -#define ModuleConfig_AudioConfig_init_default {0, 0, 0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} +#define ModuleConfig_AudioConfig_init_default {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} #define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0} @@ -196,7 +194,7 @@ extern "C" { #define ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0} #define ModuleConfig_init_zero {0, {ModuleConfig_MQTTConfig_init_zero}} #define ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0} -#define ModuleConfig_AudioConfig_init_zero {0, 0, 0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} +#define ModuleConfig_AudioConfig_init_zero {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} #define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0} @@ -206,14 +204,12 @@ extern "C" { /* Field tags (for use in manual encoding/decoding) */ #define ModuleConfig_AudioConfig_codec2_enabled_tag 1 -#define ModuleConfig_AudioConfig_mic_chan_tag 2 -#define ModuleConfig_AudioConfig_amp_pin_tag 3 -#define ModuleConfig_AudioConfig_ptt_pin_tag 4 -#define ModuleConfig_AudioConfig_bitrate_tag 5 -#define ModuleConfig_AudioConfig_i2s_ws_tag 6 -#define ModuleConfig_AudioConfig_i2s_sd_tag 7 -#define ModuleConfig_AudioConfig_i2s_din_tag 8 -#define ModuleConfig_AudioConfig_i2s_sck_tag 9 +#define ModuleConfig_AudioConfig_ptt_pin_tag 2 +#define ModuleConfig_AudioConfig_bitrate_tag 3 +#define ModuleConfig_AudioConfig_i2s_ws_tag 4 +#define ModuleConfig_AudioConfig_i2s_sd_tag 5 +#define ModuleConfig_AudioConfig_i2s_din_tag 6 +#define ModuleConfig_AudioConfig_i2s_sck_tag 7 #define ModuleConfig_CannedMessageConfig_rotary1_enabled_tag 1 #define ModuleConfig_CannedMessageConfig_inputbroker_pin_a_tag 2 #define ModuleConfig_CannedMessageConfig_inputbroker_pin_b_tag 3 @@ -300,14 +296,12 @@ X(a, STATIC, SINGULAR, BOOL, json_enabled, 6) #define ModuleConfig_AudioConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, BOOL, codec2_enabled, 1) \ -X(a, STATIC, SINGULAR, UINT32, mic_chan, 2) \ -X(a, STATIC, SINGULAR, UINT32, amp_pin, 3) \ -X(a, STATIC, SINGULAR, UINT32, ptt_pin, 4) \ -X(a, STATIC, SINGULAR, UENUM, bitrate, 5) \ -X(a, STATIC, SINGULAR, UINT32, i2s_ws, 6) \ -X(a, STATIC, SINGULAR, UINT32, i2s_sd, 7) \ -X(a, STATIC, SINGULAR, UINT32, i2s_din, 8) \ -X(a, STATIC, SINGULAR, UINT32, i2s_sck, 9) +X(a, STATIC, SINGULAR, UINT32, ptt_pin, 2) \ +X(a, STATIC, SINGULAR, UENUM, bitrate, 3) \ +X(a, STATIC, SINGULAR, UINT32, i2s_ws, 4) \ +X(a, STATIC, SINGULAR, UINT32, i2s_sd, 5) \ +X(a, STATIC, SINGULAR, UINT32, i2s_din, 6) \ +X(a, STATIC, SINGULAR, UINT32, i2s_sck, 7) #define ModuleConfig_AudioConfig_CALLBACK NULL #define ModuleConfig_AudioConfig_DEFAULT NULL @@ -395,7 +389,7 @@ extern const pb_msgdesc_t ModuleConfig_CannedMessageConfig_msg; #define ModuleConfig_CannedMessageConfig_fields &ModuleConfig_CannedMessageConfig_msg /* Maximum encoded size of messages (where known) */ -#define ModuleConfig_AudioConfig_size 25 +#define ModuleConfig_AudioConfig_size 19 #define ModuleConfig_CannedMessageConfig_size 49 #define ModuleConfig_ExternalNotificationConfig_size 22 #define ModuleConfig_MQTTConfig_size 169 diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index a47f47bd8..b1433a0ae 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -8,8 +8,6 @@ #include "Router.h" #include "FSCommon.h" -#include -#include #include /* @@ -23,23 +21,14 @@ Basic Usage: 1) Enable the module by setting audio.codec2_enabled to 1. - 2a) Set the pins for the I2S interface if you want to use digital audio. Recommended on TLora is I2S_WS 13/I2S_SD 15/I2S_SIN 2/I2S_SCK 14 - 2b) Set the pins (audio.mic_pin / audio.amp_pin) for your preferred microphone and amplifier GPIO pins if you want to use analog audio. - This is rather heavy on the CPU and not recommended. - On tbeam, best use: - audio.mic_chan 6 (GPIO 34) - audio.amp_pin 14 - audio.ptt_pin 39 + 2) Set the pins for the I2S interface. Recommended on TLora is I2S_WS 13/I2S_SD 15/I2S_SIN 2/I2S_SCK 14 3) Set audio.bitrate to the desired codec2 rate (CODEC2_3200, CODEC2_2400, CODEC2_1600, CODEC2_1400, CODEC2_1300, CODEC2_1200, CODEC2_700, CODEC2_700B) KNOWN PROBLEMS - * Until the module is initilized by the startup sequence, the amp_pin is in a floating - state. This may produce a bit of "noise". + * Half Duplex * Will not work on NRF and the Linux device targets (yet?). */ -#define AMIC 6 -#define AAMP 14 #define PTT_PIN 39 ButterworthFilter hp_filter(240, 8000, ButterworthFilter::ButterworthFilter::Highpass, 1); @@ -60,67 +49,10 @@ AudioModule *audioModule; #define YIELD_FROM_ISR(x) portYIELD_FROM_ISR(x) #endif -portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; -hw_timer_t* adcTimer = NULL; - //int16_t 1KHz sine test tone int16_t Sine1KHz[8] = { -21210 , -30000, -21210, 0 , 21210 , 30000 , 21210, 0 }; int Sine1KHz_index = 0; -int IRAM_ATTR local_adc1_read(int channel) { - uint16_t adc_value; -#if CONFIG_IDF_TARGET_ESP32S3 - SENS.sar_meas1_ctrl2.sar1_en_pad = (1 << channel); // only one channel is selected - while (SENS.sar_slave_addr1.meas_status != 0); - SENS.sar_meas1_ctrl2.meas1_start_sar = 0; - SENS.sar_meas1_ctrl2.meas1_start_sar = 1; - while (SENS.sar_meas1_ctrl2.meas1_done_sar == 0); - adc_value = SENS.sar_meas1_ctrl2.meas1_data_sar; -#else - SENS.sar_meas_start1.sar1_en_pad = (1 << channel); // only one channel is selected - while (SENS.sar_slave_addr1.meas_status != 0); - SENS.sar_meas_start1.meas1_start_sar = 0; - SENS.sar_meas_start1.meas1_start_sar = 1; - while (SENS.sar_meas_start1.meas1_done_sar == 0); - adc_value = SENS.sar_meas_start1.meas1_data_sar; -#endif - return adc_value; -} - -IRAM_ATTR void am_onTimer() -{ - portENTER_CRITICAL_ISR(&timerMux); //Enter crital code without interruptions - if ((audioModule->radio_state == RadioState::tx) && (!moduleConfig.audio.i2s_sd)) { - audioModule->adc_buffer[audioModule->adc_buffer_index++] = (16 * local_adc1_read(audioModule->mic_chan)) - 32768; - - //If you want to test with a 1KHz tone, comment the line above and descomment the three lines below - - // adc_buffer[adc_buffer_index++] = Sine1KHz[Sine1KHz_index++]; - // if (Sine1KHz_index >= 8) - // Sine1KHz_index = 0; - - if (audioModule->adc_buffer_index == audioModule->adc_buffer_size) { - audioModule->adc_buffer_index = 0; - memcpy((void*)audioModule->speech, (void*)audioModule->adc_buffer, 2 * audioModule->adc_buffer_size); - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR(codec2HandlerTask, &xHigherPriorityTaskWoken); - if (xHigherPriorityTaskWoken == pdTRUE) - YIELD_FROM_ISR(xHigherPriorityTaskWoken); - } - } else if ((audioModule->radio_state == RadioState::rx) && (!moduleConfig.audio.i2s_din)) { - // ESP32-S3 does not have DAC support -#if !defined(CONFIG_IDF_TARGET_ESP32S3) - - //Get a value from audio_fifo and convert it to 0 - 255 to play it in the ADC - if (audioModule->fifo.get(&audioModule->sample)) - audioModule->rx_raw_audio_value = (uint8_t)((audioModule->sample + 32768) / 256); - - dacWrite(moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP, audioModule->rx_raw_audio_value); -#endif - } - portEXIT_CRITICAL_ISR(&timerMux); // exit critical code -} - void run_codec2(void* parameter) { while (true) { @@ -128,10 +60,12 @@ void run_codec2(void* parameter) if (tcount != 0) { if (audioModule->radio_state == RadioState::tx) { - + + // Apply the TX filter for (int i = 0; i < audioModule->adc_buffer_size; i++) audioModule->speech[i] = (int16_t)hp_filter.Update((float)audioModule->speech[i]); + // Encode the audio codec2_encode(audioModule->codec2, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index, audioModule->speech); //increment the pointer where the encoded frame must be saved @@ -141,7 +75,7 @@ void run_codec2(void* parameter) if (audioModule->tx_encode_frame_index == audioModule->encode_frame_size) { //Transmit it - DEBUG_MSG("♪♫♪ Sending %d bytes\n", audioModule->encode_frame_size); + DEBUG_MSG("♪♫♪ Sending %d codec2 bytes\n", audioModule->encode_frame_size); audioModule->sendPayload(); audioModule->tx_encode_frame_index = 0; } @@ -152,16 +86,8 @@ void run_codec2(void* parameter) { //Decode the codec2 frame codec2_decode(audioModule->codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i); - - if (moduleConfig.audio.i2s_din) { - size_t bytesOut = 0; - - i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(40)); - } else { - //Put the decoded audio in the fifo - for (int j = 0; j < audioModule->adc_buffer_size; j++) - audioModule->fifo.put(audioModule->output_buffer[j]); - } + size_t bytesOut = 0; + i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500)); } } } @@ -171,7 +97,6 @@ void run_codec2(void* parameter) AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") { if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { - fifo.init(); DEBUG_MSG("♪♫♪ Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); codec2 = codec2_create((moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); codec2_set_lpc_post_filter(codec2, 1, 0, 0.8, 0.2); @@ -191,86 +116,39 @@ int32_t AudioModule::runOnce() if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { esp_err_t res; if (firstTime) { - // if we have I2S_SD defined, take samples from digital mic. I2S_DIN means digital output to amp. - if (moduleConfig.audio.i2s_sd || moduleConfig.audio.i2s_din) { - // Set up I2S Processor configuration. This will produce 16bit samples at 8 kHz instead of 12 from the ADC - DEBUG_MSG("♪♫♪ Initializing I2S SD: %d DIN: %d WS: %d SCK:%d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck); - i2s_config_t i2s_config = { - .mode = (i2s_mode_t)(I2S_MODE_MASTER | (moduleConfig.audio.i2s_sd ? I2S_MODE_RX : 0) | (moduleConfig.audio.i2s_din ? I2S_MODE_TX : 0)), - .sample_rate = 8000, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, - .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S), - .intr_alloc_flags = 0, - .dma_buf_count = 8, - .dma_buf_len = adc_buffer_size, // 320 * 2 bytes - .use_apll = false, - .tx_desc_auto_clear = true, - .fixed_mclk = 0 - }; - res = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); - if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to install I2S driver: %d\n", res); + // Set up I2S Processor configuration. This will produce 16bit samples at 8 kHz instead of 12 from the ADC + DEBUG_MSG("♪♫♪ Initializing I2S SD: %d DIN: %d WS: %d SCK:%d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck); + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | (moduleConfig.audio.i2s_sd ? I2S_MODE_RX : 0) | (moduleConfig.audio.i2s_din ? I2S_MODE_TX : 0)), + .sample_rate = 8000, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, + .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S), + .intr_alloc_flags = 0, + .dma_buf_count = 8, + .dma_buf_len = adc_buffer_size, // 320 * 2 bytes + .use_apll = false, + .tx_desc_auto_clear = true, + .fixed_mclk = 0 + }; + res = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); + if(res != ESP_OK) + DEBUG_MSG("♪♫♪ Failed to install I2S driver: %d\n", res); - const i2s_pin_config_t pin_config = { - .bck_io_num = moduleConfig.audio.i2s_sck, - .ws_io_num = moduleConfig.audio.i2s_ws, - .data_out_num = moduleConfig.audio.i2s_din ? moduleConfig.audio.i2s_din : I2S_PIN_NO_CHANGE, - .data_in_num = moduleConfig.audio.i2s_sd ? moduleConfig.audio.i2s_sd : I2S_PIN_NO_CHANGE - }; - res = i2s_set_pin(I2S_PORT, &pin_config); - if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to set I2S pin config: %d\n", res); + const i2s_pin_config_t pin_config = { + .bck_io_num = moduleConfig.audio.i2s_sck, + .ws_io_num = moduleConfig.audio.i2s_ws, + .data_out_num = moduleConfig.audio.i2s_din ? moduleConfig.audio.i2s_din : I2S_PIN_NO_CHANGE, + .data_in_num = moduleConfig.audio.i2s_sd ? moduleConfig.audio.i2s_sd : I2S_PIN_NO_CHANGE + }; + res = i2s_set_pin(I2S_PORT, &pin_config); + if(res != ESP_OK) + DEBUG_MSG("♪♫♪ Failed to set I2S pin config: %d\n", res); - res = i2s_start(I2S_PORT); - if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to start I2S: %d\n", res); - } - - // setup analogue DAC only if we don't use I2S for output. This is not available on ESP32-S3 -#if !defined(CONFIG_IDF_TARGET_ESP32S3) - if (!moduleConfig.audio.i2s_din) - DEBUG_MSG("♪♫♪ Initializing DAC on Pin %u\n", moduleConfig.audio.amp_pin ? moduleConfig.audio.amp_pin : AAMP); -#else - if (!moduleConfig.audio.i2s_din) { - DEBUG_MSG("♪♫♪ ESP32-S3 does not support DAC. Audio Module Disabled.\n"); - return INT32_MAX; - } -#endif + res = i2s_start(I2S_PORT); + if(res != ESP_OK) + DEBUG_MSG("♪♫♪ Failed to start I2S: %d\n", res); - if (!moduleConfig.audio.i2s_sd) { - // Set up ADC if we don't have a digital microphone. - DEBUG_MSG("♪♫♪ Initializing ADC on Channel %u\n", moduleConfig.audio.mic_chan ? moduleConfig.audio.mic_chan : AMIC); - mic_chan = moduleConfig.audio.mic_chan ? (adc1_channel_t)(int)moduleConfig.audio.mic_chan : (adc1_channel_t)AMIC; - adc1_config_width(ADC_WIDTH_12Bit); - adc1_config_channel_atten(mic_chan, ADC_ATTEN_DB_6); - adc1_get_raw(mic_chan); - } - - if ((!moduleConfig.audio.i2s_sd) || (!moduleConfig.audio.i2s_din)) { - // Start a timer at 8kHz to sample the ADC and play the audio on the DAC, but only if we have analogue audio to process - uint32_t cpufreq = getCpuFrequencyMhz(); - switch (cpufreq){ - case 160: - adcTimer = timerBegin(3, 1000, true); // 160 MHz / 1000 = 160KHz - break; - case 240: - adcTimer = timerBegin(3, 1500, true); // 240 MHz / 1500 = 160KHz - break; - case 320: - adcTimer = timerBegin(3, 2000, true); // 320 MHz / 2000 = 160KHz - break; - case 80: - default: - adcTimer = timerBegin(3, 500, true); // 80 MHz / 500 = 160KHz - break; - } - DEBUG_MSG("♪♫♪ Timer CPU Frequency: %u MHz\n", cpufreq); - timerAttachInterrupt(adcTimer, &am_onTimer, false); - timerAlarmWrite(adcTimer, 20, true); // Interrupts when counter == 20, 8.000 times a second - timerAlarmEnable(adcTimer); - } - radio_state = RadioState::rx; // Configure PTT input @@ -289,7 +167,7 @@ int32_t AudioModule::runOnce() if (radio_state == RadioState::tx) { if (tx_encode_frame_index > 0) { // Send the incomplete frame - DEBUG_MSG("♪♫♪ Sending %d bytes (incomplete)\n", tx_encode_frame_index); + DEBUG_MSG("♪♫♪ Sending %d codec2 bytes (incomplete)\n", tx_encode_frame_index); sendPayload(); } DEBUG_MSG("♪♫♪ PTT released, switching to RX\n"); @@ -297,7 +175,7 @@ int32_t AudioModule::runOnce() radio_state = RadioState::rx; } } - if ((radio_state == RadioState::tx) && moduleConfig.audio.i2s_sd) { + if (radio_state == RadioState::tx) { // Get I2S data from the microphone and place in data buffer size_t bytesIn = 0; res = i2s_read(I2S_PORT, adc_buffer + adc_buffer_index, adc_buffer_size - adc_buffer_index, &bytesIn, pdMS_TO_TICKS(40)); // wait 40ms for audio to arrive. diff --git a/src/modules/esp32/AudioModule.h b/src/modules/esp32/AudioModule.h index c81c5c310..aee09a0b0 100644 --- a/src/modules/esp32/AudioModule.h +++ b/src/modules/esp32/AudioModule.h @@ -7,11 +7,9 @@ #include "NodeDB.h" #include #include -// #include #include #include #include -#include #define ADC_BUFFER_SIZE_MAX 320 @@ -32,11 +30,9 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread int encode_codec_size = 0; int encode_frame_size = 0; volatile RadioState radio_state = RadioState::rx; - FastAudioFIFO fifo; + struct CODEC2* codec2 = NULL; - int16_t sample; - adc1_channel_t mic_chan = (adc1_channel_t)0; - uint8_t rx_raw_audio_value = 127; + // int16_t sample; AudioModule(); @@ -49,7 +45,6 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread int encode_frame_num = 0; bool firstTime = true; - virtual int32_t runOnce() override; virtual MeshPacket *allocReply() override; From 8f94463eacc2a63968eaa11a570727206cf84fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Fri, 2 Dec 2022 13:44:36 +0100 Subject: [PATCH 15/52] send a 4 byte magic header including the codec version --- src/modules/esp32/AudioModule.cpp | 25 +++++++++++-------------- src/modules/esp32/AudioModule.h | 20 +++++++++++++++++--- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index b1433a0ae..4e784e1a9 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -29,16 +29,8 @@ * Will not work on NRF and the Linux device targets (yet?). */ -#define PTT_PIN 39 - ButterworthFilter hp_filter(240, 8000, ButterworthFilter::ButterworthFilter::Highpass, 1); -// Use I2S Processor 0 -#define I2S_PORT I2S_NUM_0 - -#define AUDIO_MODULE_RX_BUFFER 128 -#define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 - TaskHandle_t codec2HandlerTask; AudioModule *audioModule; @@ -55,6 +47,9 @@ int Sine1KHz_index = 0; void run_codec2(void* parameter) { + // 4 bytes of header in each frame Kennung hex c0 de c2 plus the bitrate + memcpy(audioModule->tx_encode_frame,&audioModule->tx_header,sizeof(audioModule->tx_header)); + while (true) { uint32_t tcount = ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(10000)); @@ -72,12 +67,12 @@ void run_codec2(void* parameter) audioModule->tx_encode_frame_index += audioModule->encode_codec_size; //If it this is reached we have a ready trasnmission frame - if (audioModule->tx_encode_frame_index == audioModule->encode_frame_size) + if (audioModule->tx_encode_frame_index == (audioModule->encode_frame_size + sizeof(audioModule->tx_header))) { //Transmit it DEBUG_MSG("♪♫♪ Sending %d codec2 bytes\n", audioModule->encode_frame_size); audioModule->sendPayload(); - audioModule->tx_encode_frame_index = 0; + audioModule->tx_encode_frame_index = sizeof(audioModule->tx_header); } } if (audioModule->radio_state == RadioState::rx) { @@ -99,10 +94,12 @@ AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { DEBUG_MSG("♪♫♪ Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); codec2 = codec2_create((moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); + memcpy(tx_header.magic,c2_magic,sizeof(c2_magic)); + tx_header.mode = (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1; codec2_set_lpc_post_filter(codec2, 1, 0, 0.8, 0.2); encode_codec_size = (codec2_bits_per_frame(codec2) + 7) / 8; - encode_frame_num = Constants_DATA_PAYLOAD_LEN / encode_codec_size; - encode_frame_size = encode_frame_num * encode_codec_size; // max 237 bytes + encode_frame_num = (Constants_DATA_PAYLOAD_LEN - sizeof(tx_header)) / encode_codec_size; + encode_frame_size = encode_frame_num * encode_codec_size; // max 233 bytes + 4 header bytes adc_buffer_size = codec2_samples_per_frame(codec2); DEBUG_MSG(" using %d frames of %d bytes for a total payload length of %d bytes\n", encode_frame_num, encode_codec_size, encode_frame_size); xTaskCreate(&run_codec2, "codec2_task", 30000, NULL, 5, &codec2HandlerTask); @@ -165,13 +162,13 @@ int32_t AudioModule::runOnce() } } else { if (radio_state == RadioState::tx) { - if (tx_encode_frame_index > 0) { + if (tx_encode_frame_index > sizeof(tx_header)) { // Send the incomplete frame DEBUG_MSG("♪♫♪ Sending %d codec2 bytes (incomplete)\n", tx_encode_frame_index); sendPayload(); } DEBUG_MSG("♪♫♪ PTT released, switching to RX\n"); - tx_encode_frame_index = 0; + tx_encode_frame_index = sizeof(tx_header); radio_state = RadioState::rx; } } diff --git a/src/modules/esp32/AudioModule.h b/src/modules/esp32/AudioModule.h index aee09a0b0..8e813ad1e 100644 --- a/src/modules/esp32/AudioModule.h +++ b/src/modules/esp32/AudioModule.h @@ -11,21 +11,35 @@ #include #include -#define ADC_BUFFER_SIZE_MAX 320 - enum RadioState { standby, rx, tx }; +const char c2_magic[3] = {0xc0, 0xde, 0xc2}; // Magic number for codec2 header + +struct c2_header { + char magic[3]; + char mode; +}; + +#define ADC_BUFFER_SIZE_MAX 320 +#define PTT_PIN 39 + +#define I2S_PORT I2S_NUM_0 + +#define AUDIO_MODULE_RX_BUFFER 128 +#define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 + class AudioModule : public SinglePortModule, private concurrency::OSThread { public: unsigned char rx_encode_frame[Constants_DATA_PAYLOAD_LEN] = {}; unsigned char tx_encode_frame[Constants_DATA_PAYLOAD_LEN] = {}; + c2_header tx_header = {}; int16_t speech[ADC_BUFFER_SIZE_MAX] = {}; int16_t output_buffer[ADC_BUFFER_SIZE_MAX] = {}; uint16_t adc_buffer[ADC_BUFFER_SIZE_MAX] = {}; int adc_buffer_size = 0; uint16_t adc_buffer_index = 0; - int tx_encode_frame_index = 0; + int tx_encode_frame_index = sizeof(c2_header); // leave room for header int rx_encode_frame_index = 0; int encode_codec_size = 0; int encode_frame_size = 0; From cea8393a7fe370813fdf5bd44b730f285b9984ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 6 Dec 2022 14:08:04 +0100 Subject: [PATCH 16/52] Merge pull request #2026 from GUVWAF/develop TraceRouteModule --- src/mesh/FloodingRouter.cpp | 5 ++ src/mesh/FloodingRouter.h | 1 + src/modules/Modules.cpp | 2 + src/modules/TraceRouteModule.cpp | 86 ++++++++++++++++++++++++++++++++ src/modules/TraceRouteModule.h | 36 +++++++++++++ 5 files changed, 130 insertions(+) create mode 100644 src/modules/TraceRouteModule.cpp create mode 100644 src/modules/TraceRouteModule.h diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index 818bacf45..925735903 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -41,6 +41,11 @@ void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c) tosend->hop_limit--; // bump down the hop count + // If it is a traceRoute request, update the route that it went via me + if (p->which_payload_variant == MeshPacket_decoded_tag && traceRouteModule->wantPacket(p)) { + traceRouteModule->updateRoute(tosend); + } + printPacket("Rebroadcasting received floodmsg to neighbors", p); // Note: we are careful to resend using the original senders node id // We are careful not to call our hooked version of send() - because we don't want to check this again diff --git a/src/mesh/FloodingRouter.h b/src/mesh/FloodingRouter.h index 387b4576b..7e6271fc0 100644 --- a/src/mesh/FloodingRouter.h +++ b/src/mesh/FloodingRouter.h @@ -2,6 +2,7 @@ #include "PacketHistory.h" #include "Router.h" +#include "modules/TraceRouteModule.h" /** * This is a mixin that extends Router with the ability to do Naive Flooding (in the standard mesh protocol sense) diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp index b0b923863..ece160ced 100644 --- a/src/modules/Modules.cpp +++ b/src/modules/Modules.cpp @@ -11,6 +11,7 @@ #include "modules/ReplyModule.h" #include "modules/RoutingModule.h" #include "modules/TextMessageModule.h" +#include "modules/TraceRouteModule.h" #include "modules/WaypointModule.h" #if HAS_TELEMETRY #include "modules/Telemetry/DeviceTelemetry.h" @@ -40,6 +41,7 @@ void setupModules() positionModule = new PositionModule(); waypointModule = new WaypointModule(); textMessageModule = new TextMessageModule(); + traceRouteModule = new TraceRouteModule(); // Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance // to a global variable. diff --git a/src/modules/TraceRouteModule.cpp b/src/modules/TraceRouteModule.cpp new file mode 100644 index 000000000..1c5fd97d3 --- /dev/null +++ b/src/modules/TraceRouteModule.cpp @@ -0,0 +1,86 @@ +#include "TraceRouteModule.h" +#include "MeshService.h" +#include "FloodingRouter.h" + +TraceRouteModule *traceRouteModule; + + +bool TraceRouteModule::handleReceivedProtobuf(const MeshPacket &mp, RouteDiscovery *r) +{ + // Only handle a response + if (mp.decoded.request_id) { + printRoute(r, mp.to, mp.from); + } + + return false; // let it be handled by RoutingModule +} + + +void TraceRouteModule::updateRoute(MeshPacket* p) +{ + auto &incoming = p->decoded; + // Only append an ID for the request (one way) + if (!incoming.request_id) { + RouteDiscovery scratch; + RouteDiscovery *updated = NULL; + memset(&scratch, 0, sizeof(scratch)); + pb_decode_from_bytes(incoming.payload.bytes, incoming.payload.size, RouteDiscovery_fields, &scratch); + updated = &scratch; + + appendMyID(updated); + printRoute(updated, p->from, NODENUM_BROADCAST); + + // Set updated route to the payload of the to be flooded packet + p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), RouteDiscovery_fields, updated); + } +} + + +void TraceRouteModule::appendMyID(RouteDiscovery* updated) +{ + // Length of route array can normally not be exceeded due to the max. hop_limit of 7 + if (updated->route_count < sizeof(updated->route)/sizeof(updated->route[0])) { + updated->route[updated->route_count] = myNodeInfo.my_node_num; + updated->route_count += 1; + } else { + DEBUG_MSG("WARNING: Route exceeded maximum hop limit, are you bridging networks?\n"); + } +} + + +void TraceRouteModule::printRoute(RouteDiscovery* r, uint32_t origin, uint32_t dest) +{ + DEBUG_MSG("Route traced:\n"); + DEBUG_MSG("0x%x --> ", origin); + for (uint8_t i=0; iroute_count; i++) { + DEBUG_MSG("0x%x --> ", r->route[i]); + } + if (dest != NODENUM_BROADCAST) DEBUG_MSG("0x%x\n", dest); else DEBUG_MSG("...\n"); +} + + +MeshPacket* TraceRouteModule::allocReply() +{ + assert(currentRequest); + + // Copy the payload of the current request + auto req = *currentRequest; + auto &p = req.decoded; + RouteDiscovery scratch; + RouteDiscovery *updated = NULL; + memset(&scratch, 0, sizeof(scratch)); + pb_decode_from_bytes(p.payload.bytes, p.payload.size, RouteDiscovery_fields, &scratch); + updated = &scratch; + + printRoute(updated, req.from, req.to); + + // Create a MeshPacket with this payload and set it as the reply + MeshPacket* reply = allocDataProtobuf(*updated); + + return reply; +} + + +TraceRouteModule::TraceRouteModule() : ProtobufModule("traceroute", PortNum_TRACEROUTE_APP, RouteDiscovery_fields) { + ourPortNum = PortNum_TRACEROUTE_APP; +} diff --git a/src/modules/TraceRouteModule.h b/src/modules/TraceRouteModule.h new file mode 100644 index 000000000..15b7a564d --- /dev/null +++ b/src/modules/TraceRouteModule.h @@ -0,0 +1,36 @@ +#pragma once +#include "ProtobufModule.h" + + +/** + * A module that traces the route to a certain destination node + */ +class TraceRouteModule : public ProtobufModule +{ + public: + TraceRouteModule(); + + // Let FloodingRouter call updateRoute upon rebroadcasting a TraceRoute request + friend class FloodingRouter; + + protected: + bool handleReceivedProtobuf(const MeshPacket &mp, RouteDiscovery *r) override; + + virtual MeshPacket *allocReply() override; + + /* Call before rebroadcasting a RouteDiscovery payload in order to update + the route array containing the IDs of nodes this packet went through */ + void updateRoute(MeshPacket* p); + + private: + // Call to add your ID to the route array of a RouteDiscovery message + void appendMyID(RouteDiscovery *r); + + /* Call to print the route array of a RouteDiscovery message. + Set origin to where the request came from. + Set dest to the ID of its destination, or NODENUM_BROADCAST if it has not yet arrived there. */ + void printRoute(RouteDiscovery* r, uint32_t origin, uint32_t dest); + +}; + +extern TraceRouteModule *traceRouteModule; \ No newline at end of file From aec091e7aaa8b91e23842218dae5b7ed8311f521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 6 Dec 2022 16:56:38 +0100 Subject: [PATCH 17/52] manual master merge # Conflicts: # src/Power.cpp --- src/modules/esp32/StoreForwardModule.cpp | 273 ++++++++++++----------- src/modules/esp32/StoreForwardModule.h | 14 +- 2 files changed, 146 insertions(+), 141 deletions(-) diff --git a/src/modules/esp32/StoreForwardModule.cpp b/src/modules/esp32/StoreForwardModule.cpp index e51296824..9d1c44c18 100644 --- a/src/modules/esp32/StoreForwardModule.cpp +++ b/src/modules/esp32/StoreForwardModule.cpp @@ -16,52 +16,50 @@ StoreForwardModule *storeForwardModule; int32_t StoreForwardModule::runOnce() { - #ifdef ARCH_ESP32 + if (moduleConfig.store_forward.enabled && is_server) { + // Send out the message queue. + if (this->busy) { + // Only send packets if the channel is less than 25% utilized. + if (airTime->channelUtilizationPercent() < polite_channel_util_percent) { - if (moduleConfig.store_forward.enabled) { + // DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index); + storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index); - if (config.device.role == Config_DeviceConfig_Role_ROUTER) { - - // Send out the message queue. - if (this->busy) { - - // Only send packets if the channel is less than 25% utilized. - if (airTime->channelUtilizationPercent() < polite_channel_util_percent) { - - // DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index); - storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index); - - if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) { - strcpy(this->routerMessage, "** S&F - Done"); - storeForwardModule->sendMessage(this->busyTo, this->routerMessage); - - // DEBUG_MSG("--- --- --- In busy loop - Done \n"); - this->packetHistoryTXQueue_index = 0; - this->busy = false; - } else { - this->packetHistoryTXQueue_index++; - } + if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) { + strcpy(this->routerMessage, "** S&F - Done"); + storeForwardModule->sendMessage(this->busyTo, this->routerMessage); + // DEBUG_MSG("--- --- --- In busy loop - Done \n"); + this->packetHistoryTXQueue_index = 0; + this->busy = false; } else { - DEBUG_MSG("Channel utilization is too high. Skipping this opportunity to send and will retry later.\n"); + this->packetHistoryTXQueue_index++; } + + } else { + DEBUG_MSG("Channel utilization is too high. Retrying later.\n"); } - DEBUG_MSG("SF myNodeInfo.bitrate = %f bytes / sec\n", myNodeInfo.bitrate); + DEBUG_MSG("SF bitrate = %f bytes / sec\n", myNodeInfo.bitrate); - return (this->packetTimeMax); - } else { - DEBUG_MSG("Store & Forward Module - Disabled (is_router = false)\n"); + } else if (millis() - lastHeartbeat > 300000) { + lastHeartbeat = millis(); + DEBUG_MSG("Sending heartbeat\n"); + + StoreAndForward sf; + sf.rr = StoreAndForward_RequestResponse_ROUTER_HEARTBEAT; + sf.has_heartbeat = true; + sf.heartbeat.period = 300; + sf.heartbeat.secondary = 0; // TODO we always have one primary router for now - return (INT32_MAX); + MeshPacket *p = allocDataProtobuf(sf); + p->to = NODENUM_BROADCAST; + p->decoded.want_response = false; + p->priority = MeshPacket_Priority_MIN; + service.sendToMesh(p); } - - } else { - DEBUG_MSG("Store & Forward Module - Disabled\n"); - - return (INT32_MAX); + return (this->packetTimeMax); } - #endif return (INT32_MAX); } @@ -76,12 +74,7 @@ void StoreForwardModule::populatePSRAM() https://learn.upesy.com/en/programmation/psram.html#psram-tab */ - DEBUG_MSG("Before PSRAM initilization:\n"); - - DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize()); - DEBUG_MSG(" Free heap: %d\n", ESP.getFreeHeap()); - DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize()); - DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram()); + 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))); @@ -93,19 +86,12 @@ void StoreForwardModule::populatePSRAM() this->packetHistory = static_cast(ps_calloc(numberOfPackets, sizeof(PacketHistoryStruct))); - DEBUG_MSG("After PSRAM initilization:\n"); - - DEBUG_MSG(" Total heap: %d\n", ESP.getHeapSize()); - DEBUG_MSG(" Free heap: %d\n", ESP.getFreeHeap()); - DEBUG_MSG(" Total PSRAM: %d\n", ESP.getPsramSize()); - DEBUG_MSG(" Free PSRAM: %d\n", ESP.getFreePsram()); - DEBUG_MSG("Store and Forward Stats:\n"); - 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("Iterating through the message history...\n"); DEBUG_MSG("Message history contains %u records\n", this->packetHistoryCurrent); } @@ -246,8 +232,8 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp) DEBUG_MSG("--- S&F Received something\n"); - // The router node should not be sending messages as a client. - if (getFrom(&mp) != nodeDB.getNodeNum()) { + // 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"); @@ -264,6 +250,7 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp) } 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, @@ -278,14 +265,12 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp) } } 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); } } - - } else { - DEBUG_MSG("Store & Forward Module - Disabled\n"); } #endif @@ -293,92 +278,107 @@ ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp) return ProcessMessage::CONTINUE; // Let others look at this message also if they want } -ProcessMessage StoreForwardModule::handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p) +bool StoreForwardModule::handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p) { if (!moduleConfig.store_forward.enabled) { // If this module is not enabled in any capacity, don't handle the packet, and allow other modules to consume - return ProcessMessage::CONTINUE; + return false; } - if (mp.decoded.portnum == PortNum_TEXT_MESSAGE_APP) { - DEBUG_MSG("Packet came from an PortNum_TEXT_MESSAGE_APP port %u\n", mp.decoded.portnum); - return ProcessMessage::CONTINUE; - } 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); - + 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 an UNKNOWN port %u\n", mp.decoded.portnum); - return ProcessMessage::CONTINUE; - } + DEBUG_MSG("Packet came from PortNum_STORE_FORWARD_APP port %u\n", mp.decoded.portnum); + switch (p->rr) { - case StoreAndForward_RequestResponse_CLIENT_ERROR: - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_ERROR\n"); - break; + case StoreAndForward_RequestResponse_CLIENT_ERROR: + if(is_server) { + // Do nothing + DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_ERROR\n"); + } + break; - case StoreAndForward_RequestResponse_CLIENT_HISTORY: - DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_HISTORY\n"); + case StoreAndForward_RequestResponse_CLIENT_HISTORY: + if(is_server) { + DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_HISTORY\n"); + // Send the last 60 minutes of messages. + if (this->busy) { + strcpy(this->routerMessage, "** S&F - Busy. Try again shortly."); + storeForwardModule->sendMessage(getFrom(&mp), this->routerMessage); + } else { + storeForwardModule->historySend(1000 * 60, getFrom(&mp)); + } + } + break; - // Send the last 60 minutes of messages. - 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)); + case StoreAndForward_RequestResponse_CLIENT_PING: + if(is_server) { + // Do nothing + DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PING\n"); + } + break; + + case StoreAndForward_RequestResponse_CLIENT_PONG: + if(is_server) { + // Do nothing + DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PONG\n"); + } + break; + + case StoreAndForward_RequestResponse_CLIENT_STATS: + if(is_server) { + // Do nothing + DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_STATS\n"); + } + break; + + case StoreAndForward_RequestResponse_ROUTER_BUSY: + if(is_client) { + // Do nothing + DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_BUSY\n"); + } + break; + + case StoreAndForward_RequestResponse_ROUTER_ERROR: + if(is_client) { + // Do nothing + DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_ERROR\n"); + } + break; + + case StoreAndForward_RequestResponse_ROUTER_HEARTBEAT: + if(is_client) { + // Do nothing + DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_HEARTBEAT\n"); + } + break; + + case StoreAndForward_RequestResponse_ROUTER_PING: + if(is_client) { + // Do nothing + DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PING\n"); + } + break; + + case StoreAndForward_RequestResponse_ROUTER_PONG: + if(is_client) { + // Do nothing + DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PONG\n"); + } + break; + + default: + assert(0); // unexpected state - FIXME, make an error code and reboot } - - break; - - case StoreAndForward_RequestResponse_CLIENT_PING: - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PING\n"); - break; - - case StoreAndForward_RequestResponse_CLIENT_PONG: - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_PONG\n"); - break; - - case StoreAndForward_RequestResponse_CLIENT_STATS: - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_CLIENT_STATS\n"); - break; - - case StoreAndForward_RequestResponse_ROUTER_BUSY: - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_BUSY\n"); - break; - - case StoreAndForward_RequestResponse_ROUTER_ERROR: - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_ERROR\n"); - break; - - case StoreAndForward_RequestResponse_ROUTER_HEARTBEAT: - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_HEARTBEAT\n"); - break; - - case StoreAndForward_RequestResponse_ROUTER_PING: - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PING\n"); - break; - - case StoreAndForward_RequestResponse_ROUTER_PONG: - // Do nothing - DEBUG_MSG("StoreAndForward_RequestResponse_ROUTER_PONG\n"); - break; - - default: - assert(0); // unexpected state - FIXME, make an error code and reboot } - return ProcessMessage::STOP; // There's no need for others to look at this message. + return true; // There's no need for others to look at this message. } StoreForwardModule::StoreForwardModule() - : SinglePortModule("StoreForwardModule", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("StoreForwardModule") + : concurrency::OSThread("StoreForwardModule"), ProtobufModule("StoreForward", PortNum_STORE_FORWARD_APP, &StoreAndForward_msg) { #ifdef ARCH_ESP32 @@ -397,9 +397,9 @@ StoreForwardModule::StoreForwardModule() if (moduleConfig.store_forward.enabled) { // Router - if (config.device.role == Config_DeviceConfig_Role_ROUTER) { - DEBUG_MSG("Initializing Store & Forward Module - Enabled as Router\n"); - if (ESP.getPsramSize()) { + 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"); + if (ESP.getPsramSize() > 0) { if (ESP.getFreePsram() >= 1024 * 1024) { // Do the startup here @@ -416,26 +416,27 @@ StoreForwardModule::StoreForwardModule() if (moduleConfig.store_forward.records) this->records = moduleConfig.store_forward.records; - // Maximum number of records to store in memory + // send heartbeat advertising? if (moduleConfig.store_forward.heartbeat) this->heartbeat = moduleConfig.store_forward.heartbeat; // Popupate PSRAM with our data structures. this->populatePSRAM(); - + is_server = true; } else { - DEBUG_MSG("Device has less than 1M of PSRAM free. Aborting startup.\n"); - DEBUG_MSG("Store & Forward Module - Aborting Startup.\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 - Aborting Startup.\n"); + DEBUG_MSG("Store & Forward Module - disabling server.\n"); } // Client - } else { - DEBUG_MSG("Initializing Store & Forward Module - Enabled as Client\n"); + } + if ((config.device.role == Config_DeviceConfig_Role_CLIENT) || (config.device.role == Config_DeviceConfig_Role_ROUTER_CLIENT)) { + is_client = true; + DEBUG_MSG("Initializing Store & Forward Module in Client mode\n"); } } #endif diff --git a/src/modules/esp32/StoreForwardModule.h b/src/modules/esp32/StoreForwardModule.h index 2c6d6ec6e..32d3cddc9 100644 --- a/src/modules/esp32/StoreForwardModule.h +++ b/src/modules/esp32/StoreForwardModule.h @@ -1,6 +1,6 @@ #pragma once -#include "SinglePortModule.h" +#include "ProtobufModule.h" #include "concurrency/OSThread.h" #include "mesh/generated/storeforward.pb.h" @@ -18,9 +18,8 @@ struct PacketHistoryStruct { pb_size_t payload_size; }; -class StoreForwardModule : public SinglePortModule, private concurrency::OSThread +class StoreForwardModule : private concurrency::OSThread, public ProtobufModule { - // bool firstTime = 1; bool busy = 0; uint32_t busyTo = 0; char routerMessage[Constants_DATA_PAYLOAD_LEN] = {0}; @@ -34,7 +33,12 @@ class StoreForwardModule : public SinglePortModule, private concurrency::OSThrea uint32_t packetHistoryTXQueue_size = 0; uint32_t packetHistoryTXQueue_index = 0; - uint32_t packetTimeMax = 2000; + uint32_t packetTimeMax = 5000; + + unsigned long lastHeartbeat = 0; + + bool is_client = false; + bool is_server = false; public: StoreForwardModule(); @@ -78,7 +82,7 @@ class StoreForwardModule : public SinglePortModule, private concurrency::OSThrea it */ virtual ProcessMessage handleReceived(const MeshPacket &mp) override; - virtual ProcessMessage handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p); + virtual bool handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p); }; From 706ddf6e95ffd35e91a72af182fc4cda609b59ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Thu, 8 Dec 2022 17:17:48 +0100 Subject: [PATCH 18/52] show appropriate message when going into OTA mode --- src/graphics/Screen.cpp | 6 +----- src/modules/AdminModule.cpp | 3 +++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index eac78dd64..b5a3ac4d5 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -329,11 +329,7 @@ static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, i display->drawString(64 + x, y, "Updating"); display->setFont(FONT_SMALL); - if ((millis() / 1000) % 2) { - display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . ."); - } else { - display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . "); - } + display->drawStringMaxWidth(0 + x, 2 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), "Please be patient and do not power off."); } /// Draw the last text message we received diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 3ce8731e7..2639e0594 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -112,12 +112,15 @@ bool AdminModule::handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *r) #ifdef ARCH_ESP32 if (BleOta::getOtaAppVersion().isEmpty()) { DEBUG_MSG("No OTA firmware available, scheduling regular reboot in %d seconds\n", s); + screen->startRebootScreen(); }else{ + screen->startFirmwareUpdateScreen(); BleOta::switchToOtaApp(); DEBUG_MSG("Rebooting to OTA in %d seconds\n", s); } #else DEBUG_MSG("Not on ESP32, scheduling regular reboot in %d seconds\n", s); + screen->startRebootScreen(); #endif rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000); break; From c75ea87f6bff7e700d3b2b6738d67c7ff45dc035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Thu, 8 Dec 2022 17:34:14 +0100 Subject: [PATCH 19/52] Format received message screen like sender in canned messages. --- src/graphics/Screen.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index b5a3ac4d5..7919e9891 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -360,6 +360,9 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state { displayedNodeNum = 0; // Not currently showing a node pane + // the max length of this buffer is much longer than we can possibly print + static char tempBuf[237]; + MeshPacket &mp = devicestate.rx_text_message; NodeInfo *node = nodeDB.getNode(getFrom(&mp)); // DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from, @@ -369,16 +372,13 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state // with the third parameter you can define the width after which words will // be wrapped. Currently only spaces and "-" are allowed for wrapping display->setTextAlignment(TEXT_ALIGN_LEFT); - display->setFont(FONT_MEDIUM); - String sender = (node && node->has_user) ? node->user.short_name : "???"; - display->drawString(0 + x, 0 + y, sender); display->setFont(FONT_SMALL); - - // the max length of this buffer is much longer than we can possibly print - static char tempBuf[96]; - snprintf(tempBuf, sizeof(tempBuf), " %s", mp.decoded.payload.bytes); - - display->drawStringMaxWidth(4 + x, 10 + y, SCREEN_WIDTH - (6 + x), tempBuf); + display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); + display->setColor(BLACK); + display->drawStringf(0 + x, 0 + y, tempBuf, "From: %s", (node && node->has_user) ? node->user.short_name : "???"); + display->setColor(WHITE); + snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.payload.bytes); + display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf); } /// Draw a series of fields in a column, wrapping to multiple colums if needed From d9cd3dd3e1c976cb7f1826b6ee6674e7fd75529a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Thu, 8 Dec 2022 21:48:01 +0100 Subject: [PATCH 20/52] Change Boot Message format --- src/graphics/Screen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 7919e9891..abd18d442 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -329,7 +329,8 @@ static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, i display->drawString(64 + x, y, "Updating"); display->setFont(FONT_SMALL); - display->drawStringMaxWidth(0 + x, 2 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), "Please be patient and do not power off."); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawStringMaxWidth(0 + x, 2 + y + FONT_HEIGHT_SMALL *2, x + display->getWidth(), "Please be patient and do not power off."); } /// Draw the last text message we received From fab08b645171a20f29872323005872a3287b711b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Fri, 9 Dec 2022 11:27:12 +0100 Subject: [PATCH 21/52] fix building for screenless devices --- src/graphics/Screen.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 23828b3ee..3988fa1a8 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -21,6 +21,7 @@ class Screen void startBluetoothPinScreen(uint32_t pin) {} void stopBluetoothPinScreen() {} void startRebootScreen() {} + void startFirmwareUpdateScreen() {} }; } From 44a33ed463fee088d1ba3366e8815a26fc5d1093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sun, 11 Dec 2022 21:17:46 +0100 Subject: [PATCH 22/52] add IO7 to RAK pinouts - only comments changed --- variants/rak4631/variant.h | 1 + variants/rak4631_epaper/variant.h | 1 + 2 files changed, 2 insertions(+) diff --git a/variants/rak4631/variant.h b/variants/rak4631/variant.h index 0c1e15f4b..7b07c9aff 100644 --- a/variants/rak4631/variant.h +++ b/variants/rak4631/variant.h @@ -169,6 +169,7 @@ static const uint8_t SCK = PIN_SPI_SCK; IO4 <-> P0.04 (Arduino GPIO number 4) IO5 <-> P0.09 (Arduino GPIO number 9) IO6 <-> P0.10 (Arduino GPIO number 10) + IO7 <-> P0.28 (Arduino GPIO number 28) SW1 <-> P0.01 (Arduino GPIO number 1) A0 <-> P0.04/AIN2 (Arduino Analog A2 A1 <-> P0.31/AIN7 (Arduino Analog A7 diff --git a/variants/rak4631_epaper/variant.h b/variants/rak4631_epaper/variant.h index 2b626cf91..890f00ddd 100644 --- a/variants/rak4631_epaper/variant.h +++ b/variants/rak4631_epaper/variant.h @@ -169,6 +169,7 @@ static const uint8_t SCK = PIN_SPI_SCK; IO4 <-> P0.04 (Arduino GPIO number 4) IO5 <-> P0.09 (Arduino GPIO number 9) IO6 <-> P0.10 (Arduino GPIO number 10) + IO7 <-> P0.28 (Arduino GPIO number 28) SW1 <-> P0.01 (Arduino GPIO number 1) A0 <-> P0.04/AIN2 (Arduino Analog A2 A1 <-> P0.31/AIN7 (Arduino Analog A7 From ab6a5a5e07963548525afb98d6ac9957e47bde73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sun, 11 Dec 2022 23:12:00 +0100 Subject: [PATCH 23/52] if we get different frames than our own transmission setup, decode and play them anyway --- src/modules/esp32/AudioModule.cpp | 32 +++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 4e784e1a9..f29c11056 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -47,7 +47,7 @@ int Sine1KHz_index = 0; void run_codec2(void* parameter) { - // 4 bytes of header in each frame Kennung hex c0 de c2 plus the bitrate + // 4 bytes of header in each frame hex c0 de c2 plus the bitrate memcpy(audioModule->tx_encode_frame,&audioModule->tx_header,sizeof(audioModule->tx_header)); while (true) { @@ -76,13 +76,29 @@ void run_codec2(void* parameter) } } if (audioModule->radio_state == RadioState::rx) { - //Make a cycle to get each codec2 frame from the received frame - for (int i = 0; i < audioModule->rx_encode_frame_index; i += audioModule->encode_codec_size) - { - //Decode the codec2 frame - codec2_decode(audioModule->codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i); - size_t bytesOut = 0; - i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500)); + size_t bytesOut = 0; + if (memcmp(audioModule->rx_encode_frame, &audioModule->tx_header, sizeof(audioModule->tx_header)) == 0) { + // Make a cycle to get each codec2 frame from the received frame + for (int i = 4; i < audioModule->rx_encode_frame_index; i += audioModule->encode_codec_size) + { + //Decode the codec2 frame + codec2_decode(audioModule->codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i); + i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500)); + } + } else { + // if the buffer header does not match our own codec, make a temp decoding setup. + CODEC2* tmp_codec2 = codec2_create(audioModule->rx_encode_frame[3]); + codec2_set_lpc_post_filter(tmp_codec2, 1, 0, 0.8, 0.2); + int tmp_encode_codec_size = (codec2_bits_per_frame(tmp_codec2) + 7) / 8; + int tmp_adc_buffer_size = codec2_samples_per_frame(tmp_codec2); + // Make a cycle to get each codec2 frame from the received frame + for (int i = 4; i < audioModule->rx_encode_frame_index; i += tmp_encode_codec_size) + { + //Decode the codec2 frame + codec2_decode(tmp_codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i); + i2s_write(I2S_PORT, &audioModule->output_buffer, tmp_adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500)); + } + codec2_destroy(tmp_codec2); } } } From 0f2d0d1f0745135ee9bd71339e0809f3ce59c0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 13 Dec 2022 12:33:51 +0100 Subject: [PATCH 24/52] change on screen graphics to support bicolor screens --- src/graphics/Screen.cpp | 46 +++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index abd18d442..6f4c1fb58 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -377,6 +377,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); display->setColor(BLACK); display->drawStringf(0 + x, 0 + y, tempBuf, "From: %s", (node && node->has_user) ? node->user.short_name : "???"); + display->drawStringf(1 + x, 0 + y, tempBuf, "From: %s", (node && node->has_user) ? node->user.short_name : "???"); display->setColor(WHITE); snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.payload.bytes); display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf); @@ -392,6 +393,10 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char * int xo = x, yo = y; while (*f) { display->drawString(xo, yo, *f); + if (display->getColor() == BLACK) + display->drawString(xo + 1, yo, *f); + + display->setColor(WHITE); yo += FONT_HEIGHT_SMALL; if (yo > SCREEN_HEIGHT - FONT_HEIGHT_SMALL) { xo += SCREEN_WIDTH / 2; @@ -462,6 +467,7 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *no sprintf(usersString, "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal()); display->drawFastImage(x, y, 8, 8, imgUser); display->drawString(x + 10, y - 2, usersString); + display->drawString(x + 11, y - 2, usersString); } // Draw GPS status summary @@ -470,15 +476,18 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus if (config.position.fixed_position) { // GPS coordinates are currently fixed display->drawString(x - 1, y - 2, "Fixed GPS"); + display->drawString(x, y - 2, "Fixed GPS"); return; } if (!gps->getIsConnected()) { display->drawString(x, y - 2, "No GPS"); + display->drawString(x + 1, y - 2, "No GPS"); return; } display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty); if (!gps->getHasLock()) { display->drawString(x + 8, y - 2, "No sats"); + display->drawString(x + 9, y - 2, "No sats"); return; } else { char satsString[3]; @@ -666,16 +675,16 @@ static uint16_t getCompassDiam(OLEDDisplay *display) { uint16_t diam = 0; // get the smaller of the 2 dimensions and subtract 20 - if(display->getWidth() > display->getHeight()) { - diam = display->getHeight(); + if(display->getWidth() > (display->getHeight() - FONT_HEIGHT_SMALL)) { + diam = display->getHeight() - FONT_HEIGHT_SMALL; // if 2/3 of the other size would be smaller, use that if (diam > (display->getWidth() * 2 / 3)) { diam = display->getWidth() * 2 / 3; } } else { diam = display->getWidth(); - if (diam > (display->getHeight() * 2 / 3)) { - diam = display->getHeight() * 2 / 3; + if (diam > ((display->getHeight() - FONT_HEIGHT_SMALL) * 2 / 3)) { + diam = (display->getHeight() - FONT_HEIGHT_SMALL) * 2 / 3; } } @@ -755,6 +764,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ // The coordinates define the left starting point of the text display->setTextAlignment(TEXT_ALIGN_LEFT); + display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); + const char *username = node->has_user ? node->user.long_name : "Unknown Name"; static char signalStr[20]; @@ -785,7 +796,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ const char *fields[] = {username, distStr, signalStr, lastStr, NULL}; // coordinates for the center of the compass/circle - int16_t compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5, compassY = y + SCREEN_HEIGHT / 2; + int16_t compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5, compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - FONT_HEIGHT_SMALL) / 2; bool hasNodeHeading = false; if (ourNode && hasPosition(ourNode)) { @@ -828,6 +839,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?"); display->drawCircle(compassX, compassY, getCompassDiam(display) / 2); + display->setColor(BLACK); // Must be after distStr is populated drawColumns(display, x, y, fields); } @@ -1354,6 +1366,9 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 // The coordinates define the left starting point of the text display->setTextAlignment(TEXT_ALIGN_LEFT); + display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); + display->setColor(BLACK); + char channelStr[20]; { concurrency::LockGuard guard(&lock); @@ -1363,14 +1378,15 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 // Display power status if (powerStatus->getHasBattery()) - drawBattery(display, x, y + 2, imgBattery, powerStatus); + drawBattery(display, x + 1, y + 3, imgBattery, powerStatus); else if (powerStatus->knowsUSB()) - display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower); + display->drawFastImage(x + 1, y + 3, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower); // Display nodes status - drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus); + drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 3, nodeStatus); // Display GPS status - drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus); + drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 3, gpsStatus); + display->setColor(WHITE); // Draw the channel name display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr); // Draw our hardware ID to assist with bluetooth pairing @@ -1401,6 +1417,10 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i // The coordinates define the left starting point of the text display->setTextAlignment(TEXT_ALIGN_LEFT); + display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); + display->setColor(BLACK); + display->setColor(WHITE); + if (WiFi.status() != WL_CONNECTED) { display->drawString(x, y, String("WiFi: Not Connected")); } else { @@ -1518,6 +1538,9 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat // The coordinates define the left starting point of the text display->setTextAlignment(TEXT_ALIGN_LEFT); + display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); + display->setColor(BLACK); + char batStr[20]; if (powerStatus->getHasBattery()) { int batV = powerStatus->getBatteryVoltageMv() / 1000; @@ -1528,9 +1551,11 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat // Line 1 display->drawString(x, y, batStr); + display->drawString(x + 1, y, batStr); } else { // Line 1 display->drawString(x, y, String("USB")); + display->drawString(x + 1, y, String("USB")); } auto mode = ""; @@ -1563,6 +1588,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat } display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode); + display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode); // Line 2 uint32_t currentMillis = millis(); @@ -1575,6 +1601,8 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat // minutes %= 60; // hours %= 24; + display->setColor(WHITE); + // Show uptime as days, hours, minutes OR seconds String uptime; if (days >= 2) From 86d7860d8642300c503af6425ac9ce55554d30d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 13 Dec 2022 17:31:01 +0100 Subject: [PATCH 25/52] made a nice PTT/RECV screen for audio module. And cleaned up screen graphics a bit. --- src/graphics/Screen.cpp | 7 +++- src/graphics/images.h | 30 --------------- src/graphics/img/compass.xbm | 28 -------------- src/graphics/img/pin.xbm | 6 --- src/modules/esp32/AudioModule.cpp | 62 +++++++++++++++++++++++++++++-- src/modules/esp32/AudioModule.h | 15 +++++++- 6 files changed, 78 insertions(+), 70 deletions(-) delete mode 100644 src/graphics/img/compass.xbm delete mode 100644 src/graphics/img/pin.xbm diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 6f4c1fb58..f1c3fdccc 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1419,17 +1419,22 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); display->setColor(BLACK); - display->setColor(WHITE); if (WiFi.status() != WL_CONNECTED) { display->drawString(x, y, String("WiFi: Not Connected")); + display->drawString(x + 1, y, String("WiFi: Not Connected")); } else { display->drawString(x, y, String("WiFi: Connected")); + display->drawString(x + 1, y, String("WiFi: Connected")); display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())), y, "RSSI " + String(WiFi.RSSI())); + display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())) - 1, y, + "RSSI " + String(WiFi.RSSI())); } + display->setColor(WHITE); + /* - WL_CONNECTED: assigned when connected to a WiFi network; - WL_NO_SSID_AVAIL: assigned when no SSID are available; diff --git a/src/graphics/images.h b/src/graphics/images.h index 41b390eaf..4680b9475 100644 --- a/src/graphics/images.h +++ b/src/graphics/images.h @@ -15,33 +15,3 @@ const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF const uint8_t imgInfo[] PROGMEM = { 0xFF, 0x81, 0x81, 0xB5, 0xB5, 0x81, 0x81, 0xFF }; #include "img/icon.xbm" - -// We now programmatically draw our compass -#if 0 -const -#include "img/compass.xbm" -#endif - -#if 0 -const uint8_t activeSymbol[] PROGMEM = { - B00000000, - B00000000, - B00011000, - B00100100, - B01000010, - B01000010, - B00100100, - B00011000 -}; - -const uint8_t inactiveSymbol[] PROGMEM = { - B00000000, - B00000000, - B00000000, - B00000000, - B00011000, - B00011000, - B00000000, - B00000000 -}; -#endif \ No newline at end of file diff --git a/src/graphics/img/compass.xbm b/src/graphics/img/compass.xbm deleted file mode 100644 index 115a7a1d4..000000000 --- a/src/graphics/img/compass.xbm +++ /dev/null @@ -1,28 +0,0 @@ -#define compass_width 48 -#define compass_height 48 -static char compass_bits[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, - 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, - 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x01, 0x00, - 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0x00, - 0x00, 0xF8, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0xFC, 0x07, 0xE0, 0x3F, 0x00, - 0x00, 0xFE, 0x01, 0x80, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, - 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00, 0x80, 0x3F, 0x00, 0x00, 0xFC, 0x01, - 0x80, 0x1F, 0x00, 0x00, 0xF8, 0x01, 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x01, - 0xC0, 0x0F, 0x00, 0x00, 0xF0, 0x03, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, - 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, - 0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F, 0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F, - 0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F, 0xFC, 0x07, 0x00, 0x00, 0xE0, 0x3F, - 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, - 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x03, 0xC0, 0x0F, 0x00, 0x00, 0xF0, 0x03, - 0x80, 0x0F, 0x00, 0x00, 0xF0, 0x01, 0x80, 0x1F, 0x00, 0x00, 0xF8, 0x01, - 0x80, 0x3F, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x00, - 0x00, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, 0xFE, 0x01, 0x80, 0x7F, 0x00, - 0x00, 0xFC, 0x07, 0xE0, 0x3F, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0x1F, 0x00, - 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0xC0, 0xFF, 0xFF, 0x03, 0x00, - 0x00, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x00, 0x00, - 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, - 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }; diff --git a/src/graphics/img/pin.xbm b/src/graphics/img/pin.xbm deleted file mode 100644 index e8c8f8930..000000000 --- a/src/graphics/img/pin.xbm +++ /dev/null @@ -1,6 +0,0 @@ -#define pin_width 13 -#define pin_height 13 -static char pin_bits[] = { - 0x00, 0x00, 0xF0, 0x01, 0xF8, 0x03, 0xFC, 0x07, 0xBC, 0x07, 0xBC, 0x07, - 0xFC, 0x07, 0xF8, 0x03, 0xF8, 0x03, 0xF0, 0x01, 0xE0, 0x00, 0xE0, 0x00, - 0x00, 0x00, }; diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index f29c11056..75e329ce5 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -41,9 +41,26 @@ AudioModule *audioModule; #define YIELD_FROM_ISR(x) portYIELD_FROM_ISR(x) #endif -//int16_t 1KHz sine test tone -int16_t Sine1KHz[8] = { -21210 , -30000, -21210, 0 , 21210 , 30000 , 21210, 0 }; -int Sine1KHz_index = 0; +#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) +// The screen is bigger so use bigger fonts +#define FONT_SMALL ArialMT_Plain_16 +#define FONT_MEDIUM ArialMT_Plain_24 +#define FONT_LARGE ArialMT_Plain_24 +#else +#ifdef OLED_RU +#define FONT_SMALL ArialMT_Plain_10_RU +#else +#define FONT_SMALL ArialMT_Plain_10 +#endif +#define FONT_MEDIUM ArialMT_Plain_16 +#define FONT_LARGE ArialMT_Plain_24 +#endif + +#define fontHeight(font) ((font)[1] + 1) // height is position 1 + +#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL) +#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM) +#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE) void run_codec2(void* parameter) { @@ -124,6 +141,30 @@ AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), } } +void AudioModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + displayedNodeNum = 0; // Not currently showing a node pane + + char buffer[50]; + + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_SMALL); + display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); + display->setColor(BLACK); + display->drawStringf(0 + x, 0 + y, buffer, "Codec2 Mode %d Audio", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); + display->setColor(WHITE); + display->setFont(FONT_LARGE); + display->setTextAlignment(TEXT_ALIGN_CENTER); + switch (radio_state) { + case RadioState::tx: + display->drawString(display->getWidth() / 2 + x, (display->getHeight() - FONT_HEIGHT_SMALL) / 2 + y, "PTT"); + break; + default: + display->drawString(display->getWidth() / 2 + x, (display->getHeight() - FONT_HEIGHT_SMALL) / 2 + y, "Receive"); + break; + } +} + int32_t AudioModule::runOnce() { if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { @@ -170,11 +211,14 @@ int32_t AudioModule::runOnce() firstTime = false; } else { + UIFrameEvent e = {false, true}; // Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive. if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) { if (radio_state == RadioState::rx) { DEBUG_MSG("♪♫♪ PTT pressed, switching to TX\n"); radio_state = RadioState::tx; + e.frameChanged = true; + this->notifyObservers(&e); } } else { if (radio_state == RadioState::tx) { @@ -183,9 +227,11 @@ int32_t AudioModule::runOnce() DEBUG_MSG("♪♫♪ Sending %d codec2 bytes (incomplete)\n", tx_encode_frame_index); sendPayload(); } - DEBUG_MSG("♪♫♪ PTT released, switching to RX\n"); + DEBUG_MSG("♪♫♪ PTT released, switching to RX\n"); tx_encode_frame_index = sizeof(tx_header); radio_state = RadioState::rx; + e.frameChanged = true; + this->notifyObservers(&e); } } if (radio_state == RadioState::tx) { @@ -222,6 +268,14 @@ MeshPacket *AudioModule::allocReply() return reply; } +bool AudioModule::shouldDraw() +{ + if (!moduleConfig.audio.codec2_enabled) { + return false; + } + return (radio_state == RadioState::tx); +} + void AudioModule::sendPayload(NodeNum dest, bool wantReplies) { MeshPacket *p = allocReply(); diff --git a/src/modules/esp32/AudioModule.h b/src/modules/esp32/AudioModule.h index 8e813ad1e..1a41ab6c4 100644 --- a/src/modules/esp32/AudioModule.h +++ b/src/modules/esp32/AudioModule.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include enum RadioState { standby, rx, tx }; @@ -28,7 +30,7 @@ struct c2_header { #define AUDIO_MODULE_RX_BUFFER 128 #define AUDIO_MODULE_MODE ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 -class AudioModule : public SinglePortModule, private concurrency::OSThread +class AudioModule : public SinglePortModule, public Observable, private concurrency::OSThread { public: unsigned char rx_encode_frame[Constants_DATA_PAYLOAD_LEN] = {}; @@ -50,6 +52,8 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread AudioModule(); + bool shouldDraw(); + /** * Send our payload into the mesh */ @@ -63,6 +67,15 @@ class AudioModule : public SinglePortModule, private concurrency::OSThread virtual MeshPacket *allocReply() override; + virtual bool wantUIFrame() override { return this->shouldDraw(); } + virtual Observable* getUIFrameObservable() override { return this; } +#if !HAS_SCREEN + void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); +#else + virtual void drawFrame( + OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override; +#endif + /** Called to handle a particular incoming message * @return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for it */ From 055146602abd8531be5ac352cec487caff814902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 14 Dec 2022 13:32:26 +0100 Subject: [PATCH 26/52] support ESP32-S2 CPUs Note: these don't have Bluetooth and only a single physical core. --- arch/esp32/esp32s2.ini | 47 +++++++++++++++++++++++++++++++ src/modules/SerialModule.cpp | 4 +-- src/platform/esp32/main-esp32.cpp | 17 +++++++---- src/sleep.cpp | 2 ++ 4 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 arch/esp32/esp32s2.ini diff --git a/arch/esp32/esp32s2.ini b/arch/esp32/esp32s2.ini new file mode 100644 index 000000000..ca4f576d6 --- /dev/null +++ b/arch/esp32/esp32s2.ini @@ -0,0 +1,47 @@ +[esp32s2_base] +extends = arduino_base +platform = platformio/espressif32@^5.2.0 +build_src_filter = + ${arduino_base.build_src_filter} - - - - - +upload_speed = 961200 +monitor_speed = 115200 +debug_init_break = tbreak setup +monitor_filters = esp32_exception_decoder +board_build.filesystem = littlefs + +# Remove -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL for low level BLE logging. +# See library directory for BLE logging possible values: .pio/libdeps/tbeam/NimBLE-Arduino/src/log_common/log_common.h +# This overrides the BLE logging default of LOG_LEVEL_INFO (1) from: .pio/libdeps/tbeam/NimBLE-Arduino/src/esp_nimble_cfg.h +build_flags = + ${arduino_base.build_flags} + -Wall + -Wextra + -Isrc/platform/esp32 + -std=c++11 + -DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG + -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG + -DMYNEWT_VAL_BLE_HS_LOG_LVL=LOG_LEVEL_CRITICAL + -DAXP_DEBUG_PORT=Serial + -DCONFIG_BT_NIMBLE_ENABLED + -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 + -DCONFIG_BT_NIMBLE_MAX_CCCDS=20 + -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING + -DHAS_BLUETOOTH=0 + -DDEBUG_HEAP + +lib_deps = + ${arduino_base.lib_deps} + ${networking_base.lib_deps} + ${environmental_base.lib_deps} + https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2 + https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 + caveman99/ESP32 Codec2@^1.0.1 + +lib_ignore = + segger_rtt + ESP32 BLE Arduino + +; customize the partition table +; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables +board_build.partitions = partition-table.csv + diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index 116690d74..c25f69fa4 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -82,7 +82,7 @@ SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio") int32_t SerialModule::runOnce() { -#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) +#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2) /* Uncomment the preferences below if you want to use the module without having to configure it from the PythonAPI or WebUI. @@ -239,7 +239,7 @@ void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies) ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp) { -#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) +#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(TTGO_T_ECHO) && !defined(CONFIG_IDF_TARGET_ESP32S2) if (moduleConfig.serial.enabled) { auto &p = mp.decoded; diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp index 21df018d8..e36a8cfd8 100644 --- a/src/platform/esp32/main-esp32.cpp +++ b/src/platform/esp32/main-esp32.cpp @@ -3,7 +3,9 @@ #include "esp_task_wdt.h" #include "main.h" +#if !defined(CONFIG_IDF_TARGET_ESP32S2) #include "nimble/NimbleBluetooth.h" +#endif #include "BleOta.h" #include "mesh/http/WiFiAPClient.h" @@ -16,13 +18,9 @@ #include #include "soc/rtc.h" +#if !defined(CONFIG_IDF_TARGET_ESP32S2) NimbleBluetooth *nimbleBluetooth; -void getMacAddr(uint8_t *dmac) -{ - assert(esp_efuse_mac_get_default(dmac) == ESP_OK); -} - void setBluetoothEnable(bool on) { if (!isWifiAvailable() && config.bluetooth.enabled == true) { @@ -36,6 +34,15 @@ void setBluetoothEnable(bool on) { } } } +#else +void setBluetoothEnable(bool on) { } +void updateBatteryLevel(uint8_t level) { } +#endif + +void getMacAddr(uint8_t *dmac) +{ + assert(esp_efuse_mac_get_default(dmac) == ESP_OK); +} #ifdef HAS_32768HZ #define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk) diff --git a/src/sleep.cpp b/src/sleep.cpp index 39ee43ebc..6625da2a3 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -300,6 +300,8 @@ void enableModemSleep() #if CONFIG_IDF_TARGET_ESP32S3 esp32_config.max_freq_mhz = CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ; +#elif CONFIG_IDF_TARGET_ESP32S2 + esp32_config.max_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ; #else esp32_config.max_freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; #endif From 9794995d7ad645ee52ca30781dc0e9c0f0945f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 14 Dec 2022 14:33:59 +0100 Subject: [PATCH 27/52] fix building DIY-1 target --- src/modules/esp32/AudioModule.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 75e329ce5..17e5cf4fa 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -10,6 +10,10 @@ #include +#ifdef OLED_RU +#include "graphics/fonts/OLEDDisplayFontsRU.h" +#endif + /* AudioModule A interface to send raw codec2 audio data over the mesh network. Based on the example code from the ESP32_codec2 project. From 4eb620d47b51460bcfd2e371afbd8957573a9842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Fri, 16 Dec 2022 20:25:51 +0100 Subject: [PATCH 28/52] Heap Debug: only show if delta occurs --- src/Power.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Power.cpp b/src/Power.cpp index be44c0d49..d5fe312e9 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -284,7 +284,10 @@ void Power::readPowerStatus() powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); newStatus.notifyObservers(&powerStatus2); #ifdef DEBUG_HEAP - DEBUG_MSG("Heap status: %d/%d bytes free, running %d threads\n", ESP.getFreeHeap(), ESP.getHeapSize(), concurrency::mainController.size(false)); + if (lastheap != ESP.getFreeHeap()){ + DEBUG_MSG("Heap status: %d/%d bytes free (%d), running %d threads\n", ESP.getFreeHeap(), ESP.getHeapSize(), ESP.getFreeHeap() - lastheap , concurrency::mainController.size(false)); + lastheap = ESP.getFreeHeap(); + } #endif // If we have a battery at all and it is less than 10% full, force deep sleep if we have more than 3 low readings in a row From 941786669ba0c8ade6b91e031c4bd0a872984e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Fri, 16 Dec 2022 20:26:22 +0100 Subject: [PATCH 29/52] fix compiler warnings --- src/mesh/NodeDB.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 6b2d5bfb3..2faff2aae 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -214,9 +214,9 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.has_external_notification = true; moduleConfig.has_canned_message = true; - strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(default_mqtt_address)); - strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(default_mqtt_username)); - strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(default_mqtt_password)); + strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(moduleConfig.mqtt.address)); + strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(moduleConfig.mqtt.username)); + strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(moduleConfig.mqtt.password)); initModuleConfigIntervals(); } From 220859d0aaa7ae2efe27d5e560d8b8a6e5667b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sat, 17 Dec 2022 23:32:20 +0100 Subject: [PATCH 30/52] Merge pull request #2019 from code8buster/gps-toggle-final Adds a flag to turn the GPS power rail off entirely on tbeam --- src/ButtonThread.h | 17 +++++++++++++++-- src/GPSStatus.h | 9 +++++++-- src/gps/GPS.cpp | 24 ++++++++++++++++++++---- src/gps/GPS.h | 3 +++ src/graphics/Screen.cpp | 33 ++++++++++++++++++++++++++++++--- src/sleep.cpp | 32 ++++++++++++++++++++++++++++++++ src/sleep.h | 4 +++- variants/tbeam/platformio.ini | 1 + 8 files changed, 111 insertions(+), 12 deletions(-) diff --git a/src/ButtonThread.h b/src/ButtonThread.h index 088642099..0e9830f3f 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -51,6 +51,7 @@ class ButtonThread : public concurrency::OSThread pinMode(BUTTON_PIN, INPUT_PULLUP_SENSE); #endif userButton.attachClick(userButtonPressed); + userButton.setClickTicks(300); userButton.attachDuringLongPress(userButtonPressedLong); userButton.attachDoubleClick(userButtonDoublePressed); userButton.attachMultiClick(userButtonMultiPressed); @@ -159,9 +160,21 @@ class ButtonThread : public concurrency::OSThread static void userButtonDoublePressed() { -#if defined(USE_EINK) && defined(PIN_EINK_EN) + #if defined(USE_EINK) && defined(PIN_EINK_EN) digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW); -#endif + #endif + #if defined(GPS_POWER_TOGGLE) + if(config.position.gps_enabled) + { + DEBUG_MSG("Flag set to false for gps power\n"); + } + else + { + DEBUG_MSG("Flag set to true to restore power\n"); + } + config.position.gps_enabled = !(config.position.gps_enabled); + doGPSpowersave(config.position.gps_enabled); + #endif } static void userButtonMultiPressed() diff --git a/src/GPSStatus.h b/src/GPSStatus.h index 35a0b11f2..ef97c59b7 100644 --- a/src/GPSStatus.h +++ b/src/GPSStatus.h @@ -20,16 +20,19 @@ class GPSStatus : public Status bool hasLock = false; // default to false, until we complete our first read bool isConnected = false; // Do we have a GPS we are talking to + bool isPowerSaving = false; //Are we in power saving state + Position p = Position_init_default; public: GPSStatus() { statusType = STATUS_TYPE_GPS; } // preferred method - GPSStatus(bool hasLock, bool isConnected, const Position &pos) : Status() + GPSStatus(bool hasLock, bool isConnected, bool isPowerSaving, const Position &pos) : Status() { this->hasLock = hasLock; this->isConnected = isConnected; + this->isPowerSaving = isPowerSaving; // all-in-one struct copy this->p = pos; @@ -44,6 +47,8 @@ class GPSStatus : public Status bool getIsConnected() const { return isConnected; } + bool getIsPowerSaving() const { return isPowerSaving;} + int32_t getLatitude() const { if (config.position.fixed_position) { @@ -94,7 +99,7 @@ class GPSStatus : public Status #ifdef GPS_EXTRAVERBOSE DEBUG_MSG("GPSStatus.match() new pos@%x to old pos@%x\n", newStatus->p.pos_timestamp, p.pos_timestamp); #endif - return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected || + return (newStatus->hasLock != hasLock || newStatus->isConnected != isConnected || newStatus->isPowerSaving !=isPowerSaving || newStatus->p.latitude_i != p.latitude_i || newStatus->p.longitude_i != p.longitude_i || newStatus->p.altitude != p.altitude || newStatus->p.altitude_hae != p.altitude_hae || newStatus->p.PDOP != p.PDOP || newStatus->p.ground_track != p.ground_track || diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 54dc59cbb..7e860cdd0 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -270,21 +270,30 @@ bool GPS::setup() pinMode(PIN_GPS_EN, OUTPUT); #endif +#ifdef HAS_PMU +if(config.position.gps_enabled){ + setGPSPower(true); +} +#endif + #ifdef PIN_GPS_RESET digitalWrite(PIN_GPS_RESET, 1); // assert for 10ms pinMode(PIN_GPS_RESET, OUTPUT); delay(10); digitalWrite(PIN_GPS_RESET, 0); #endif - setAwake(true); // Wake GPS power before doing any init bool ok = setupGPS(); if (ok) { notifySleepObserver.observe(¬ifySleep); notifyDeepSleepObserver.observe(¬ifyDeepSleep); + notifyGPSSleepObserver.observe(¬ifyGPSSleep); + } + if (config.position.gps_enabled==false) { + setAwake(false); + doGPSpowersave(false); } - return ok; } @@ -293,6 +302,7 @@ GPS::~GPS() // we really should unregister our sleep observer notifySleepObserver.unobserve(¬ifySleep); notifyDeepSleepObserver.unobserve(¬ifyDeepSleep); + notifyGPSSleepObserver.observe(¬ifyGPSSleep); } bool GPS::hasLock() @@ -405,7 +415,7 @@ void GPS::publishUpdate() DEBUG_MSG("publishing pos@%x:2, hasVal=%d, GPSlock=%d\n", p.timestamp, hasValidLocation, hasLock()); // Notify any status instances that are observing us - const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasValidLocation, isConnected(), p); + const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasValidLocation, isConnected(), isPowerSaving(), p); newStatus.notifyObservers(&status); } } @@ -416,7 +426,7 @@ int32_t GPS::runOnce() // if we have received valid NMEA claim we are connected setConnected(); } else { - if(gnssModel == GNSS_MODEL_UBLOX){ + if((config.position.gps_enabled == 1) && (gnssModel == GNSS_MODEL_UBLOX)){ // reset the GPS on next bootup if(devicestate.did_gps_reset && (millis() > 60000) && !hasFlow()) { DEBUG_MSG("GPS is not communicating, trying factory reset on next bootup.\n"); @@ -518,6 +528,7 @@ int GPS::prepareDeepSleep(void *unused) DEBUG_MSG("GPS deep sleep!\n"); // For deep sleep we also want abandon any lock attempts (because we want minimum power) + getSleepTime(); setAwake(false); return 0; @@ -653,6 +664,11 @@ GPS *createGps() return new_gps; } } + else{ + GPS *new_gps = new NMEAGPS(); + new_gps->setup(); + return new_gps; + } return nullptr; #endif } diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 8f04b6793..5d24268d7 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -49,6 +49,7 @@ class GPS : private concurrency::OSThread CallbackObserver notifySleepObserver = CallbackObserver(this, &GPS::prepareSleep); CallbackObserver notifyDeepSleepObserver = CallbackObserver(this, &GPS::prepareDeepSleep); + CallbackObserver notifyGPSSleepObserver = CallbackObserver(this, &GPS::prepareDeepSleep); public: /** If !NULL we will use this serial port to construct our GPS */ @@ -77,6 +78,8 @@ class GPS : private concurrency::OSThread /// Return true if we are connected to a GPS bool isConnected() const { return hasGPS; } + bool isPowerSaving() const { return !config.position.gps_enabled;} + /** * Restart our lock attempt - try to get and broadcast a GPS reading ASAP * called after the CPU wakes from light-sleep state diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index f1c3fdccc..3ea4463ea 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -512,6 +512,22 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus } } +//Draw status when gps is disabled by PMU +static void drawGPSpowerstat(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps){ +String displayLine = ""; +displayLine = "GPS disabled"; +int16_t xPos = display->getStringWidth(displayLine); + #ifdef HAS_PMU + if (!config.position.gps_enabled){ + display->drawString(x + xPos, y, displayLine); + #ifdef GPS_POWER_TOGGLE + display->drawString(x + xPos, y - 2 + FONT_HEIGHT_SMALL, " by button"); + #endif + //display->drawString(x + xPos, y + 2, displayLine); + } + #endif +} + static void drawGPSAltitude(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps) { String displayLine = ""; @@ -1384,7 +1400,16 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 // Display nodes status drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 3, nodeStatus); // Display GPS status - drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 3, gpsStatus); + if (!config.position.gps_enabled){ + int16_t yPos = y + 2; +#ifdef GPS_POWER_TOGGLE + yPos = (y + 10 + FONT_HEIGHT_SMALL); +#endif + drawGPSpowerstat(display, x, yPos, gpsStatus); + } else { + drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus); + drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 3, gpsStatus); + } display->setColor(WHITE); // Draw the channel name @@ -1643,7 +1668,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat char chUtil[13]; sprintf(chUtil, "ChUtil %2.0f%%", airTime->channelUtilizationPercent()); display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil), y + FONT_HEIGHT_SMALL * 1, chUtil); - +if (config.position.gps_enabled) { // Line 3 if (config.display.gps_format != Config_DisplayConfig_GpsCoordinateFormat_DMS) // if DMS then don't draw altitude @@ -1651,7 +1676,9 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat // Line 4 drawGPScoordinates(display, x, y + FONT_HEIGHT_SMALL * 3, gpsStatus); - +} else { + drawGPSpowerstat(display, x - (SCREEN_WIDTH / 4), y + FONT_HEIGHT_SMALL * 2, gpsStatus); +} /* Display a heartbeat pixel that blinks every time the frame is redrawn */ #ifdef SHOW_REDRAWS if (heartbeat) diff --git a/src/sleep.cpp b/src/sleep.cpp index 6625da2a3..40426a777 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -29,7 +29,9 @@ Observable preflightSleep; /// Called to tell observers we are now entering sleep and you should prepare. Must return 0 /// notifySleep will be called for light or deep sleep, notifyDeepSleep is only called for deep sleep +/// notifyGPSSleep will be called when config.position.gps_enabled is set to 0 or from buttonthread when GPS_POWER_TOGGLE is enabled. Observable notifySleep, notifyDeepSleep; +Observable notifyGPSSleep; // deep sleep support RTC_DATA_ATTR int bootCount = 0; @@ -167,6 +169,36 @@ static void waitEnterSleep() notifySleep.notifyObservers(NULL); } +void doGPSpowersave(bool on) +{ + #ifdef HAS_PMU + if (on) + { + DEBUG_MSG("Turning GPS back on\n"); + gps->forceWake(1); + setGPSPower(1); + } + else + { + DEBUG_MSG("Turning off GPS chip\n"); + notifyGPSSleep.notifyObservers(NULL); + setGPSPower(0); + } + #endif + #ifdef PIN_GPS_WAKE + if (on) + { + DEBUG_MSG("Waking GPS"); + gps->forceWake(1); + } + else + { + DEBUG_MSG("GPS entering sleep"); + notifyGPSSleep.notifyObservers(NULL); + } + #endif +} + void doDeepSleep(uint64_t msecToWake) { DEBUG_MSG("Entering deep sleep for %lu seconds\n", msecToWake / 1000); diff --git a/src/sleep.h b/src/sleep.h index 7f0592af4..af59a8dad 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -13,7 +13,7 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t msecToWake); extern esp_sleep_source_t wakeCause; #endif void setGPSPower(bool on); - +void doGPSpowersave(bool on); // Perform power on init that we do on each wake from deep sleep void initDeepSleep(); @@ -37,4 +37,6 @@ extern Observable notifySleep; /// Called to tell observers we are now entering (deep) sleep and you should prepare. Must return 0 extern Observable notifyDeepSleep; +/// Called to tell GPS thread to enter deep sleep independently of LoRa/MCU sleep, prior to full poweroff. Must return 0 +extern Observable notifyGPSSleep; void enableModemSleep(); \ No newline at end of file diff --git a/variants/tbeam/platformio.ini b/variants/tbeam/platformio.ini index 578a74f7b..76a03d126 100644 --- a/variants/tbeam/platformio.ini +++ b/variants/tbeam/platformio.ini @@ -6,4 +6,5 @@ lib_deps = ${esp32_base.lib_deps} build_flags = ${esp32_base.build_flags} -D TBEAM_V10 -I variants/tbeam + -DGPS_POWER_TOGGLE ; comment this line to disable double press function on the user button to turn off gps entirely. upload_speed = 921600 From 7396d0f2419b916b65dbc9664dc402ae53ec5590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 21 Dec 2022 13:36:38 +0100 Subject: [PATCH 31/52] Cherry Picking Stuff from develop... --- bin/regen-protos.bat | 2 +- bin/regen-protos.sh | 6 +- src/Power.cpp | 3 + src/airtime.cpp | 14 +++ src/airtime.h | 2 + src/graphics/Screen.cpp | 89 +++++++++--------- src/graphics/images.h | 14 ++- src/mesh/NodeDB.cpp | 1 + src/mesh/Router.cpp | 13 +++ src/mesh/http/WiFiAPClient.cpp | 134 +++++++++++++++++----------- src/mesh/http/WiFiAPClient.h | 4 + src/modules/AdminModule.cpp | 5 +- src/modules/CannedMessageModule.cpp | 4 + src/modules/esp32/AudioModule.cpp | 48 +++++----- src/power.h | 3 + 15 files changed, 220 insertions(+), 122 deletions(-) diff --git a/bin/regen-protos.bat b/bin/regen-protos.bat index 12ee7d7ab..5c576c5b2 100644 --- a/bin/regen-protos.bat +++ b/bin/regen-protos.bat @@ -1 +1 @@ -cd protobufs && ..\nanopb-0.4.6\generator-bin\protoc.exe --nanopb_out=-v:..\src\mesh\generated -I=..\protobufs *.proto +cd protobufs && ..\nanopb-0.4.7\generator-bin\protoc.exe --nanopb_out=-v:..\src\mesh\generated -I=..\protobufs *.proto diff --git a/bin/regen-protos.sh b/bin/regen-protos.sh index 2734c213b..133d587a3 100755 --- a/bin/regen-protos.sh +++ b/bin/regen-protos.sh @@ -2,13 +2,13 @@ set -e -echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.6 to be located in the" +echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.7 to be located in the" echo "firmware root directory if the following step fails, you should download the correct" -echo "prebuilt binaries for your computer into nanopb-0.4.6" +echo "prebuilt binaries for your computer into nanopb-0.4.7" # the nanopb tool seems to require that the .options file be in the current directory! cd protobufs -../nanopb-0.4.6/generator-bin/protoc --nanopb_out=-v:../src/mesh/generated -I=../protobufs *.proto +../nanopb-0.4.7/generator-bin/protoc --nanopb_out=-v:../src/mesh/generated -I=../protobufs *.proto #echo "Regenerating protobuf documentation - if you see an error message" #echo "you can ignore it unless doing a new protobuf release to github." diff --git a/src/Power.cpp b/src/Power.cpp index d5fe312e9..d2c5b01a0 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -182,6 +182,9 @@ Power::Power() : OSThread("Power") { statusHandler = {}; low_voltage_counter = 0; +#ifdef DEBUG_HEAP + lastheap = ESP.getFreeHeap(); +#endif } bool Power::analogInit() diff --git a/src/airtime.cpp b/src/airtime.cpp index fed4ef8aa..1c2fb3233 100644 --- a/src/airtime.cpp +++ b/src/airtime.cpp @@ -117,6 +117,20 @@ float AirTime::utilizationTXPercent() return (float(sum) / float(MS_IN_HOUR)) * 100; } +// Get the amount of minutes we have to be silent before we can send again +uint8_t AirTime::getSilentMinutes(float txPercent, float dutyCycle) +{ + float newTxPercent = txPercent; + for (int8_t i = MINUTES_IN_HOUR-1; i >= 0; --i) { + newTxPercent -= ((float)this->utilizationTX[i] / (MS_IN_MINUTE * MINUTES_IN_HOUR / 100)); + if (newTxPercent < dutyCycle) + return MINUTES_IN_HOUR-1-i; + } + + return MINUTES_IN_HOUR; +} + + AirTime::AirTime() : concurrency::OSThread("AirTime"),airtimes({}) { } diff --git a/src/airtime.h b/src/airtime.h index f6b9bdcb5..3f38f39f8 100644 --- a/src/airtime.h +++ b/src/airtime.h @@ -29,6 +29,7 @@ #define PERIODS_TO_LOG 8 #define MINUTES_IN_HOUR 60 #define SECONDS_IN_MINUTE 60 +#define MS_IN_MINUTE (SECONDS_IN_MINUTE * 1000) #define MS_IN_HOUR (MINUTES_IN_HOUR * SECONDS_IN_MINUTE * 1000) @@ -57,6 +58,7 @@ class AirTime : private concurrency::OSThread uint32_t getSecondsPerPeriod(); uint32_t getSecondsSinceBoot(); uint32_t *airtimeReport(reportTypes reportType); + uint8_t getSilentMinutes(float txPercent, float dutyCycle); private: bool firstTime = true; diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 3ea4463ea..30fc396a9 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -35,6 +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/esp32/StoreForwardModule.h" #include "sleep.h" #include "target_specific.h" #include "utils.h" @@ -95,17 +96,17 @@ static uint16_t displayWidth, displayHeight; #if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) // The screen is bigger so use bigger fonts -#define FONT_SMALL ArialMT_Plain_16 -#define FONT_MEDIUM ArialMT_Plain_24 -#define FONT_LARGE ArialMT_Plain_24 +#define FONT_SMALL ArialMT_Plain_16 // Height: 19 +#define FONT_MEDIUM ArialMT_Plain_24 // Height: 28 +#define FONT_LARGE ArialMT_Plain_24 // Height: 28 #else #ifdef OLED_RU #define FONT_SMALL ArialMT_Plain_10_RU #else -#define FONT_SMALL ArialMT_Plain_10 +#define FONT_SMALL ArialMT_Plain_10 // Height: 13 #endif -#define FONT_MEDIUM ArialMT_Plain_16 -#define FONT_LARGE ArialMT_Plain_24 +#define FONT_MEDIUM ArialMT_Plain_16 // Height: 19 +#define FONT_LARGE ArialMT_Plain_24 // Height: 28 #endif #define fontHeight(font) ((font)[1] + 1) // height is position 1 @@ -465,7 +466,11 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *no { char usersString[20]; sprintf(usersString, "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal()); +#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) + display->drawFastImage(x, y + 3, 8, 8, imgUser); +#else display->drawFastImage(x, y, 8, 8, imgUser); +#endif display->drawString(x + 10, y - 2, usersString); display->drawString(x + 11, y - 2, usersString); } @@ -513,19 +518,20 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus } //Draw status when gps is disabled by PMU -static void drawGPSpowerstat(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps){ -String displayLine = ""; -displayLine = "GPS disabled"; -int16_t xPos = display->getStringWidth(displayLine); - #ifdef HAS_PMU +static void drawGPSpowerstat(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps) +{ +#ifdef HAS_PMU + String displayLine = "GPS disabled"; + int16_t xPos = display->getStringWidth(displayLine); + if (!config.position.gps_enabled){ - display->drawString(x + xPos, y, displayLine); - #ifdef GPS_POWER_TOGGLE - display->drawString(x + xPos, y - 2 + FONT_HEIGHT_SMALL, " by button"); - #endif + display->drawString(x + xPos, y, displayLine); +#ifdef GPS_POWER_TOGGLE + display->drawString(x + xPos, y - 2 + FONT_HEIGHT_SMALL, " by button"); +#endif //display->drawString(x + xPos, y + 2, displayLine); } - #endif +#endif } static void drawGPSAltitude(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps) @@ -860,29 +866,6 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ drawColumns(display, x, y, fields); } -#if 0 -void _screen_header() -{ - if (!disp) - return; - - // Message count - //snprintf(buffer, sizeof(buffer), "#%03d", ttn_get_count() % 1000); - //display->setTextAlignment(TEXT_ALIGN_LEFT); - //display->drawString(0, 2, buffer); - - // Datetime - display->setTextAlignment(TEXT_ALIGN_CENTER); - display->drawString(display->getWidth()/2, 2, gps.getTimeStr()); - - // Satellite count - display->setTextAlignment(TEXT_ALIGN_RIGHT); - char buffer[10]; - display->drawString(display->getWidth() - SATELLITE_IMAGE_WIDTH - 4, 2, itoa(gps.satellites.value(), buffer, 10)); - display->drawXbm(display->getWidth() - SATELLITE_IMAGE_WIDTH, 0, SATELLITE_IMAGE_WIDTH, SATELLITE_IMAGE_HEIGHT, SATELLITE_IMAGE); -} -#endif - // #ifdef RAK4630 // Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl), // dispdev_oled(address, sda, scl), ui(&dispdev) @@ -1414,8 +1397,32 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 display->setColor(WHITE); // Draw the channel name display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr); - // Draw our hardware ID to assist with bluetooth pairing - display->drawFastImage(x + SCREEN_WIDTH - (10) - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8, imgInfo); + // Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo + if (moduleConfig.store_forward.enabled) { + 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); + display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8, imgQuestionL2); +#else + display->drawFastImage(x + SCREEN_WIDTH - 10 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8, imgQuestion); +#endif + } else { +#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) + display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 16, 8, imgSFL1); + display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 16, 8, imgSFL2); +#else + display->drawFastImage(x + SCREEN_WIDTH - 13 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 11, 8, imgSF); +#endif + } + } else { +#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, imgInfoL1); + display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8, imgInfoL2); +#else + display->drawFastImage(x + SCREEN_WIDTH - 10 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8, imgInfo); +#endif + } + display->drawString(x + SCREEN_WIDTH - display->getStringWidth(ourId), y + FONT_HEIGHT_SMALL, ourId); // Draw any log messages diff --git a/src/graphics/images.h b/src/graphics/images.h index 4680b9475..9bf66a3a3 100644 --- a/src/graphics/images.h +++ b/src/graphics/images.h @@ -12,6 +12,18 @@ const uint8_t imgPower[] PROGMEM = { 0x40, 0x40, 0x40, 0x58, 0x48, 0x08, const uint8_t imgUser[] PROGMEM = { 0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C }; const uint8_t imgPositionEmpty[] PROGMEM = { 0x20, 0x30, 0x28, 0x24, 0x42, 0xFF }; const uint8_t imgPositionSolid[] PROGMEM = { 0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF }; -const uint8_t imgInfo[] PROGMEM = { 0xFF, 0x81, 0x81, 0xB5, 0xB5, 0x81, 0x81, 0xFF }; + +#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) +const uint8_t imgQuestionL1[] PROGMEM = { 0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff }; +const uint8_t imgQuestionL2[] PROGMEM = { 0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f }; +const uint8_t imgInfoL1[] PROGMEM = { 0xff, 0x01, 0x01, 0x01, 0x1e, 0x7f, 0x1e, 0x01, 0x01, 0x01, 0x01, 0xff }; +const uint8_t imgInfoL2[] PROGMEM = { 0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f }; +const uint8_t imgSFL1[] PROGMEM = { 0xb6, 0x8f, 0x19, 0x11, 0x31, 0xe3, 0xc2, 0x01, 0x01, 0xf9, 0xf9, 0x89, 0x89, 0x89, 0x09, 0xeb}; +const uint8_t imgSFL2[] PROGMEM = { 0x0e, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x00, 0x0f, 0x0f, 0x00, 0x08, 0x08, 0x08, 0x0f}; +#else +const uint8_t imgInfo[] PROGMEM = { 0xff, 0x81, 0x00, 0xfb, 0xfb, 0x00, 0x81, 0xff }; +const uint8_t imgQuestion[] PROGMEM = { 0xbf, 0x41, 0xc0, 0x8b, 0xdb, 0x70, 0xa1, 0xdf }; +const uint8_t imgSF[] PROGMEM = { 0xd2, 0xb7, 0xad, 0xbb, 0x92, 0x01, 0xfd, 0xfd, 0x15, 0x85, 0xf5}; +#endif #include "img/icon.xbm" diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 2faff2aae..1e40b8d92 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -162,6 +162,7 @@ void NodeDB::installDefaultConfig() config.has_network = true; config.has_bluetooth = true; config.lora.tx_enabled = true; // FIXME: maybe false in the future, and setting region to enable it. (unset region forces it off) + config.lora.override_duty_cycle = false; config.lora.region = Config_LoRaConfig_RegionCode_UNSET; config.lora.modem_preset = Config_LoRaConfig_ModemPreset_LONG_FAST; config.lora.hop_limit = HOP_RELIABLE; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 6b6a03a3a..5ce26f49a 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -2,6 +2,7 @@ #include "Channels.h" #include "CryptoEngine.h" #include "NodeDB.h" +#include "MeshRadio.h" #include "RTC.h" #include "configuration.h" #include "main.h" @@ -187,6 +188,18 @@ ErrorCode Router::send(MeshPacket *p) { assert(p->to != nodeDB.getNodeNum()); // should have already been handled by sendLocal + // Abort sending if we are violating the duty cycle + if (!config.lora.override_duty_cycle && myRegion->dutyCycle != 100) { + float hourlyTxPercent = airTime->utilizationTXPercent(); + if (hourlyTxPercent > myRegion->dutyCycle) { + uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle); + DEBUG_MSG("WARNING: Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes); + Routing_Error err = Routing_Error_DUTY_CYCLE_LIMIT; + abortSendAndNak(err, p); + return err; + } + } + // PacketId nakId = p->decoded.which_ackVariant == SubPacket_fail_id_tag ? p->decoded.ackVariant.fail_id : 0; // assert(!nakId); // I don't think we ever send 0hop naks over the wire (other than to the phone), test that assumption with // assert diff --git a/src/mesh/http/WiFiAPClient.cpp b/src/mesh/http/WiFiAPClient.cpp index bb4542468..731197e60 100644 --- a/src/mesh/http/WiFiAPClient.cpp +++ b/src/mesh/http/WiFiAPClient.cpp @@ -1,7 +1,7 @@ -#include "mesh/http/WiFiAPClient.h" #include "NodeDB.h" #include "RTC.h" #include "concurrency/Periodic.h" +#include "mesh/http/WiFiAPClient.h" #include "configuration.h" #include "main.h" #include "mesh/http/WebServer.h" @@ -37,9 +37,9 @@ bool APStartupComplete = 0; unsigned long lastrun_ntp = 0; -static bool needReconnect = true; // If we create our reconnector, run it once at the beginning +bool needReconnect = true; // If we create our reconnector, run it once at the beginning -static Periodic *wifiReconnect; +Periodic *wifiReconnect; static int32_t reconnectWiFi() { @@ -56,29 +56,15 @@ static int32_t reconnectWiFi() // Make sure we clear old connection credentials WiFi.disconnect(false, true); - DEBUG_MSG("... Reconnecting to WiFi access point %s\n",wifiName); - - int n = WiFi.scanNetworks(); - - if (n > 0) { - for (int i = 0; i < n; ++i) { - DEBUG_MSG("Found WiFi network %s, signal strength %d\n", WiFi.SSID(i).c_str(), WiFi.RSSI(i)); - yield(); - } - WiFi.mode(WIFI_MODE_STA); - WiFi.begin(wifiName, wifiPsw); - } else { - DEBUG_MSG("No networks found during site survey. Rebooting MCU...\n"); - screen->startRebootScreen(); - rebootAtMsec = millis() + 5000; - } - + DEBUG_MSG("Reconnecting to WiFi access point %s\n",wifiName); + WiFi.mode(WIFI_MODE_STA); + WiFi.begin(wifiName, wifiPsw); } #ifndef DISABLE_NTP if (WiFi.isConnected() && (((millis() - lastrun_ntp) > 43200000) || (lastrun_ntp == 0))) { // every 12 hours - DEBUG_MSG("Updating NTP time\n"); + DEBUG_MSG("Updating NTP time from %s\n",config.network.ntp_server); if (timeClient.update()) { DEBUG_MSG("NTP Request Success - Setting RTCQualityNTP if needed\n"); @@ -129,7 +115,7 @@ static void onNetworkConnected() { if (!APStartupComplete) { // Start web server - DEBUG_MSG("... Starting network services\n"); + DEBUG_MSG("Starting network services\n"); // start mdns if (!MDNS.begin("Meshtastic")) { @@ -168,6 +154,8 @@ bool initWifi() createSSLCert(); + esp_wifi_set_storage(WIFI_STORAGE_RAM); // Disable flash storage for WiFi credentials + if (!*wifiPsw) // Treat empty password as no password wifiPsw = NULL; @@ -194,7 +182,7 @@ bool initWifi() WiFi.onEvent( [](WiFiEvent_t event, WiFiEventInfo_t info) { - Serial.print("\nWiFi lost connection. Reason: "); + Serial.print("WiFi lost connection. Reason: "); Serial.println(info.wifi_sta_disconnected.reason); /* @@ -221,91 +209,137 @@ bool initWifi() // Called by the Espressif SDK to static void WiFiEvent(WiFiEvent_t event) { - DEBUG_MSG("************ [WiFi-event] event: %d ************\n", event); + DEBUG_MSG("WiFi-Event %d: ", event); switch (event) { - case SYSTEM_EVENT_WIFI_READY: + case ARDUINO_EVENT_WIFI_READY: DEBUG_MSG("WiFi interface ready\n"); break; - case SYSTEM_EVENT_SCAN_DONE: + case ARDUINO_EVENT_WIFI_SCAN_DONE: DEBUG_MSG("Completed scan for access points\n"); break; - case SYSTEM_EVENT_STA_START: + case ARDUINO_EVENT_WIFI_STA_START: DEBUG_MSG("WiFi station started\n"); break; - case SYSTEM_EVENT_STA_STOP: + case ARDUINO_EVENT_WIFI_STA_STOP: DEBUG_MSG("WiFi station stopped\n"); break; - case SYSTEM_EVENT_STA_CONNECTED: + case ARDUINO_EVENT_WIFI_STA_CONNECTED: DEBUG_MSG("Connected to access point\n"); break; - case SYSTEM_EVENT_STA_DISCONNECTED: + case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: DEBUG_MSG("Disconnected from WiFi access point\n"); WiFi.disconnect(false, true); needReconnect = true; wifiReconnect->setIntervalFromNow(1000); break; - case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: + case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: DEBUG_MSG("Authentication mode of access point has changed\n"); break; - case SYSTEM_EVENT_STA_GOT_IP: + case ARDUINO_EVENT_WIFI_STA_GOT_IP: DEBUG_MSG("Obtained IP address: "); Serial.println(WiFi.localIP()); onNetworkConnected(); break; - case SYSTEM_EVENT_STA_LOST_IP: + case ARDUINO_EVENT_WIFI_STA_GOT_IP6: + DEBUG_MSG("Obtained IP6 address: "); + Serial.println(WiFi.localIPv6()); + break; + case ARDUINO_EVENT_WIFI_STA_LOST_IP: DEBUG_MSG("Lost IP address and IP address is reset to 0\n"); WiFi.disconnect(false, true); needReconnect = true; wifiReconnect->setIntervalFromNow(1000); break; - case SYSTEM_EVENT_STA_WPS_ER_SUCCESS: + case ARDUINO_EVENT_WPS_ER_SUCCESS: DEBUG_MSG("WiFi Protected Setup (WPS): succeeded in enrollee mode\n"); break; - case SYSTEM_EVENT_STA_WPS_ER_FAILED: + case ARDUINO_EVENT_WPS_ER_FAILED: DEBUG_MSG("WiFi Protected Setup (WPS): failed in enrollee mode\n"); break; - case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT: + case ARDUINO_EVENT_WPS_ER_TIMEOUT: DEBUG_MSG("WiFi Protected Setup (WPS): timeout in enrollee mode\n"); break; - case SYSTEM_EVENT_STA_WPS_ER_PIN: + case ARDUINO_EVENT_WPS_ER_PIN: DEBUG_MSG("WiFi Protected Setup (WPS): pin code in enrollee mode\n"); break; - case SYSTEM_EVENT_AP_START: + case ARDUINO_EVENT_WPS_ER_PBC_OVERLAP: + DEBUG_MSG("WiFi Protected Setup (WPS): push button overlap in enrollee mode\n"); + break; + case ARDUINO_EVENT_WIFI_AP_START: DEBUG_MSG("WiFi access point started\n"); break; - case SYSTEM_EVENT_AP_STOP: + case ARDUINO_EVENT_WIFI_AP_STOP: DEBUG_MSG("WiFi access point stopped\n"); break; - case SYSTEM_EVENT_AP_STACONNECTED: + case ARDUINO_EVENT_WIFI_AP_STACONNECTED: DEBUG_MSG("Client connected\n"); break; - case SYSTEM_EVENT_AP_STADISCONNECTED: + case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED: DEBUG_MSG("Client disconnected\n"); break; - case SYSTEM_EVENT_AP_STAIPASSIGNED: + case ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED: DEBUG_MSG("Assigned IP address to client\n"); break; - case SYSTEM_EVENT_AP_PROBEREQRECVED: + case ARDUINO_EVENT_WIFI_AP_PROBEREQRECVED: DEBUG_MSG("Received probe request\n"); break; - case SYSTEM_EVENT_GOT_IP6: + case ARDUINO_EVENT_WIFI_AP_GOT_IP6: DEBUG_MSG("IPv6 is preferred\n"); break; - case SYSTEM_EVENT_ETH_START: + case ARDUINO_EVENT_WIFI_FTM_REPORT: + DEBUG_MSG("Fast Transition Management report\n"); + break; + case ARDUINO_EVENT_ETH_START: DEBUG_MSG("Ethernet started\n"); break; - case SYSTEM_EVENT_ETH_STOP: + case ARDUINO_EVENT_ETH_STOP: DEBUG_MSG("Ethernet stopped\n"); break; - case SYSTEM_EVENT_ETH_CONNECTED: + case ARDUINO_EVENT_ETH_CONNECTED: DEBUG_MSG("Ethernet connected\n"); break; - case SYSTEM_EVENT_ETH_DISCONNECTED: + case ARDUINO_EVENT_ETH_DISCONNECTED: DEBUG_MSG("Ethernet disconnected\n"); break; - case SYSTEM_EVENT_ETH_GOT_IP: - DEBUG_MSG("Obtained IP address (SYSTEM_EVENT_ETH_GOT_IP)\n"); + case ARDUINO_EVENT_ETH_GOT_IP: + DEBUG_MSG("Obtained IP address (ARDUINO_EVENT_ETH_GOT_IP)\n"); + break; + case ARDUINO_EVENT_ETH_GOT_IP6: + DEBUG_MSG("Obtained IP6 address (ARDUINO_EVENT_ETH_GOT_IP6)\n"); + break; + case ARDUINO_EVENT_SC_SCAN_DONE: + DEBUG_MSG("SmartConfig: Scan done\n"); + break; + case ARDUINO_EVENT_SC_FOUND_CHANNEL: + DEBUG_MSG("SmartConfig: Found channel\n"); + break; + case ARDUINO_EVENT_SC_GOT_SSID_PSWD: + DEBUG_MSG("SmartConfig: Got SSID and password\n"); + break; + case ARDUINO_EVENT_SC_SEND_ACK_DONE: + DEBUG_MSG("SmartConfig: Send ACK done\n"); + break; + case ARDUINO_EVENT_PROV_INIT: + DEBUG_MSG("Provisioning: Init\n"); + break; + case ARDUINO_EVENT_PROV_DEINIT: + DEBUG_MSG("Provisioning: Stopped\n"); + break; + case ARDUINO_EVENT_PROV_START: + DEBUG_MSG("Provisioning: Started\n"); + break; + case ARDUINO_EVENT_PROV_END: + DEBUG_MSG("Provisioning: End\n"); + break; + case ARDUINO_EVENT_PROV_CRED_RECV: + DEBUG_MSG("Provisioning: Credentials received\n"); + break; + case ARDUINO_EVENT_PROV_CRED_FAIL: + DEBUG_MSG("Provisioning: Credentials failed\n"); + break; + case ARDUINO_EVENT_PROV_CRED_SUCCESS: + DEBUG_MSG("Provisioning: Credentials success\n"); break; default: break; diff --git a/src/mesh/http/WiFiAPClient.h b/src/mesh/http/WiFiAPClient.h index 9729c24b5..a11330ad0 100644 --- a/src/mesh/http/WiFiAPClient.h +++ b/src/mesh/http/WiFiAPClient.h @@ -1,6 +1,7 @@ #pragma once #include "configuration.h" +#include "concurrency/Periodic.h" #include #include @@ -8,6 +9,9 @@ #include #endif +extern bool needReconnect; +extern concurrency::Periodic *wifiReconnect; + /// @return true if wifi is now in use bool initWifi(); diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 2639e0594..0b617adea 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -195,6 +195,7 @@ bool AdminModule::handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *r) void AdminModule::handleSetOwner(const User &o) { int changed = 0; + bool licensed_changed = false; if (*o.long_name) { changed |= strcmp(owner.long_name, o.long_name); @@ -210,12 +211,14 @@ void AdminModule::handleSetOwner(const User &o) } if (owner.is_licensed != o.is_licensed) { changed = 1; + licensed_changed = true; owner.is_licensed = o.is_licensed; + config.lora.override_duty_cycle = owner.is_licensed; // override duty cycle for licensed operators } if (changed) { // If nothing really changed, don't broadcast on the network or write to flash service.reloadOwner(!hasOpenEditTransaction); - saveChanges(SEGMENT_DEVICESTATE); + licensed_changed ? saveChanges(SEGMENT_CONFIG | SEGMENT_DEVICESTATE) : saveChanges(SEGMENT_DEVICESTATE); } } diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index e7b34b9a7..d148768ad 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -451,11 +451,15 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st if (this->destSelect) { display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL); display->setColor(BLACK); + display->drawStringf(1 + x, 0 + y, buffer, "To: %s", cannedMessageModule->getNodeName(this->dest)); } display->drawStringf(0 + x, 0 + y, buffer, "To: %s", cannedMessageModule->getNodeName(this->dest)); // used chars right aligned sprintf(buffer, "%d left", Constants_DATA_PAYLOAD_LEN - this->freetext.length()); display->drawString(x + display->getWidth() - display->getStringWidth(buffer), y + 0, buffer); + if (this->destSelect) { + display->drawString(x + display->getWidth() - display->getStringWidth(buffer) - 1, y + 0, buffer); + } display->setColor(WHITE); display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), cannedMessageModule->drawWithCursor(cannedMessageModule->freetext, cannedMessageModule->cursor)); } else { diff --git a/src/modules/esp32/AudioModule.cpp b/src/modules/esp32/AudioModule.cpp index 17e5cf4fa..e7132fcc4 100644 --- a/src/modules/esp32/AudioModule.cpp +++ b/src/modules/esp32/AudioModule.cpp @@ -71,27 +71,22 @@ void run_codec2(void* parameter) // 4 bytes of header in each frame hex c0 de c2 plus the bitrate memcpy(audioModule->tx_encode_frame,&audioModule->tx_header,sizeof(audioModule->tx_header)); + DEBUG_MSG("Starting codec2 task\n"); + while (true) { uint32_t tcount = ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(10000)); if (tcount != 0) { if (audioModule->radio_state == RadioState::tx) { - - // Apply the TX filter for (int i = 0; i < audioModule->adc_buffer_size; i++) audioModule->speech[i] = (int16_t)hp_filter.Update((float)audioModule->speech[i]); - // Encode the audio codec2_encode(audioModule->codec2, audioModule->tx_encode_frame + audioModule->tx_encode_frame_index, audioModule->speech); - - //increment the pointer where the encoded frame must be saved audioModule->tx_encode_frame_index += audioModule->encode_codec_size; - //If it this is reached we have a ready trasnmission frame if (audioModule->tx_encode_frame_index == (audioModule->encode_frame_size + sizeof(audioModule->tx_header))) { - //Transmit it - DEBUG_MSG("♪♫♪ Sending %d codec2 bytes\n", audioModule->encode_frame_size); + DEBUG_MSG("Sending %d codec2 bytes\n", audioModule->encode_frame_size); audioModule->sendPayload(); audioModule->tx_encode_frame_index = sizeof(audioModule->tx_header); } @@ -99,10 +94,8 @@ void run_codec2(void* parameter) if (audioModule->radio_state == RadioState::rx) { size_t bytesOut = 0; if (memcmp(audioModule->rx_encode_frame, &audioModule->tx_header, sizeof(audioModule->tx_header)) == 0) { - // Make a cycle to get each codec2 frame from the received frame for (int i = 4; i < audioModule->rx_encode_frame_index; i += audioModule->encode_codec_size) { - //Decode the codec2 frame codec2_decode(audioModule->codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i); i2s_write(I2S_PORT, &audioModule->output_buffer, audioModule->adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500)); } @@ -112,10 +105,8 @@ void run_codec2(void* parameter) codec2_set_lpc_post_filter(tmp_codec2, 1, 0, 0.8, 0.2); int tmp_encode_codec_size = (codec2_bits_per_frame(tmp_codec2) + 7) / 8; int tmp_adc_buffer_size = codec2_samples_per_frame(tmp_codec2); - // Make a cycle to get each codec2 frame from the received frame for (int i = 4; i < audioModule->rx_encode_frame_index; i += tmp_encode_codec_size) { - //Decode the codec2 frame codec2_decode(tmp_codec2, audioModule->output_buffer, audioModule->rx_encode_frame + i); i2s_write(I2S_PORT, &audioModule->output_buffer, tmp_adc_buffer_size, &bytesOut, pdMS_TO_TICKS(500)); } @@ -128,8 +119,15 @@ void run_codec2(void* parameter) AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), concurrency::OSThread("AudioModule") { + // moduleConfig.audio.codec2_enabled = true; + // moduleConfig.audio.i2s_ws = 13; + // moduleConfig.audio.i2s_sd = 15; + // moduleConfig.audio.i2s_din = 22; + // moduleConfig.audio.i2s_sck = 14; + // moduleConfig.audio.ptt_pin = 39; + if ((moduleConfig.audio.codec2_enabled) && (myRegion->audioPermitted)) { - DEBUG_MSG("♪♫♪ Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); + DEBUG_MSG("Setting up codec2 in mode %u", (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); codec2 = codec2_create((moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1); memcpy(tx_header.magic,c2_magic,sizeof(c2_magic)); tx_header.mode = (moduleConfig.audio.bitrate ? moduleConfig.audio.bitrate : AUDIO_MODULE_MODE) - 1; @@ -141,7 +139,7 @@ AudioModule::AudioModule() : SinglePortModule("AudioModule", PortNum_AUDIO_APP), DEBUG_MSG(" using %d frames of %d bytes for a total payload length of %d bytes\n", encode_frame_num, encode_codec_size, encode_frame_size); xTaskCreate(&run_codec2, "codec2_task", 30000, NULL, 5, &codec2HandlerTask); } else { - DEBUG_MSG("♪♫♪ Codec2 disabled (AudioModule %d, Region %s, permitted %d)\n", moduleConfig.audio.codec2_enabled, myRegion->name, myRegion->audioPermitted); + DEBUG_MSG("Codec2 disabled (AudioModule %d, Region %s, permitted %d)\n", moduleConfig.audio.codec2_enabled, myRegion->name, myRegion->audioPermitted); } } @@ -175,7 +173,7 @@ int32_t AudioModule::runOnce() esp_err_t res; if (firstTime) { // Set up I2S Processor configuration. This will produce 16bit samples at 8 kHz instead of 12 from the ADC - DEBUG_MSG("♪♫♪ Initializing I2S SD: %d DIN: %d WS: %d SCK:%d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck); + DEBUG_MSG("Initializing I2S SD: %d DIN: %d WS: %d SCK: %d\n", moduleConfig.audio.i2s_sd, moduleConfig.audio.i2s_din, moduleConfig.audio.i2s_ws, moduleConfig.audio.i2s_sck); i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | (moduleConfig.audio.i2s_sd ? I2S_MODE_RX : 0) | (moduleConfig.audio.i2s_din ? I2S_MODE_TX : 0)), .sample_rate = 8000, @@ -191,7 +189,7 @@ int32_t AudioModule::runOnce() }; res = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to install I2S driver: %d\n", res); + DEBUG_MSG("Failed to install I2S driver: %d\n", res); const i2s_pin_config_t pin_config = { .bck_io_num = moduleConfig.audio.i2s_sck, @@ -201,16 +199,16 @@ int32_t AudioModule::runOnce() }; res = i2s_set_pin(I2S_PORT, &pin_config); if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to set I2S pin config: %d\n", res); + DEBUG_MSG("Failed to set I2S pin config: %d\n", res); res = i2s_start(I2S_PORT); if(res != ESP_OK) - DEBUG_MSG("♪♫♪ Failed to start I2S: %d\n", res); + DEBUG_MSG("Failed to start I2S: %d\n", res); radio_state = RadioState::rx; // Configure PTT input - DEBUG_MSG("♪♫♪ Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); + DEBUG_MSG("Initializing PTT on Pin %u\n", moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN); pinMode(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN, INPUT); firstTime = false; @@ -219,19 +217,19 @@ int32_t AudioModule::runOnce() // Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive. if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) { if (radio_state == RadioState::rx) { - DEBUG_MSG("♪♫♪ PTT pressed, switching to TX\n"); + DEBUG_MSG("PTT pressed, switching to TX\n"); radio_state = RadioState::tx; e.frameChanged = true; this->notifyObservers(&e); } } else { if (radio_state == RadioState::tx) { + DEBUG_MSG("PTT released, switching to RX\n"); if (tx_encode_frame_index > sizeof(tx_header)) { // Send the incomplete frame - DEBUG_MSG("♪♫♪ Sending %d codec2 bytes (incomplete)\n", tx_encode_frame_index); + DEBUG_MSG("Sending %d codec2 bytes (incomplete)\n", tx_encode_frame_index); sendPayload(); } - DEBUG_MSG("♪♫♪ PTT released, switching to RX\n"); tx_encode_frame_index = sizeof(tx_header); radio_state = RadioState::rx; e.frameChanged = true; @@ -260,7 +258,7 @@ int32_t AudioModule::runOnce() } return 100; } else { - DEBUG_MSG("♪♫♪ Audio Module Disabled\n"); + DEBUG_MSG("Audio Module Disabled\n"); return INT32_MAX; } @@ -268,7 +266,7 @@ int32_t AudioModule::runOnce() MeshPacket *AudioModule::allocReply() { - auto reply = allocDataPacket(); // Allocate a packet for sending + auto reply = allocDataPacket(); return reply; } @@ -286,7 +284,7 @@ void AudioModule::sendPayload(NodeNum dest, bool wantReplies) p->to = dest; p->decoded.want_response = wantReplies; - p->want_ack = false; // Audio is shoot&forget. TODO: Is this really suppressing retransmissions? + p->want_ack = false; // Audio is shoot&forget. No need to wait for ACKs. p->priority = MeshPacket_Priority_MAX; // Audio is important, because realtime p->decoded.payload.size = tx_encode_frame_index; diff --git a/src/power.h b/src/power.h index 64043eb86..b370e0248 100644 --- a/src/power.h +++ b/src/power.h @@ -40,6 +40,9 @@ class Power : private concurrency::OSThread private: uint8_t low_voltage_counter; +#ifdef DEBUG_HEAP + uint32_t lastheap; +#endif }; extern Power *power; From 755c0b7008c7dd6bb278e0447e0ca8578cb53368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 21 Dec 2022 13:37:38 +0100 Subject: [PATCH 32/52] use nanopb 0.4.7 --- .github/workflows/update_protobufs.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update_protobufs.yml b/.github/workflows/update_protobufs.yml index 3b32c578c..6944d827e 100644 --- a/.github/workflows/update_protobufs.yml +++ b/.github/workflows/update_protobufs.yml @@ -17,9 +17,9 @@ jobs: - name: Download nanopb run: | - wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.6-linux-x86.tar.gz - tar xvzf nanopb-0.4.6-linux-x86.tar.gz - mv nanopb-0.4.6-linux-x86 nanopb-0.4.6 + wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.7-linux-x86.tar.gz + tar xvzf nanopb-0.4.7-linux-x86.tar.gz + mv nanopb-0.4.7-linux-x86 nanopb-0.4.7 - name: Re-generate protocol buffers run: | From 201b786f777f4588e2998e206651fe631247b84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 21 Dec 2022 14:06:02 +0100 Subject: [PATCH 33/52] fix RAK build --- src/PowerFSM.cpp | 5 +---- src/graphics/Screen.cpp | 5 ++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 647167242..e60056ccd 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -326,10 +326,10 @@ 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; -#ifdef ARCH_ESP32 // 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 @@ -340,9 +340,6 @@ void PowerFSM_setup() if (config.power.sds_secs != UINT32_MAX) powerFSM.add_timed_transition(lowPowerState, &stateSDS, getConfiguredOrDefaultMs(config.power.sds_secs), NULL, "mesh timeout"); - -#elif defined (ARCH_NRF52) - lowPowerState = &stateDARK; #endif diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 30fc396a9..8d3429454 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/esp32/StoreForwardModule.h" + #include "sleep.h" #include "target_specific.h" #include "utils.h" @@ -43,6 +43,7 @@ along with this program. If not, see . #ifdef ARCH_ESP32 #include "esp_task_wdt.h" #include "mesh/http/WiFiAPClient.h" +#include "modules/esp32/StoreForwardModule.h" #endif #ifdef OLED_RU @@ -1399,6 +1400,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 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); @@ -1414,6 +1416,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16 display->drawFastImage(x + SCREEN_WIDTH - 13 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 11, 8, imgSF); #endif } +#endif } else { #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, imgInfoL1); From 815f9bfc5f0a551c516268bd5410824cb2690f92 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 21 Dec 2022 07:41:26 -0600 Subject: [PATCH 34/52] Move to upstream espressif arduino --- variants/heltec_v3/platformio.ini | 4 ++-- variants/heltec_wsl_v3/platformio.ini | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 6cd75bed8..14f3c89bd 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -2,8 +2,8 @@ platform = https://github.com/Baptou88/platform-espressif32.git extends = esp32_base board = heltec_wifi_lora_32_V3 -# Temporary: https://community.platformio.org/t/heltec-esp32-lora-v3-board-support/30406/2 +# Temporary until espressif creates a release with this new target platform_packages = - framework-arduinoespressif32@https://github.com/Baptou88/arduino-esp32.git + framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git build_flags = ${esp32s3_base.build_flags} -D HELTEC_V3 -I variants/heltec_v3 diff --git a/variants/heltec_wsl_v3/platformio.ini b/variants/heltec_wsl_v3/platformio.ini index 8854b1a44..9cc6ddb23 100644 --- a/variants/heltec_wsl_v3/platformio.ini +++ b/variants/heltec_wsl_v3/platformio.ini @@ -2,8 +2,8 @@ platform = https://github.com/Baptou88/platform-espressif32.git extends = esp32_base board = heltec_wifi_lora_32_V3 -# Temporary: https://community.platformio.org/t/heltec-esp32-lora-v3-board-support/30406/2 +# Temporary until espressif creates a release with this new target platform_packages = - framework-arduinoespressif32@https://github.com/Baptou88/arduino-esp32.git + framework-arduinoespressif32@tps://github.com/espressif/arduino-esp32.git build_flags = ${esp32s3_base.build_flags} -D HELTEC_WSL_V3 -I variants/heltec_wsl_v3 From 92c9b34d4f53fa85832a4fcaeb323bff7a474a98 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 21 Dec 2022 07:46:57 -0600 Subject: [PATCH 35/52] Copy pasta --- variants/heltec_wsl_v3/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/heltec_wsl_v3/platformio.ini b/variants/heltec_wsl_v3/platformio.ini index 76ffe0a74..ea2874e0b 100644 --- a/variants/heltec_wsl_v3/platformio.ini +++ b/variants/heltec_wsl_v3/platformio.ini @@ -4,6 +4,6 @@ extends = esp32s3_base board = heltec_wifi_lora_32_V3 # Temporary until espressif creates a release with this new target platform_packages = - framework-arduinoespressif32@tps://github.com/espressif/arduino-esp32.git + framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git build_flags = ${esp32s3_base.build_flags} -D HELTEC_WSL_V3 -I variants/heltec_wsl_v3 From 606262e55d337c00f9bcfe2edc7f092f2a480657 Mon Sep 17 00:00:00 2001 From: thebentern Date: Wed, 21 Dec 2022 18:54:52 +0000 Subject: [PATCH 36/52] [create-pull-request] automated change --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index 9964c8bdb..6a242ed9b 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 0 -build = 8 +build = 9 From b9df41c55f5aabe88ad46c3dcd711b2d8e14f26b Mon Sep 17 00:00:00 2001 From: Mykhailo Lesyk Date: Wed, 21 Dec 2022 22:37:30 -0800 Subject: [PATCH 37/52] [modules][serial] switch simple mode from ascii to bytes --- src/modules/SerialModule.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index c25f69fa4..55ce7b75e 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -49,7 +49,6 @@ #define RXD2 16 #define TXD2 17 #define RX_BUFFER 128 -#define STRING_MAX Constants_DATA_PAYLOAD_LEN #define TIMEOUT 250 #define BAUD 38400 #define ACK 1 @@ -59,7 +58,8 @@ SerialModuleRadio *serialModuleRadio; SerialModule::SerialModule() : concurrency::OSThread("SerialModule") {} -char serialStringChar[Constants_DATA_PAYLOAD_LEN]; +char serialBytes[Constants_DATA_PAYLOAD_LEN]; +size_t serialPayloadSize; SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio") { @@ -188,15 +188,9 @@ int32_t SerialModule::runOnce() Serial2.printf("%s", outbuf); } } else { - String serialString; - while (Serial2.available()) { - serialString = Serial2.readString(); - serialString.toCharArray(serialStringChar, Constants_DATA_PAYLOAD_LEN); - + serialPayloadSize = Serial2.readBytes(serialBytes, Constants_DATA_PAYLOAD_LEN); serialModuleRadio->sendPayload(); - - DEBUG_MSG("Received: %s\n", serialStringChar); } } } @@ -231,8 +225,8 @@ void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies) p->want_ack = ACK; - 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); + 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); service.sendToMesh(p); } @@ -268,7 +262,7 @@ ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp) if (moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_DEFAULT || moduleConfig.serial.mode == ModuleConfig_SerialConfig_Serial_Mode_SIMPLE) { - Serial2.printf("%s", p.payload.bytes); + Serial2.write(p.payload.bytes, p.payload.size); } 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 : "???"; From 5c9996dfa1dc10bec1f9aa469a878a529ada4ee1 Mon Sep 17 00:00:00 2001 From: lewis Date: Sun, 25 Dec 2022 19:01:04 +0800 Subject: [PATCH 38/52] Fixeddisplay model is SH1106 --- variants/tbeam-s3-core/variant.h | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/tbeam-s3-core/variant.h b/variants/tbeam-s3-core/variant.h index 9aaba6010..001b50a30 100644 --- a/variants/tbeam-s3-core/variant.h +++ b/variants/tbeam-s3-core/variant.h @@ -66,3 +66,4 @@ //has 32768 Hz crystal #define HAS_32768HZ +#define USE_SH1106 \ No newline at end of file From 580ec590fa41f0a692128c06451c3a4aa8570fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Fri, 23 Dec 2022 11:41:58 +0100 Subject: [PATCH 39/52] something broke pico toolchain. 'twasn'tme .. --- arch/rp2040/rp2040.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/rp2040/rp2040.ini b/arch/rp2040/rp2040.ini index 8b7bdbff9..cf898a60f 100644 --- a/arch/rp2040/rp2040.ini +++ b/arch/rp2040/rp2040.ini @@ -1,6 +1,8 @@ ; Common settings for rp2040 Processor based targets [rp2040_base] -platform = https://github.com/maxgerhardt/platform-raspberrypi.git#5ce1a228e7cae453f366deb8962252b9b7356bbc +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#20c7dbfcfe6677c5305fa28ecf5e3870321cb157 +platform_packages = + earlephilhower/toolchain-rp2040-earlephilhower@^5.100300.221223 extends = arduino_base board_build.core = earlephilhower board_build.filesystem_size = 0.5m From 21fff392b4bd157d14cbb3c39146d68873575e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Sun, 25 Dec 2022 13:02:40 +0100 Subject: [PATCH 40/52] Backport RP2040 update --- platformio.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index dbddf7535..f3cf9f937 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,7 +10,6 @@ ;default_envs = heltec-v2.0 ;default_envs = heltec-v2.1 ;default_envs = tlora-v1 -;default_envs = tlora-v1 ;default_envs = tlora_v1_3 ;default_envs = tlora-v2 ;default_envs = tlora-v2-1-1.6 From 506bae4a8b5a0eaecb568e338237ef14a4db617a Mon Sep 17 00:00:00 2001 From: miaoski Date: Tue, 27 Dec 2022 19:39:51 +0800 Subject: [PATCH 41/52] Support SHT31 temperature sensor --- platformio.ini | 3 +- src/configuration.h | 1 + src/detect/i2cScan.h | 4 +++ .../Telemetry/EnvironmentTelemetry.cpp | 6 ++++ src/modules/Telemetry/Sensor/SHT31Sensor.cpp | 31 +++++++++++++++++++ src/modules/Telemetry/Sensor/SHT31Sensor.h | 16 ++++++++++ 6 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/modules/Telemetry/Sensor/SHT31Sensor.cpp create mode 100644 src/modules/Telemetry/Sensor/SHT31Sensor.h diff --git a/platformio.ini b/platformio.ini index f3cf9f937..4461d1a5f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -105,4 +105,5 @@ lib_deps = adafruit/Adafruit INA260 Library@^1.5.0 adafruit/Adafruit INA219@^1.2.0 adafruit/Adafruit SHTC3 Library@^1.0.0 - adafruit/Adafruit LPS2X@^2.0.4 \ No newline at end of file + adafruit/Adafruit LPS2X@^2.0.4 + adafruit/Adafruit SHT31 Library@^2.2.0 diff --git a/src/configuration.h b/src/configuration.h index e68bd8d16..67862609a 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -114,6 +114,7 @@ along with this program. If not, see . #define SHTC3_ADDR 0x70 #define LPS22HB_ADDR 0x5C #define LPS22HB_ADDR_ALT 0x5D +#define SHT31_ADDR 0x44 // ----------------------------------------------------------------------------- // Security diff --git a/src/detect/i2cScan.h b/src/detect/i2cScan.h index 4eac50f03..940ebb100 100644 --- a/src/detect/i2cScan.h +++ b/src/detect/i2cScan.h @@ -191,6 +191,10 @@ void scanI2Cdevice() nodeTelemetrySensorsMap[TelemetrySensorType_MCP9808] = addr; DEBUG_MSG("MCP9808 sensor found\n"); } + if (addr == SHT31_ADDR) { + DEBUG_MSG("SHT31 sensor found\n"); + nodeTelemetrySensorsMap[TelemetrySensorType_SHT31] = addr; + } if (addr == SHTC3_ADDR) { DEBUG_MSG("SHTC3 sensor found\n"); nodeTelemetrySensorsMap[TelemetrySensorType_SHTC3] = addr; diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index cd5cd03bc..7668a1f19 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -20,6 +20,7 @@ #include "Sensor/INA219Sensor.h" #include "Sensor/SHTC3Sensor.h" #include "Sensor/LPS22HBSensor.h" +#include "Sensor/SHT31Sensor.h" BMP280Sensor bmp280Sensor; BME280Sensor bme280Sensor; @@ -29,6 +30,7 @@ INA260Sensor ina260Sensor; INA219Sensor ina219Sensor; SHTC3Sensor shtc3Sensor; LPS22HBSensor lps22hbSensor; +SHT31Sensor sht31Sensor; #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true @@ -93,6 +95,8 @@ int32_t EnvironmentTelemetryModule::runOnce() if (lps22hbSensor.hasSensor()) { result = lps22hbSensor.runOnce(); } + if (sht31Sensor.hasSensor()) + result = sht31Sensor.runOnce(); } return result; } else { @@ -211,6 +215,8 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) m.variant.environment_metrics.temperature = 0; m.variant.environment_metrics.voltage = 0; + if (sht31Sensor.hasSensor()) + sht31Sensor.getMetrics(&m); if (lps22hbSensor.hasSensor()) lps22hbSensor.getMetrics(&m); if (shtc3Sensor.hasSensor()) diff --git a/src/modules/Telemetry/Sensor/SHT31Sensor.cpp b/src/modules/Telemetry/Sensor/SHT31Sensor.cpp new file mode 100644 index 000000000..94591e6cb --- /dev/null +++ b/src/modules/Telemetry/Sensor/SHT31Sensor.cpp @@ -0,0 +1,31 @@ +#include "../mesh/generated/telemetry.pb.h" +#include "configuration.h" +#include "TelemetrySensor.h" +#include "SHT31Sensor.h" +#include + +SHT31Sensor::SHT31Sensor() : + TelemetrySensor(TelemetrySensorType_SHT31, "SHT31") +{ +} + +int32_t SHT31Sensor::runOnce() { + DEBUG_MSG("Init sensor: %s\n", sensorName); + if (!hasSensor()) { + return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; + } + status = sht31.begin(); + return initI2CSensor(); +} + +void SHT31Sensor::setup() +{ + // Set up oversampling and filter initialization +} + +bool SHT31Sensor::getMetrics(Telemetry *measurement) { + measurement->variant.environment_metrics.temperature = sht31.readTemperature(); + measurement->variant.environment_metrics.relative_humidity = sht31.readHumidity(); + + return true; +} diff --git a/src/modules/Telemetry/Sensor/SHT31Sensor.h b/src/modules/Telemetry/Sensor/SHT31Sensor.h new file mode 100644 index 000000000..5f236e21e --- /dev/null +++ b/src/modules/Telemetry/Sensor/SHT31Sensor.h @@ -0,0 +1,16 @@ +#include "../mesh/generated/telemetry.pb.h" +#include "TelemetrySensor.h" +#include + +class SHT31Sensor : virtual public TelemetrySensor { +private: + Adafruit_SHT31 sht31 = Adafruit_SHT31(); + +protected: + virtual void setup() override; + +public: + SHT31Sensor(); + virtual int32_t runOnce() override; + virtual bool getMetrics(Telemetry *measurement) override; +}; From b808ffa9aba2cba3c2ed35a7d40b2d3842dba562 Mon Sep 17 00:00:00 2001 From: caveman99 Date: Tue, 27 Dec 2022 12:53:56 +0000 Subject: [PATCH 42/52] [create-pull-request] automated change --- protobufs | 2 +- src/mesh/generated/admin.pb.c | 2 +- src/mesh/generated/admin.pb.h | 64 ++- src/mesh/generated/apponly.pb.c | 2 +- src/mesh/generated/apponly.pb.h | 4 +- src/mesh/generated/cannedmessages.pb.c | 2 +- src/mesh/generated/cannedmessages.pb.h | 4 +- src/mesh/generated/channel.pb.c | 2 +- src/mesh/generated/channel.pb.h | 36 +- src/mesh/generated/config.pb.c | 2 +- src/mesh/generated/config.pb.h | 501 +++++++++++++----- src/mesh/generated/device_metadata.pb.c | 2 +- src/mesh/generated/device_metadata.pb.h | 4 +- src/mesh/generated/deviceonly.pb.c | 2 +- src/mesh/generated/deviceonly.pb.h | 50 +- src/mesh/generated/localonly.pb.c | 2 +- src/mesh/generated/localonly.pb.h | 8 +- src/mesh/generated/mesh.pb.c | 2 +- src/mesh/generated/mesh.pb.h | 675 ++++++++++++------------ src/mesh/generated/module_config.pb.c | 2 +- src/mesh/generated/module_config.pb.h | 393 +++++++++----- src/mesh/generated/mqtt.pb.c | 2 +- src/mesh/generated/mqtt.pb.h | 4 +- src/mesh/generated/portnums.pb.c | 2 +- src/mesh/generated/portnums.pb.h | 56 +- src/mesh/generated/remote_hardware.pb.c | 2 +- src/mesh/generated/remote_hardware.pb.h | 35 +- src/mesh/generated/storeforward.pb.c | 2 +- src/mesh/generated/storeforward.pb.h | 111 ++-- src/mesh/generated/telemetry.pb.c | 2 +- src/mesh/generated/telemetry.pb.h | 51 +- 31 files changed, 1255 insertions(+), 773 deletions(-) diff --git a/protobufs b/protobufs index 0f2a3304e..bd8a7c72c 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 0f2a3304eebd89b5950ccfab98082778de3353a0 +Subproject commit bd8a7c72ca3d7989d6dd409de22fd0ae9f0c24e5 diff --git a/src/mesh/generated/admin.pb.c b/src/mesh/generated/admin.pb.c index 43c32055f..3fdd0cb3d 100644 --- a/src/mesh/generated/admin.pb.c +++ b/src/mesh/generated/admin.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #include "admin.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/admin.pb.h b/src/mesh/generated/admin.pb.h index 4257405fa..cdc62c795 100644 --- a/src/mesh/generated/admin.pb.h +++ b/src/mesh/generated/admin.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #ifndef PB_ADMIN_PB_H_INCLUDED #define PB_ADMIN_PB_H_INCLUDED @@ -15,32 +15,49 @@ #endif /* Enum definitions */ -typedef enum _AdminMessage_ConfigType { - AdminMessage_ConfigType_DEVICE_CONFIG = 0, - AdminMessage_ConfigType_POSITION_CONFIG = 1, - AdminMessage_ConfigType_POWER_CONFIG = 2, - AdminMessage_ConfigType_NETWORK_CONFIG = 3, - AdminMessage_ConfigType_DISPLAY_CONFIG = 4, - AdminMessage_ConfigType_LORA_CONFIG = 5, - AdminMessage_ConfigType_BLUETOOTH_CONFIG = 6 +/* TODO: REPLACE */ +typedef enum _AdminMessage_ConfigType { + /* TODO: REPLACE */ + AdminMessage_ConfigType_DEVICE_CONFIG = 0, + /* TODO: REPLACE */ + AdminMessage_ConfigType_POSITION_CONFIG = 1, + /* TODO: REPLACE */ + AdminMessage_ConfigType_POWER_CONFIG = 2, + /* TODO: REPLACE */ + AdminMessage_ConfigType_NETWORK_CONFIG = 3, + /* TODO: REPLACE */ + AdminMessage_ConfigType_DISPLAY_CONFIG = 4, + /* TODO: REPLACE */ + AdminMessage_ConfigType_LORA_CONFIG = 5, + /* TODO: REPLACE */ + AdminMessage_ConfigType_BLUETOOTH_CONFIG = 6 } AdminMessage_ConfigType; -typedef enum _AdminMessage_ModuleConfigType { - AdminMessage_ModuleConfigType_MQTT_CONFIG = 0, - AdminMessage_ModuleConfigType_SERIAL_CONFIG = 1, - AdminMessage_ModuleConfigType_EXTNOTIF_CONFIG = 2, - AdminMessage_ModuleConfigType_STOREFORWARD_CONFIG = 3, - AdminMessage_ModuleConfigType_RANGETEST_CONFIG = 4, - AdminMessage_ModuleConfigType_TELEMETRY_CONFIG = 5, - AdminMessage_ModuleConfigType_CANNEDMSG_CONFIG = 6, - AdminMessage_ModuleConfigType_AUDIO_CONFIG = 7 +/* TODO: REPLACE */ +typedef enum _AdminMessage_ModuleConfigType { + /* TODO: REPLACE */ + AdminMessage_ModuleConfigType_MQTT_CONFIG = 0, + /* TODO: REPLACE */ + AdminMessage_ModuleConfigType_SERIAL_CONFIG = 1, + /* TODO: REPLACE */ + AdminMessage_ModuleConfigType_EXTNOTIF_CONFIG = 2, + /* TODO: REPLACE */ + AdminMessage_ModuleConfigType_STOREFORWARD_CONFIG = 3, + /* TODO: REPLACE */ + AdminMessage_ModuleConfigType_RANGETEST_CONFIG = 4, + /* TODO: REPLACE */ + AdminMessage_ModuleConfigType_TELEMETRY_CONFIG = 5, + /* TODO: REPLACE */ + AdminMessage_ModuleConfigType_CANNEDMSG_CONFIG = 6, + /* TODO: REPLACE */ + AdminMessage_ModuleConfigType_AUDIO_CONFIG = 7 } AdminMessage_ModuleConfigType; /* Struct definitions */ /* This message is handled by the Admin module and is responsible for all settings/channel read/write operations. This message is used to do settings operations to both remote AND local nodes. (Prior to 1.2 these operations were done via special ToRadio operations) */ -typedef struct _AdminMessage { +typedef struct _AdminMessage { pb_size_t which_payload_variant; union { /* Send the specified channel in the response to this message @@ -112,6 +129,10 @@ typedef struct _AdminMessage { } AdminMessage; +#ifdef __cplusplus +extern "C" { +#endif + /* Helper constants for enums */ #define _AdminMessage_ConfigType_MIN AdminMessage_ConfigType_DEVICE_CONFIG #define _AdminMessage_ConfigType_MAX AdminMessage_ConfigType_BLUETOOTH_CONFIG @@ -121,10 +142,9 @@ typedef struct _AdminMessage { #define _AdminMessage_ModuleConfigType_MAX AdminMessage_ModuleConfigType_AUDIO_CONFIG #define _AdminMessage_ModuleConfigType_ARRAYSIZE ((AdminMessage_ModuleConfigType)(AdminMessage_ModuleConfigType_AUDIO_CONFIG+1)) +#define AdminMessage_payload_variant_get_config_request_ENUMTYPE AdminMessage_ConfigType +#define AdminMessage_payload_variant_get_module_config_request_ENUMTYPE AdminMessage_ModuleConfigType -#ifdef __cplusplus -extern "C" { -#endif /* Initializer values for message structs */ #define AdminMessage_init_default {0, {0}} diff --git a/src/mesh/generated/apponly.pb.c b/src/mesh/generated/apponly.pb.c index 092c8e0b3..ce68d47c6 100644 --- a/src/mesh/generated/apponly.pb.c +++ b/src/mesh/generated/apponly.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #include "apponly.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/apponly.pb.h b/src/mesh/generated/apponly.pb.h index e5e933873..e0a6d7cd5 100644 --- a/src/mesh/generated/apponly.pb.h +++ b/src/mesh/generated/apponly.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #ifndef PB_APPONLY_PB_H_INCLUDED #define PB_APPONLY_PB_H_INCLUDED @@ -17,7 +17,7 @@ any SECONDARY channels. No DISABLED channels are included. This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL */ -typedef struct _ChannelSet { +typedef struct _ChannelSet { /* Channel list with settings */ pb_size_t settings_count; ChannelSettings settings[8]; diff --git a/src/mesh/generated/cannedmessages.pb.c b/src/mesh/generated/cannedmessages.pb.c index faea133e6..448517f13 100644 --- a/src/mesh/generated/cannedmessages.pb.c +++ b/src/mesh/generated/cannedmessages.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #include "cannedmessages.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/cannedmessages.pb.h b/src/mesh/generated/cannedmessages.pb.h index 274601e4c..4981fef18 100644 --- a/src/mesh/generated/cannedmessages.pb.h +++ b/src/mesh/generated/cannedmessages.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #ifndef PB_CANNEDMESSAGES_PB_H_INCLUDED #define PB_CANNEDMESSAGES_PB_H_INCLUDED @@ -11,7 +11,7 @@ /* Struct definitions */ /* Canned message module configuration. */ -typedef struct _CannedMessageModuleConfig { +typedef struct _CannedMessageModuleConfig { /* Predefined messages for canned message module separated by '|' characters. */ char messages[201]; } CannedMessageModuleConfig; diff --git a/src/mesh/generated/channel.pb.c b/src/mesh/generated/channel.pb.c index 2df73d5b3..d8e4d8332 100644 --- a/src/mesh/generated/channel.pb.c +++ b/src/mesh/generated/channel.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #include "channel.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/channel.pb.h b/src/mesh/generated/channel.pb.h index cdb29c553..2dc16bce5 100644 --- a/src/mesh/generated/channel.pb.h +++ b/src/mesh/generated/channel.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #ifndef PB_CHANNEL_PB_H_INCLUDED #define PB_CHANNEL_PB_H_INCLUDED @@ -10,10 +10,23 @@ #endif /* Enum definitions */ -typedef enum _Channel_Role { - Channel_Role_DISABLED = 0, - Channel_Role_PRIMARY = 1, - Channel_Role_SECONDARY = 2 +/* How this channel is being used (or not). + Note: this field is an enum to give us options for the future. + In particular, someday we might make a 'SCANNING' option. + SCANNING channels could have different frequencies and the radio would + occasionally check that freq to see if anything is being transmitted. + For devices that have multiple physical radios attached, we could keep multiple PRIMARY/SCANNING channels active at once to allow + cross band routing as needed. + If a device has only a single radio (the common case) only one channel can be PRIMARY at a time + (but any number of SECONDARY channels can't be sent received on that common frequency) */ +typedef enum _Channel_Role { + /* This channel is not in use right now */ + Channel_Role_DISABLED = 0, + /* This channel is used to set the frequency for the radio - all other enabled channels must be SECONDARY */ + Channel_Role_PRIMARY = 1, + /* Secondary channels are only used for encryption/decryption/authentication purposes. + Their radio settings (freq etc) are ignored, only psk is used. */ + Channel_Role_SECONDARY = 2 } Channel_Role; /* Struct definitions */ @@ -37,7 +50,7 @@ typedef PB_BYTES_ARRAY_T(32) ChannelSettings_psk_t; FIXME: Add description of multi-channel support and how primary vs secondary channels are used. FIXME: explain how apps use channels for security. explain how remote settings and remote gpio are managed as an example */ -typedef struct _ChannelSettings { +typedef struct _ChannelSettings { /* Deprecated in favor of LoraConfig.channel_num */ uint32_t channel_num; /* A simple pre-shared key for now for crypto. @@ -79,7 +92,7 @@ typedef struct _ChannelSettings { } ChannelSettings; /* A pair of a channel number, mode and the (sharable) settings for that channel */ -typedef struct _Channel { +typedef struct _Channel { /* The index of this channel in the channel table (from 0 to MAX_NUM_CHANNELS-1) (Someday - not currently implemented) An index of -1 could be used to mean "set by name", in which case the target node will find and set the channel by settings.name. */ @@ -92,15 +105,18 @@ typedef struct _Channel { } Channel; +#ifdef __cplusplus +extern "C" { +#endif + /* Helper constants for enums */ #define _Channel_Role_MIN Channel_Role_DISABLED #define _Channel_Role_MAX Channel_Role_SECONDARY #define _Channel_Role_ARRAYSIZE ((Channel_Role)(Channel_Role_SECONDARY+1)) -#ifdef __cplusplus -extern "C" { -#endif +#define Channel_role_ENUMTYPE Channel_Role + /* Initializer values for message structs */ #define ChannelSettings_init_default {0, {0, {0}}, "", 0, 0, 0} diff --git a/src/mesh/generated/config.pb.c b/src/mesh/generated/config.pb.c index c5bc69552..8d1c55934 100644 --- a/src/mesh/generated/config.pb.c +++ b/src/mesh/generated/config.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #include "config.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/config.pb.h b/src/mesh/generated/config.pb.h index cdc89664c..95252bb4e 100644 --- a/src/mesh/generated/config.pb.h +++ b/src/mesh/generated/config.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #ifndef PB_CONFIG_PB_H_INCLUDED #define PB_CONFIG_PB_H_INCLUDED @@ -10,169 +10,371 @@ #endif /* Enum definitions */ -typedef enum _Config_DeviceConfig_Role { - Config_DeviceConfig_Role_CLIENT = 0, - Config_DeviceConfig_Role_CLIENT_MUTE = 1, - Config_DeviceConfig_Role_ROUTER = 2, - Config_DeviceConfig_Role_ROUTER_CLIENT = 3 +/* Defines the device's role on the Mesh network */ +typedef enum _Config_DeviceConfig_Role { + /* Client device role */ + Config_DeviceConfig_Role_CLIENT = 0, + /* Client Mute device role + Same as a client except packets will not hop over this node, does not contribute to routing packets for mesh. */ + Config_DeviceConfig_Role_CLIENT_MUTE = 1, + /* Router device role. + Mesh packets will prefer to be routed over this node. This node will not be used by client apps. + The wifi/ble radios and the oled screen will be put to sleep. */ + Config_DeviceConfig_Role_ROUTER = 2, + /* Router Client device role + Mesh packets will prefer to be routed over this node. The Router Client can be used as both a Router and an app connected Client. */ + Config_DeviceConfig_Role_ROUTER_CLIENT = 3 } Config_DeviceConfig_Role; -typedef enum _Config_PositionConfig_PositionFlags { - Config_PositionConfig_PositionFlags_UNSET = 0, - Config_PositionConfig_PositionFlags_ALTITUDE = 1, - Config_PositionConfig_PositionFlags_ALTITUDE_MSL = 2, - Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION = 4, - Config_PositionConfig_PositionFlags_DOP = 8, - Config_PositionConfig_PositionFlags_HVDOP = 16, - Config_PositionConfig_PositionFlags_SATINVIEW = 32, - Config_PositionConfig_PositionFlags_SEQ_NO = 64, - Config_PositionConfig_PositionFlags_TIMESTAMP = 128, - Config_PositionConfig_PositionFlags_HEADING = 256, - Config_PositionConfig_PositionFlags_SPEED = 512 +/* Bit field of boolean configuration options, indicating which optional + fields to include when assembling POSITION messages + Longitude and latitude are always included (also time if GPS-synced) + NOTE: the more fields are included, the larger the message will be - + leading to longer airtime and a higher risk of packet loss */ +typedef enum _Config_PositionConfig_PositionFlags { + /* Required for compilation */ + Config_PositionConfig_PositionFlags_UNSET = 0, + /* Include an altitude value (if available) */ + Config_PositionConfig_PositionFlags_ALTITUDE = 1, + /* Altitude value is MSL */ + Config_PositionConfig_PositionFlags_ALTITUDE_MSL = 2, + /* Include geoidal separation */ + Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION = 4, + /* Include the DOP value ; PDOP used by default, see below */ + Config_PositionConfig_PositionFlags_DOP = 8, + /* If POS_DOP set, send separate HDOP / VDOP values instead of PDOP */ + Config_PositionConfig_PositionFlags_HVDOP = 16, + /* Include number of "satellites in view" */ + Config_PositionConfig_PositionFlags_SATINVIEW = 32, + /* Include a sequence number incremented per packet */ + Config_PositionConfig_PositionFlags_SEQ_NO = 64, + /* Include positional timestamp (from GPS solution) */ + Config_PositionConfig_PositionFlags_TIMESTAMP = 128, + /* Include positional heading + Intended for use with vehicle not walking speeds + walking speeds are likely to be error prone like the compass */ + Config_PositionConfig_PositionFlags_HEADING = 256, + /* Include positional speed + Intended for use with vehicle not walking speeds + walking speeds are likely to be error prone like the compass */ + Config_PositionConfig_PositionFlags_SPEED = 512 } Config_PositionConfig_PositionFlags; -typedef enum _Config_NetworkConfig_EthMode { - Config_NetworkConfig_EthMode_DHCP = 0, - Config_NetworkConfig_EthMode_STATIC = 1 +typedef enum _Config_NetworkConfig_EthMode { + /* obtain ip address via DHCP */ + Config_NetworkConfig_EthMode_DHCP = 0, + /* use static ip address */ + Config_NetworkConfig_EthMode_STATIC = 1 } Config_NetworkConfig_EthMode; -typedef enum _Config_DisplayConfig_GpsCoordinateFormat { - Config_DisplayConfig_GpsCoordinateFormat_DEC = 0, - Config_DisplayConfig_GpsCoordinateFormat_DMS = 1, - Config_DisplayConfig_GpsCoordinateFormat_UTM = 2, - Config_DisplayConfig_GpsCoordinateFormat_MGRS = 3, - Config_DisplayConfig_GpsCoordinateFormat_OLC = 4, - Config_DisplayConfig_GpsCoordinateFormat_OSGR = 5 +/* How the GPS coordinates are displayed on the OLED screen. */ +typedef enum _Config_DisplayConfig_GpsCoordinateFormat { + /* GPS coordinates are displayed in the normal decimal degrees format: + DD.DDDDDD DDD.DDDDDD */ + Config_DisplayConfig_GpsCoordinateFormat_DEC = 0, + /* GPS coordinates are displayed in the degrees minutes seconds format: + DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant */ + Config_DisplayConfig_GpsCoordinateFormat_DMS = 1, + /* Universal Transverse Mercator format: + ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing */ + Config_DisplayConfig_GpsCoordinateFormat_UTM = 2, + /* Military Grid Reference System format: + ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square, + E is easting, N is northing */ + Config_DisplayConfig_GpsCoordinateFormat_MGRS = 3, + /* Open Location Code (aka Plus Codes). */ + Config_DisplayConfig_GpsCoordinateFormat_OLC = 4, + /* Ordnance Survey Grid Reference (the National Grid System of the UK). + Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square, + E is the easting, N is the northing */ + Config_DisplayConfig_GpsCoordinateFormat_OSGR = 5 } Config_DisplayConfig_GpsCoordinateFormat; -typedef enum _Config_DisplayConfig_DisplayUnits { - Config_DisplayConfig_DisplayUnits_METRIC = 0, - Config_DisplayConfig_DisplayUnits_IMPERIAL = 1 +/* Unit display preference */ +typedef enum _Config_DisplayConfig_DisplayUnits { + /* Metric (Default) */ + Config_DisplayConfig_DisplayUnits_METRIC = 0, + /* Imperial */ + Config_DisplayConfig_DisplayUnits_IMPERIAL = 1 } Config_DisplayConfig_DisplayUnits; -typedef enum _Config_DisplayConfig_OledType { - Config_DisplayConfig_OledType_OLED_AUTO = 0, - Config_DisplayConfig_OledType_OLED_SSD1306 = 1, - Config_DisplayConfig_OledType_OLED_SH1106 = 2 +/* Override OLED outo detect with this if it fails. */ +typedef enum _Config_DisplayConfig_OledType { + /* Default / Auto */ + Config_DisplayConfig_OledType_OLED_AUTO = 0, + /* Default / Auto */ + Config_DisplayConfig_OledType_OLED_SSD1306 = 1, + /* Default / Auto */ + Config_DisplayConfig_OledType_OLED_SH1106 = 2 } Config_DisplayConfig_OledType; -typedef enum _Config_LoRaConfig_RegionCode { - Config_LoRaConfig_RegionCode_UNSET = 0, - Config_LoRaConfig_RegionCode_US = 1, - Config_LoRaConfig_RegionCode_EU_433 = 2, - Config_LoRaConfig_RegionCode_EU_868 = 3, - Config_LoRaConfig_RegionCode_CN = 4, - Config_LoRaConfig_RegionCode_JP = 5, - Config_LoRaConfig_RegionCode_ANZ = 6, - Config_LoRaConfig_RegionCode_KR = 7, - Config_LoRaConfig_RegionCode_TW = 8, - Config_LoRaConfig_RegionCode_RU = 9, - Config_LoRaConfig_RegionCode_IN = 10, - Config_LoRaConfig_RegionCode_NZ_865 = 11, - Config_LoRaConfig_RegionCode_TH = 12, - Config_LoRaConfig_RegionCode_LORA_24 = 13 +typedef enum _Config_LoRaConfig_RegionCode { + /* Region is not set */ + Config_LoRaConfig_RegionCode_UNSET = 0, + /* United States */ + Config_LoRaConfig_RegionCode_US = 1, + /* European Union 433mhz */ + Config_LoRaConfig_RegionCode_EU_433 = 2, + /* European Union 433mhz */ + Config_LoRaConfig_RegionCode_EU_868 = 3, + /* China */ + Config_LoRaConfig_RegionCode_CN = 4, + /* Japan */ + Config_LoRaConfig_RegionCode_JP = 5, + /* Australia / New Zealand */ + Config_LoRaConfig_RegionCode_ANZ = 6, + /* Korea */ + Config_LoRaConfig_RegionCode_KR = 7, + /* Taiwan */ + Config_LoRaConfig_RegionCode_TW = 8, + /* Russia */ + Config_LoRaConfig_RegionCode_RU = 9, + /* India */ + Config_LoRaConfig_RegionCode_IN = 10, + /* New Zealand 865mhz */ + Config_LoRaConfig_RegionCode_NZ_865 = 11, + /* Thailand */ + Config_LoRaConfig_RegionCode_TH = 12, + /* WLAN Band */ + Config_LoRaConfig_RegionCode_LORA_24 = 13 } Config_LoRaConfig_RegionCode; -typedef enum _Config_LoRaConfig_ModemPreset { - Config_LoRaConfig_ModemPreset_LONG_FAST = 0, - Config_LoRaConfig_ModemPreset_LONG_SLOW = 1, - Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW = 2, - Config_LoRaConfig_ModemPreset_MEDIUM_SLOW = 3, - Config_LoRaConfig_ModemPreset_MEDIUM_FAST = 4, - Config_LoRaConfig_ModemPreset_SHORT_SLOW = 5, - Config_LoRaConfig_ModemPreset_SHORT_FAST = 6 +/* Standard predefined channel settings + Note: these mappings must match ModemPreset Choice in the device code. */ +typedef enum _Config_LoRaConfig_ModemPreset { + /* Long Range - Fast */ + Config_LoRaConfig_ModemPreset_LONG_FAST = 0, + /* Long Range - Slow */ + Config_LoRaConfig_ModemPreset_LONG_SLOW = 1, + /* Very Long Range - Slow */ + Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW = 2, + /* Medium Range - Slow */ + Config_LoRaConfig_ModemPreset_MEDIUM_SLOW = 3, + /* Medium Range - Fast */ + Config_LoRaConfig_ModemPreset_MEDIUM_FAST = 4, + /* Short Range - Slow */ + Config_LoRaConfig_ModemPreset_SHORT_SLOW = 5, + /* Short Range - Fast */ + Config_LoRaConfig_ModemPreset_SHORT_FAST = 6 } Config_LoRaConfig_ModemPreset; -typedef enum _Config_BluetoothConfig_PairingMode { - Config_BluetoothConfig_PairingMode_RANDOM_PIN = 0, - Config_BluetoothConfig_PairingMode_FIXED_PIN = 1, - Config_BluetoothConfig_PairingMode_NO_PIN = 2 +typedef enum _Config_BluetoothConfig_PairingMode { + /* Device generates a random pin that will be shown on the screen of the device for pairing */ + Config_BluetoothConfig_PairingMode_RANDOM_PIN = 0, + /* Device requires a specified fixed pin for pairing */ + Config_BluetoothConfig_PairingMode_FIXED_PIN = 1, + /* Device requires no pin for pairing */ + Config_BluetoothConfig_PairingMode_NO_PIN = 2 } Config_BluetoothConfig_PairingMode; /* Struct definitions */ -typedef struct _Config_BluetoothConfig { - bool enabled; - Config_BluetoothConfig_PairingMode mode; - uint32_t fixed_pin; -} Config_BluetoothConfig; - -typedef struct _Config_DeviceConfig { +/* Configuration */ +typedef struct _Config_DeviceConfig { + /* Sets the role of node */ Config_DeviceConfig_Role role; + /* Disabling this will disable the SerialConsole by not initilizing the StreamAPI */ bool serial_enabled; + /* By default we turn off logging as soon as an API client connects (to keep shared serial link quiet). + Set this to true to leave the debug log outputting even when API is active. */ bool debug_log_enabled; + /* For boards without a hard wired button, this is the pin number that will be used + Boards that have more than one button can swap the function with this one. defaults to BUTTON_PIN if defined. */ uint32_t button_gpio; + /* For boards without a PWM buzzer, this is the pin number that will be used + Defaults to PIN_BUZZER if defined. */ uint32_t buzzer_gpio; } Config_DeviceConfig; -typedef struct _Config_DisplayConfig { - uint32_t screen_on_secs; - Config_DisplayConfig_GpsCoordinateFormat gps_format; - uint32_t auto_screen_carousel_secs; - bool compass_north_top; - bool flip_screen; - Config_DisplayConfig_DisplayUnits units; - Config_DisplayConfig_OledType oled; -} Config_DisplayConfig; - -typedef struct _Config_LoRaConfig { - bool use_preset; - Config_LoRaConfig_ModemPreset modem_preset; - uint16_t bandwidth; - uint32_t spread_factor; - uint8_t coding_rate; - float frequency_offset; - Config_LoRaConfig_RegionCode region; - uint32_t hop_limit; - bool tx_enabled; - int8_t tx_power; - uint16_t channel_num; - bool override_duty_cycle; - pb_size_t ignore_incoming_count; - uint32_t ignore_incoming[3]; -} Config_LoRaConfig; - -typedef struct _Config_NetworkConfig_IpV4Config { - uint32_t ip; - uint32_t gateway; - uint32_t subnet; - uint32_t dns; -} Config_NetworkConfig_IpV4Config; - -typedef struct _Config_PositionConfig { +/* Position Config */ +typedef struct _Config_PositionConfig { + /* We should send our position this often (but only if it has changed significantly) + Defaults to 15 minutes */ uint32_t position_broadcast_secs; + /* Adaptive position braoadcast, which is now the default. */ bool position_broadcast_smart_enabled; + /* If set, this node is at a fixed position. + We will generate GPS position updates at the regular interval, but use whatever the last lat/lon/alt we have for the node. + The lat/lon/alt can be set by an internal GPS or with the help of the app. */ bool fixed_position; + /* Is GPS enabled for this node? */ bool gps_enabled; + /* How often should we try to get GPS position (in seconds) + or zero for the default of once every 30 seconds + or a very large value (maxint) to update only once at boot. */ uint32_t gps_update_interval; + /* How long should we try to get our position during each gps_update_interval attempt? (in seconds) + Or if zero, use the default of 30 seconds. + If we don't get a new gps fix in that time, the gps will be put into sleep until the next gps_update_rate + window. */ uint32_t gps_attempt_time; + /* Bit field of boolean configuration options for POSITION messages + (bitwise OR of PositionFlags) */ uint32_t position_flags; + /* (Re)define GPS_RX_PIN for your board. */ uint32_t rx_gpio; + /* (Re)define GPS_TX_PIN for your board. */ uint32_t tx_gpio; } Config_PositionConfig; -typedef struct _Config_PowerConfig { +/* Power Config\ + See [Power Config](/docs/settings/config/power) for additional power config details. */ +typedef struct _Config_PowerConfig { + /* If set, we are powered from a low-current source (i.e. solar), so even if it looks like we have power flowing in + we should try to minimize power consumption as much as possible. + YOU DO NOT NEED TO SET THIS IF YOU'VE set is_router (it is implied in that case). + Advanced Option */ bool is_power_saving; + /* If non-zero, the device will fully power off this many seconds after external power is removed. */ uint32_t on_battery_shutdown_after_secs; + /* Ratio of voltage divider for battery pin eg. 3.20 (R1=100k, R2=220k) + Overrides the ADC_MULTIPLIER defined in variant for battery voltage calculation. + Should be set to floating point value between 2 and 4 + Fixes issues on Heltec v2 */ float adc_multiplier_override; + /* Wait Bluetooth Seconds + The number of seconds for to wait before turning off BLE in No Bluetooth states + 0 for default of 1 minute */ uint32_t wait_bluetooth_secs; + /* Mesh Super Deep Sleep Timeout Seconds + While in Light Sleep if this value is exceeded we will lower into super deep sleep + for sds_secs (default 1 year) or a button press + 0 for default of two hours, MAXUINT for disabled */ uint32_t mesh_sds_timeout_secs; + /* Super Deep Sleep Seconds + While in Light Sleep if mesh_sds_timeout_secs is exceeded we will lower into super deep sleep + for this value (default 1 year) or a button press + 0 for default of one year */ uint32_t sds_secs; + /* Light Sleep Seconds + In light sleep the CPU is suspended, LoRa radio is on, BLE is off an GPS is on + ESP32 Only + 0 for default of 300 */ uint32_t ls_secs; + /* Minimum Wake Seconds + While in light sleep when we receive packets on the LoRa radio we will wake and handle them and stay awake in no BLE mode for this value + 0 for default of 10 seconds */ uint32_t min_wake_secs; } Config_PowerConfig; -typedef struct _Config_NetworkConfig { +typedef struct _Config_NetworkConfig_IpV4Config { + /* Static IP address */ + uint32_t ip; + /* Static gateway address */ + uint32_t gateway; + /* Static subnet mask */ + uint32_t subnet; + /* Static DNS server address */ + uint32_t dns; +} Config_NetworkConfig_IpV4Config; + +/* Network Config */ +typedef struct _Config_NetworkConfig { + /* Enable WiFi (disables Bluetooth) */ bool wifi_enabled; + /* If set, this node will try to join the specified wifi network and + acquire an address via DHCP */ char wifi_ssid[33]; + /* If set, will be use to authenticate to the named wifi */ char wifi_psk[64]; + /* NTP server to use if WiFi is conneced, defaults to `0.pool.ntp.org` */ char ntp_server[33]; + /* Enable Ethernet */ bool eth_enabled; + /* acquire an address via DHCP or assign static */ Config_NetworkConfig_EthMode eth_mode; + /* struct to keep static address */ bool has_ipv4_config; Config_NetworkConfig_IpV4Config ipv4_config; } Config_NetworkConfig; -typedef struct _Config { +/* Display Config */ +typedef struct _Config_DisplayConfig { + /* Number of seconds the screen stays on after pressing the user button or receiving a message + 0 for default of one minute MAXUINT for always on */ + uint32_t screen_on_secs; + /* How the GPS coordinates are formatted on the OLED screen. */ + Config_DisplayConfig_GpsCoordinateFormat gps_format; + /* Automatically toggles to the next page on the screen like a carousel, based the specified interval in seconds. + Potentially useful for devices without user buttons. */ + uint32_t auto_screen_carousel_secs; + /* If this is set, the displayed compass will always point north. if unset, the old behaviour + (top of display is heading direction) is used. */ + bool compass_north_top; + /* Flip screen vertically, for cases that mount the screen upside down */ + bool flip_screen; + /* Perferred display units */ + Config_DisplayConfig_DisplayUnits units; + /* Override auto-detect in screen */ + Config_DisplayConfig_OledType oled; +} Config_DisplayConfig; + +/* Lora Config */ +typedef struct _Config_LoRaConfig { + /* When enabled, the `modem_preset` fields will be adheared to, else the `bandwidth`/`spread_factor`/`coding_rate` + will be taked from their respective manually defined fields */ + bool use_preset; + /* Either modem_config or bandwidth/spreading/coding will be specified - NOT BOTH. + As a heuristic: If bandwidth is specified, do not use modem_config. + Because protobufs take ZERO space when the value is zero this works out nicely. + This value is replaced by bandwidth/spread_factor/coding_rate. + If you'd like to experiment with other options add them to MeshRadio.cpp in the device code. */ + Config_LoRaConfig_ModemPreset modem_preset; + /* Bandwidth in MHz + Certain bandwidth numbers are 'special' and will be converted to the + appropriate floating point value: 31 -> 31.25MHz */ + uint16_t bandwidth; + /* A number from 7 to 12. + Indicates number of chirps per symbol as 1<. Note: app developers are encouraged to also use the following standard @@ -481,8 +369,62 @@ typedef struct _User { bool is_licensed; } User; +/* A message used in our Dynamic Source Routing protocol (RFC 4728 based) */ +typedef struct _RouteDiscovery { + /* The list of nodenums this packet has visited so far */ + pb_size_t route_count; + uint32_t route[8]; +} RouteDiscovery; + +/* A Routing control Data packet handled by the routing module */ +typedef struct _Routing { + pb_size_t which_variant; + union { + /* A route request going from the requester */ + RouteDiscovery route_request; + /* A route reply */ + RouteDiscovery route_reply; + /* A failure in delivering a message (usually used for routing control messages, but might be provided + in addition to ack.fail_id to provide details on the type of failure). */ + Routing_Error error_reason; + }; +} Routing; + +typedef PB_BYTES_ARRAY_T(237) Data_payload_t; +/* (Formerly called SubPacket) + The payload portion fo a packet, this is the actual bytes that are sent + inside a radio packet (because from/to are broken out by the comms library) */ +typedef struct _Data { + /* Formerly named typ and of type Type */ + PortNum portnum; + /* TODO: REPLACE */ + Data_payload_t payload; + /* Not normally used, but for testing a sender can request that recipient + responds in kind (i.e. if it received a position, it should unicast back it's position). + Note: that if you set this on a broadcast you will receive many replies. */ + bool want_response; + /* The address of the destination node. + This field is is filled in by the mesh radio device software, application + layer software should never need it. + RouteDiscovery messages _must_ populate this. + Other message types might need to if they are doing multihop routing. */ + uint32_t dest; + /* The address of the original sender for this message. + This field should _only_ be populated for reliable multihop packets (to keep + packets small). */ + uint32_t source; + /* Only used in routing or response messages. + Indicates the original message ID that this message is reporting failure on. (formerly called original_id) */ + uint32_t request_id; + /* If set, this message is intened to be a reply to a previously sent message with the defined id. */ + uint32_t reply_id; + /* Defaults to false. If true, then what is in the payload should be treated as an emoji like giving + a message a heart or poop emoji. */ + uint32_t emoji; +} Data; + /* Waypoint message, used to share arbitrary locations across the mesh */ -typedef struct _Waypoint { +typedef struct _Waypoint { /* Id of the waypoint */ uint32_t id; /* latitude_i */ @@ -504,7 +446,7 @@ typedef PB_BYTES_ARRAY_T(256) MeshPacket_encrypted_t; /* A packet envelope sent/received over the mesh only payload_variant is sent in the payload portion of the LORA packet. The other fields are either not sent at all, or sent in the special 16 byte LORA header. */ -typedef struct _MeshPacket { +typedef struct _MeshPacket { /* The sending node number. Note: Our crypto implementation uses this field as well. See [crypto](/docs/overview/encryption) for details. @@ -588,7 +530,7 @@ typedef struct _MeshPacket { level etc) SET_CONFIG (switches device to a new set of radio params and preshared key, drops all existing nodes, force our node to rejoin this new group) Full information about a node on the mesh */ -typedef struct _NodeInfo { +typedef struct _NodeInfo { /* The node number */ uint32_t num; /* The user info for this node */ @@ -608,25 +550,80 @@ typedef struct _NodeInfo { DeviceMetrics device_metrics; } NodeInfo; -/* A Routing control Data packet handled by the routing module */ -typedef struct _Routing { - pb_size_t which_variant; - union { - /* A route request going from the requester */ - RouteDiscovery route_request; - /* A route reply */ - RouteDiscovery route_reply; - /* A failure in delivering a message (usually used for routing control messages, but might be provided - in addition to ack.fail_id to provide details on the type of failure). */ - Routing_Error error_reason; - }; -} Routing; +/* Unique local debugging info for this node + Note: we don't include position or the user info, because that will come in the + Sent to the phone in response to WantNodes. */ +typedef struct _MyNodeInfo { + /* Tells the phone what our node number is, default starting value is + lowbyte of macaddr, but it will be fixed if that is already in use */ + uint32_t my_node_num; + /* Note: This flag merely means we detected a hardware GPS in our node. + Not the same as UserPreferences.location_sharing */ + bool has_gps; + /* The maximum number of 'software' channels that can be set on this node. */ + uint32_t max_channels; + /* 0.0.5 etc... */ + char firmware_version[18]; + /* An error message we'd like to report back to the mothership through analytics. + It indicates a serious bug occurred on the device, the device coped with it, + but we still want to tell the devs about the bug. + This field will be cleared after the phone reads MyNodeInfo + (i.e. it will only be reported once) + a numeric error code to go with error message, zero means no error */ + CriticalErrorCode error_code; + /* A numeric error address (nonzero if available) */ + uint32_t error_address; + /* The total number of errors this node has ever encountered + (well - since the last time we discarded preferences) */ + uint32_t error_count; + /* The total number of reboots this node has ever encountered + (well - since the last time we discarded preferences) */ + uint32_t reboot_count; + /* Calculated bitrate of the current channel (in Bytes Per Second) */ + float bitrate; + /* How long before we consider a message abandoned and we can clear our + caches of any messages in flight Normally quite large to handle the worst case + message delivery time, 5 minutes. + Formerly called FLOOD_EXPIRE_TIME in the device code */ + uint32_t message_timeout_msec; + /* The minimum app version that can talk to this device. + Phone/PC apps should compare this to their build number and if too low tell the user they must update their app */ + uint32_t min_app_version; + /* 24 time windows of 1hr each with the airtime transmitted out of the device per hour. */ + pb_size_t air_period_tx_count; + uint32_t air_period_tx[8]; + /* 24 time windows of 1hr each with the airtime of valid packets for your mesh. */ + pb_size_t air_period_rx_count; + uint32_t air_period_rx[8]; + /* Is the device wifi capable? */ + bool has_wifi; + /* Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */ + float channel_utilization; + /* Percent of airtime for transmission used within the last hour. */ + float air_util_tx; +} MyNodeInfo; + +/* Debug output from the device. + To minimize the size of records inside the device code, if a time/source/level is not set + on the message it is assumed to be a continuation of the previously sent message. + This allows the device code to use fixed maxlen 64 byte strings for messages, + and then extend as needed by emitting multiple records. */ +typedef struct _LogRecord { + /* Log levels, chosen to match python logging conventions. */ + char message[64]; + /* Seconds since 1970 - or 0 for unknown/unset */ + uint32_t time; + /* Usually based on thread name - if known */ + char source[8]; + /* Not yet set */ + LogRecord_Level level; +} LogRecord; /* Packets from the radio to the phone will appear on the fromRadio characteristic. It will support READ and NOTIFY. When a new packet arrives the device will BLE notify? It will sit in that descriptor until consumed by the phone, at which point the next item in the FIFO will be populated. */ -typedef struct _FromRadio { +typedef struct _FromRadio { /* The packet id, used to allow the phone to request missing read packets from the FIFO, see our bluetooth docs */ uint32_t id; @@ -663,7 +660,7 @@ typedef struct _FromRadio { /* Packets/commands to the radio will be written (reliably) to the toRadio characteristic. Once the write completes the phone can assume it is handled. */ -typedef struct _ToRadio { +typedef struct _ToRadio { pb_size_t which_payload_variant; union { /* Send this packet on the mesh */ @@ -684,6 +681,19 @@ typedef struct _ToRadio { }; } ToRadio; +typedef PB_BYTES_ARRAY_T(237) Compressed_data_t; +/* Compressed message payload */ +typedef struct _Compressed { + /* PortNum to determine the how to handle the compressed payload. */ + PortNum portnum; + /* Compressed data. */ + Compressed_data_t data; +} Compressed; + + +#ifdef __cplusplus +extern "C" { +#endif /* Helper constants for enums */ #define _HardwareModel_MIN HardwareModel_UNSET @@ -722,10 +732,29 @@ typedef struct _ToRadio { #define _LogRecord_Level_MAX LogRecord_Level_CRITICAL #define _LogRecord_Level_ARRAYSIZE ((LogRecord_Level)(LogRecord_Level_CRITICAL+1)) +#define Position_location_source_ENUMTYPE Position_LocSource +#define Position_altitude_source_ENUMTYPE Position_AltSource + +#define User_hw_model_ENUMTYPE HardwareModel + + +#define Routing_variant_error_reason_ENUMTYPE Routing_Error + +#define Data_portnum_ENUMTYPE PortNum + + +#define MeshPacket_priority_ENUMTYPE MeshPacket_Priority +#define MeshPacket_delayed_ENUMTYPE MeshPacket_Delayed + + +#define MyNodeInfo_error_code_ENUMTYPE CriticalErrorCode + +#define LogRecord_level_ENUMTYPE LogRecord_Level + + + +#define Compressed_portnum_ENUMTYPE PortNum -#ifdef __cplusplus -extern "C" { -#endif /* Initializer values for message structs */ #define Position_init_default {0, 0, 0, 0, _Position_LocSource_MIN, _Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} @@ -756,36 +785,6 @@ extern "C" { #define Compressed_init_zero {_PortNum_MIN, {0, {0}}} /* Field tags (for use in manual encoding/decoding) */ -#define Compressed_portnum_tag 1 -#define Compressed_data_tag 2 -#define Data_portnum_tag 1 -#define Data_payload_tag 2 -#define Data_want_response_tag 3 -#define Data_dest_tag 4 -#define Data_source_tag 5 -#define Data_request_id_tag 6 -#define Data_reply_id_tag 7 -#define Data_emoji_tag 8 -#define LogRecord_message_tag 1 -#define LogRecord_time_tag 2 -#define LogRecord_source_tag 3 -#define LogRecord_level_tag 4 -#define MyNodeInfo_my_node_num_tag 1 -#define MyNodeInfo_has_gps_tag 2 -#define MyNodeInfo_max_channels_tag 3 -#define MyNodeInfo_firmware_version_tag 4 -#define MyNodeInfo_error_code_tag 5 -#define MyNodeInfo_error_address_tag 6 -#define MyNodeInfo_error_count_tag 7 -#define MyNodeInfo_reboot_count_tag 8 -#define MyNodeInfo_bitrate_tag 9 -#define MyNodeInfo_message_timeout_msec_tag 10 -#define MyNodeInfo_min_app_version_tag 11 -#define MyNodeInfo_air_period_tx_tag 12 -#define MyNodeInfo_air_period_rx_tag 13 -#define MyNodeInfo_has_wifi_tag 14 -#define MyNodeInfo_channel_utilization_tag 15 -#define MyNodeInfo_air_util_tx_tag 16 #define Position_latitude_i_tag 1 #define Position_longitude_i_tag 2 #define Position_altitude_tag 3 @@ -808,13 +807,24 @@ extern "C" { #define Position_sensor_id_tag 20 #define Position_next_update_tag 21 #define Position_seq_number_tag 22 -#define RouteDiscovery_route_tag 1 #define User_id_tag 1 #define User_long_name_tag 2 #define User_short_name_tag 3 #define User_macaddr_tag 4 #define User_hw_model_tag 5 #define User_is_licensed_tag 6 +#define RouteDiscovery_route_tag 1 +#define Routing_route_request_tag 1 +#define Routing_route_reply_tag 2 +#define Routing_error_reason_tag 3 +#define Data_portnum_tag 1 +#define Data_payload_tag 2 +#define Data_want_response_tag 3 +#define Data_dest_tag 4 +#define Data_source_tag 5 +#define Data_request_id_tag 6 +#define Data_reply_id_tag 7 +#define Data_emoji_tag 8 #define Waypoint_id_tag 1 #define Waypoint_latitude_i_tag 2 #define Waypoint_longitude_i_tag 3 @@ -841,9 +851,26 @@ extern "C" { #define NodeInfo_snr_tag 4 #define NodeInfo_last_heard_tag 5 #define NodeInfo_device_metrics_tag 6 -#define Routing_route_request_tag 1 -#define Routing_route_reply_tag 2 -#define Routing_error_reason_tag 3 +#define MyNodeInfo_my_node_num_tag 1 +#define MyNodeInfo_has_gps_tag 2 +#define MyNodeInfo_max_channels_tag 3 +#define MyNodeInfo_firmware_version_tag 4 +#define MyNodeInfo_error_code_tag 5 +#define MyNodeInfo_error_address_tag 6 +#define MyNodeInfo_error_count_tag 7 +#define MyNodeInfo_reboot_count_tag 8 +#define MyNodeInfo_bitrate_tag 9 +#define MyNodeInfo_message_timeout_msec_tag 10 +#define MyNodeInfo_min_app_version_tag 11 +#define MyNodeInfo_air_period_tx_tag 12 +#define MyNodeInfo_air_period_rx_tag 13 +#define MyNodeInfo_has_wifi_tag 14 +#define MyNodeInfo_channel_utilization_tag 15 +#define MyNodeInfo_air_util_tx_tag 16 +#define LogRecord_message_tag 1 +#define LogRecord_time_tag 2 +#define LogRecord_source_tag 3 +#define LogRecord_level_tag 4 #define FromRadio_id_tag 1 #define FromRadio_packet_tag 2 #define FromRadio_my_info_tag 3 @@ -857,6 +884,8 @@ extern "C" { #define ToRadio_packet_tag 1 #define ToRadio_want_config_id_tag 3 #define ToRadio_disconnect_tag 4 +#define Compressed_portnum_tag 1 +#define Compressed_data_tag 2 /* Struct field encoding specification for nanopb */ #define Position_FIELDLIST(X, a) \ diff --git a/src/mesh/generated/module_config.pb.c b/src/mesh/generated/module_config.pb.c index 381ce6351..7d7de956e 100644 --- a/src/mesh/generated/module_config.pb.c +++ b/src/mesh/generated/module_config.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #include "module_config.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/module_config.pb.h b/src/mesh/generated/module_config.pb.h index 4b2e29f17..6d677fd41 100644 --- a/src/mesh/generated/module_config.pb.h +++ b/src/mesh/generated/module_config.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #ifndef PB_MODULE_CONFIG_PB_H_INCLUDED #define PB_MODULE_CONFIG_PB_H_INCLUDED @@ -10,134 +10,244 @@ #endif /* Enum definitions */ -typedef enum _ModuleConfig_AudioConfig_Audio_Baud { - ModuleConfig_AudioConfig_Audio_Baud_CODEC2_DEFAULT = 0, - ModuleConfig_AudioConfig_Audio_Baud_CODEC2_3200 = 1, - ModuleConfig_AudioConfig_Audio_Baud_CODEC2_2400 = 2, - ModuleConfig_AudioConfig_Audio_Baud_CODEC2_1600 = 3, - ModuleConfig_AudioConfig_Audio_Baud_CODEC2_1400 = 4, - ModuleConfig_AudioConfig_Audio_Baud_CODEC2_1300 = 5, - ModuleConfig_AudioConfig_Audio_Baud_CODEC2_1200 = 6, - ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 = 7, - ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700B = 8 +/* Baudrate for codec2 voice */ +typedef enum _ModuleConfig_AudioConfig_Audio_Baud { + ModuleConfig_AudioConfig_Audio_Baud_CODEC2_DEFAULT = 0, + ModuleConfig_AudioConfig_Audio_Baud_CODEC2_3200 = 1, + ModuleConfig_AudioConfig_Audio_Baud_CODEC2_2400 = 2, + ModuleConfig_AudioConfig_Audio_Baud_CODEC2_1600 = 3, + ModuleConfig_AudioConfig_Audio_Baud_CODEC2_1400 = 4, + ModuleConfig_AudioConfig_Audio_Baud_CODEC2_1300 = 5, + ModuleConfig_AudioConfig_Audio_Baud_CODEC2_1200 = 6, + ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 = 7, + ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700B = 8 } ModuleConfig_AudioConfig_Audio_Baud; -typedef enum _ModuleConfig_SerialConfig_Serial_Baud { - ModuleConfig_SerialConfig_Serial_Baud_BAUD_DEFAULT = 0, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_110 = 1, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_300 = 2, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_600 = 3, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_1200 = 4, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_2400 = 5, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_4800 = 6, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_9600 = 7, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_19200 = 8, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_38400 = 9, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_57600 = 10, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_115200 = 11, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_230400 = 12, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_460800 = 13, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_576000 = 14, - ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600 = 15 +/* TODO: REPLACE */ +typedef enum _ModuleConfig_SerialConfig_Serial_Baud { + ModuleConfig_SerialConfig_Serial_Baud_BAUD_DEFAULT = 0, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_110 = 1, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_300 = 2, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_600 = 3, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_1200 = 4, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_2400 = 5, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_4800 = 6, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_9600 = 7, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_19200 = 8, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_38400 = 9, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_57600 = 10, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_115200 = 11, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_230400 = 12, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_460800 = 13, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_576000 = 14, + ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600 = 15 } ModuleConfig_SerialConfig_Serial_Baud; -typedef enum _ModuleConfig_SerialConfig_Serial_Mode { - ModuleConfig_SerialConfig_Serial_Mode_DEFAULT = 0, - ModuleConfig_SerialConfig_Serial_Mode_SIMPLE = 1, - ModuleConfig_SerialConfig_Serial_Mode_PROTO = 2, - ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG = 3, - ModuleConfig_SerialConfig_Serial_Mode_NMEA = 4 +/* TODO: REPLACE */ +typedef enum _ModuleConfig_SerialConfig_Serial_Mode { + ModuleConfig_SerialConfig_Serial_Mode_DEFAULT = 0, + ModuleConfig_SerialConfig_Serial_Mode_SIMPLE = 1, + ModuleConfig_SerialConfig_Serial_Mode_PROTO = 2, + ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG = 3, + ModuleConfig_SerialConfig_Serial_Mode_NMEA = 4 } ModuleConfig_SerialConfig_Serial_Mode; -typedef enum _ModuleConfig_CannedMessageConfig_InputEventChar { - ModuleConfig_CannedMessageConfig_InputEventChar_NONE = 0, - ModuleConfig_CannedMessageConfig_InputEventChar_UP = 17, - ModuleConfig_CannedMessageConfig_InputEventChar_DOWN = 18, - ModuleConfig_CannedMessageConfig_InputEventChar_LEFT = 19, - ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT = 20, - ModuleConfig_CannedMessageConfig_InputEventChar_SELECT = 10, - ModuleConfig_CannedMessageConfig_InputEventChar_BACK = 27, - ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL = 24 +/* TODO: REPLACE */ +typedef enum _ModuleConfig_CannedMessageConfig_InputEventChar { + /* TODO: REPLACE */ + ModuleConfig_CannedMessageConfig_InputEventChar_NONE = 0, + /* TODO: REPLACE */ + ModuleConfig_CannedMessageConfig_InputEventChar_UP = 17, + /* TODO: REPLACE */ + ModuleConfig_CannedMessageConfig_InputEventChar_DOWN = 18, + /* TODO: REPLACE */ + ModuleConfig_CannedMessageConfig_InputEventChar_LEFT = 19, + /* TODO: REPLACE */ + ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT = 20, + /* '\n' */ + ModuleConfig_CannedMessageConfig_InputEventChar_SELECT = 10, + /* TODO: REPLACE */ + ModuleConfig_CannedMessageConfig_InputEventChar_BACK = 27, + /* TODO: REPLACE */ + ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL = 24 } ModuleConfig_CannedMessageConfig_InputEventChar; /* Struct definitions */ -typedef struct _ModuleConfig_AudioConfig { - bool codec2_enabled; - uint8_t ptt_pin; - ModuleConfig_AudioConfig_Audio_Baud bitrate; - uint8_t i2s_ws; - uint8_t i2s_sd; - uint8_t i2s_din; - uint8_t i2s_sck; -} ModuleConfig_AudioConfig; - -typedef struct _ModuleConfig_CannedMessageConfig { - bool rotary1_enabled; - uint32_t inputbroker_pin_a; - uint32_t inputbroker_pin_b; - uint32_t inputbroker_pin_press; - ModuleConfig_CannedMessageConfig_InputEventChar inputbroker_event_cw; - ModuleConfig_CannedMessageConfig_InputEventChar inputbroker_event_ccw; - ModuleConfig_CannedMessageConfig_InputEventChar inputbroker_event_press; - bool updown1_enabled; - bool enabled; - char allow_input_source[16]; - bool send_bell; -} ModuleConfig_CannedMessageConfig; - -typedef struct _ModuleConfig_ExternalNotificationConfig { - bool enabled; - uint32_t output_ms; - uint32_t output; - bool active; - bool alert_message; - bool alert_bell; - bool use_pwm; -} ModuleConfig_ExternalNotificationConfig; - -typedef struct _ModuleConfig_MQTTConfig { +/* MQTT Client Config */ +typedef struct _ModuleConfig_MQTTConfig { + /* If a meshtastic node is able to reach the internet it will normally attempt to gateway any channels that are marked as + is_uplink_enabled or is_downlink_enabled. */ bool enabled; + /* The server to use for our MQTT global message gateway feature. + If not set, the default server will be used */ char address[32]; + /* MQTT username to use (most useful for a custom MQTT server). + If using a custom server, this will be honoured even if empty. + If using the default server, this will only be honoured if set, otherwise the device will use the default username */ char username[64]; + /* MQTT password to use (most useful for a custom MQTT server). + If using a custom server, this will be honoured even if empty. + If using the default server, this will only be honoured if set, otherwise the device will use the default password */ char password[64]; + /* Whether to send encrypted or decrypted packets to MQTT. + This parameter is only honoured if you also set server + (the default official mqtt.meshtastic.org server can handle encrypted packets) + Decrypted packets may be useful for external systems that want to consume meshtastic packets */ bool encryption_enabled; + /* Whether to send / consume json packets on MQTT */ bool json_enabled; } ModuleConfig_MQTTConfig; -typedef struct _ModuleConfig_RangeTestConfig { - bool enabled; - uint32_t sender; - bool save; -} ModuleConfig_RangeTestConfig; +/* Audio Config for codec2 voice */ +typedef struct _ModuleConfig_AudioConfig { + /* Whether Audio is enabled */ + bool codec2_enabled; + /* PTT Pin */ + uint8_t ptt_pin; + /* The audio sample rate to use for codec2 */ + ModuleConfig_AudioConfig_Audio_Baud bitrate; + /* I2S Word Select */ + uint8_t i2s_ws; + /* I2S Data IN */ + uint8_t i2s_sd; + /* I2S Data OUT */ + uint8_t i2s_din; + /* I2S Clock */ + uint8_t i2s_sck; +} ModuleConfig_AudioConfig; -typedef struct _ModuleConfig_SerialConfig { +/* Serial Config */ +typedef struct _ModuleConfig_SerialConfig { + /* Preferences for the SerialModule + FIXME - Move this out of UserPreferences and into a section for module configuration. */ bool enabled; + /* TODO: REPLACE */ bool echo; + /* TODO: REPLACE */ uint32_t rxd; + /* TODO: REPLACE */ uint32_t txd; + /* TODO: REPLACE */ ModuleConfig_SerialConfig_Serial_Baud baud; + /* TODO: REPLACE */ uint32_t timeout; + /* TODO: REPLACE */ ModuleConfig_SerialConfig_Serial_Mode mode; } ModuleConfig_SerialConfig; -typedef struct _ModuleConfig_StoreForwardConfig { +/* External Notifications Config */ +typedef struct _ModuleConfig_ExternalNotificationConfig { + /* Enable the ExternalNotificationModule */ bool enabled; + /* When using in On/Off mode, keep the output on for this many + milliseconds. Default 1000ms (1 second). */ + uint32_t output_ms; + /* Define the output pin GPIO setting Defaults to + EXT_NOTIFY_OUT if set for the board. + In standalone devices this pin should drive the LED to match the UI. */ + uint32_t output; + /* IF this is true, the 'output' Pin will be pulled active high, false + means active low. */ + bool active; + /* True: Alert when a text message arrives (output) */ + bool alert_message; + /* True: Alert when the bell character is received (output) */ + bool alert_bell; + /* use a PWM output instead of a simple on/off output. This will ignore + the 'output', 'output_ms' and 'active' settings and use the + device.buzzer_gpio instead. */ + bool use_pwm; + /* Optional: Define a secondary output pin for a vibra motor + This is used in standalone devices to match the UI. */ + uint8_t output_vibra; + /* Optional: Define a tertiary output pin for an active buzzer + This is used in standalone devices to to match the UI. */ + uint8_t output_buzzer; + /* True: Alert when a text message arrives (output_vibra) */ + bool alert_message_vibra; + /* True: Alert when a text message arrives (output_buzzer) */ + bool alert_message_buzzer; + /* True: Alert when the bell character is received (output_vibra) */ + bool alert_bell_vibra; + /* True: Alert when the bell character is received (output_buzzer) */ + bool alert_bell_buzzer; + /* The notification will toggle with 'output_ms' for this time of seconds. + Default is 0 which means don't repeat at all. 60 would mean blink + and/or beep for 60 seconds */ + uint16_t nag_timeout; +} ModuleConfig_ExternalNotificationConfig; + +/* Store and Forward Module Config */ +typedef struct _ModuleConfig_StoreForwardConfig { + /* Enable the Store and Forward Module */ + bool enabled; + /* TODO: REPLACE */ bool heartbeat; + /* TODO: REPLACE */ uint32_t records; + /* TODO: REPLACE */ uint32_t history_return_max; + /* TODO: REPLACE */ uint32_t history_return_window; } ModuleConfig_StoreForwardConfig; -typedef struct _ModuleConfig_TelemetryConfig { +/* Preferences for the RangeTestModule */ +typedef struct _ModuleConfig_RangeTestConfig { + /* Enable the Range Test Module */ + bool enabled; + /* Send out range test messages from this node */ + uint32_t sender; + /* Bool value indicating that this node should save a RangeTest.csv file. + ESP32 Only */ + bool save; +} ModuleConfig_RangeTestConfig; + +/* Configuration for both device and environment metrics */ +typedef struct _ModuleConfig_TelemetryConfig { + /* Interval in seconds of how often we should try to send our + device metrics to the mesh */ uint32_t device_update_interval; uint32_t environment_update_interval; + /* Preferences for the Telemetry Module (Environment) + Enable/Disable the telemetry measurement module measurement collection */ bool environment_measurement_enabled; + /* Enable/Disable the telemetry measurement module on-device display */ bool environment_screen_enabled; + /* We'll always read the sensor in Celsius, but sometimes we might want to + display the results in Fahrenheit as a "user preference". */ bool environment_display_fahrenheit; } ModuleConfig_TelemetryConfig; +/* TODO: REPLACE */ +typedef struct _ModuleConfig_CannedMessageConfig { + /* Enable the rotary encoder #1. This is a 'dumb' encoder sending pulses on both A and B pins while rotating. */ + bool rotary1_enabled; + /* GPIO pin for rotary encoder A port. */ + uint32_t inputbroker_pin_a; + /* GPIO pin for rotary encoder B port. */ + uint32_t inputbroker_pin_b; + /* GPIO pin for rotary encoder Press port. */ + uint32_t inputbroker_pin_press; + /* Generate input event on CW of this kind. */ + ModuleConfig_CannedMessageConfig_InputEventChar inputbroker_event_cw; + /* Generate input event on CCW of this kind. */ + ModuleConfig_CannedMessageConfig_InputEventChar inputbroker_event_ccw; + /* Generate input event on Press of this kind. */ + ModuleConfig_CannedMessageConfig_InputEventChar inputbroker_event_press; + /* Enable the Up/Down/Select input device. Can be RAK rotary encoder or 3 buttons. Uses the a/b/press definitions from inputbroker. */ + bool updown1_enabled; + /* Enable/disable CannedMessageModule. */ + bool enabled; + /* Input event origin accepted by the canned message module. + Can be e.g. "rotEnc1", "upDownEnc1" or keyword "_any" */ + char allow_input_source[16]; + /* CannedMessageModule also sends a bell character with the messages. + ExternalNotificationModule can benefit from this feature. */ + bool send_bell; +} ModuleConfig_CannedMessageConfig; + /* Module Config */ -typedef struct _ModuleConfig { +typedef struct _ModuleConfig { pb_size_t which_payload_variant; union { /* TODO: REPLACE */ @@ -160,6 +270,10 @@ typedef struct _ModuleConfig { } ModuleConfig; +#ifdef __cplusplus +extern "C" { +#endif + /* Helper constants for enums */ #define _ModuleConfig_AudioConfig_Audio_Baud_MIN ModuleConfig_AudioConfig_Audio_Baud_CODEC2_DEFAULT #define _ModuleConfig_AudioConfig_Audio_Baud_MAX ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700B @@ -178,16 +292,27 @@ typedef struct _ModuleConfig { #define _ModuleConfig_CannedMessageConfig_InputEventChar_ARRAYSIZE ((ModuleConfig_CannedMessageConfig_InputEventChar)(ModuleConfig_CannedMessageConfig_InputEventChar_BACK+1)) -#ifdef __cplusplus -extern "C" { -#endif + +#define ModuleConfig_AudioConfig_bitrate_ENUMTYPE ModuleConfig_AudioConfig_Audio_Baud + +#define ModuleConfig_SerialConfig_baud_ENUMTYPE ModuleConfig_SerialConfig_Serial_Baud +#define ModuleConfig_SerialConfig_mode_ENUMTYPE ModuleConfig_SerialConfig_Serial_Mode + + + + + +#define ModuleConfig_CannedMessageConfig_inputbroker_event_cw_ENUMTYPE ModuleConfig_CannedMessageConfig_InputEventChar +#define ModuleConfig_CannedMessageConfig_inputbroker_event_ccw_ENUMTYPE ModuleConfig_CannedMessageConfig_InputEventChar +#define ModuleConfig_CannedMessageConfig_inputbroker_event_press_ENUMTYPE ModuleConfig_CannedMessageConfig_InputEventChar + /* Initializer values for message structs */ #define ModuleConfig_init_default {0, {ModuleConfig_MQTTConfig_init_default}} #define ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0} #define ModuleConfig_AudioConfig_init_default {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} -#define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0} +#define ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0} #define ModuleConfig_RangeTestConfig_init_default {0, 0, 0} #define ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0} @@ -196,13 +321,19 @@ extern "C" { #define ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0} #define ModuleConfig_AudioConfig_init_zero {0, 0, _ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0} #define ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _ModuleConfig_SerialConfig_Serial_Mode_MIN} -#define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0} +#define ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0} #define ModuleConfig_RangeTestConfig_init_zero {0, 0, 0} #define ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0} #define ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0} /* Field tags (for use in manual encoding/decoding) */ +#define ModuleConfig_MQTTConfig_enabled_tag 1 +#define ModuleConfig_MQTTConfig_address_tag 2 +#define ModuleConfig_MQTTConfig_username_tag 3 +#define ModuleConfig_MQTTConfig_password_tag 4 +#define ModuleConfig_MQTTConfig_encryption_enabled_tag 5 +#define ModuleConfig_MQTTConfig_json_enabled_tag 6 #define ModuleConfig_AudioConfig_codec2_enabled_tag 1 #define ModuleConfig_AudioConfig_ptt_pin_tag 2 #define ModuleConfig_AudioConfig_bitrate_tag 3 @@ -210,6 +341,40 @@ extern "C" { #define ModuleConfig_AudioConfig_i2s_sd_tag 5 #define ModuleConfig_AudioConfig_i2s_din_tag 6 #define ModuleConfig_AudioConfig_i2s_sck_tag 7 +#define ModuleConfig_SerialConfig_enabled_tag 1 +#define ModuleConfig_SerialConfig_echo_tag 2 +#define ModuleConfig_SerialConfig_rxd_tag 3 +#define ModuleConfig_SerialConfig_txd_tag 4 +#define ModuleConfig_SerialConfig_baud_tag 5 +#define ModuleConfig_SerialConfig_timeout_tag 6 +#define ModuleConfig_SerialConfig_mode_tag 7 +#define ModuleConfig_ExternalNotificationConfig_enabled_tag 1 +#define ModuleConfig_ExternalNotificationConfig_output_ms_tag 2 +#define ModuleConfig_ExternalNotificationConfig_output_tag 3 +#define ModuleConfig_ExternalNotificationConfig_active_tag 4 +#define ModuleConfig_ExternalNotificationConfig_alert_message_tag 5 +#define ModuleConfig_ExternalNotificationConfig_alert_bell_tag 6 +#define ModuleConfig_ExternalNotificationConfig_use_pwm_tag 7 +#define ModuleConfig_ExternalNotificationConfig_output_vibra_tag 8 +#define ModuleConfig_ExternalNotificationConfig_output_buzzer_tag 9 +#define ModuleConfig_ExternalNotificationConfig_alert_message_vibra_tag 10 +#define ModuleConfig_ExternalNotificationConfig_alert_message_buzzer_tag 11 +#define ModuleConfig_ExternalNotificationConfig_alert_bell_vibra_tag 12 +#define ModuleConfig_ExternalNotificationConfig_alert_bell_buzzer_tag 13 +#define ModuleConfig_ExternalNotificationConfig_nag_timeout_tag 14 +#define ModuleConfig_StoreForwardConfig_enabled_tag 1 +#define ModuleConfig_StoreForwardConfig_heartbeat_tag 2 +#define ModuleConfig_StoreForwardConfig_records_tag 3 +#define ModuleConfig_StoreForwardConfig_history_return_max_tag 4 +#define ModuleConfig_StoreForwardConfig_history_return_window_tag 5 +#define ModuleConfig_RangeTestConfig_enabled_tag 1 +#define ModuleConfig_RangeTestConfig_sender_tag 2 +#define ModuleConfig_RangeTestConfig_save_tag 3 +#define ModuleConfig_TelemetryConfig_device_update_interval_tag 1 +#define ModuleConfig_TelemetryConfig_environment_update_interval_tag 2 +#define ModuleConfig_TelemetryConfig_environment_measurement_enabled_tag 3 +#define ModuleConfig_TelemetryConfig_environment_screen_enabled_tag 4 +#define ModuleConfig_TelemetryConfig_environment_display_fahrenheit_tag 5 #define ModuleConfig_CannedMessageConfig_rotary1_enabled_tag 1 #define ModuleConfig_CannedMessageConfig_inputbroker_pin_a_tag 2 #define ModuleConfig_CannedMessageConfig_inputbroker_pin_b_tag 3 @@ -221,39 +386,6 @@ extern "C" { #define ModuleConfig_CannedMessageConfig_enabled_tag 9 #define ModuleConfig_CannedMessageConfig_allow_input_source_tag 10 #define ModuleConfig_CannedMessageConfig_send_bell_tag 11 -#define ModuleConfig_ExternalNotificationConfig_enabled_tag 1 -#define ModuleConfig_ExternalNotificationConfig_output_ms_tag 2 -#define ModuleConfig_ExternalNotificationConfig_output_tag 3 -#define ModuleConfig_ExternalNotificationConfig_active_tag 4 -#define ModuleConfig_ExternalNotificationConfig_alert_message_tag 5 -#define ModuleConfig_ExternalNotificationConfig_alert_bell_tag 6 -#define ModuleConfig_ExternalNotificationConfig_use_pwm_tag 7 -#define ModuleConfig_MQTTConfig_enabled_tag 1 -#define ModuleConfig_MQTTConfig_address_tag 2 -#define ModuleConfig_MQTTConfig_username_tag 3 -#define ModuleConfig_MQTTConfig_password_tag 4 -#define ModuleConfig_MQTTConfig_encryption_enabled_tag 5 -#define ModuleConfig_MQTTConfig_json_enabled_tag 6 -#define ModuleConfig_RangeTestConfig_enabled_tag 1 -#define ModuleConfig_RangeTestConfig_sender_tag 2 -#define ModuleConfig_RangeTestConfig_save_tag 3 -#define ModuleConfig_SerialConfig_enabled_tag 1 -#define ModuleConfig_SerialConfig_echo_tag 2 -#define ModuleConfig_SerialConfig_rxd_tag 3 -#define ModuleConfig_SerialConfig_txd_tag 4 -#define ModuleConfig_SerialConfig_baud_tag 5 -#define ModuleConfig_SerialConfig_timeout_tag 6 -#define ModuleConfig_SerialConfig_mode_tag 7 -#define ModuleConfig_StoreForwardConfig_enabled_tag 1 -#define ModuleConfig_StoreForwardConfig_heartbeat_tag 2 -#define ModuleConfig_StoreForwardConfig_records_tag 3 -#define ModuleConfig_StoreForwardConfig_history_return_max_tag 4 -#define ModuleConfig_StoreForwardConfig_history_return_window_tag 5 -#define ModuleConfig_TelemetryConfig_device_update_interval_tag 1 -#define ModuleConfig_TelemetryConfig_environment_update_interval_tag 2 -#define ModuleConfig_TelemetryConfig_environment_measurement_enabled_tag 3 -#define ModuleConfig_TelemetryConfig_environment_screen_enabled_tag 4 -#define ModuleConfig_TelemetryConfig_environment_display_fahrenheit_tag 5 #define ModuleConfig_mqtt_tag 1 #define ModuleConfig_serial_tag 2 #define ModuleConfig_external_notification_tag 3 @@ -323,7 +455,14 @@ X(a, STATIC, SINGULAR, UINT32, output, 3) \ X(a, STATIC, SINGULAR, BOOL, active, 4) \ X(a, STATIC, SINGULAR, BOOL, alert_message, 5) \ X(a, STATIC, SINGULAR, BOOL, alert_bell, 6) \ -X(a, STATIC, SINGULAR, BOOL, use_pwm, 7) +X(a, STATIC, SINGULAR, BOOL, use_pwm, 7) \ +X(a, STATIC, SINGULAR, UINT32, output_vibra, 8) \ +X(a, STATIC, SINGULAR, UINT32, output_buzzer, 9) \ +X(a, STATIC, SINGULAR, BOOL, alert_message_vibra, 10) \ +X(a, STATIC, SINGULAR, BOOL, alert_message_buzzer, 11) \ +X(a, STATIC, SINGULAR, BOOL, alert_bell_vibra, 12) \ +X(a, STATIC, SINGULAR, BOOL, alert_bell_buzzer, 13) \ +X(a, STATIC, SINGULAR, UINT32, nag_timeout, 14) #define ModuleConfig_ExternalNotificationConfig_CALLBACK NULL #define ModuleConfig_ExternalNotificationConfig_DEFAULT NULL @@ -391,7 +530,7 @@ extern const pb_msgdesc_t ModuleConfig_CannedMessageConfig_msg; /* Maximum encoded size of messages (where known) */ #define ModuleConfig_AudioConfig_size 19 #define ModuleConfig_CannedMessageConfig_size 49 -#define ModuleConfig_ExternalNotificationConfig_size 22 +#define ModuleConfig_ExternalNotificationConfig_size 40 #define ModuleConfig_MQTTConfig_size 169 #define ModuleConfig_RangeTestConfig_size 10 #define ModuleConfig_SerialConfig_size 26 diff --git a/src/mesh/generated/mqtt.pb.c b/src/mesh/generated/mqtt.pb.c index 4fd6c6812..9c2a7eb63 100644 --- a/src/mesh/generated/mqtt.pb.c +++ b/src/mesh/generated/mqtt.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #include "mqtt.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/mqtt.pb.h b/src/mesh/generated/mqtt.pb.h index 789d38865..36de161cc 100644 --- a/src/mesh/generated/mqtt.pb.h +++ b/src/mesh/generated/mqtt.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #ifndef PB_MQTT_PB_H_INCLUDED #define PB_MQTT_PB_H_INCLUDED @@ -12,7 +12,7 @@ /* Struct definitions */ /* This message wraps a MeshPacket with extra metadata about the sender and how it arrived. */ -typedef struct _ServiceEnvelope { +typedef struct _ServiceEnvelope { /* The (probably encrypted) packet */ struct _MeshPacket *packet; /* The global channel ID it was sent on */ diff --git a/src/mesh/generated/portnums.pb.c b/src/mesh/generated/portnums.pb.c index c8ef38c58..4e674aac5 100644 --- a/src/mesh/generated/portnums.pb.c +++ b/src/mesh/generated/portnums.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #include "portnums.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/portnums.pb.h b/src/mesh/generated/portnums.pb.h index 74805ed10..9e6a0b5b4 100644 --- a/src/mesh/generated/portnums.pb.h +++ b/src/mesh/generated/portnums.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #ifndef PB_PORTNUMS_PB_H_INCLUDED #define PB_PORTNUMS_PB_H_INCLUDED @@ -22,89 +22,89 @@ Note: This was formerly a Type enum named 'typ' with the same id # We have change to this 'portnum' based scheme for specifying app handlers for particular payloads. This change is backwards compatible by treating the legacy OPAQUE/CLEAR_TEXT values identically. */ -typedef enum _PortNum { +typedef enum _PortNum { /* Deprecated: do not use in new code (formerly called OPAQUE) A message sent from a device outside of the mesh, in a form the mesh does not understand NOTE: This must be 0, because it is documented in IMeshService.aidl to be so */ - PortNum_UNKNOWN_APP = 0, + PortNum_UNKNOWN_APP = 0, /* A simple UTF-8 text message, which even the little micros in the mesh can understand and show on their screen eventually in some circumstances even signal might send messages in this form (see below) */ - PortNum_TEXT_MESSAGE_APP = 1, + PortNum_TEXT_MESSAGE_APP = 1, /* Reserved for built-in GPIO/example app. See remote_hardware.proto/HardwareMessage for details on the message sent/received to this port number */ - PortNum_REMOTE_HARDWARE_APP = 2, + PortNum_REMOTE_HARDWARE_APP = 2, /* The built-in position messaging app. Payload is a [Position](/docs/developers/protobufs/api#position) message */ - PortNum_POSITION_APP = 3, + PortNum_POSITION_APP = 3, /* The built-in user info app. Payload is a [User](/docs/developers/protobufs/api#user) message */ - PortNum_NODEINFO_APP = 4, + PortNum_NODEINFO_APP = 4, /* Protocol control packets for mesh protocol use. Payload is a [Routing](/docs/developers/protobufs/api#routing) message */ - PortNum_ROUTING_APP = 5, + PortNum_ROUTING_APP = 5, /* Admin control packets. Payload is a [AdminMessage](/docs/developers/protobufs/api#adminmessage) message */ - PortNum_ADMIN_APP = 6, + PortNum_ADMIN_APP = 6, /* Compressed TEXT_MESSAGE payloads. */ - PortNum_TEXT_MESSAGE_COMPRESSED_APP = 7, + PortNum_TEXT_MESSAGE_COMPRESSED_APP = 7, /* Waypoint payloads. Payload is a [Waypoint](/docs/developers/protobufs/api#waypoint) message */ - PortNum_WAYPOINT_APP = 8, + PortNum_WAYPOINT_APP = 8, /* Audio Payloads. Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now */ - PortNum_AUDIO_APP = 9, + PortNum_AUDIO_APP = 9, /* Provides a 'ping' service that replies to any packet it receives. Also serves as a small example module. */ - PortNum_REPLY_APP = 32, + PortNum_REPLY_APP = 32, /* Used for the python IP tunnel feature */ - PortNum_IP_TUNNEL_APP = 33, + PortNum_IP_TUNNEL_APP = 33, /* Provides a hardware serial interface to send and receive from the Meshtastic network. Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network. Maximum packet size of 240 bytes. Module is disabled by default can be turned on by setting SERIAL_MODULE_ENABLED = 1 in SerialPlugh.cpp. */ - PortNum_SERIAL_APP = 64, + PortNum_SERIAL_APP = 64, /* STORE_FORWARD_APP (Work in Progress) Maintained by Jm Casler (MC Hamster) : jm@casler.org */ - PortNum_STORE_FORWARD_APP = 65, + PortNum_STORE_FORWARD_APP = 65, /* Optional port for messages for the range test module. */ - PortNum_RANGE_TEST_APP = 66, + PortNum_RANGE_TEST_APP = 66, /* Provides a format to send and receive telemetry data from the Meshtastic network. Maintained by Charles Crossan (crossan007) : crossan007@gmail.com */ - PortNum_TELEMETRY_APP = 67, + PortNum_TELEMETRY_APP = 67, /* Experimental tools for estimating node position without a GPS Maintained by Github user a-f-G-U-C (a Meshtastic contributor) Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS */ - PortNum_ZPS_APP = 68, + PortNum_ZPS_APP = 68, /* Used to let multiple instances of Linux native applications communicate as if they did using their LoRa chip. Maintained by GitHub user GUVWAF. Project files at https://github.com/GUVWAF/Meshtasticator */ - PortNum_SIMULATOR_APP = 69, + PortNum_SIMULATOR_APP = 69, /* Provides a traceroute functionality to show the route a packet towards a certain destination would take on the mesh. */ - PortNum_TRACEROUTE_APP = 70, + PortNum_TRACEROUTE_APP = 70, /* Private applications should use portnums >= 256. To simplify initial development and testing you can use "PRIVATE_APP" in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) */ - PortNum_PRIVATE_APP = 256, + PortNum_PRIVATE_APP = 256, /* ATAK Forwarder Module https://github.com/paulmandal/atak-forwarder */ - PortNum_ATAK_FORWARDER = 257, + PortNum_ATAK_FORWARDER = 257, /* Currently we limit port nums to no higher than this value */ - PortNum_MAX = 511 + PortNum_MAX = 511 } PortNum; +#ifdef __cplusplus +extern "C" { +#endif + /* Helper constants for enums */ #define _PortNum_MIN PortNum_UNKNOWN_APP #define _PortNum_MAX PortNum_MAX #define _PortNum_ARRAYSIZE ((PortNum)(PortNum_MAX+1)) -#ifdef __cplusplus -extern "C" { -#endif - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/mesh/generated/remote_hardware.pb.c b/src/mesh/generated/remote_hardware.pb.c index 9168bd71f..de6c4dc4b 100644 --- a/src/mesh/generated/remote_hardware.pb.c +++ b/src/mesh/generated/remote_hardware.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #include "remote_hardware.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/remote_hardware.pb.h b/src/mesh/generated/remote_hardware.pb.h index d923c1189..6c09809c5 100644 --- a/src/mesh/generated/remote_hardware.pb.h +++ b/src/mesh/generated/remote_hardware.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #ifndef PB_REMOTE_HARDWARE_PB_H_INCLUDED #define PB_REMOTE_HARDWARE_PB_H_INCLUDED @@ -10,13 +10,22 @@ #endif /* Enum definitions */ -typedef enum _HardwareMessage_Type { - HardwareMessage_Type_UNSET = 0, - HardwareMessage_Type_WRITE_GPIOS = 1, - HardwareMessage_Type_WATCH_GPIOS = 2, - HardwareMessage_Type_GPIOS_CHANGED = 3, - HardwareMessage_Type_READ_GPIOS = 4, - HardwareMessage_Type_READ_GPIOS_REPLY = 5 +/* TODO: REPLACE */ +typedef enum _HardwareMessage_Type { + /* Unset/unused */ + HardwareMessage_Type_UNSET = 0, + /* Set gpio gpios based on gpio_mask/gpio_value */ + HardwareMessage_Type_WRITE_GPIOS = 1, + /* We are now interested in watching the gpio_mask gpios. + If the selected gpios change, please broadcast GPIOS_CHANGED. + Will implicitly change the gpios requested to be INPUT gpios. */ + HardwareMessage_Type_WATCH_GPIOS = 2, + /* The gpios listed in gpio_mask have changed, the new values are listed in gpio_value */ + HardwareMessage_Type_GPIOS_CHANGED = 3, + /* Read the gpios specified in gpio_mask, send back a READ_GPIOS_REPLY reply with gpio_value populated */ + HardwareMessage_Type_READ_GPIOS = 4, + /* A reply to READ_GPIOS. gpio_mask and gpio_value will be populated */ + HardwareMessage_Type_READ_GPIOS_REPLY = 5 } HardwareMessage_Type; /* Struct definitions */ @@ -29,7 +38,7 @@ typedef enum _HardwareMessage_Type { because no security yet (beyond the channel mechanism). It should be off by default and then protected based on some TBD mechanism (a special channel once multichannel support is included?) */ -typedef struct _HardwareMessage { +typedef struct _HardwareMessage { /* What type of HardwareMessage is this? */ HardwareMessage_Type type; /* What gpios are we changing. Not used for all MessageTypes, see MessageType for details */ @@ -40,15 +49,17 @@ typedef struct _HardwareMessage { } HardwareMessage; +#ifdef __cplusplus +extern "C" { +#endif + /* Helper constants for enums */ #define _HardwareMessage_Type_MIN HardwareMessage_Type_UNSET #define _HardwareMessage_Type_MAX HardwareMessage_Type_READ_GPIOS_REPLY #define _HardwareMessage_Type_ARRAYSIZE ((HardwareMessage_Type)(HardwareMessage_Type_READ_GPIOS_REPLY+1)) +#define HardwareMessage_type_ENUMTYPE HardwareMessage_Type -#ifdef __cplusplus -extern "C" { -#endif /* Initializer values for message structs */ #define HardwareMessage_init_default {_HardwareMessage_Type_MIN, 0, 0} diff --git a/src/mesh/generated/storeforward.pb.c b/src/mesh/generated/storeforward.pb.c index 034137116..69db0e3bf 100644 --- a/src/mesh/generated/storeforward.pb.c +++ b/src/mesh/generated/storeforward.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #include "storeforward.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/storeforward.pb.h b/src/mesh/generated/storeforward.pb.h index edc965cbd..625fb273c 100644 --- a/src/mesh/generated/storeforward.pb.h +++ b/src/mesh/generated/storeforward.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #ifndef PB_STOREFORWARD_PB_H_INCLUDED #define PB_STOREFORWARD_PB_H_INCLUDED @@ -10,48 +10,82 @@ #endif /* Enum definitions */ -typedef enum _StoreAndForward_RequestResponse { - StoreAndForward_RequestResponse_UNSET = 0, - StoreAndForward_RequestResponse_ROUTER_ERROR = 1, - StoreAndForward_RequestResponse_ROUTER_HEARTBEAT = 2, - StoreAndForward_RequestResponse_ROUTER_PING = 3, - StoreAndForward_RequestResponse_ROUTER_PONG = 4, - StoreAndForward_RequestResponse_ROUTER_BUSY = 5, - StoreAndForward_RequestResponse_ROUTER_HISTORY = 6, - StoreAndForward_RequestResponse_CLIENT_ERROR = 101, - StoreAndForward_RequestResponse_CLIENT_HISTORY = 102, - StoreAndForward_RequestResponse_CLIENT_STATS = 103, - StoreAndForward_RequestResponse_CLIENT_PING = 104, - StoreAndForward_RequestResponse_CLIENT_PONG = 105, - StoreAndForward_RequestResponse_CLIENT_ABORT = 106 +/* 1 - 99 = From Router + 101 - 199 = From Client */ +typedef enum _StoreAndForward_RequestResponse { + /* Unset/unused */ + StoreAndForward_RequestResponse_UNSET = 0, + /* Router is an in error state. */ + StoreAndForward_RequestResponse_ROUTER_ERROR = 1, + /* Router heartbeat */ + StoreAndForward_RequestResponse_ROUTER_HEARTBEAT = 2, + /* Router has requested the client respond. This can work as a + "are you there" message. */ + StoreAndForward_RequestResponse_ROUTER_PING = 3, + /* The response to a "Ping" */ + StoreAndForward_RequestResponse_ROUTER_PONG = 4, + /* Router is currently busy. Please try again later. */ + StoreAndForward_RequestResponse_ROUTER_BUSY = 5, + /* Router is responding to a request for history. */ + StoreAndForward_RequestResponse_ROUTER_HISTORY = 6, + /* Client is an in error state. */ + StoreAndForward_RequestResponse_CLIENT_ERROR = 101, + /* Client has requested a replay from the router. */ + StoreAndForward_RequestResponse_CLIENT_HISTORY = 102, + /* Client has requested stats from the router. */ + StoreAndForward_RequestResponse_CLIENT_STATS = 103, + /* Client has requested the router respond. This can work as a + "are you there" message. */ + StoreAndForward_RequestResponse_CLIENT_PING = 104, + /* The response to a "Ping" */ + StoreAndForward_RequestResponse_CLIENT_PONG = 105, + /* Client has requested that the router abort processing the client's request */ + StoreAndForward_RequestResponse_CLIENT_ABORT = 106 } StoreAndForward_RequestResponse; /* Struct definitions */ -typedef struct _StoreAndForward_Heartbeat { - uint32_t period; - uint32_t secondary; -} StoreAndForward_Heartbeat; - -typedef struct _StoreAndForward_History { - uint32_t history_messages; - uint32_t window; - uint32_t last_request; -} StoreAndForward_History; - -typedef struct _StoreAndForward_Statistics { +/* TODO: REPLACE */ +typedef struct _StoreAndForward_Statistics { + /* Number of messages we have ever seen */ uint32_t messages_total; + /* Number of messages we have currently saved our history. */ uint32_t messages_saved; + /* Maximum number of messages we will save */ uint32_t messages_max; + /* Router uptime in seconds */ uint32_t up_time; + /* Number of times any client sent a request to the S&F. */ uint32_t requests; + /* Number of times the history was requested. */ uint32_t requests_history; + /* Is the heartbeat enabled on the server? */ bool heartbeat; + /* Is the heartbeat enabled on the server? */ uint32_t return_max; + /* Is the heartbeat enabled on the server? */ uint32_t return_window; } StoreAndForward_Statistics; /* TODO: REPLACE */ -typedef struct _StoreAndForward { +typedef struct _StoreAndForward_History { + /* Number of that will be sent to the client */ + uint32_t history_messages; + /* The window of messages that was used to filter the history client requested */ + uint32_t window; + /* The window of messages that was used to filter the history client requested */ + uint32_t last_request; +} StoreAndForward_History; + +/* TODO: REPLACE */ +typedef struct _StoreAndForward_Heartbeat { + /* Number of that will be sent to the client */ + uint32_t period; + /* If set, this is not the primary Store & Forward router on the mesh */ + uint32_t secondary; +} StoreAndForward_Heartbeat; + +/* TODO: REPLACE */ +typedef struct _StoreAndForward { /* TODO: REPLACE */ StoreAndForward_RequestResponse rr; /* TODO: REPLACE */ @@ -66,15 +100,20 @@ typedef struct _StoreAndForward { } StoreAndForward; +#ifdef __cplusplus +extern "C" { +#endif + /* Helper constants for enums */ #define _StoreAndForward_RequestResponse_MIN StoreAndForward_RequestResponse_UNSET #define _StoreAndForward_RequestResponse_MAX StoreAndForward_RequestResponse_CLIENT_ABORT #define _StoreAndForward_RequestResponse_ARRAYSIZE ((StoreAndForward_RequestResponse)(StoreAndForward_RequestResponse_CLIENT_ABORT+1)) +#define StoreAndForward_rr_ENUMTYPE StoreAndForward_RequestResponse + + + -#ifdef __cplusplus -extern "C" { -#endif /* 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} @@ -87,11 +126,6 @@ extern "C" { #define StoreAndForward_Heartbeat_init_zero {0, 0} /* Field tags (for use in manual encoding/decoding) */ -#define StoreAndForward_Heartbeat_period_tag 1 -#define StoreAndForward_Heartbeat_secondary_tag 2 -#define StoreAndForward_History_history_messages_tag 1 -#define StoreAndForward_History_window_tag 2 -#define StoreAndForward_History_last_request_tag 3 #define StoreAndForward_Statistics_messages_total_tag 1 #define StoreAndForward_Statistics_messages_saved_tag 2 #define StoreAndForward_Statistics_messages_max_tag 3 @@ -101,6 +135,11 @@ extern "C" { #define StoreAndForward_Statistics_heartbeat_tag 7 #define StoreAndForward_Statistics_return_max_tag 8 #define StoreAndForward_Statistics_return_window_tag 9 +#define StoreAndForward_History_history_messages_tag 1 +#define StoreAndForward_History_window_tag 2 +#define StoreAndForward_History_last_request_tag 3 +#define StoreAndForward_Heartbeat_period_tag 1 +#define StoreAndForward_Heartbeat_secondary_tag 2 #define StoreAndForward_rr_tag 1 #define StoreAndForward_stats_tag 2 #define StoreAndForward_history_tag 3 diff --git a/src/mesh/generated/telemetry.pb.c b/src/mesh/generated/telemetry.pb.c index a68af6e14..50202f4d6 100644 --- a/src/mesh/generated/telemetry.pb.c +++ b/src/mesh/generated/telemetry.pb.c @@ -1,5 +1,5 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #include "telemetry.pb.h" #if PB_PROTO_HEADER_VERSION != 40 diff --git a/src/mesh/generated/telemetry.pb.h b/src/mesh/generated/telemetry.pb.h index 8c5c68bfa..e5c71a2b7 100644 --- a/src/mesh/generated/telemetry.pb.h +++ b/src/mesh/generated/telemetry.pb.h @@ -1,5 +1,5 @@ /* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.6 */ +/* Generated by nanopb-0.4.7 */ #ifndef PB_TELEMETRY_PB_H_INCLUDED #define PB_TELEMETRY_PB_H_INCLUDED @@ -11,36 +11,38 @@ /* Enum definitions */ /* TODO: REPLACE */ -typedef enum _TelemetrySensorType { +typedef enum _TelemetrySensorType { /* No external telemetry sensor explicitly set */ - TelemetrySensorType_SENSOR_UNSET = 0, + TelemetrySensorType_SENSOR_UNSET = 0, /* High accuracy temperature, pressure, humidity */ - TelemetrySensorType_BME280 = 1, + TelemetrySensorType_BME280 = 1, /* High accuracy temperature, pressure, humidity, and air resistance */ - TelemetrySensorType_BME680 = 2, + TelemetrySensorType_BME680 = 2, /* Very high accuracy temperature */ - TelemetrySensorType_MCP9808 = 3, + TelemetrySensorType_MCP9808 = 3, /* Moderate accuracy current and voltage */ - TelemetrySensorType_INA260 = 4, + TelemetrySensorType_INA260 = 4, /* Moderate accuracy current and voltage */ - TelemetrySensorType_INA219 = 5, + TelemetrySensorType_INA219 = 5, /* High accuracy temperature and pressure */ - TelemetrySensorType_BMP280 = 6, + TelemetrySensorType_BMP280 = 6, /* High accuracy temperature and humidity */ - TelemetrySensorType_SHTC3 = 7, + TelemetrySensorType_SHTC3 = 7, /* High accuracy pressure */ - TelemetrySensorType_LPS22 = 8, + TelemetrySensorType_LPS22 = 8, /* 3-Axis magnetic sensor */ - TelemetrySensorType_QMC6310 = 9, + TelemetrySensorType_QMC6310 = 9, /* 6-Axis inertial measurement sensor */ - TelemetrySensorType_QMI8658 = 10, + TelemetrySensorType_QMI8658 = 10, /* 3-Axis magnetic sensor */ - TelemetrySensorType_QMC5883L = 11 + TelemetrySensorType_QMC5883L = 11, + /* High accuracy temperature and humidity */ + TelemetrySensorType_SHT31 = 12 } TelemetrySensorType; /* Struct definitions */ /* Key native device metrics such as battery level */ -typedef struct _DeviceMetrics { +typedef struct _DeviceMetrics { /* 1-100 (0 means powered) */ uint32_t battery_level; /* Voltage measured */ @@ -52,7 +54,7 @@ typedef struct _DeviceMetrics { } DeviceMetrics; /* Weather station or other environmental metrics */ -typedef struct _EnvironmentMetrics { +typedef struct _EnvironmentMetrics { /* Temperature measured */ float temperature; /* Relative humidity percent measured */ @@ -68,7 +70,7 @@ typedef struct _EnvironmentMetrics { } EnvironmentMetrics; /* Types of Measurements the telemetry module is equipped to handle */ -typedef struct _Telemetry { +typedef struct _Telemetry { /* This is usually not sent over the mesh (to save space), but it is sent from the phone so that the local device can set its RTC If it is sent over the mesh (because there are devices on the mesh without GPS), it will only @@ -85,16 +87,19 @@ typedef struct _Telemetry { } Telemetry; -/* Helper constants for enums */ -#define _TelemetrySensorType_MIN TelemetrySensorType_SENSOR_UNSET -#define _TelemetrySensorType_MAX TelemetrySensorType_QMC5883L -#define _TelemetrySensorType_ARRAYSIZE ((TelemetrySensorType)(TelemetrySensorType_QMC5883L+1)) - - #ifdef __cplusplus extern "C" { #endif +/* Helper constants for enums */ +#define _TelemetrySensorType_MIN TelemetrySensorType_SENSOR_UNSET +#define _TelemetrySensorType_MAX TelemetrySensorType_SHT31 +#define _TelemetrySensorType_ARRAYSIZE ((TelemetrySensorType)(TelemetrySensorType_SHT31+1)) + + + + + /* Initializer values for message structs */ #define DeviceMetrics_init_default {0, 0, 0, 0} #define EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0} 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 43/52] 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 From 2505bdbc219e9ea2baad3e24799178f39fdca3a2 Mon Sep 17 00:00:00 2001 From: caveman99 Date: Wed, 28 Dec 2022 14:42:46 +0000 Subject: [PATCH 44/52] [create-pull-request] automated change --- protobufs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobufs b/protobufs index bd8a7c72c..79e213fbc 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit bd8a7c72ca3d7989d6dd409de22fd0ae9f0c24e5 +Subproject commit 79e213fbcc6f432923ab131bfa7b8630eb93270e From 6ea0963f4b8e366bb9fd564eb9ec650f51e48aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Wed, 28 Dec 2022 16:03:34 +0100 Subject: [PATCH 45/52] fix master too --- src/modules/ExternalNotificationModule.cpp | 12 ++++++++++++ src/modules/ExternalNotificationModule.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp index 6c31fdabc..be3e6cd8b 100644 --- a/src/modules/ExternalNotificationModule.cpp +++ b/src/modules/ExternalNotificationModule.cpp @@ -37,7 +37,11 @@ int32_t ExternalNotificationModule::runOnce() if (!moduleConfig.external_notification.enabled) { return INT32_MAX; // we don't need this thread here... } else { +#ifndef ARCH_PORTDUINO if ((nagCycleCutoff < millis()) && !rtttl::isPlaying()) { +#else + if (nagCycleCutoff < millis()) { +#endif nagCycleCutoff = UINT32_MAX; DEBUG_MSG("Turning off external notification: "); for (int i = 0; i < 2; i++) { @@ -71,6 +75,7 @@ int32_t ExternalNotificationModule::runOnce() } // now let the PWM buzzer play +#ifndef ARCH_PORTDUINO if (moduleConfig.external_notification.use_pwm) { if (rtttl::isPlaying()) { rtttl::play(); @@ -79,6 +84,7 @@ int32_t ExternalNotificationModule::runOnce() rtttl::begin(config.device.buzzer_gpio, pwmRingtone); } } +#endif return 25; } } @@ -129,7 +135,9 @@ bool ExternalNotificationModule::getExternal(uint8_t index) } void ExternalNotificationModule::stopNow() { +#ifndef ARCH_PORTDUINO rtttl::stop(); +#endif nagCycleCutoff = 1; // small value setIntervalFromNow(0); } @@ -240,7 +248,9 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp) if (!moduleConfig.external_notification.use_pwm) { setExternalOn(2); } else { +#ifndef ARCH_PORTDUINO rtttl::begin(config.device.buzzer_gpio, pwmRingtone); +#endif } if (moduleConfig.external_notification.nag_timeout) { nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; @@ -276,7 +286,9 @@ ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp) if (!moduleConfig.external_notification.use_pwm) { setExternalOn(2); } else { +#ifndef ARCH_PORTDUINO rtttl::begin(config.device.buzzer_gpio, pwmRingtone); +#endif } if (moduleConfig.external_notification.nag_timeout) { nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; diff --git a/src/modules/ExternalNotificationModule.h b/src/modules/ExternalNotificationModule.h index 04de235cf..258078357 100644 --- a/src/modules/ExternalNotificationModule.h +++ b/src/modules/ExternalNotificationModule.h @@ -3,7 +3,9 @@ #include "SinglePortModule.h" #include "concurrency/OSThread.h" #include "configuration.h" +#ifndef ARCH_PORTDUINO #include +#endif #include #include From 541f4aa9989384c0b05ddde01133f5c75e2ebeda Mon Sep 17 00:00:00 2001 From: thebentern Date: Wed, 28 Dec 2022 21:27:27 +0000 Subject: [PATCH 46/52] [create-pull-request] automated change --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index 6a242ed9b..4ba2878d1 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 0 -build = 9 +build = 10 From 7f3a624ee39b57e2e41bfd634f91881ba98a1d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Thu, 29 Dec 2022 11:55:19 +0100 Subject: [PATCH 47/52] Hotfix for user button not working --- src/graphics/Screen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 7b34aee55..6c09c89cc 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1072,7 +1072,7 @@ int32_t Screen::runOnce() break; case Cmd::ON_PRESS: // If a nag notification is running, stop it - if (externalNotificationModule->nagCycleCutoff != UINT32_MAX) { + if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) { externalNotificationModule->stopNow(); } else { // Don't advance the screen if we just wanted to switch off the nag notification From 7baf30dcb8f7a10b662004967b1336654f888b41 Mon Sep 17 00:00:00 2001 From: thebentern Date: Thu, 29 Dec 2022 12:51:16 +0000 Subject: [PATCH 48/52] [create-pull-request] automated change --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index 4ba2878d1..5c253336e 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 0 -build = 10 +build = 11 From 840079c25bbe47aef84315b0dac2688c46172bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Thu, 29 Dec 2022 14:26:21 +0100 Subject: [PATCH 49/52] build SX1280 on Master too --- .github/workflows/main_matrix.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index e10369465..ea9bc80c4 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -32,6 +32,7 @@ jobs: - board: station-g1 - board: m5stack-coreink - board: tbeam-s3-core + - board: tlora-t3s3-v1 runs-on: ubuntu-latest steps: @@ -53,6 +54,7 @@ jobs: - board: tlora-v1 - board: tlora_v1_3 - board: tlora-v2-1-1.6 + - board: tlora-v2-1-1.8 - board: tbeam - board: heltec-v1 - board: heltec-v2.0 @@ -67,6 +69,7 @@ jobs: - board: m5stack-core - board: m5stack-coreink - board: tbeam-s3-core + - board: tlora-t3s3-v1 uses: ./.github/workflows/build_esp32.yml with: board: ${{ matrix.board }} From 5ccd66864ba833a9b8f886ff88d75fccc36d46a5 Mon Sep 17 00:00:00 2001 From: GUVWAF Date: Fri, 30 Dec 2022 13:45:02 +0100 Subject: [PATCH 50/52] Report actual Routing_Error upon abort --- src/mesh/Router.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 66e21d21d..0dbb33e62 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -137,7 +137,7 @@ void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelI void Router::abortSendAndNak(Routing_Error err, MeshPacket *p) { DEBUG_MSG("Error=%d, returning NAK and dropping packet.\n", err); - sendAckNak(Routing_Error_NO_INTERFACE, getFrom(p), p->id, p->channel); + sendAckNak(err, getFrom(p), p->id, p->channel); packetPool.release(p); } From 52903f83e3e0eacf8cfc379e81ef91043baa2e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 3 Jan 2023 19:19:11 +0100 Subject: [PATCH 51/52] remove Merge duplication --- src/detect/i2cScan.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/detect/i2cScan.h b/src/detect/i2cScan.h index 482a24a57..77d1fff17 100644 --- a/src/detect/i2cScan.h +++ b/src/detect/i2cScan.h @@ -195,10 +195,6 @@ void scanI2Cdevice() LOG_INFO("SHT31 sensor found\n"); nodeTelemetrySensorsMap[TelemetrySensorType_SHT31] = addr; } - if (addr == SHT31_ADDR) { - DEBUG_MSG("SHT31 sensor found\n"); - nodeTelemetrySensorsMap[TelemetrySensorType_SHT31] = addr; - } if (addr == SHTC3_ADDR) { LOG_INFO("SHTC3 sensor found\n"); nodeTelemetrySensorsMap[TelemetrySensorType_SHTC3] = addr; From 64895cc7eb891fd938d4a4ce91b15d704071fcfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=B6ttgens?= Date: Tue, 3 Jan 2023 19:21:11 +0100 Subject: [PATCH 52/52] remove Merge duplication --- src/graphics/Screen.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index cfa30a21b..53ca75a51 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -545,23 +545,6 @@ static void drawGPSpowerstat(OLEDDisplay *display, int16_t x, int16_t y, const G #endif } -//Draw status when gps is disabled by PMU -static void drawGPSpowerstat(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps) -{ -#ifdef HAS_PMU - String displayLine = "GPS disabled"; - int16_t xPos = display->getStringWidth(displayLine); - - if (!config.position.gps_enabled){ - display->drawString(x + xPos, y, displayLine); -#ifdef GPS_POWER_TOGGLE - display->drawString(x + xPos, y - 2 + FONT_HEIGHT_SMALL, " by button"); -#endif - //display->drawString(x + xPos, y + 2, displayLine); - } -#endif -} - static void drawGPSAltitude(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps) { String displayLine = "";