From 7320acb740b38c69ebd9babd53f284c1f384e6d1 Mon Sep 17 00:00:00 2001 From: Linar Yusupov Date: Mon, 27 Nov 2023 09:39:41 +0300 Subject: [PATCH] Ham: playback of pre-recorded weather/traffic/other advisory voice message (WAV file) from micro-SD card into air with BOOT+PTT buttons press --- README.md | 2 +- .../source/SoftRF/src/platform/ESP32.cpp | 82 ++++++++++++++++++- .../source/SoftRF/src/platform/ESP32.h | 1 + .../SoftRF/src/platform/iomap/LilyGO_TTWR.h | 1 + 4 files changed, 84 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5abdb04a6..18fa53d8f 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ Data|FLARM NMEA|

![](https://github.com/lyusupov/SoftRF/raw/mas 1 - it is necessary for a reader to distinguish the difference between statement "**compatible**" and statement "**fully compatible**".
     SoftRF implements only a reasonable minimum of the protocols specs. No "bells and whistles" so far.
2 - FANET+ can not receive FLARM. However it is able to transmit it.
-3 - valid for [**Prime Mark III**](https://github.com/lyusupov/SoftRF/wiki/Prime-Edition-MkIII) , [**Prime Mark II**](https://github.com/lyusupov/SoftRF/wiki/Prime-Edition-MkII) , [**Dongle**](https://github.com/lyusupov/SoftRF/wiki/Dongle-Edition) , [**Mini**](https://github.com/lyusupov/SoftRF/wiki/Mini-Edition) , [**Badge**](https://github.com/lyusupov/SoftRF/wiki/Badge-Edition) , [**Bracelet**](https://github.com/lyusupov/SoftRF/wiki/Bracelet-Edition) , [**Academy**](https://github.com/lyusupov/SoftRF/wiki/Academy-Edition) , [**Octave**](https://github.com/lyusupov/SoftRF/wiki/Octave-Concept) , [**Lego**](https://github.com/lyusupov/SoftRF/wiki/Lego-Edition) and [**Balkan**](https://github.com/lyusupov/SoftRF/wiki/Balkan-Edition) **Editions**; valid for [**Standalone**](https://github.com/lyusupov/SoftRF/wiki/Standalone-Edition) , [**UAV**](https://github.com/lyusupov/SoftRF/wiki/UAV-Edition) and [**Uni**](https://github.com/lyusupov/SoftRF/wiki/Uni-Edition) **Editions** with optional DIY [SoftRF LoRa RF module](https://github.com/lyusupov/SoftRF/wiki/SoftRF-LoRa-module)
+3 - valid for [**Prime Mark III**](https://github.com/lyusupov/SoftRF/wiki/Prime-Edition-MkIII) , [**Prime Mark II**](https://github.com/lyusupov/SoftRF/wiki/Prime-Edition-MkII) , [**Dongle**](https://github.com/lyusupov/SoftRF/wiki/Dongle-Edition) , [**Mini**](https://github.com/lyusupov/SoftRF/wiki/Mini-Edition) , [**Badge**](https://github.com/lyusupov/SoftRF/wiki/Badge-Edition) , [**Bracelet**](https://github.com/lyusupov/SoftRF/wiki/Bracelet-Edition) , [**Academy**](https://github.com/lyusupov/SoftRF/wiki/Academy-Edition) , [**Octave**](https://github.com/lyusupov/SoftRF/wiki/Octave-Concept) , [**Lego**](https://github.com/lyusupov/SoftRF/wiki/Lego-Edition) , [**Balkan**](https://github.com/lyusupov/SoftRF/wiki/Balkan-Edition) and [**Midi**](https://github.com/lyusupov/SoftRF/wiki/Midi-Edition) **Editions**; valid for [**Standalone**](https://github.com/lyusupov/SoftRF/wiki/Standalone-Edition) , [**UAV**](https://github.com/lyusupov/SoftRF/wiki/UAV-Edition) and [**Uni**](https://github.com/lyusupov/SoftRF/wiki/Uni-Edition) **Editions** with optional DIY [SoftRF LoRa RF module](https://github.com/lyusupov/SoftRF/wiki/SoftRF-LoRa-module)
4 - [**Reception**](https://github.com/lyusupov/SoftRF/wiki/Uni-Edition#ads-b-out-remark) of traffic 'downlink' frames only. Valid for [**Uni Edition**](https://github.com/lyusupov/SoftRF/wiki/Uni-Edition) alone and for [**Standalone Edition**](https://github.com/lyusupov/SoftRF/wiki/Standalone-Edition) with optional DIY [SoftRF UAT module](https://github.com/lyusupov/UAT-test-signal#variant-2-advanced)
5 - Reception of traffic 'downlink' frames only. Valid for [**ES Edition**](https://github.com/lyusupov/SoftRF/wiki/ES-Edition)
6 - APRS is the only available with [**Ham Edition**](https://github.com/lyusupov/SoftRF/wiki/Ham-Edition)
diff --git a/software/firmware/source/SoftRF/src/platform/ESP32.cpp b/software/firmware/source/SoftRF/src/platform/ESP32.cpp index 1c83f1e52..105d82e50 100644 --- a/software/firmware/source/SoftRF/src/platform/ESP32.cpp +++ b/software/firmware/source/SoftRF/src/platform/ESP32.cpp @@ -378,6 +378,56 @@ extern SA818Controller controller; extern uint32_t Data_Frequency; extern uint32_t Voice_Frequency; extern void sa868_Tx_LED_state(bool); + +#if !defined(EXCLUDE_VOICE_MESSAGE) +#include +#include +#include +#include + +#define MAX_FILENAME_LEN 64 +#define WAV_FILE_PREFIX "/Audio/" +#define WAV_FILE_SUFFIX ".wav" + +AudioGeneratorWAV *Audio_Gen; +AudioFileSourceSdFat *Audio_Source; +AudioOutputI2S *Audio_Sink; + +bool playback_inited = false; + +static bool play_file(char *filename) +{ + bool rval = false; + + if (Audio_Source->open(filename)) { + unsigned long Audio_Timemarker = millis(); + Audio_Gen->begin(Audio_Source, Audio_Sink); + + bool wdt_status = loopTaskWDTEnabled; + + if (wdt_status) { + disableLoopWDT(); + } + + while (Audio_Gen->loop()) { + // feedLoopWDT(); + if (millis() - Audio_Timemarker > 30000) { + Audio_Gen->stop(); + Serial.println("ERROR: Audio timeout. Playback aborted."); + Serial.flush(); + } + } + + if (wdt_status) { + enableLoopWDT(); + } + + rval = true; + } + + return rval; +} +#endif /* EXCLUDE_VOICE_MESSAGE */ #endif /* USE_SA8X8 */ #endif /* CONFIG_IDF_TARGET_ESP32S3 */ @@ -1140,6 +1190,23 @@ static void ESP32_setup() digitalWrite(uSD_SS_pin, HIGH); uSD_is_attached = uSD.cardBegin(SD_CONFIG); + +#if !defined(EXCLUDE_VOICE_MESSAGE) + if (uSD_is_attached && uSD.card()->cardSize() > 0 && uSD.volumeBegin()) { + Audio_Gen = new AudioGeneratorWAV(); + Audio_Source = new AudioFileSourceSdFat(uSD); + + Audio_Sink = new AudioOutputI2S(0, AudioOutputI2S::INTERNAL_PDM); + Audio_Sink->SetPinout(I2S_PIN_NO_CHANGE, + I2S_PIN_NO_CHANGE, + SOC_GPIO_PIN_TWR2_PDM_OUT, + I2S_PIN_NO_CHANGE); + Audio_Sink->SetOutputModeMono(true); + Audio_Sink->SetChannels(1); + Audio_Sink->SetMclk(false); + playback_inited = true; + } +#endif /* EXCLUDE_VOICE_MESSAGE */ } } else if (hw_info.model == SOFTRF_MODEL_MIDI) { @@ -3823,7 +3890,11 @@ void handleMainEvent(AceButton* button, uint8_t eventType, if (button == &button_ptt && Voice_Frequency > 0 && (settings->power_save & POWER_SAVE_NORECEIVE)) { - pinMode(SOC_GPIO_PIN_TWR2_MIC_CH_SEL, INPUT_PULLDOWN); + bool playback = false; +#if !defined(EXCLUDE_VOICE_MESSAGE) + playback = playback_inited && (digitalRead(SOC_GPIO_PIN_S3_BUTTON) == 0); +#endif /* EXCLUDE_VOICE_MESSAGE */ + if (!playback) {pinMode(SOC_GPIO_PIN_TWR2_MIC_CH_SEL, INPUT_PULLDOWN);} PTT_TxF = controller.getTXF(); controller.setTXF(Voice_Frequency / 1000000.0); controller.update(); @@ -3844,6 +3915,15 @@ void handleMainEvent(AceButton* button, uint8_t eventType, #endif /* USE_OLED */ sa868_Tx_LED_state(true); controller.transmit(); +#if !defined(EXCLUDE_VOICE_MESSAGE) + if (playback) { + char filename[MAX_FILENAME_LEN]; + strcpy(filename, WAV_FILE_PREFIX); + strcat(filename, "message"); + strcat(filename, WAV_FILE_SUFFIX); + play_file(filename); + } +#endif /* EXCLUDE_VOICE_MESSAGE */ } break; #endif /* USE_SA8X8 */ diff --git a/software/firmware/source/SoftRF/src/platform/ESP32.h b/software/firmware/source/SoftRF/src/platform/ESP32.h index 28bb60aad..172f9a2d3 100644 --- a/software/firmware/source/SoftRF/src/platform/ESP32.h +++ b/software/firmware/source/SoftRF/src/platform/ESP32.h @@ -310,6 +310,7 @@ struct rst_info { //#define USE_GDL90_MSL #define USE_OGN_ENCRYPTION #define ENABLE_PROL +#define EXCLUDE_VOICE_MESSAGE //#define EXCLUDE_GNSS_UBLOX /* Neo-6/7/8, M10 */ #define ENABLE_UBLOX_RFS /* revert factory settings (when necessary) */ diff --git a/software/firmware/source/SoftRF/src/platform/iomap/LilyGO_TTWR.h b/software/firmware/source/SoftRF/src/platform/iomap/LilyGO_TTWR.h index da291e80e..e92cca640 100644 --- a/software/firmware/source/SoftRF/src/platform/iomap/LilyGO_TTWR.h +++ b/software/firmware/source/SoftRF/src/platform/iomap/LilyGO_TTWR.h @@ -16,6 +16,7 @@ #define SOC_GPIO_PIN_TWR2_RADIO_PD 40 #define SOC_GPIO_PIN_TWR2_RADIO_SQL 2 /* T-TWR 2.1 only */ #define SOC_GPIO_PIN_TWR2_MIC_CH_SEL 17 +#define SOC_GPIO_PIN_TWR2_PDM_OUT 18 // Radio SPI (not in use) #define SOC_GPIO_PIN_TWR2_MOSI 16