Merge branch 'master' into apollo

This commit is contained in:
Ben Meadors 2023-12-12 17:39:18 -06:00 committed by GitHub
commit b2a313780f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 477 additions and 128 deletions

View File

@ -66,6 +66,7 @@ jobs:
- board: tlora-v2-1-1_6 - board: tlora-v2-1-1_6
- board: tlora-v2-1-1_8 - board: tlora-v2-1-1_8
- board: tbeam - board: tbeam
- board: heltec-ht62-esp32c3-sx1262
- board: heltec-v1 - board: heltec-v1
- board: heltec-v2_0 - board: heltec-v2_0
- board: heltec-v2_1 - board: heltec-v2_1
@ -125,6 +126,7 @@ jobs:
- board: pico - board: pico
- board: picow - board: picow
- board: rak11310 - board: rak11310
- board: senselora_rp2040
uses: ./.github/workflows/build_rpi2040.yml uses: ./.github/workflows/build_rpi2040.yml
with: with:
board: ${{ matrix.board }} board: ${{ matrix.board }}
@ -211,6 +213,9 @@ jobs:
repository: ${{github.event.pull_request.head.repo.full_name}} repository: ${{github.event.pull_request.head.repo.full_name}}
gather-artifacts: gather-artifacts:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
[ [
@ -284,14 +289,13 @@ jobs:
- name: Create request artifacts - name: Create request artifacts
continue-on-error: true # FIXME: Why are we getting 502, but things still work? 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' }} 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: with:
commit: ${{ (github.event.pull_request_target || github.event.pull_request).head.sha }} commit: ${{ (github.event.pull_request_target || github.event.pull_request).head.sha }}
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
artifacts-token: ${{ secrets.ARTIFACTS_TOKEN }} artifacts-token: ${{ secrets.ARTIFACTS_TOKEN }}
artifacts-repo: meshtastic/artifacts artifacts-repo: meshtastic/artifacts
artifacts-branch: device artifacts-branch: device
artifacts-dir: pr
artifacts: ./firmware-${{ steps.version.outputs.version }}.zip artifacts: ./firmware-${{ steps.version.outputs.version }}.zip
release-artifacts: release-artifacts:

View File

@ -12,10 +12,9 @@ lint:
- checkov@3.1.9 - checkov@3.1.9
- terrascan@1.18.5 - terrascan@1.18.5
- trivy@0.47.0 - trivy@0.47.0
- trufflehog@3.63.2-rc0 #- trufflehog@3.63.2-rc0
- taplo@0.8.1 - taplo@0.8.1
- ruff@0.1.6 - ruff@0.1.6
- yamllint@1.33.0
- isort@5.12.0 - isort@5.12.0
- markdownlint@0.37.0 - markdownlint@0.37.0
- oxipng@9.0.0 - oxipng@9.0.0

View File

@ -44,7 +44,6 @@ lib_deps =
${environmental_base.lib_deps} ${environmental_base.lib_deps}
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2 https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
h2zero/NimBLE-Arduino@^1.4.1 h2zero/NimBLE-Arduino@^1.4.1
jgromes/RadioLib@^6.2.0
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f

View File

@ -25,7 +25,6 @@ build_src_filter =
lib_deps= lib_deps=
${arduino_base.lib_deps} ${arduino_base.lib_deps}
jgromes/RadioLib@^6.2.0
lib_ignore = lib_ignore =
BluetoothOTA BluetoothOTA

View File

@ -24,7 +24,6 @@ lib_deps =
${env.lib_deps} ${env.lib_deps}
${networking_base.lib_deps} ${networking_base.lib_deps}
rweather/Crypto@^0.4.0 rweather/Crypto@^0.4.0
jgromes/RadioLib@^6.1.0
build_flags = build_flags =
${arduino_base.build_flags} ${arduino_base.build_flags}

View File

@ -2,7 +2,7 @@
[rp2040_base] [rp2040_base]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#612de5399d68b359053f1307ed223d400aea975c platform = https://github.com/maxgerhardt/platform-raspberrypi.git#612de5399d68b359053f1307ed223d400aea975c
extends = arduino_base 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.core = earlephilhower
board_build.filesystem_size = 0.5m board_build.filesystem_size = 0.5m
@ -29,5 +29,4 @@ lib_ignore =
lib_deps = lib_deps =
${arduino_base.lib_deps} ${arduino_base.lib_deps}
${environmental_base.lib_deps} ${environmental_base.lib_deps}
jgromes/RadioLib@^6.2.0
rweather/Crypto rweather/Crypto

View File

@ -34,7 +34,6 @@ upload_protocol = stlink
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
jgromes/RadioLib@^6.2.0
https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b
https://github.com/littlefs-project/littlefs.git#v2.5.1 https://github.com/littlefs-project/littlefs.git#v2.5.1
https://github.com/stm32duino/STM32FreeRTOS.git#10.3.1 https://github.com/stm32duino/STM32FreeRTOS.git#10.3.1

View File

@ -68,6 +68,7 @@ build_flags = -Wno-missing-field-initializers
monitor_speed = 115200 monitor_speed = 115200
lib_deps = lib_deps =
jgromes/RadioLib@^6.3.0
https://github.com/meshtastic/esp8266-oled-ssd1306.git#b38094e03dfa964fbc0e799bc374e91a605c1223 ; ESP8266_SSD1306 https://github.com/meshtastic/esp8266-oled-ssd1306.git#b38094e03dfa964fbc0e799bc374e91a605c1223 ; ESP8266_SSD1306
mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159

@ -1 +1 @@
Subproject commit 9148427a3be535c9e3f17e846ecbb64ce04b6521 Subproject commit a34b2c680e2c1c240643c515e57c5532b29c91a7

77
src/AudioThread.h Normal file
View File

@ -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 <AudioFileSourcePROGMEM.h>
#include <AudioGeneratorRTTTL.h>
#include <AudioOutputI2S.h>
#include <ESP8266SAM.h>
#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

View File

@ -5,6 +5,7 @@
#include "configuration.h" #include "configuration.h"
#include "graphics/Screen.h" #include "graphics/Screen.h"
#include "main.h" #include "main.h"
#include "modules/ExternalNotificationModule.h"
#include "power.h" #include "power.h"
#include <OneButton.h> #include <OneButton.h>
@ -205,6 +206,12 @@ class ButtonThread : public concurrency::OSThread
static void userButtonPressedLongStart() 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) { if (millis() > 30 * 1000) {
LOG_DEBUG("Long press start!\n"); LOG_DEBUG("Long press start!\n");
longPressTime = millis(); longPressTime = millis();

View File

@ -33,7 +33,7 @@ class PowerFSMThread : public OSThread
powerFSM.trigger(EVENT_SHUTDOWN); powerFSM.trigger(EVENT_SHUTDOWN);
} }
return 10; return 100;
} }
}; };

