diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml index e53c35bd2..8b28090ca 100644 --- a/.github/workflows/main_matrix.yml +++ b/.github/workflows/main_matrix.yml @@ -66,6 +66,7 @@ jobs: - board: tlora-v2-1-1_6 - board: tlora-v2-1-1_8 - board: tbeam + - board: heltec-ht62-esp32c3-sx1262 - board: heltec-v1 - board: heltec-v2_0 - board: heltec-v2_1 @@ -125,6 +126,7 @@ jobs: - board: pico - board: picow - board: rak11310 + - board: senselora_rp2040 uses: ./.github/workflows/build_rpi2040.yml with: board: ${{ matrix.board }} @@ -211,6 +213,9 @@ jobs: repository: ${{github.event.pull_request.head.repo.full_name}} gather-artifacts: + permissions: + contents: write + pull-requests: write runs-on: ubuntu-latest needs: [ @@ -284,14 +289,13 @@ jobs: - name: Create request artifacts continue-on-error: true # FIXME: Why are we getting 502, but things still work? if: ${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request' }} - uses: gavv/pull-request-artifacts@v1.1.0 + uses: gavv/pull-request-artifacts@v2.1.0 with: commit: ${{ (github.event.pull_request_target || github.event.pull_request).head.sha }} repo-token: ${{ secrets.GITHUB_TOKEN }} artifacts-token: ${{ secrets.ARTIFACTS_TOKEN }} artifacts-repo: meshtastic/artifacts artifacts-branch: device - artifacts-dir: pr artifacts: ./firmware-${{ steps.version.outputs.version }}.zip release-artifacts: diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 645d3863a..81a35f8f1 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -12,10 +12,9 @@ lint: - checkov@3.1.9 - terrascan@1.18.5 - trivy@0.47.0 - - trufflehog@3.63.2-rc0 + #- trufflehog@3.63.2-rc0 - taplo@0.8.1 - ruff@0.1.6 - - yamllint@1.33.0 - isort@5.12.0 - markdownlint@0.37.0 - oxipng@9.0.0 diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini index 49ee4a5e4..d79418bee 100644 --- a/arch/esp32/esp32.ini +++ b/arch/esp32/esp32.ini @@ -44,7 +44,6 @@ lib_deps = ${environmental_base.lib_deps} https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2 h2zero/NimBLE-Arduino@^1.4.1 - jgromes/RadioLib@^6.2.0 https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini index de76d1ca4..79294d290 100644 --- a/arch/nrf52/nrf52.ini +++ b/arch/nrf52/nrf52.ini @@ -25,7 +25,6 @@ build_src_filter = lib_deps= ${arduino_base.lib_deps} - jgromes/RadioLib@^6.2.0 lib_ignore = BluetoothOTA \ No newline at end of file diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini index 93ebf2082..07f13e8fb 100644 --- a/arch/portduino/portduino.ini +++ b/arch/portduino/portduino.ini @@ -24,7 +24,6 @@ lib_deps = ${env.lib_deps} ${networking_base.lib_deps} rweather/Crypto@^0.4.0 - jgromes/RadioLib@^6.1.0 build_flags = ${arduino_base.build_flags} diff --git a/arch/rp2040/rp2040.ini b/arch/rp2040/rp2040.ini index 61bf2d013..efd8a0895 100644 --- a/arch/rp2040/rp2040.ini +++ b/arch/rp2040/rp2040.ini @@ -2,7 +2,7 @@ [rp2040_base] platform = https://github.com/maxgerhardt/platform-raspberrypi.git#612de5399d68b359053f1307ed223d400aea975c extends = arduino_base -platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#d2461a14ad5aa920e44508d236c2f459e3befbf8 +platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.6.2 board_build.core = earlephilhower board_build.filesystem_size = 0.5m @@ -29,5 +29,4 @@ lib_ignore = lib_deps = ${arduino_base.lib_deps} ${environmental_base.lib_deps} - jgromes/RadioLib@^6.2.0 rweather/Crypto \ No newline at end of file diff --git a/arch/stm32/stm32wl5e.ini b/arch/stm32/stm32wl5e.ini index 0120c4c6f..cadddd1d5 100644 --- a/arch/stm32/stm32wl5e.ini +++ b/arch/stm32/stm32wl5e.ini @@ -34,7 +34,6 @@ upload_protocol = stlink lib_deps = ${env.lib_deps} - jgromes/RadioLib@^6.2.0 https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b https://github.com/littlefs-project/littlefs.git#v2.5.1 https://github.com/stm32duino/STM32FreeRTOS.git#10.3.1 diff --git a/platformio.ini b/platformio.ini index f176823d1..d7ad05337 100644 --- a/platformio.ini +++ b/platformio.ini @@ -68,6 +68,7 @@ build_flags = -Wno-missing-field-initializers monitor_speed = 115200 lib_deps = + jgromes/RadioLib@^6.3.0 https://github.com/meshtastic/esp8266-oled-ssd1306.git#b38094e03dfa964fbc0e799bc374e91a605c1223 ; ESP8266_SSD1306 mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 diff --git a/protobufs b/protobufs index 9148427a3..a34b2c680 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit 9148427a3be535c9e3f17e846ecbb64ce04b6521 +Subproject commit a34b2c680e2c1c240643c515e57c5532b29c91a7 diff --git a/src/AudioThread.h b/src/AudioThread.h new file mode 100644 index 000000000..c9f253440 --- /dev/null +++ b/src/AudioThread.h @@ -0,0 +1,77 @@ +#pragma once +#include "PowerFSM.h" +#include "concurrency/OSThread.h" +#include "configuration.h" +#include "main.h" +#include "sleep.h" + +#ifdef HAS_I2S +#include +#include +#include +#include + +#define AUDIO_THREAD_INTERVAL_MS 100 + +class AudioThread : public concurrency::OSThread +{ + public: + AudioThread() : OSThread("AudioThread") { initOutput(); } + + void beginRttl(const void *data, uint32_t len) + { + setCPUFast(true); + rtttlFile = new AudioFileSourcePROGMEM(data, len); + i2sRtttl = new AudioGeneratorRTTTL(); + i2sRtttl->begin(rtttlFile, audioOut); + } + + bool isPlaying() + { + if (i2sRtttl != nullptr) { + return i2sRtttl->isRunning() && i2sRtttl->loop(); + } + return false; + } + + void stop() + { + if (i2sRtttl != nullptr) { + i2sRtttl->stop(); + delete i2sRtttl; + i2sRtttl = nullptr; + } + if (rtttlFile != nullptr) { + delete rtttlFile; + rtttlFile = nullptr; + } + + setCPUFast(false); + } + + protected: + int32_t runOnce() override + { + canSleep = true; // Assume we should not keep the board awake + + // if (i2sRtttl != nullptr && i2sRtttl->isRunning()) { + // i2sRtttl->loop(); + // } + return AUDIO_THREAD_INTERVAL_MS; + } + + private: + void initOutput() + { + audioOut = new AudioOutputI2S(1, AudioOutputI2S::EXTERNAL_I2S); + audioOut->SetPinout(DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT); + audioOut->SetGain(0.2); + }; + + AudioGeneratorRTTTL *i2sRtttl = nullptr; + AudioOutputI2S *audioOut; + + AudioFileSourcePROGMEM *rtttlFile; +}; + +#endif diff --git a/src/ButtonThread.h b/src/ButtonThread.h index a60b7730a..5f68aa5b6 100644 --- a/src/ButtonThread.h +++ b/src/ButtonThread.h @@ -5,6 +5,7 @@ #include "configuration.h" #include "graphics/Screen.h" #include "main.h" +#include "modules/ExternalNotificationModule.h" #include "power.h" #include @@ -205,6 +206,12 @@ class ButtonThread : public concurrency::OSThread static void userButtonPressedLongStart() { +#ifdef T_DECK + // False positive long-press triggered on T-Deck with i2s audio, so short circuit + if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) { + return; + } +#endif if (millis() > 30 * 1000) { LOG_DEBUG("Long press start!\n"); longPressTime = millis(); diff --git a/src/PowerFSMThread.h b/src/PowerFSMThread.h index 541522f43..b757f3abb 100644 --- a/src/PowerFSMThread.h +++ b/src/PowerFSMThread.h @@ -33,7 +33,7 @@ class PowerFSMThread : public OSThread powerFSM.trigger(EVENT_SHUTDOWN); } - return 10; + return 100; } }; diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index e7f85a0e2..68a081882 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -293,7 +293,7 @@ bool GPS::setup() gnssModel = GNSS_MODEL_UNKNOWN; } #else - gnssModel = GNSS_MODEL_UC6850; + gnssModel = GNSS_MODEL_UC6580; #endif if (gnssModel == GNSS_MODEL_MTK) { @@ -311,11 +311,23 @@ bool GPS::setup() // Switch to Vehicle Mode, since SoftRF enables Aviation < 2g _serial_gps->write("$PCAS11,3*1E\r\n"); delay(250); - } else if (gnssModel == GNSS_MODEL_UC6850) { - - // use GPS + GLONASS - _serial_gps->write("$CFGSYS,h15\r\n"); + } else if (gnssModel == GNSS_MODEL_UC6580) { + // The Unicore UC6580 can use a lot of sat systems, enable it to + // use GPS L1 & L5 + BDS B1I & B2a + GLONASS L1 + GALILEO E1 & E5a + SBAS + // This will reset the receiver, so wait a bit afterwards + // The paranoid will wait for the OK*04 confirmation response after each command. + _serial_gps->write("$CFGSYS,h25155\r\n"); + delay(750); + // Must be done after the CFGSYS command + // Turn off GSV messages, we don't really care about which and where the sats are, maybe someday. + _serial_gps->write("$CFGMSG,0,3,0\r\n"); delay(250); + // Turn off NOTICE __TXT messages, these may provide Unicore some info but we don't care. + _serial_gps->write("$CFGMSG,6,0,0\r\n"); + delay(250); + _serial_gps->write("$CFGMSG,6,1,0\r\n"); + delay(250); + } else if (gnssModel == GNSS_MODEL_UBLOX) { // Configure GNSS system to GPS+SBAS+GLONASS (Module may restart after this command) // We need set it because by default it is GPS only, and we want to use GLONASS too diff --git a/src/gps/GPS.h b/src/gps/GPS.h index d52c79182..4cbdae06b 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -23,7 +23,7 @@ struct uBloxGnssModelInfo { typedef enum { GNSS_MODEL_MTK, GNSS_MODEL_UBLOX, - GNSS_MODEL_UC6850, + GNSS_MODEL_UC6580, GNSS_MODEL_UNKNOWN, } GnssModel_t; diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp index ef438a7dd..10e9e0331 100644 --- a/src/gps/RTC.cpp +++ b/src/gps/RTC.cpp @@ -152,7 +152,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv) #endif // nrf52 doesn't have a readable RTC (yet - software not written) -#ifdef HAS_RTC +#if HAS_RTC readFromRTC(); #endif @@ -208,4 +208,4 @@ uint32_t getTime() uint32_t getValidTime(RTCQuality minQuality) { return (currentQuality >= minQuality) ? getTime() : 0; -} \ No newline at end of file +} diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index e75a432d4..417a6e454 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -43,9 +43,12 @@ along with this program. If not, see . #include "sleep.h" #include "target_specific.h" +#if HAS_WIFI && !defined(ARCH_RASPBERRY_PI) +#include "mesh/wifi/WiFiAPClient.h" +#endif + #ifdef ARCH_ESP32 #include "esp_task_wdt.h" -#include "mesh/wifi/WiFiAPClient.h" #include "modules/esp32/StoreForwardModule.h" #endif @@ -1294,7 +1297,7 @@ void Screen::setFrames() // call a method on debugInfoScreen object (for more details) normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline; -#ifdef ARCH_ESP32 +#if HAS_WIFI && !defined(ARCH_RASPBERRY_PI) if (isWifiAvailable()) { // call a method on debugInfoScreen object (for more details) normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline; diff --git a/src/input/TouchScreenImpl1.cpp b/src/input/TouchScreenImpl1.cpp index b3152c88a..e38d6c324 100644 --- a/src/input/TouchScreenImpl1.cpp +++ b/src/input/TouchScreenImpl1.cpp @@ -2,6 +2,7 @@ #include "InputBroker.h" #include "PowerFSM.h" #include "configuration.h" +#include "modules/ExternalNotificationModule.h" TouchScreenImpl1 *touchScreenImpl1; @@ -63,7 +64,11 @@ void TouchScreenImpl1::onEvent(const TouchEvent &event) break; } case TOUCH_ACTION_TAP: { - powerFSM.trigger(EVENT_INPUT); + if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) { + externalNotificationModule->stopNow(); + } else { + powerFSM.trigger(EVENT_INPUT); + } break; } default: diff --git a/src/main.cpp b/src/main.cpp index 7319f2d91..3693b207e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -84,6 +84,11 @@ NRF52Bluetooth *nrf52Bluetooth; #include "AmbientLightingThread.h" #endif +#ifdef HAS_I2S +#include "AudioThread.h" +AudioThread *audioThread; +#endif + using namespace concurrency; // We always create a screen object, but we only init it if we find the hardware @@ -122,6 +127,7 @@ ATECCX08A atecc; #ifdef T_WATCH_S3 Adafruit_DRV2605 drv; #endif + bool isVibrating = false; bool eink_found = true; @@ -432,6 +438,10 @@ void setup() auto i2cCount = i2cScanner->countDevices(); if (i2cCount == 0) { LOG_INFO("No I2C devices found\n"); + Wire.end(); +#ifdef I2C_SDA1 + Wire1.end(); +#endif } else { LOG_INFO("%i I2C devices found\n", i2cCount); } @@ -576,10 +586,13 @@ void setup() // but we need to do this after main cpu init (esp32setup), because we need the random seed set nodeDB.init(); - // If we're taking on the repeater role, use flood router - if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) + // If we're taking on the repeater role, use flood router and turn off 3V3_S rail because peripherals are not needed + if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) { router = new FloodingRouter(); - else +#ifdef PIN_3V3_EN + digitalWrite(PIN_3V3_EN, LOW); +#endif + } else router = new ReliableRouter(); #if HAS_BUTTON || defined(ARCH_RASPBERRY_PI) @@ -656,7 +669,10 @@ void setup() readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time) - gps = GPS::createGps(); + // If we're taking on the repeater role, ignore GPS + if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) { + gps = GPS::createGps(); + } if (gps) { gpsStatus->observe(&gps->newStatus); } else { @@ -664,6 +680,11 @@ void setup() } nodeStatus->observe(&nodeDB.newStatus); +#ifdef HAS_I2S + LOG_DEBUG("Starting audio thread\n"); + audioThread = new AudioThread(); +#endif + service.init(); // Now that the mesh service is created, create any modules @@ -873,7 +894,6 @@ void setup() // This must be _after_ service.init because we need our preferences loaded from flash to have proper timeout values PowerFSM_setup(); // we will transition to ON in a couple of seconds, FIXME, only do this for cold boots, not waking from SDS powerFSMthread = new PowerFSMThread(); - setCPUFast(false); // 80MHz is fine for our slow peripherals } @@ -944,4 +964,4 @@ void loop() mainDelay.delay(delayMsec); } // if (didWake) LOG_DEBUG("wake!\n"); -} +} \ No newline at end of file diff --git a/src/main.h b/src/main.h index 76391f17a..e780516f6 100644 --- a/src/main.h +++ b/src/main.h @@ -42,6 +42,12 @@ extern ATECCX08A atecc; #include extern Adafruit_DRV2605 drv; #endif + +#ifdef HAS_I2S +#include "AudioThread.h" +extern AudioThread *audioThread; +#endif + extern bool isVibrating; extern int TCPPort; // set by Portduino diff --git a/src/mesh/MemoryPool.h b/src/mesh/MemoryPool.h index 84cac7eff..d30404b9f 100644 --- a/src/mesh/MemoryPool.h +++ b/src/mesh/MemoryPool.h @@ -73,58 +73,3 @@ template class MemoryDynamic : public Allocator return p; } }; - -/** - * A pool based allocator - * - */ -template class MemoryPool : public Allocator -{ - PointerQueue dead; - - T *buf; // our large raw block of memory - - size_t maxElements; - - public: - explicit MemoryPool(size_t _maxElements) : dead(_maxElements), maxElements(_maxElements) - { - buf = new T[maxElements]; - - // prefill dead - for (size_t i = 0; i < maxElements; i++) - release(&buf[i]); - } - - ~MemoryPool() { delete[] buf; } - - /// Return a buffer for use by others - void release(T *p) - { - assert(p >= buf && - (size_t)(p - buf) < - maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool - assert(dead.enqueue(p, 0)); - } - -#ifdef HAS_FREE_RTOS - /// Return a buffer from an ISR, if higherPriWoken is set to true you have some work to do ;-) - void releaseFromISR(T *p, BaseType_t *higherPriWoken) - { - assert(p >= buf && - (size_t)(p - buf) < - maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool - assert(dead.enqueueFromISR(p, higherPriWoken)); - } -#endif - - protected: - /// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you - /// probably don't want this version). - virtual T *alloc(TickType_t maxWait) - { - T *p = dead.dequeuePtr(maxWait); - assert(p); - return p; - } -}; diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index f1c0d8b95..4b2bc33fe 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -140,6 +140,22 @@ void MeshService::reloadOwner(bool shouldSave) } } +// search the queue for a request id and return the matching nodenum +NodeNum MeshService::getNodenumFromRequestId(uint32_t request_id) +{ + NodeNum nodenum = 0; + for (int i = 0; i < toPhoneQueue.numUsed(); i++) { + meshtastic_MeshPacket *p = toPhoneQueue.dequeuePtr(0); + if (p->id == request_id) { + nodenum = p->to; + // make sure to continue this to make one full loop + } + // put it right back on the queue + toPhoneQueue.enqueue(p, 0); + } + return nodenum; +} + /** * Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh) * Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep a diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index eb40b7712..6d73c076a 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -82,6 +82,9 @@ class MeshService /// Return the next MqttClientProxyMessage packet destined to the phone. meshtastic_MqttClientProxyMessage *getMqttClientProxyMessageForPhone() { return toPhoneMqttProxyQueue.dequeuePtr(0); } + // search the queue for a request id and return the matching nodenum + NodeNum getNodenumFromRequestId(uint32_t request_id); + // Release QueueStatus packet to pool void releaseQueueStatusToPool(meshtastic_QueueStatus *p) { queueStatusPool.release(p); } diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index be80d0ebb..b30b5756d 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -245,9 +245,12 @@ void NodeDB::installDefaultModuleConfig() moduleConfig.external_notification.output_ms = 1000; moduleConfig.external_notification.nag_timeout = 60; #endif -#ifdef T_WATCH_S3 - // Don't worry about the other settings, we'll use the DRV2056 behavior for notifications +#ifdef HAS_I2S + // Don't worry about the other settings for T-Watch, we'll also use the DRV2056 behavior for notifications moduleConfig.external_notification.enabled = true; + moduleConfig.external_notification.use_i2s_as_buzzer = true; + moduleConfig.external_notification.alert_message_buzzer = true; + moduleConfig.external_notification.nag_timeout = 60; #endif #ifdef NANO_G2_ULTRA moduleConfig.external_notification.enabled = true; @@ -302,6 +305,15 @@ void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role) (meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING | meshtastic_Config_PositionConfig_PositionFlags_DOP); moduleConfig.telemetry.device_update_interval = ONE_DAY; + } else if (role == meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) { + config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY; + config.device.node_info_broadcast_secs = UINT32_MAX; + config.position.position_broadcast_smart_enabled = false; + config.position.position_broadcast_secs = UINT32_MAX; + moduleConfig.neighbor_info.update_interval = UINT32_MAX; + moduleConfig.telemetry.device_update_interval = UINT32_MAX; + moduleConfig.telemetry.environment_update_interval = UINT32_MAX; + moduleConfig.telemetry.air_quality_interval = UINT32_MAX; } } diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 59c32f2fc..931642fca 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -299,6 +299,12 @@ bool perhapsDecode(meshtastic_MeshPacket *p) config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_ALL_SKIP_DECODING) return false; + if (config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY && + !nodeDB.getMeshNode(p->from)->has_user) { + LOG_DEBUG("Node 0x%x not in NodeDB. Rebroadcast mode KNOWN_ONLY will ignore packet\n", p->from); + return false; + } + if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) return true; // If packet was already decoded just return diff --git a/src/mesh/TypedQueue.h b/src/mesh/TypedQueue.h index c08f9183b..c96edae8e 100644 --- a/src/mesh/TypedQueue.h +++ b/src/mesh/TypedQueue.h @@ -27,6 +27,8 @@ template class TypedQueue bool isEmpty() { return uxQueueMessagesWaiting(h) == 0; } + int numUsed() { return uxQueueMessagesWaiting(h); } + /** euqueue a packet. Also, maxWait used to default to portMAX_DELAY, but we now want to callers to THINK about what blocking * they want */ bool enqueue(T x, TickType_t maxWait) @@ -80,6 +82,8 @@ template class TypedQueue bool isEmpty() { return q.empty(); } + int numUsed() { return q.size(); } + bool enqueue(T x, TickType_t maxWait = portMAX_DELAY) { if (reader) { diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h index 53e92a948..8406dc887 100644 --- a/src/mesh/generated/meshtastic/config.pb.h +++ b/src/mesh/generated/meshtastic/config.pb.h @@ -43,7 +43,18 @@ typedef enum _meshtastic_Config_DeviceConfig_Role { Used for nodes dedicated for connection to an ATAK EUD. Turns off many of the routine broadcasts to favor CoT packet stream from the Meshtastic ATAK plugin -> IMeshService -> Node */ - meshtastic_Config_DeviceConfig_Role_TAK = 7 + meshtastic_Config_DeviceConfig_Role_TAK = 7, + /* Client Hidden device role + Used for nodes that "only speak when spoken to" + Turns all of the routine broadcasts but allows for ad-hoc communication + Still rebroadcasts, but with local only rebroadcast mode (known meshes only) + Can be used for clandestine operation or to dramatically reduce airtime / power consumption */ + meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN = 8, + /* Lost and Found device role + Used to automatically send a text message to the mesh + with the current position of the device on a frequent interval: + "I'm lost! Position: lat / long" */ + meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND = 9 } meshtastic_Config_DeviceConfig_Role; /* Defines the device's behavior for how messages are rebroadcast */ @@ -56,7 +67,10 @@ typedef enum _meshtastic_Config_DeviceConfig_RebroadcastMode { meshtastic_Config_DeviceConfig_RebroadcastMode_ALL_SKIP_DECODING = 1, /* Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. Only rebroadcasts message on the nodes local primary / secondary channels. */ - meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY = 2 + meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY = 2, + /* Ignores observed messages from foreign meshes like LOCAL_ONLY, + but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB) */ + meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY = 3 } meshtastic_Config_DeviceConfig_RebroadcastMode; /* Bit field of boolean configuration options, indicating which optional @@ -479,12 +493,12 @@ extern "C" { /* Helper constants for enums */ #define _meshtastic_Config_DeviceConfig_Role_MIN meshtastic_Config_DeviceConfig_Role_CLIENT -#define _meshtastic_Config_DeviceConfig_Role_MAX meshtastic_Config_DeviceConfig_Role_TAK -#define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_TAK+1)) +#define _meshtastic_Config_DeviceConfig_Role_MAX meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND +#define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND+1)) #define _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN meshtastic_Config_DeviceConfig_RebroadcastMode_ALL -#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY -#define _meshtastic_Config_DeviceConfig_RebroadcastMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_RebroadcastMode)(meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY+1)) +#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY +#define _meshtastic_Config_DeviceConfig_RebroadcastMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_RebroadcastMode)(meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY+1)) #define _meshtastic_Config_PositionConfig_PositionFlags_MIN meshtastic_Config_PositionConfig_PositionFlags_UNSET #define _meshtastic_Config_PositionConfig_PositionFlags_MAX meshtastic_Config_PositionConfig_PositionFlags_SPEED diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index 59005db48..ae80b3fe5 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -67,6 +67,10 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_STATION_G1 = 25, /* RAK11310 (RP2040 + SX1262) */ meshtastic_HardwareModel_RAK11310 = 26, + /* Makerfabs SenseLoRA Receiver (RP2040 + RFM96) */ + meshtastic_HardwareModel_SENSELORA_RP2040 = 27, + /* Makerfabs SenseLoRA Industrial Monitor (ESP32-S3 + RFM96) */ + meshtastic_HardwareModel_SENSELORA_S3 = 28, /* --------------------------------------------------------------------------- Less common/prototype boards listed here (needs one more byte over the air) --------------------------------------------------------------------------- */ diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index 06573fd60..1e521e033 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -9,13 +9,11 @@ #include "target_specific.h" #include #include -#ifndef ARCH_RP2040 +#ifdef ARCH_ESP32 #include "mesh/http/WebServer.h" #include #include static void WiFiEvent(WiFiEvent_t event); -#else -#include #endif #ifndef DISABLE_NTP @@ -53,6 +51,7 @@ static void onNetworkConnected() // Start web server LOG_INFO("Starting network services\n"); +#ifdef ARCH_ESP32 // start mdns if (!MDNS.begin("Meshtastic")) { LOG_ERROR("Error setting up MDNS responder!\n"); @@ -62,6 +61,9 @@ static void onNetworkConnected() MDNS.addService("http", "tcp", 80); MDNS.addService("https", "tcp", 443); } +#else // ESP32 handles this in WiFiEvent + LOG_INFO("Obtained IP address: %s\n", WiFi.localIP().toString().c_str()); +#endif #ifndef DISABLE_NTP LOG_INFO("Starting NTP time client\n"); @@ -89,7 +91,7 @@ static void onNetworkConnected() syslog.enable(); } -#ifndef ARCH_RP2040 +#ifdef ARCH_ESP32 initWebServer(); #endif initApiServer(); @@ -149,6 +151,11 @@ static int32_t reconnectWiFi() #endif if (config.network.wifi_enabled && !WiFi.isConnected()) { +#ifdef ARCH_RP2040 // (ESP32 handles this in WiFiEvent) + /* If APStartupComplete, but we're not connected, try again. + Shouldn't try again before APStartupComplete. */ + needReconnect = APStartupComplete; +#endif return 1000; // check once per second } else { #ifdef ARCH_RP2040 @@ -245,7 +252,7 @@ bool initWifi() } } -#ifndef ARCH_RP2040 +#ifdef ARCH_ESP32 // Called by the Espressif SDK to static void WiFiEvent(WiFiEvent_t event) { @@ -279,11 +286,11 @@ static void WiFiEvent(WiFiEvent_t event) LOG_INFO("Authentication mode of access point has changed\n"); break; case ARDUINO_EVENT_WIFI_STA_GOT_IP: - LOG_INFO("Obtained IP address: ", WiFi.localIPv6()); + LOG_INFO("Obtained IP address: %s\n", WiFi.localIP().toString().c_str()); onNetworkConnected(); break; case ARDUINO_EVENT_WIFI_STA_GOT_IP6: - LOG_INFO("Obtained IP6 address: %s", WiFi.localIPv6()); + LOG_INFO("Obtained IP6 address: %s\n", WiFi.localIPv6().toString().c_str()); break; case ARDUINO_EVENT_WIFI_STA_LOST_IP: LOG_INFO("Lost IP address and IP address is reset to 0\n"); @@ -391,4 +398,4 @@ static void WiFiEvent(WiFiEvent_t event) uint8_t getWifiDisconnectReason() { return wifiDisconnectReason; -} \ No newline at end of file +} diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp index ade9d0e5a..dedfdb850 100644 --- a/src/modules/CannedMessageModule.cpp +++ b/src/modules/CannedMessageModule.cpp @@ -233,7 +233,9 @@ void CannedMessageModule::sendText(NodeNum dest, const char *message, bool wantR LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); - service.sendToMesh(p); + service.sendToMesh( + p, RX_SRC_LOCAL, + true); // send to mesh, cc to phone. Even if there's no phone connected, this stores the message to match ACKs } int32_t CannedMessageModule::runOnce() @@ -244,7 +246,8 @@ int32_t CannedMessageModule::runOnce() } // LOG_DEBUG("Check status\n"); UIFrameEvent e = {false, true}; - if (this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) { + if ((this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) || + (this->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED)) { // TODO: might have some feedback of sendig state this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; e.frameChanged = true; @@ -483,7 +486,18 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st { char buffer[50]; - if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) { + if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) { + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(FONT_MEDIUM); + String displayString; + if (this->ack) { + displayString = "Delivered to\n%s"; + } else { + displayString = "Delivery failed\nto %s"; + } + display->drawStringf(display->getWidth() / 2 + x, 0 + y + 12, buffer, displayString, + cannedMessageModule->getNodeName(this->incoming)); + } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) { display->setTextAlignment(TEXT_ALIGN_CENTER); display->setFont(FONT_MEDIUM); display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending..."); @@ -546,6 +560,27 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st } } +ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &mp) +{ + if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP) { + // look for a request_id + if (mp.decoded.request_id != 0) { + UIFrameEvent e = {false, true}; + e.frameChanged = true; + this->runState = CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED; + this->incoming = service.getNodenumFromRequestId(mp.decoded.request_id); + meshtastic_Routing decoded = meshtastic_Routing_init_default; + pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded); + this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE; + this->notifyObservers(&e); + // run the next time 2 seconds later + setIntervalFromNow(2000); + } + } + + return ProcessMessage::CONTINUE; +} + void CannedMessageModule::loadProtoForModule() { if (!nodeDB.loadProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size, @@ -650,4 +685,4 @@ String CannedMessageModule::drawWithCursor(String text, int cursor) return result; } -#endif +#endif \ No newline at end of file diff --git a/src/modules/CannedMessageModule.h b/src/modules/CannedMessageModule.h index 98467215e..b41fba045 100644 --- a/src/modules/CannedMessageModule.h +++ b/src/modules/CannedMessageModule.h @@ -9,6 +9,7 @@ enum cannedMessageModuleRunState { CANNED_MESSAGE_RUN_STATE_ACTIVE, CANNED_MESSAGE_RUN_STATE_FREETEXT, CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE, + CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED, CANNED_MESSAGE_RUN_STATE_ACTION_SELECT, CANNED_MESSAGE_RUN_STATE_ACTION_UP, CANNED_MESSAGE_RUN_STATE_ACTION_DOWN, @@ -37,15 +38,29 @@ class CannedMessageModule : public SinglePortModule, public Observabledecoded.portnum) { + case meshtastic_PortNum_TEXT_MESSAGE_APP: + case meshtastic_PortNum_ROUTING_APP: + return true; + default: + return false; + } + } + protected: virtual int32_t runOnce() override; @@ -63,6 +78,12 @@ class CannedMessageModule : public SinglePortModule, public Observable -#include "main.h" - #ifdef HAS_NCP5623 #include @@ -54,6 +53,8 @@ bool ascending = true; #endif #define EXT_NOTIFICATION_MODULE_OUTPUT_MS 1000 +#define EXT_NOTIFICATION_DEFAULT_THREAD_MS 25 + #define ASCII_BELL 0x07 meshtastic_RTTTLConfig rtttlConfig; @@ -71,7 +72,12 @@ int32_t ExternalNotificationModule::runOnce() if (!moduleConfig.external_notification.enabled) { return INT32_MAX; // we don't need this thread here... } else { - if ((nagCycleCutoff < millis()) && !rtttl::isPlaying()) { + + bool isPlaying = rtttl::isPlaying(); +#ifdef HAS_I2S + isPlaying = rtttl::isPlaying() || audioThread->isPlaying(); +#endif + if ((nagCycleCutoff < millis()) && !isPlaying) { // let the song finish if we reach timeout nagCycleCutoff = UINT32_MAX; LOG_INFO("Turning off external notification: "); @@ -132,6 +138,16 @@ int32_t ExternalNotificationModule::runOnce() #endif } + // Play RTTTL over i2s audio interface if enabled as buzzer +#ifdef HAS_I2S + if (moduleConfig.external_notification.use_i2s_as_buzzer) { + if (audioThread->isPlaying()) { + // Continue playing + } else if (isNagging && (nagCycleCutoff >= millis())) { + audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone)); + } + } +#endif // now let the PWM buzzer play if (moduleConfig.external_notification.use_pwm) { if (rtttl::isPlaying()) { @@ -142,7 +158,7 @@ int32_t ExternalNotificationModule::runOnce() } } - return 25; + return EXT_NOTIFICATION_DEFAULT_THREAD_MS; } } @@ -175,6 +191,7 @@ void ExternalNotificationModule::setExternalOn(uint8_t index) digitalWrite(output, (moduleConfig.external_notification.active ? true : false)); break; } + #ifdef HAS_NCP5623 if (rgb_found.type == ScanI2C::NCP5623) { rgb.setColor(red, green, blue); @@ -226,6 +243,9 @@ bool ExternalNotificationModule::getExternal(uint8_t index) void ExternalNotificationModule::stopNow() { rtttl::stop(); +#ifdef HAS_I2S + audioThread->stop(); +#endif nagCycleCutoff = 1; // small value isNagging = false; setIntervalFromNow(0); @@ -246,6 +266,7 @@ ExternalNotificationModule::ExternalNotificationModule() // moduleConfig.external_notification.alert_message = true; // moduleConfig.external_notification.alert_message_buzzer = true; // moduleConfig.external_notification.alert_message_vibra = true; + // moduleConfig.external_notification.use_i2s_as_buzzer = true; // moduleConfig.external_notification.active = true; // moduleConfig.external_notification.alert_bell = 1; @@ -255,6 +276,13 @@ ExternalNotificationModule::ExternalNotificationModule() // moduleConfig.external_notification.output_vibra = 28; // RAK4631 IO7 // moduleConfig.external_notification.nag_timeout = 300; + // T-Watch / T-Deck i2s audio as buzzer: + // moduleConfig.external_notification.enabled = true; + // moduleConfig.external_notification.nag_timeout = 300; + // moduleConfig.external_notification.output_ms = 1000; + // moduleConfig.external_notification.use_i2s_as_buzzer = true; + // moduleConfig.external_notification.alert_message_buzzer = true; + if (moduleConfig.external_notification.enabled) { if (!nodeDB.loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig), &meshtastic_RTTTLConfig_msg, &rtttlConfig)) { @@ -309,14 +337,13 @@ ExternalNotificationModule::ExternalNotificationModule() ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshPacket &mp) { if (moduleConfig.external_notification.enabled) { -#if T_WATCH_S3 +#ifdef T_WATCH_S3 drv.setWaveform(0, 75); drv.setWaveform(1, 56); drv.setWaveform(2, 0); drv.go(); #endif if (getFrom(&mp) != nodeDB.getNodeNum()) { - // 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; @@ -359,7 +386,11 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP if (!moduleConfig.external_notification.use_pwm) { setExternalOn(2); } else { +#ifdef HAS_I2S + audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone)); +#else rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone); +#endif } if (moduleConfig.external_notification.nag_timeout) { nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; @@ -394,10 +425,16 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP if (moduleConfig.external_notification.alert_message_buzzer) { LOG_INFO("externalNotificationModule - Notification Module (Buzzer)\n"); isNagging = true; - if (!moduleConfig.external_notification.use_pwm) { + if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) { setExternalOn(2); } else { +#ifdef HAS_I2S + if (moduleConfig.external_notification.use_i2s_as_buzzer) { + audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone)); + } +#else rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone); +#endif } if (moduleConfig.external_notification.nag_timeout) { nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp index 855ba9cde..799f6ec7c 100644 --- a/src/modules/NodeInfoModule.cpp +++ b/src/modules/NodeInfoModule.cpp @@ -89,7 +89,7 @@ int32_t NodeInfoModule::runOnce() bool requestReplies = currentGeneration != radioGeneration; currentGeneration = radioGeneration; - if (airTime->isTxAllowedAirUtil()) { + if (airTime->isTxAllowedAirUtil() && config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) { LOG_INFO("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies); sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies) } diff --git a/src/modules/RoutingModule.cpp b/src/modules/RoutingModule.cpp index d81311481..edeb1fb86 100644 --- a/src/modules/RoutingModule.cpp +++ b/src/modules/RoutingModule.cpp @@ -46,5 +46,6 @@ void RoutingModule::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketI RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg) { isPromiscuous = true; - encryptedOk = config.device.rebroadcast_mode != meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY; + encryptedOk = config.device.rebroadcast_mode != meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY && + config.device.rebroadcast_mode != meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY; } diff --git a/src/modules/Telemetry/DeviceTelemetry.cpp b/src/modules/Telemetry/DeviceTelemetry.cpp index bc6c03c52..a6eecda80 100644 --- a/src/modules/Telemetry/DeviceTelemetry.cpp +++ b/src/modules/Telemetry/DeviceTelemetry.cpp @@ -18,7 +18,8 @@ int32_t DeviceTelemetryModule::runOnce() if (((lastSentToMesh == 0) || ((now - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval))) && airTime->isTxAllowedChannelUtil() && airTime->isTxAllowedAirUtil() && - config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) { + config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && + config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) { sendTelemetry(); lastSentToMesh = now; } else if (service.isToPhoneQueueEmpty()) { diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 1047ade1d..9c7b406e9 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -97,9 +97,9 @@ int32_t EnvironmentTelemetryModule::runOnce() result = lps22hbSensor.runOnce(); if (sht31Sensor.hasSensor()) result = sht31Sensor.runOnce(); - if (ina219Sensor.hasSensor() && !ina219Sensor.isInitialized()) + if (ina219Sensor.hasSensor()) result = ina219Sensor.runOnce(); - if (ina260Sensor.hasSensor() && !ina260Sensor.isInitialized()) + if (ina260Sensor.hasSensor()) result = ina260Sensor.runOnce(); } return result; diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 0686aa59f..451d7ffbe 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -121,6 +121,10 @@ #define HW_VENDOR meshtastic_HardwareModel_PICOMPUTER_S3 #elif defined(HELTEC_HT62) #define HW_VENDOR meshtastic_HardwareModel_HELTEC_HT62 +#elif defined(SENSELORA_S3) +#define HW_VENDOR meshtastic_HardwareModel_SENSELORA_S3 +#elif defined(HELTEC_HT62) +#define HW_VENDOR meshtastic_HardwareModel_HELTEC_HT62 #endif // ----------------------------------------------------------------------------- diff --git a/src/platform/nrf52/NRF52Bluetooth.cpp b/src/platform/nrf52/NRF52Bluetooth.cpp index c29739542..dd81929c8 100644 --- a/src/platform/nrf52/NRF52Bluetooth.cpp +++ b/src/platform/nrf52/NRF52Bluetooth.cpp @@ -211,6 +211,7 @@ void NRF52Bluetooth::shutdown() // Shutdown bluetooth for minimum power draw LOG_INFO("Disable NRF52 bluetooth\n"); Bluefruit.Advertising.stop(); + Bluefruit.setTxPower(0); // Minimum power } bool NRF52Bluetooth::isConnected() @@ -333,4 +334,4 @@ void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_statu LOG_INFO("BLE pairing failed\n"); screen->stopBluetoothPinScreen(); -} +} \ No newline at end of file diff --git a/src/platform/rp2040/architecture.h b/src/platform/rp2040/architecture.h index 762a2dc83..61eb1bbe8 100644 --- a/src/platform/rp2040/architecture.h +++ b/src/platform/rp2040/architecture.h @@ -25,4 +25,6 @@ #define HW_VENDOR meshtastic_HardwareModel_RPI_PICO #elif defined(RAK11310) #define HW_VENDOR meshtastic_HardwareModel_RAK11310 +#elif defined(SENSELORA_RP2040) +#define HW_VENDOR meshtastic_HardwareModel_SENSELORA_RP2040 #endif \ No newline at end of file diff --git a/suppressions.txt b/suppressions.txt index e65afc0bf..6cbd38d47 100644 --- a/suppressions.txt +++ b/suppressions.txt @@ -49,4 +49,5 @@ virtualCallInConstructor passedByValue:*/RedirectablePrint.h -internalAstError:*/CrossPlatformCryptoEngine.cpp \ No newline at end of file +internalAstError:*/CrossPlatformCryptoEngine.cpp +uninitMemberVar:*/AudioThread.h \ No newline at end of file diff --git a/variants/senselora_rp2040/pins_arduino.h b/variants/senselora_rp2040/pins_arduino.h new file mode 100644 index 000000000..bb0ee637e --- /dev/null +++ b/variants/senselora_rp2040/pins_arduino.h @@ -0,0 +1,50 @@ +#pragma once + +#define PIN_A0 (26u) +#define PIN_A1 (27u) +#define PIN_A2 (28u) +#define PIN_A3 (29u) + +static const uint8_t A0 = PIN_A0; +static const uint8_t A1 = PIN_A1; +static const uint8_t A2 = PIN_A2; +static const uint8_t A3 = PIN_A3; + +// LEDs +#define PIN_LED (23u) +#define PIN_LED1 PIN_LED +#define LED_BUILTIN PIN_LED + +#define ADC_RESOLUTION 12 + +// Serial +#define PIN_SERIAL1_TX (0ul) +#define PIN_SERIAL1_RX (1ul) + +#define PIN_SERIAL2_TX (4ul) +#define PIN_SERIAL2_RX (5ul) + +// SPI +#define PIN_SPI0_MISO (16u) +#define PIN_SPI0_MOSI (19u) +#define PIN_SPI0_SCK (18u) +#define PIN_SPI0_SS (17u) + +// Wire +#define PIN_WIRE0_SDA (6u) +#define PIN_WIRE0_SCL (7u) + +#define PIN_WIRE1_SDA (-1) +#define PIN_WIRE1_SCL (-1) + +#define SERIAL_HOWMANY (3u) +#define SPI_HOWMANY (2u) +#define WIRE_HOWMANY (1u) + +static const uint8_t SS = PIN_SPI0_SS; +static const uint8_t MOSI = PIN_SPI0_MOSI; +static const uint8_t MISO = PIN_SPI0_MISO; +static const uint8_t SCK = PIN_SPI0_SCK; + +static const uint8_t SDA = PIN_WIRE0_SDA; +static const uint8_t SCL = PIN_WIRE0_SCL; \ No newline at end of file diff --git a/variants/senselora_rp2040/platformio.ini b/variants/senselora_rp2040/platformio.ini new file mode 100644 index 000000000..3b3253ee8 --- /dev/null +++ b/variants/senselora_rp2040/platformio.ini @@ -0,0 +1,13 @@ +[env:senselora_rp2040] +extends = rp2040_base +board = rpipico +upload_protocol = picotool + +# add our variants files to the include and src paths +build_flags = ${rp2040_base.build_flags} + -DSENSELORA_RP2040 + -Ivariants/senselora_rp2040 + -DDEBUG_RP2040_PORT=Serial + -L "${platformio.libdeps_dir}/${this.__env__}/BSEC2 Software Library/src/cortex-m0plus" +lib_deps = + ${rp2040_base.lib_deps} \ No newline at end of file diff --git a/variants/senselora_rp2040/variant.h b/variants/senselora_rp2040/variant.h new file mode 100644 index 000000000..9eda65521 --- /dev/null +++ b/variants/senselora_rp2040/variant.h @@ -0,0 +1,26 @@ +#define ARDUINO_ARCH_AVR + +#define USE_SSD1306 + +#define BUTTON_PIN 2 +#define BUTTON_NEED_PULLUP + +#define LED_PIN PIN_LED + +#undef BATTERY_PIN + +#undef LORA_SCK +#undef LORA_MISO +#undef LORA_MOSI +#undef LORA_CS + +#define USE_RF95 +#define LORA_SCK PIN_SPI0_SCK +#define LORA_MISO PIN_SPI0_MISO +#define LORA_MOSI PIN_SPI0_MOSI +#define LORA_CS PIN_SPI0_SS + +#define LORA_DIO0 21 +#define LORA_DIO1 22 +#define LORA_DIO2 RADIOLIB_NC +#define LORA_RESET 20 \ No newline at end of file diff --git a/variants/t-deck/platformio.ini b/variants/t-deck/platformio.ini index 38e334a30..cb6033300 100644 --- a/variants/t-deck/platformio.ini +++ b/variants/t-deck/platformio.ini @@ -2,8 +2,8 @@ [env:t-deck] extends = esp32s3_base board = t-deck -upload_protocol = esp-builtin -debug_tool = esp-builtin +upload_protocol = esptool +#upload_port = COM29 build_flags = ${esp32_base.build_flags} -DT_DECK @@ -12,4 +12,6 @@ build_flags = ${esp32_base.build_flags} -Ivariants/t-deck lib_deps = ${esp32s3_base.lib_deps} - lovyan03/LovyanGFX@^1.1.9 \ No newline at end of file + lovyan03/LovyanGFX@^1.1.9 + earlephilhower/ESP8266Audio@^1.9.7 + earlephilhower/ESP8266SAM@^1.0.1 \ No newline at end of file diff --git a/variants/t-deck/variant.h b/variants/t-deck/variant.h index 446e22732..62ac0a373 100644 --- a/variants/t-deck/variant.h +++ b/variants/t-deck/variant.h @@ -65,6 +65,12 @@ #define ES7210_LRCK 21 #define ES7210_MCLK 48 +// dac / amp +#define HAS_I2S +#define DAC_I2S_BCK 7 +#define DAC_I2S_WS 5 +#define DAC_I2S_DOUT 6 + // LoRa #define USE_SX1262 #define USE_SX1268 diff --git a/variants/t-watch-s3/platformio.ini b/variants/t-watch-s3/platformio.ini index 162384bfd..d03273ed4 100644 --- a/variants/t-watch-s3/platformio.ini +++ b/variants/t-watch-s3/platformio.ini @@ -3,6 +3,8 @@ extends = esp32s3_base board = t-watch-s3 upload_protocol = esptool +upload_speed = 115200 +upload_port = /dev/tty.usbmodem3485188D636C1 build_flags = ${esp32_base.build_flags} -DT_WATCH_S3 @@ -12,4 +14,6 @@ build_flags = ${esp32_base.build_flags} lib_deps = ${esp32s3_base.lib_deps} lovyan03/LovyanGFX@^1.1.9 lewisxhe/PCF8563_Library@1.0.1 - adafruit/Adafruit DRV2605 Library@^1.2.2 \ No newline at end of file + adafruit/Adafruit DRV2605 Library@^1.2.2 + earlephilhower/ESP8266Audio@^1.9.7 + earlephilhower/ESP8266SAM@^1.0.1 \ No newline at end of file diff --git a/variants/t-watch-s3/variant.h b/variants/t-watch-s3/variant.h index c30224034..c66fac5ef 100644 --- a/variants/t-watch-s3/variant.h +++ b/variants/t-watch-s3/variant.h @@ -30,6 +30,11 @@ #define TFT_BL ST7789_BACKLIGHT_EN +#define HAS_I2S +#define DAC_I2S_BCK 48 +#define DAC_I2S_WS 15 +#define DAC_I2S_DOUT 46 + #define HAS_AXP2101 #define HAS_RTC 1 @@ -37,8 +42,6 @@ #define I2C_SDA 10 // For QMC6310 sensors and screens #define I2C_SCL 11 // For QMC6310 sensors and screens -#define BUTTON_PIN 0 - #define BMA4XX_INT 14 // Interrupt for BMA_423 axis sensor #define HAS_GPS 0