View File

@ -293,7 +293,7 @@ bool GPS::setup()
gnssModel = GNSS_MODEL_UNKNOWN; gnssModel = GNSS_MODEL_UNKNOWN;
} }
#else #else
gnssModel = GNSS_MODEL_UC6850; gnssModel = GNSS_MODEL_UC6580;
#endif #endif
if (gnssModel == GNSS_MODEL_MTK) { if (gnssModel == GNSS_MODEL_MTK) {
@ -311,11 +311,23 @@ bool GPS::setup()
// Switch to Vehicle Mode, since SoftRF enables Aviation < 2g // Switch to Vehicle Mode, since SoftRF enables Aviation < 2g
_serial_gps->write("$PCAS11,3*1E\r\n"); _serial_gps->write("$PCAS11,3*1E\r\n");
delay(250); delay(250);
} else if (gnssModel == GNSS_MODEL_UC6850) { } else if (gnssModel == GNSS_MODEL_UC6580) {
// The Unicore UC6580 can use a lot of sat systems, enable it to
// use GPS + GLONASS // use GPS L1 & L5 + BDS B1I & B2a + GLONASS L1 + GALILEO E1 & E5a + SBAS
_serial_gps->write("$CFGSYS,h15\r\n"); // 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); 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) { } else if (gnssModel == GNSS_MODEL_UBLOX) {
// Configure GNSS system to GPS+SBAS+GLONASS (Module may restart after this command) // 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 // We need set it because by default it is GPS only, and we want to use GLONASS too

View File

@ -23,7 +23,7 @@ struct uBloxGnssModelInfo {
typedef enum { typedef enum {
GNSS_MODEL_MTK, GNSS_MODEL_MTK,
GNSS_MODEL_UBLOX, GNSS_MODEL_UBLOX,
GNSS_MODEL_UC6850, GNSS_MODEL_UC6580,
GNSS_MODEL_UNKNOWN, GNSS_MODEL_UNKNOWN,
} GnssModel_t; } GnssModel_t;

View File

@ -152,7 +152,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
#endif #endif
// nrf52 doesn't have a readable RTC (yet - software not written) // nrf52 doesn't have a readable RTC (yet - software not written)
#ifdef HAS_RTC #if HAS_RTC
readFromRTC(); readFromRTC();
#endif #endif
@ -208,4 +208,4 @@ uint32_t getTime()
uint32_t getValidTime(RTCQuality minQuality) uint32_t getValidTime(RTCQuality minQuality)
{ {
return (currentQuality >= minQuality) ? getTime() : 0; return (currentQuality >= minQuality) ? getTime() : 0;
} }

View File

@ -43,9 +43,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "sleep.h" #include "sleep.h"
#include "target_specific.h" #include "target_specific.h"
#if HAS_WIFI && !defined(ARCH_RASPBERRY_PI)
#include "mesh/wifi/WiFiAPClient.h"
#endif
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "mesh/wifi/WiFiAPClient.h"
#include "modules/esp32/StoreForwardModule.h" #include "modules/esp32/StoreForwardModule.h"
#endif #endif
@ -1294,7 +1297,7 @@ void Screen::setFrames()
// call a method on debugInfoScreen object (for more details) // call a method on debugInfoScreen object (for more details)
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline; normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
#ifdef ARCH_ESP32 #if HAS_WIFI && !defined(ARCH_RASPBERRY_PI)
if (isWifiAvailable()) { if (isWifiAvailable()) {
// call a method on debugInfoScreen object (for more details) // call a method on debugInfoScreen object (for more details)
normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline; normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline;

View File

@ -2,6 +2,7 @@
#include "InputBroker.h" #include "InputBroker.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "configuration.h" #include "configuration.h"
#include "modules/ExternalNotificationModule.h"
TouchScreenImpl1 *touchScreenImpl1; TouchScreenImpl1 *touchScreenImpl1;
@ -63,7 +64,11 @@ void TouchScreenImpl1::onEvent(const TouchEvent &event)
break; break;
} }
case TOUCH_ACTION_TAP: { 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; break;
} }
default: default:

View File

@ -84,6 +84,11 @@ NRF52Bluetooth *nrf52Bluetooth;
#include "AmbientLightingThread.h" #include "AmbientLightingThread.h"
#endif #endif
#ifdef HAS_I2S
#include "AudioThread.h"
AudioThread *audioThread;
#endif
using namespace concurrency; using namespace concurrency;
// We always create a screen object, but we only init it if we find the hardware // 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 #ifdef T_WATCH_S3
Adafruit_DRV2605 drv; Adafruit_DRV2605 drv;
#endif #endif
bool isVibrating = false; bool isVibrating = false;
bool eink_found = true; bool eink_found = true;
@ -432,6 +438,10 @@ void setup()
auto i2cCount = i2cScanner->countDevices(); auto i2cCount = i2cScanner->countDevices();
if (i2cCount == 0) { if (i2cCount == 0) {
LOG_INFO("No I2C devices found\n"); LOG_INFO("No I2C devices found\n");
Wire.end();
#ifdef I2C_SDA1
Wire1.end();
#endif
} else { } else {
LOG_INFO("%i I2C devices found\n", i2cCount); 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 // but we need to do this after main cpu init (esp32setup), because we need the random seed set
nodeDB.init(); nodeDB.init();
// If we're taking on the repeater role, use flood router // 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) if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
router = new FloodingRouter(); router = new FloodingRouter();
else #ifdef PIN_3V3_EN
digitalWrite(PIN_3V3_EN, LOW);
#endif
} else
router = new ReliableRouter(); router = new ReliableRouter();
#if HAS_BUTTON || defined(ARCH_RASPBERRY_PI) #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) 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) { if (gps) {
gpsStatus->observe(&gps->newStatus); gpsStatus->observe(&gps->newStatus);
} else { } else {
@ -664,6 +680,11 @@ void setup()
} }
nodeStatus->observe(&nodeDB.newStatus); nodeStatus->observe(&nodeDB.newStatus);
#ifdef HAS_I2S
LOG_DEBUG("Starting audio thread\n");
audioThread = new AudioThread();
#endif
service.init(); service.init();
// Now that the mesh service is created, create any modules // 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 // 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 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(); powerFSMthread = new PowerFSMThread();
setCPUFast(false); // 80MHz is fine for our slow peripherals setCPUFast(false); // 80MHz is fine for our slow peripherals
} }
@ -944,4 +964,4 @@ void loop()
mainDelay.delay(delayMsec); mainDelay.delay(delayMsec);
} }
// if (didWake) LOG_DEBUG("wake!\n"); // if (didWake) LOG_DEBUG("wake!\n");
} }

View File

@ -42,6 +42,12 @@ extern ATECCX08A atecc;
#include <Adafruit_DRV2605.h> #include <Adafruit_DRV2605.h>
extern Adafruit_DRV2605 drv; extern Adafruit_DRV2605 drv;
#endif #endif
#ifdef HAS_I2S
#include "AudioThread.h"
extern AudioThread *audioThread;
#endif
extern bool isVibrating; extern bool isVibrating;
extern int TCPPort; // set by Portduino extern int TCPPort; // set by Portduino

View File

@ -73,58 +73,3 @@ template <class T> class MemoryDynamic : public Allocator<T>
return p; return p;
} }
}; };
/**
* A pool based allocator
*
*/
template <class T> class MemoryPool : public Allocator<T>
{
PointerQueue<T> 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;
}
};

View File

@ -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) * 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 * Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep a

View File

@ -82,6 +82,9 @@ class MeshService
/// Return the next MqttClientProxyMessage packet destined to the phone. /// Return the next MqttClientProxyMessage packet destined to the phone.
meshtastic_MqttClientProxyMessage *getMqttClientProxyMessageForPhone() { return toPhoneMqttProxyQueue.dequeuePtr(0); } 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 // Release QueueStatus packet to pool
void releaseQueueStatusToPool(meshtastic_QueueStatus *p) { queueStatusPool.release(p); } void releaseQueueStatusToPool(meshtastic_QueueStatus *p) { queueStatusPool.release(p); }

View File

@ -245,9 +245,12 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.output_ms = 1000; moduleConfig.external_notification.output_ms = 1000;
moduleConfig.external_notification.nag_timeout = 60; moduleConfig.external_notification.nag_timeout = 60;
#endif #endif
#ifdef T_WATCH_S3 #ifdef HAS_I2S
// Don't worry about the other settings, we'll use the DRV2056 behavior for notifications // 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.enabled = true;
moduleConfig.external_notification.use_i2s_as_buzzer = true;
moduleConfig.external_notification.alert_message_buzzer = true;
moduleConfig.external_notification.nag_timeout = 60;
#endif #endif
#ifdef NANO_G2_ULTRA #ifdef NANO_G2_ULTRA
moduleConfig.external_notification.enabled = true; 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_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_SPEED |
meshtastic_Config_PositionConfig_PositionFlags_HEADING | meshtastic_Config_PositionConfig_PositionFlags_DOP); meshtastic_Config_PositionConfig_PositionFlags_HEADING | meshtastic_Config_PositionConfig_PositionFlags_DOP);
moduleConfig.telemetry.device_update_interval = ONE_DAY; 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;
} }
} }

View File

@ -299,6 +299,12 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_ALL_SKIP_DECODING) config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_ALL_SKIP_DECODING)
return false; 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) if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag)
return true; // If packet was already decoded just return return true; // If packet was already decoded just return

View File

@ -27,6 +27,8 @@ template <class T> class TypedQueue
bool isEmpty() { return uxQueueMessagesWaiting(h) == 0; } 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 /** euqueue a packet. Also, maxWait used to default to portMAX_DELAY, but we now want to callers to THINK about what blocking
* they want */ * they want */
bool enqueue(T x, TickType_t maxWait) bool enqueue(T x, TickType_t maxWait)
@ -80,6 +82,8 @@ template <class T> class TypedQueue
bool isEmpty() { return q.empty(); } bool isEmpty() { return q.empty(); }
int numUsed() { return q.size(); }
bool enqueue(T x, TickType_t maxWait = portMAX_DELAY) bool enqueue(T x, TickType_t maxWait = portMAX_DELAY)
{ {
if (reader) { if (reader) {

View File

@ -43,7 +43,18 @@ typedef enum _meshtastic_Config_DeviceConfig_Role {
Used for nodes dedicated for connection to an ATAK EUD. Used for nodes dedicated for connection to an ATAK EUD.
Turns off many of the routine broadcasts to favor CoT packet stream Turns off many of the routine broadcasts to favor CoT packet stream
from the Meshtastic ATAK plugin -> IMeshService -> Node */ 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; } meshtastic_Config_DeviceConfig_Role;
/* Defines the device's behavior for how messages are rebroadcast */ /* 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, meshtastic_Config_DeviceConfig_RebroadcastMode_ALL_SKIP_DECODING = 1,
/* Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. /* 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. */ 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; } meshtastic_Config_DeviceConfig_RebroadcastMode;
/* Bit field of boolean configuration options, indicating which optional /* Bit field of boolean configuration options, indicating which optional
@ -479,12 +493,12 @@ extern "C" {
/* Helper constants for enums */ /* Helper constants for enums */
#define _meshtastic_Config_DeviceConfig_Role_MIN meshtastic_Config_DeviceConfig_Role_CLIENT #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_MAX meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND
#define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_TAK+1)) #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_MIN meshtastic_Config_DeviceConfig_RebroadcastMode_ALL
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY #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_LOCAL_ONLY+1)) #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_MIN meshtastic_Config_PositionConfig_PositionFlags_UNSET
#define _meshtastic_Config_PositionConfig_PositionFlags_MAX meshtastic_Config_PositionConfig_PositionFlags_SPEED #define _meshtastic_Config_PositionConfig_PositionFlags_MAX meshtastic_Config_PositionConfig_PositionFlags_SPEED

View File

@ -67,6 +67,10 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_STATION_G1 = 25, meshtastic_HardwareModel_STATION_G1 = 25,
/* RAK11310 (RP2040 + SX1262) */ /* RAK11310 (RP2040 + SX1262) */
meshtastic_HardwareModel_RAK11310 = 26, 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) Less common/prototype boards listed here (needs one more byte over the air)
--------------------------------------------------------------------------- */ --------------------------------------------------------------------------- */

View File

@ -9,13 +9,11 @@
#include "target_specific.h" #include "target_specific.h"
#include <WiFi.h> #include <WiFi.h>
#include <WiFiUdp.h> #include <WiFiUdp.h>
#ifndef ARCH_RP2040 #ifdef ARCH_ESP32
#include "mesh/http/WebServer.h" #include "mesh/http/WebServer.h"
#include <ESPmDNS.h> #include <ESPmDNS.h>
#include <esp_wifi.h> #include <esp_wifi.h>
static void WiFiEvent(WiFiEvent_t event); static void WiFiEvent(WiFiEvent_t event);
#else
#include <ESP8266mDNS.h>
#endif #endif
#ifndef DISABLE_NTP #ifndef DISABLE_NTP
@ -53,6 +51,7 @@ static void onNetworkConnected()
// Start web server // Start web server
LOG_INFO("Starting network services\n"); LOG_INFO("Starting network services\n");
#ifdef ARCH_ESP32
// start mdns // start mdns
if (!MDNS.begin("Meshtastic")) { if (!MDNS.begin("Meshtastic")) {
LOG_ERROR("Error setting up MDNS responder!\n"); LOG_ERROR("Error setting up MDNS responder!\n");
@ -62,6 +61,9 @@ static void onNetworkConnected()
MDNS.addService("http", "tcp", 80); MDNS.addService("http", "tcp", 80);
MDNS.addService("https", "tcp", 443); 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 #ifndef DISABLE_NTP
LOG_INFO("Starting NTP time client\n"); LOG_INFO("Starting NTP time client\n");
@ -89,7 +91,7 @@ static void onNetworkConnected()
syslog.enable(); syslog.enable();
} }
#ifndef ARCH_RP2040 #ifdef ARCH_ESP32
initWebServer(); initWebServer();
#endif #endif
initApiServer(); initApiServer();
@ -149,6 +151,11 @@ static int32_t reconnectWiFi()
#endif #endif
if (config.network.wifi_enabled && !WiFi.isConnected()) { 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 return 1000; // check once per second
} else { } else {
#ifdef ARCH_RP2040 #ifdef ARCH_RP2040
@ -245,7 +252,7 @@ bool initWifi()
} }
} }
#ifndef ARCH_RP2040 #ifdef ARCH_ESP32
// Called by the Espressif SDK to // Called by the Espressif SDK to
static void WiFiEvent(WiFiEvent_t event) 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"); LOG_INFO("Authentication mode of access point has changed\n");
break; break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP: 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(); onNetworkConnected();
break; break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP6: 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; break;
case ARDUINO_EVENT_WIFI_STA_LOST_IP: case ARDUINO_EVENT_WIFI_STA_LOST_IP:
LOG_INFO("Lost IP address and IP address is reset to 0\n"); 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() uint8_t getWifiDisconnectReason()
{ {
return wifiDisconnectReason; return wifiDisconnectReason;
} }

View File

@ -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); 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() int32_t CannedMessageModule::runOnce()
@ -244,7 +246,8 @@ int32_t CannedMessageModule::runOnce()
} }
// LOG_DEBUG("Check status\n"); // LOG_DEBUG("Check status\n");
UIFrameEvent e = {false, true}; 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 // TODO: might have some feedback of sendig state
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
e.frameChanged = true; e.frameChanged = true;
@ -483,7 +486,18 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
{ {
char buffer[50]; 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->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM); display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending..."); 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() void CannedMessageModule::loadProtoForModule()
{ {
if (!nodeDB.loadProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size, if (!nodeDB.loadProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size,
@ -650,4 +685,4 @@ String CannedMessageModule::drawWithCursor(String text, int cursor)
return result; return result;
} }
#endif #endif

View File

@ -9,6 +9,7 @@ enum cannedMessageModuleRunState {
CANNED_MESSAGE_RUN_STATE_ACTIVE, CANNED_MESSAGE_RUN_STATE_ACTIVE,
CANNED_MESSAGE_RUN_STATE_FREETEXT, CANNED_MESSAGE_RUN_STATE_FREETEXT,
CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE, CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE,
CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED,
CANNED_MESSAGE_RUN_STATE_ACTION_SELECT, CANNED_MESSAGE_RUN_STATE_ACTION_SELECT,
CANNED_MESSAGE_RUN_STATE_ACTION_UP, CANNED_MESSAGE_RUN_STATE_ACTION_UP,
CANNED_MESSAGE_RUN_STATE_ACTION_DOWN, CANNED_MESSAGE_RUN_STATE_ACTION_DOWN,
@ -37,15 +38,29 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
const char *getMessageByIndex(int index); const char *getMessageByIndex(int index);
const char *getNodeName(NodeNum node); const char *getNodeName(NodeNum node);
bool shouldDraw(); bool shouldDraw();
void eventUp(); // void eventUp();
void eventDown(); // void eventDown();
void eventSelect(); // void eventSelect();
void handleGetCannedMessageModuleMessages(const meshtastic_MeshPacket &req, meshtastic_AdminMessage *response); void handleGetCannedMessageModuleMessages(const meshtastic_MeshPacket &req, meshtastic_AdminMessage *response);
void handleSetCannedMessageModuleMessages(const char *from_msg); void handleSetCannedMessageModuleMessages(const char *from_msg);
String drawWithCursor(String text, int cursor); String drawWithCursor(String text, int cursor);
/*
-Override the wantPacket method. We need the Routing Messages to look for ACKs.
*/
virtual bool wantPacket(const meshtastic_MeshPacket *p) override
{
switch (p->decoded.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP:
case meshtastic_PortNum_ROUTING_APP:
return true;
default:
return false;
}
}
protected: protected:
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
@ -63,6 +78,12 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
meshtastic_AdminMessage *request, meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response) override; meshtastic_AdminMessage *response) override;
/** 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 meshtastic_MeshPacket &mp) override;
void loadProtoForModule(); void loadProtoForModule();
bool saveProtoForModule(); bool saveProtoForModule();
@ -75,6 +96,8 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
String freetext = ""; // Text Buffer for Freetext Editor String freetext = ""; // Text Buffer for Freetext Editor
bool destSelect = false; // Freetext Editor Mode bool destSelect = false; // Freetext Editor Mode
NodeNum dest = NODENUM_BROADCAST; NodeNum dest = NODENUM_BROADCAST;
NodeNum incoming = NODENUM_BROADCAST;
bool ack = false; // True means ACK, false means NAK (error_reason != NONE)
char messageStore[CANNED_MESSAGE_MODULE_MESSAGES_SIZE + 1]; char messageStore[CANNED_MESSAGE_MODULE_MESSAGES_SIZE + 1];
char *messages[CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT]; char *messages[CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT];
@ -83,4 +106,4 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
}; };
extern CannedMessageModule *cannedMessageModule; extern CannedMessageModule *cannedMessageModule;
#endif #endif

View File

@ -20,11 +20,10 @@
#include "Router.h" #include "Router.h"
#include "buzz/buzz.h" #include "buzz/buzz.h"
#include "configuration.h" #include "configuration.h"
#include "main.h"
#include "mesh/generated/meshtastic/rtttl.pb.h" #include "mesh/generated/meshtastic/rtttl.pb.h"
#include <Arduino.h> #include <Arduino.h>
#include "main.h"
#ifdef HAS_NCP5623 #ifdef HAS_NCP5623
#include <graphics/RAKled.h> #include <graphics/RAKled.h>
@ -54,6 +53,8 @@ bool ascending = true;
#endif #endif
#define EXT_NOTIFICATION_MODULE_OUTPUT_MS 1000 #define EXT_NOTIFICATION_MODULE_OUTPUT_MS 1000
#define EXT_NOTIFICATION_DEFAULT_THREAD_MS 25
#define ASCII_BELL 0x07 #define ASCII_BELL 0x07
meshtastic_RTTTLConfig rtttlConfig; meshtastic_RTTTLConfig rtttlConfig;
@ -71,7 +72,12 @@ int32_t ExternalNotificationModule::runOnce()
if (!moduleConfig.external_notification.enabled) { if (!moduleConfig.external_notification.enabled) {
return INT32_MAX; // we don't need this thread here... return INT32_MAX; // we don't need this thread here...
} else { } 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 // let the song finish if we reach timeout
nagCycleCutoff = UINT32_MAX; nagCycleCutoff = UINT32_MAX;
LOG_INFO("Turning off external notification: "); LOG_INFO("Turning off external notification: ");
@ -132,6 +138,16 @@ int32_t ExternalNotificationModule::runOnce()
#endif #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 // now let the PWM buzzer play
if (moduleConfig.external_notification.use_pwm) { if (moduleConfig.external_notification.use_pwm) {
if (rtttl::isPlaying()) { 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)); digitalWrite(output, (moduleConfig.external_notification.active ? true : false));
break; break;
} }
#ifdef HAS_NCP5623 #ifdef HAS_NCP5623
if (rgb_found.type == ScanI2C::NCP5623) { if (rgb_found.type == ScanI2C::NCP5623) {
rgb.setColor(red, green, blue); rgb.setColor(red, green, blue);
@ -226,6 +243,9 @@ bool ExternalNotificationModule::getExternal(uint8_t index)
void ExternalNotificationModule::stopNow() void ExternalNotificationModule::stopNow()
{ {
rtttl::stop(); rtttl::stop();
#ifdef HAS_I2S
audioThread->stop();
#endif
nagCycleCutoff = 1; // small value nagCycleCutoff = 1; // small value
isNagging = false; isNagging = false;
setIntervalFromNow(0); setIntervalFromNow(0);
@ -246,6 +266,7 @@ ExternalNotificationModule::ExternalNotificationModule()
// moduleConfig.external_notification.alert_message = true; // moduleConfig.external_notification.alert_message = true;
// moduleConfig.external_notification.alert_message_buzzer = true; // moduleConfig.external_notification.alert_message_buzzer = true;
// moduleConfig.external_notification.alert_message_vibra = true; // moduleConfig.external_notification.alert_message_vibra = true;
// moduleConfig.external_notification.use_i2s_as_buzzer = true;
// moduleConfig.external_notification.active = true; // moduleConfig.external_notification.active = true;
// moduleConfig.external_notification.alert_bell = 1; // moduleConfig.external_notification.alert_bell = 1;
@ -255,6 +276,13 @@ ExternalNotificationModule::ExternalNotificationModule()
// moduleConfig.external_notification.output_vibra = 28; // RAK4631 IO7 // moduleConfig.external_notification.output_vibra = 28; // RAK4631 IO7
// moduleConfig.external_notification.nag_timeout = 300; // 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 (moduleConfig.external_notification.enabled) {
if (!nodeDB.loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig), if (!nodeDB.loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig),
&meshtastic_RTTTLConfig_msg, &rtttlConfig)) { &meshtastic_RTTTLConfig_msg, &rtttlConfig)) {
@ -309,14 +337,13 @@ ExternalNotificationModule::ExternalNotificationModule()
ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshPacket &mp) ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshPacket &mp)
{ {
if (moduleConfig.external_notification.enabled) { if (moduleConfig.external_notification.enabled) {
#if T_WATCH_S3 #ifdef T_WATCH_S3
drv.setWaveform(0, 75); drv.setWaveform(0, 75);
drv.setWaveform(1, 56); drv.setWaveform(1, 56);
drv.setWaveform(2, 0); drv.setWaveform(2, 0);
drv.go(); drv.go();
#endif #endif
if (getFrom(&mp) != nodeDB.getNodeNum()) { if (getFrom(&mp) != nodeDB.getNodeNum()) {
// Check if the message contains a bell character. Don't do this loop for every pin, just once. // Check if the message contains a bell character. Don't do this loop for every pin, just once.
auto &p = mp.decoded; auto &p = mp.decoded;
bool containsBell = false; bool containsBell = false;
@ -359,7 +386,11 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
if (!moduleConfig.external_notification.use_pwm) { if (!moduleConfig.external_notification.use_pwm) {
setExternalOn(2); setExternalOn(2);
} else { } else {
#ifdef HAS_I2S
audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone));
#else
rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone); rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone);
#endif
} }
if (moduleConfig.external_notification.nag_timeout) { if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; 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) { if (moduleConfig.external_notification.alert_message_buzzer) {
LOG_INFO("externalNotificationModule - Notification Module (Buzzer)\n"); LOG_INFO("externalNotificationModule - Notification Module (Buzzer)\n");
isNagging = true; isNagging = true;
if (!moduleConfig.external_notification.use_pwm) { if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) {
setExternalOn(2); setExternalOn(2);
} else { } 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); rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone);
#endif
} }
if (moduleConfig.external_notification.nag_timeout) { if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;

View File

@ -89,7 +89,7 @@ int32_t NodeInfoModule::runOnce()
bool requestReplies = currentGeneration != radioGeneration; bool requestReplies = currentGeneration != radioGeneration;
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); LOG_INFO("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies) sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
} }

View File

@ -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) RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg)
{ {
isPromiscuous = true; 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;
} }

View File

@ -18,7 +18,8 @@ int32_t DeviceTelemetryModule::runOnce()
if (((lastSentToMesh == 0) || if (((lastSentToMesh == 0) ||
((now - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval))) && ((now - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval))) &&
airTime->isTxAllowedChannelUtil() && airTime->isTxAllowedAirUtil() && 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(); sendTelemetry();
lastSentToMesh = now; lastSentToMesh = now;
} else if (service.isToPhoneQueueEmpty()) { } else if (service.isToPhoneQueueEmpty()) {

View File

@ -97,9 +97,9 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = lps22hbSensor.runOnce(); result = lps22hbSensor.runOnce();
if (sht31Sensor.hasSensor()) if (sht31Sensor.hasSensor())
result = sht31Sensor.runOnce(); result = sht31Sensor.runOnce();
if (ina219Sensor.hasSensor() && !ina219Sensor.isInitialized()) if (ina219Sensor.hasSensor())
result = ina219Sensor.runOnce(); result = ina219Sensor.runOnce();
if (ina260Sensor.hasSensor() && !ina260Sensor.isInitialized()) if (ina260Sensor.hasSensor())
result = ina260Sensor.runOnce(); result = ina260Sensor.runOnce();
} }
return result; return result;

View File

@ -121,6 +121,10 @@
#define HW_VENDOR meshtastic_HardwareModel_PICOMPUTER_S3 #define HW_VENDOR meshtastic_HardwareModel_PICOMPUTER_S3
#elif defined(HELTEC_HT62) #elif defined(HELTEC_HT62)
#define HW_VENDOR meshtastic_HardwareModel_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 #endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -211,6 +211,7 @@ void NRF52Bluetooth::shutdown()
// Shutdown bluetooth for minimum power draw // Shutdown bluetooth for minimum power draw
LOG_INFO("Disable NRF52 bluetooth\n"); LOG_INFO("Disable NRF52 bluetooth\n");
Bluefruit.Advertising.stop(); Bluefruit.Advertising.stop();
Bluefruit.setTxPower(0); // Minimum power
} }
bool NRF52Bluetooth::isConnected() bool NRF52Bluetooth::isConnected()
@ -333,4 +334,4 @@ void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_statu
LOG_INFO("BLE pairing failed\n"); LOG_INFO("BLE pairing failed\n");
screen->stopBluetoothPinScreen(); screen->stopBluetoothPinScreen();
} }

View File

@ -25,4 +25,6 @@
#define HW_VENDOR meshtastic_HardwareModel_RPI_PICO #define HW_VENDOR meshtastic_HardwareModel_RPI_PICO
#elif defined(RAK11310) #elif defined(RAK11310)
#define HW_VENDOR meshtastic_HardwareModel_RAK11310 #define HW_VENDOR meshtastic_HardwareModel_RAK11310
#elif defined(SENSELORA_RP2040)
#define HW_VENDOR meshtastic_HardwareModel_SENSELORA_RP2040
#endif #endif

View File

@ -49,4 +49,5 @@ virtualCallInConstructor
passedByValue:*/RedirectablePrint.h passedByValue:*/RedirectablePrint.h
internalAstError:*/CrossPlatformCryptoEngine.cpp internalAstError:*/CrossPlatformCryptoEngine.cpp
uninitMemberVar:*/AudioThread.h

View File

@ -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;

View File

@ -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}

View File

@ -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

View File

@ -2,8 +2,8 @@
[env:t-deck] [env:t-deck]
extends = esp32s3_base extends = esp32s3_base
board = t-deck board = t-deck
upload_protocol = esp-builtin upload_protocol = esptool
debug_tool = esp-builtin #upload_port = COM29
build_flags = ${esp32_base.build_flags} build_flags = ${esp32_base.build_flags}
-DT_DECK -DT_DECK
@ -12,4 +12,6 @@ build_flags = ${esp32_base.build_flags}
-Ivariants/t-deck -Ivariants/t-deck
lib_deps = ${esp32s3_base.lib_deps} lib_deps = ${esp32s3_base.lib_deps}
lovyan03/LovyanGFX@^1.1.9 lovyan03/LovyanGFX@^1.1.9
earlephilhower/ESP8266Audio@^1.9.7
earlephilhower/ESP8266SAM@^1.0.1

View File

@ -65,6 +65,12 @@
#define ES7210_LRCK 21 #define ES7210_LRCK 21
#define ES7210_MCLK 48 #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 // LoRa
#define USE_SX1262 #define USE_SX1262
#define USE_SX1268 #define USE_SX1268

View File

@ -3,6 +3,8 @@
extends = esp32s3_base extends = esp32s3_base
board = t-watch-s3 board = t-watch-s3
upload_protocol = esptool upload_protocol = esptool
upload_speed = 115200
upload_port = /dev/tty.usbmodem3485188D636C1
build_flags = ${esp32_base.build_flags} build_flags = ${esp32_base.build_flags}
-DT_WATCH_S3 -DT_WATCH_S3
@ -12,4 +14,6 @@ build_flags = ${esp32_base.build_flags}
lib_deps = ${esp32s3_base.lib_deps} lib_deps = ${esp32s3_base.lib_deps}
lovyan03/LovyanGFX@^1.1.9 lovyan03/LovyanGFX@^1.1.9
lewisxhe/PCF8563_Library@1.0.1 lewisxhe/PCF8563_Library@1.0.1
adafruit/Adafruit DRV2605 Library@^1.2.2 adafruit/Adafruit DRV2605 Library@^1.2.2
earlephilhower/ESP8266Audio@^1.9.7
earlephilhower/ESP8266SAM@^1.0.1

View File

@ -30,6 +30,11 @@
#define TFT_BL ST7789_BACKLIGHT_EN #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_AXP2101
#define HAS_RTC 1 #define HAS_RTC 1
@ -37,8 +42,6 @@
#define I2C_SDA 10 // For QMC6310 sensors and screens #define I2C_SDA 10 // For QMC6310 sensors and screens
#define I2C_SCL 11 // 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 BMA4XX_INT 14 // Interrupt for BMA_423 axis sensor
#define HAS_GPS 0 #define HAS_GPS 0