mirror of
https://github.com/meshtastic/firmware.git
synced 2025-10-27 23:12:39 +00:00
Develop -> Master (#8209)
Some checks are pending
CI / setup (check) (push) Waiting to run
CI / setup (esp32) (push) Waiting to run
CI / setup (esp32c3) (push) Waiting to run
CI / setup (esp32c6) (push) Waiting to run
CI / setup (esp32s3) (push) Waiting to run
CI / setup (nrf52840) (push) Waiting to run
CI / setup (rp2040) (push) Waiting to run
CI / setup (rp2350) (push) Waiting to run
CI / setup (stm32) (push) Waiting to run
CI / version (push) Waiting to run
CI / check (push) Blocked by required conditions
CI / build-esp32 (push) Blocked by required conditions
CI / build-esp32s3 (push) Blocked by required conditions
CI / build-esp32c3 (push) Blocked by required conditions
CI / build-esp32c6 (push) Blocked by required conditions
CI / build-nrf52840 (push) Blocked by required conditions
CI / build-rp2040 (push) Blocked by required conditions
CI / build-rp2350 (push) Blocked by required conditions
CI / build-stm32 (push) Blocked by required conditions
CI / build-debian-src (push) Waiting to run
CI / package-pio-deps-native-tft (push) Waiting to run
CI / test-native (push) Waiting to run
CI / docker-deb-amd64 (push) Waiting to run
CI / docker-deb-amd64-tft (push) Waiting to run
CI / docker-alp-amd64 (push) Waiting to run
CI / docker-alp-amd64-tft (push) Waiting to run
CI / docker-deb-arm64 (push) Waiting to run
CI / docker-deb-armv7 (push) Waiting to run
CI / gather-artifacts (esp32) (push) Blocked by required conditions
CI / gather-artifacts (esp32c3) (push) Blocked by required conditions
CI / gather-artifacts (esp32c6) (push) Blocked by required conditions
CI / gather-artifacts (esp32s3) (push) Blocked by required conditions
CI / gather-artifacts (nrf52840) (push) Blocked by required conditions
CI / gather-artifacts (rp2040) (push) Blocked by required conditions
CI / gather-artifacts (rp2350) (push) Blocked by required conditions
CI / gather-artifacts (stm32) (push) Blocked by required conditions
CI / release-artifacts (push) Blocked by required conditions
CI / release-firmware (esp32) (push) Blocked by required conditions
CI / release-firmware (esp32c3) (push) Blocked by required conditions
CI / release-firmware (esp32c6) (push) Blocked by required conditions
CI / release-firmware (esp32s3) (push) Blocked by required conditions
CI / release-firmware (nrf52840) (push) Blocked by required conditions
CI / release-firmware (rp2040) (push) Blocked by required conditions
CI / release-firmware (rp2350) (push) Blocked by required conditions
CI / release-firmware (stm32) (push) Blocked by required conditions
CI / publish-firmware (push) Blocked by required conditions
Some checks are pending
CI / setup (check) (push) Waiting to run
CI / setup (esp32) (push) Waiting to run
CI / setup (esp32c3) (push) Waiting to run
CI / setup (esp32c6) (push) Waiting to run
CI / setup (esp32s3) (push) Waiting to run
CI / setup (nrf52840) (push) Waiting to run
CI / setup (rp2040) (push) Waiting to run
CI / setup (rp2350) (push) Waiting to run
CI / setup (stm32) (push) Waiting to run
CI / version (push) Waiting to run
CI / check (push) Blocked by required conditions
CI / build-esp32 (push) Blocked by required conditions
CI / build-esp32s3 (push) Blocked by required conditions
CI / build-esp32c3 (push) Blocked by required conditions
CI / build-esp32c6 (push) Blocked by required conditions
CI / build-nrf52840 (push) Blocked by required conditions
CI / build-rp2040 (push) Blocked by required conditions
CI / build-rp2350 (push) Blocked by required conditions
CI / build-stm32 (push) Blocked by required conditions
CI / build-debian-src (push) Waiting to run
CI / package-pio-deps-native-tft (push) Waiting to run
CI / test-native (push) Waiting to run
CI / docker-deb-amd64 (push) Waiting to run
CI / docker-deb-amd64-tft (push) Waiting to run
CI / docker-alp-amd64 (push) Waiting to run
CI / docker-alp-amd64-tft (push) Waiting to run
CI / docker-deb-arm64 (push) Waiting to run
CI / docker-deb-armv7 (push) Waiting to run
CI / gather-artifacts (esp32) (push) Blocked by required conditions
CI / gather-artifacts (esp32c3) (push) Blocked by required conditions
CI / gather-artifacts (esp32c6) (push) Blocked by required conditions
CI / gather-artifacts (esp32s3) (push) Blocked by required conditions
CI / gather-artifacts (nrf52840) (push) Blocked by required conditions
CI / gather-artifacts (rp2040) (push) Blocked by required conditions
CI / gather-artifacts (rp2350) (push) Blocked by required conditions
CI / gather-artifacts (stm32) (push) Blocked by required conditions
CI / release-artifacts (push) Blocked by required conditions
CI / release-firmware (esp32) (push) Blocked by required conditions
CI / release-firmware (esp32c3) (push) Blocked by required conditions
CI / release-firmware (esp32c6) (push) Blocked by required conditions
CI / release-firmware (esp32s3) (push) Blocked by required conditions
CI / release-firmware (nrf52840) (push) Blocked by required conditions
CI / release-firmware (rp2040) (push) Blocked by required conditions
CI / release-firmware (rp2350) (push) Blocked by required conditions
CI / release-firmware (stm32) (push) Blocked by required conditions
CI / publish-firmware (push) Blocked by required conditions
* Create channel-mute toggle function
* Added mute state to channel settings
* Create node-mute toggle functions
* Added mute state to nodedb entries
* Rebase protos
* Decouple protobuf changes
* Disable bell-invoked ext notifs for muted nodes
* Clearly dilineate module mute from sender or channel mute
* Disable bell-invoked ext notifs for muted channels
* Trunk fmt
* Disable message-invoked ext notifs for muted channels and nodes
* Disable on-screen 'new message' popup for muted nodes and channels
* Don't mute alerts
* Make use of pre-existing channel_settings.module_settings.is_client_muted setting
* Revert previous commit - this needs it's own proto
* Regen protos
* T-Lora Pager: Interrupt based rotary encoder
* T-Lora Pager: Fix amplifier fuzzing/popping
* Fix build for other variants
* Fix - reference actual channel when changing settings
* Update protos
* Refactor ref syntax
* Fix defines
* Update comments and remove unused function
* Regen protos
* Regen protobuffs again
* Fix build failure in ci, add missing argument
* Format
* InputPollable: System for polling after interrupts
* T-Lora Pager: Use InputPollable for RotaryEncoderImpl
* Rename RotaryEncoderImpl to TLoraPagerRotaryEncoder
* Revert "Rename RotaryEncoderImpl to TLoraPagerRotaryEncoder"
This reverts commit a76cc88dc2.
* Revert unnecessary ifdefs
* Regen protos
* Use latest protos
* Regen protos for latest changes
* Decouple node-mute from channel-mute
* Regen protos
* Fix desktop build
* More flexible InputPollable paradigm
* Custom xPortInIsrContext() for nRF52/RP2xx0
* Use channel as specified in the received packet
* Use channel as specified in the received packet for OLED screen notifications
* add heltec tracker v2 board.
* Use common power amp definition for Heltec v4 and Heltec Tracker v2
* Set appropriate mqtt root upon lora region change
* Use user preferences root topic if present
* delete SX126X_MAX_POWER=11
* Assume previous root on topic change
* update mqtt root when region is changed via OLED menu handler
* Regen protos
* Removed magic numbers
* Add DIRECT_MSG_ONLY buzzer mode (#8158)
* Handle existing special case for M5STACK_UNITC6L for DIRECT_MSG_ONLY buzz mode
There already exists a special case for M5STACK_UNITC6L.
Modified it to adhere to new DIRECT_MSG_ONLY buzzer mode
* Add new buzzer mode DIRECT_MSG_ONLY to BuzzerModeMenu
* Disable notifications when buzzer mode is DIRECT_MSG_ONLY
* Change alert_message_buzzer in notification module in DIRECT_MSG_ONLY buzz mode
Better comments
* Fixed spelling in debug log
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: nexpspace <4kosjdicx@mozmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* run trunk fmt
* Update variants/esp32s3/heltec_wireless_tracker_v2/variant.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Regen protos
* Pull latest protobufs
* Don't use IS_ONE_OF when loading Modules
* GAT562: Use PRIVATE_HW (fix build) (#8198)
* ESP32s2 doesn't implement HWCDC (#8199)
* Fix build script failure under certain conditions for devices that use UF2 binaries (#8150)
* Validate CR and SF lora config (#8146)
* Validate CR and SF lora config
* No zero-bw
* Update src/modules/AdminModule.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Fix braces
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Quote firmware paths given to uf2conv
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Calculate airtime of transmitted and received packets separately (#8205)
* Correcting GPS PINs (#8087)
https://github.com/meshtastic/firmware/issues/8084
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* Clear out user.id except for sending to phone (#8202)
* Null out user.id except for sending to phone
* Fix
* Update src/modules/NodeInfoModule.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Copilot garbage
* This is unnecessary, because we don't stored user.id on userlite
* Don't need this
* Fix warning
* Just alter the protobuf
* Alter protobuf doesn't do anything with the altered data, so let's re-encode it
* Check inputbroker before access
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Add dropped packet count to LocalStats (#8207)
* Add dropped packet count to LocalStats
In case the transmit queue was full
* Trunked
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
---------
Co-authored-by: ford-jones <fordnicholasjones@gmail.com>
Co-authored-by: WillyJL <me@willyjl.dev>
Co-authored-by: Ford Jones <107664313+ford-jones@users.noreply.github.com>
Co-authored-by: Tom Fifield <tom@tomfifield.net>
Co-authored-by: Quency-D <hj_zzns@163.com>
Co-authored-by: Quency-D <55523105+Quency-D@users.noreply.github.com>
Co-authored-by: nexpspace <380097+nexpspace@users.noreply.github.com>
Co-authored-by: nexpspace <4kosjdicx@mozmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Austin Lane <vidplace7@gmail.com>
Co-authored-by: Ken Piper <kealper@ivystone.net>
Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com>
Co-authored-by: Szetya <szetya@gmail.com>
This commit is contained in:
commit
c4dff21e5b
@ -86,7 +86,7 @@ if platform.name == "espressif32":
|
||||
|
||||
if platform.name == "nordicnrf52":
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex",
|
||||
env.VerboseAction(f"\"{sys.executable}\" ./bin/uf2conv.py $BUILD_DIR/firmware.hex -c -f 0xADA52840 -o $BUILD_DIR/firmware.uf2",
|
||||
env.VerboseAction(f"\"{sys.executable}\" ./bin/uf2conv.py \"$BUILD_DIR/firmware.hex\" -c -f 0xADA52840 -o \"$BUILD_DIR/firmware.uf2\"",
|
||||
"Generating UF2 file"))
|
||||
|
||||
Import("projenv")
|
||||
|
||||
37
boards/heltec_wireless_tracker_v2.json
Normal file
37
boards/heltec_wireless_tracker_v2.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_8MB.csv"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "heltec_wireless_tracker_v2"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "Heltec Wireless Tracker V2",
|
||||
"upload": {
|
||||
"flash_size": "8MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 8388608,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://heltec.org",
|
||||
"vendor": "Heltec"
|
||||
}
|
||||
@ -11,6 +11,11 @@
|
||||
#include <AudioOutputI2S.h>
|
||||
#include <ESP8266SAM.h>
|
||||
|
||||
#ifdef USE_XL9555
|
||||
#include "ExtensionIOXL9555.hpp"
|
||||
extern ExtensionIOXL9555 io;
|
||||
#endif
|
||||
|
||||
#define AUDIO_THREAD_INTERVAL_MS 100
|
||||
|
||||
class AudioThread : public concurrency::OSThread
|
||||
@ -20,6 +25,9 @@ class AudioThread : public concurrency::OSThread
|
||||
|
||||
void beginRttl(const void *data, uint32_t len)
|
||||
{
|
||||
#ifdef T_LORA_PAGER
|
||||
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
||||
#endif
|
||||
setCPUFast(true);
|
||||
rtttlFile = new AudioFileSourcePROGMEM(data, len);
|
||||
i2sRtttl = new AudioGeneratorRTTTL();
|
||||
@ -46,6 +54,9 @@ class AudioThread : public concurrency::OSThread
|
||||
rtttlFile = nullptr;
|
||||
|
||||
setCPUFast(false);
|
||||
#ifdef T_LORA_PAGER
|
||||
io.digitalWrite(EXPANDS_AMP_EN, LOW);
|
||||
#endif
|
||||
}
|
||||
|
||||
void readAloud(const char *text)
|
||||
@ -56,10 +67,16 @@ class AudioThread : public concurrency::OSThread
|
||||
i2sRtttl = nullptr;
|
||||
}
|
||||
|
||||
#ifdef T_LORA_PAGER
|
||||
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
||||
#endif
|
||||
ESP8266SAM *sam = new ESP8266SAM;
|
||||
sam->Say(audioOut, text);
|
||||
delete sam;
|
||||
setCPUFast(false);
|
||||
#ifdef T_LORA_PAGER
|
||||
io.digitalWrite(EXPANDS_AMP_EN, LOW);
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@ -86,7 +86,7 @@ int32_t SerialConsole::runOnce()
|
||||
#endif
|
||||
|
||||
int32_t delay = runOncePart();
|
||||
#if defined(SERIAL_HAS_ON_RECEIVE)
|
||||
#if defined(SERIAL_HAS_ON_RECEIVE) || defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
return Port.available() ? delay : INT32_MAX;
|
||||
#elif defined(IS_USB_SERIAL)
|
||||
return HWCDC::isPlugged() ? delay : (1000 * 20);
|
||||
|
||||
@ -15,7 +15,8 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
|
||||
{
|
||||
// Only provide feedback if buzzer is enabled for notifications
|
||||
if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED ||
|
||||
config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY) {
|
||||
config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY ||
|
||||
config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY) {
|
||||
return 0; // Let other handlers process the event
|
||||
}
|
||||
|
||||
|
||||
@ -117,7 +117,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define SX126X_MAX_POWER 22
|
||||
#endif
|
||||
|
||||
#ifdef HELTEC_V4
|
||||
#ifdef USE_GC1109_PA
|
||||
// Power Amps are often non-linear, so we can use an array of values for the power curve
|
||||
#define NUM_PA_POINTS 22
|
||||
#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7
|
||||
|
||||
@ -100,7 +100,7 @@ namespace graphics
|
||||
#define NUM_EXTRA_FRAMES 3 // text message and debug frame
|
||||
// if defined a pixel will blink to show redraws
|
||||
// #define SHOW_REDRAWS
|
||||
|
||||
#define ASCII_BELL '\x07'
|
||||
// A text message frame + debug frame + all the node infos
|
||||
FrameCallback *normalFrames;
|
||||
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
||||
@ -453,7 +453,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
||||
#endif
|
||||
|
||||
dispdev->displayOn();
|
||||
#ifdef HELTEC_TRACKER_V1_X
|
||||
#if defined(HELTEC_TRACKER_V1_X) || defined(HELTEC_WIRELESS_TRACKER_V2)
|
||||
ui->init();
|
||||
#endif
|
||||
#ifdef USE_ST7789
|
||||
@ -1458,28 +1458,36 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
}
|
||||
// === Prepare banner content ===
|
||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
||||
const meshtastic_Channel channel =
|
||||
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
|
||||
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
|
||||
|
||||
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
|
||||
|
||||
char banner[256];
|
||||
|
||||
// Check for bell character in message to determine alert type
|
||||
bool isAlert = false;
|
||||
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
|
||||
if (msgRaw[i] == '\x07') {
|
||||
isAlert = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleConfig.external_notification.alert_bell || moduleConfig.external_notification.alert_bell_vibra ||
|
||||
moduleConfig.external_notification.alert_bell_buzzer)
|
||||
// Check for bell character to determine if this message is an alert
|
||||
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
|
||||
if (msgRaw[i] == ASCII_BELL) {
|
||||
isAlert = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Unlike generic messages, alerts (when enabled via the ext notif module) ignore any
|
||||
// 'mute' preferences set to any specific node or channel.
|
||||
if (isAlert) {
|
||||
if (longName && longName[0]) {
|
||||
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
||||
} else {
|
||||
strcpy(banner, "Alert Received");
|
||||
}
|
||||
} else {
|
||||
screen->showSimpleBanner(banner, 3000);
|
||||
} else if (!channel.settings.mute) {
|
||||
if (longName && longName[0]) {
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
strcpy(banner, "New Message");
|
||||
@ -1490,14 +1498,21 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
} else {
|
||||
strcpy(banner, "New Message");
|
||||
}
|
||||
}
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
screen->setOn(true);
|
||||
screen->showSimpleBanner(banner, 1500);
|
||||
playLongBeep();
|
||||
screen->setOn(true);
|
||||
screen->showSimpleBanner(banner, 1500);
|
||||
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
|
||||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
|
||||
(!isBroadcast(packet->to) && isToUs(p))) {
|
||||
// Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either
|
||||
// - packet contains an alert and alert bell buzzer is enabled
|
||||
// - packet is a non-broadcast that is addressed to this node
|
||||
playLongBeep();
|
||||
}
|
||||
#else
|
||||
screen->showSimpleBanner(banner, 3000);
|
||||
screen->showSimpleBanner(banner, 3000);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -116,6 +116,8 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) {
|
||||
config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected);
|
||||
auto changes = SEGMENT_CONFIG;
|
||||
|
||||
// This is needed as we wait til picking the LoRa region to generate keys for the first time.
|
||||
if (!owner.is_licensed) {
|
||||
bool keygenSuccess = false;
|
||||
@ -124,6 +126,7 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
||||
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||
keygenSuccess = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG_INFO("Generate new PKI keys");
|
||||
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||
@ -141,7 +144,14 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
||||
if (myRegion->dutyCycle < 100) {
|
||||
config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit
|
||||
}
|
||||
service->reloadConfig(SEGMENT_CONFIG);
|
||||
|
||||
if (strncmp(moduleConfig.mqtt.root, default_mqtt_root, strlen(default_mqtt_root)) == 0) {
|
||||
// Default broker is in use, so subscribe to the appropriate MQTT root topic for this region
|
||||
sprintf(moduleConfig.mqtt.root, "%s/%s", default_mqtt_root, myRegion->name);
|
||||
changes |= SEGMENT_MODULECONFIG;
|
||||
}
|
||||
|
||||
service->reloadConfig(changes);
|
||||
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
||||
}
|
||||
};
|
||||
@ -911,11 +921,11 @@ void menuHandler::BluetoothToggleMenu()
|
||||
|
||||
void menuHandler::BuzzerModeMenu()
|
||||
{
|
||||
static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only"};
|
||||
static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only", "DMs Only"};
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Buzzer Mode";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = 4;
|
||||
bannerOptions.optionsCount = 5;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
config.device.buzzer_mode = (meshtastic_Config_DeviceConfig_BuzzerMode)selected;
|
||||
service->reloadConfig(SEGMENT_CONFIG);
|
||||
|
||||
@ -3,16 +3,66 @@
|
||||
|
||||
InputBroker *inputBroker = nullptr;
|
||||
|
||||
InputBroker::InputBroker(){};
|
||||
InputBroker::InputBroker()
|
||||
{
|
||||
#ifdef HAS_FREE_RTOS
|
||||
inputEventQueue = xQueueCreate(5, sizeof(InputEvent));
|
||||
pollSoonQueue = xQueueCreate(5, sizeof(InputPollable *));
|
||||
xTaskCreate(pollSoonWorker, "input-pollSoon", 2 * 1024, this, 10, &pollSoonTask);
|
||||
#endif
|
||||
}
|
||||
|
||||
void InputBroker::registerSource(Observable<const InputEvent *> *source)
|
||||
{
|
||||
this->inputEventObserver.observe(source);
|
||||
}
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
void InputBroker::requestPollSoon(InputPollable *pollable)
|
||||
{
|
||||
if (xPortInIsrContext() == pdTRUE) {
|
||||
xQueueSendFromISR(pollSoonQueue, &pollable, NULL);
|
||||
} else {
|
||||
xQueueSend(pollSoonQueue, &pollable, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void InputBroker::queueInputEvent(const InputEvent *event)
|
||||
{
|
||||
if (xPortInIsrContext() == pdTRUE) {
|
||||
xQueueSendFromISR(inputEventQueue, event, NULL);
|
||||
} else {
|
||||
xQueueSend(inputEventQueue, event, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void InputBroker::processInputEventQueue()
|
||||
{
|
||||
InputEvent event;
|
||||
while (xQueueReceive(inputEventQueue, &event, 0)) {
|
||||
handleInputEvent(&event);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int InputBroker::handleInputEvent(const InputEvent *event)
|
||||
{
|
||||
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
|
||||
this->notifyObservers(event);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
void InputBroker::pollSoonWorker(void *p)
|
||||
{
|
||||
InputBroker *instance = (InputBroker *)p;
|
||||
while (true) {
|
||||
InputPollable *pollable = NULL;
|
||||
xQueueReceive(instance->pollSoonQueue, &pollable, portMAX_DELAY);
|
||||
if (pollable) {
|
||||
pollable->pollOnce();
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Observer.h"
|
||||
#include "freertosinc.h"
|
||||
|
||||
enum input_broker_event {
|
||||
INPUT_BROKER_NONE = 0,
|
||||
@ -41,6 +43,13 @@ typedef struct _InputEvent {
|
||||
uint16_t touchX;
|
||||
uint16_t touchY;
|
||||
} InputEvent;
|
||||
|
||||
class InputPollable
|
||||
{
|
||||
public:
|
||||
virtual void pollOnce() = 0;
|
||||
};
|
||||
|
||||
class InputBroker : public Observable<const InputEvent *>
|
||||
{
|
||||
CallbackObserver<InputBroker, const InputEvent *> inputEventObserver =
|
||||
@ -50,9 +59,22 @@ class InputBroker : public Observable<const InputEvent *>
|
||||
InputBroker();
|
||||
void registerSource(Observable<const InputEvent *> *source);
|
||||
void injectInputEvent(const InputEvent *event) { handleInputEvent(event); }
|
||||
#ifdef HAS_FREE_RTOS
|
||||
void requestPollSoon(InputPollable *pollable);
|
||||
void queueInputEvent(const InputEvent *event);
|
||||
void processInputEventQueue();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
int handleInputEvent(const InputEvent *event);
|
||||
|
||||
private:
|
||||
#ifdef HAS_FREE_RTOS
|
||||
QueueHandle_t inputEventQueue;
|
||||
QueueHandle_t pollSoonQueue;
|
||||
TaskHandle_t pollSoonTask;
|
||||
static void pollSoonWorker(void *p);
|
||||
#endif
|
||||
};
|
||||
|
||||
extern InputBroker *inputBroker;
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
RotaryEncoderImpl *rotaryEncoderImpl;
|
||||
|
||||
RotaryEncoderImpl::RotaryEncoderImpl() : concurrency::OSThread(ORIGIN_NAME), originName(ORIGIN_NAME)
|
||||
RotaryEncoderImpl::RotaryEncoderImpl()
|
||||
{
|
||||
rotary = nullptr;
|
||||
}
|
||||
@ -18,7 +18,6 @@ bool RotaryEncoderImpl::init()
|
||||
if (!moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.inputbroker_pin_a == 0 ||
|
||||
moduleConfig.canned_message.inputbroker_pin_b == 0) {
|
||||
// Input device is disabled.
|
||||
disable();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -30,7 +29,11 @@ bool RotaryEncoderImpl::init()
|
||||
moduleConfig.canned_message.inputbroker_pin_press);
|
||||
rotary->resetButton();
|
||||
|
||||
inputBroker->registerSource(this);
|
||||
interruptInstance = this;
|
||||
auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); };
|
||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE);
|
||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE);
|
||||
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE);
|
||||
|
||||
LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a,
|
||||
moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw,
|
||||
@ -38,36 +41,36 @@ bool RotaryEncoderImpl::init()
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t RotaryEncoderImpl::runOnce()
|
||||
void RotaryEncoderImpl::pollOnce()
|
||||
{
|
||||
InputEvent e{originName, INPUT_BROKER_NONE, 0, 0, 0};
|
||||
InputEvent e{ORIGIN_NAME, INPUT_BROKER_NONE, 0, 0, 0};
|
||||
|
||||
static uint32_t lastPressed = millis();
|
||||
if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) {
|
||||
if (lastPressed + 200 < millis()) {
|
||||
LOG_DEBUG("Rotary event Press");
|
||||
lastPressed = millis();
|
||||
e.inputEvent = this->eventPressed;
|
||||
}
|
||||
} else {
|
||||
switch (rotary->process()) {
|
||||
case RotaryEncoder::DIRECTION_CW:
|
||||
LOG_DEBUG("Rotary event CW");
|
||||
e.inputEvent = this->eventCw;
|
||||
break;
|
||||
case RotaryEncoder::DIRECTION_CCW:
|
||||
LOG_DEBUG("Rotary event CCW");
|
||||
e.inputEvent = this->eventCcw;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
inputBroker->queueInputEvent(&e);
|
||||
}
|
||||
}
|
||||
|
||||
if (e.inputEvent != INPUT_BROKER_NONE) {
|
||||
this->notifyObservers(&e);
|
||||
switch (rotary->process()) {
|
||||
case RotaryEncoder::DIRECTION_CW:
|
||||
LOG_DEBUG("Rotary event CW");
|
||||
e.inputEvent = this->eventCw;
|
||||
inputBroker->queueInputEvent(&e);
|
||||
break;
|
||||
case RotaryEncoder::DIRECTION_CCW:
|
||||
LOG_DEBUG("Rotary event CCW");
|
||||
e.inputEvent = this->eventCcw;
|
||||
inputBroker->queueInputEvent(&e);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 10;
|
||||
}
|
||||
|
||||
RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance;
|
||||
|
||||
#endif
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
// This is a non-interrupt version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library)
|
||||
// This is a version of RotaryEncoder which is based on a debounce inherent FSM table (see RotaryEncoder library)
|
||||
|
||||
#include "InputBroker.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
@ -8,21 +8,21 @@
|
||||
|
||||
class RotaryEncoder;
|
||||
|
||||
class RotaryEncoderImpl : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||
class RotaryEncoderImpl : public InputPollable
|
||||
{
|
||||
public:
|
||||
RotaryEncoderImpl();
|
||||
bool init(void);
|
||||
virtual void pollOnce() override;
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce() override;
|
||||
static RotaryEncoderImpl *interruptInstance;
|
||||
|
||||
input_broker_event eventCw = INPUT_BROKER_NONE;
|
||||
input_broker_event eventCcw = INPUT_BROKER_NONE;
|
||||
input_broker_event eventPressed = INPUT_BROKER_NONE;
|
||||
|
||||
RotaryEncoder *rotary;
|
||||
const char *originName;
|
||||
};
|
||||
|
||||
extern RotaryEncoderImpl *rotaryEncoderImpl;
|
||||
|
||||
@ -381,7 +381,7 @@ void setup()
|
||||
io.pinMode(EXPANDS_DRV_EN, OUTPUT);
|
||||
io.digitalWrite(EXPANDS_DRV_EN, HIGH);
|
||||
io.pinMode(EXPANDS_AMP_EN, OUTPUT);
|
||||
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
|
||||
io.digitalWrite(EXPANDS_AMP_EN, LOW);
|
||||
io.pinMode(EXPANDS_LORA_EN, OUTPUT);
|
||||
io.digitalWrite(EXPANDS_LORA_EN, HIGH);
|
||||
io.pinMode(EXPANDS_GPS_EN, OUTPUT);
|
||||
@ -1600,6 +1600,10 @@ void loop()
|
||||
#endif
|
||||
|
||||
service->loop();
|
||||
#if !MESHTASTIC_EXCLUDE_INPUTBROKER && defined(HAS_FREE_RTOS)
|
||||
if (inputBroker)
|
||||
inputBroker->processInputEventQueue();
|
||||
#endif
|
||||
#if defined(LGFX_SDL)
|
||||
if (screen) {
|
||||
auto dispdev = screen->getDisplayDevice();
|
||||
|
||||
@ -65,5 +65,7 @@ template <class T> class LR11x0Interface : public RadioLibInterface
|
||||
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override;
|
||||
|
||||
virtual void setStandby() override;
|
||||
|
||||
uint32_t getPacketTime(uint32_t pl, bool received) override { return computePacketTime(lora, pl, received); }
|
||||
};
|
||||
#endif
|
||||
@ -65,7 +65,7 @@ void fixPriority(meshtastic_MeshPacket *p)
|
||||
}
|
||||
|
||||
/** enqueue a packet, return false if full */
|
||||
bool MeshPacketQueue::enqueue(meshtastic_MeshPacket *p)
|
||||
bool MeshPacketQueue::enqueue(meshtastic_MeshPacket *p, bool *dropped)
|
||||
{
|
||||
// no space - try to replace a lower priority packet in the queue
|
||||
if (queue.size() >= maxLen) {
|
||||
@ -73,9 +73,16 @@ bool MeshPacketQueue::enqueue(meshtastic_MeshPacket *p)
|
||||
if (!replaced) {
|
||||
LOG_WARN("TX queue is full, and there is no lower-priority packet available to evict in favour of 0x%08x", p->id);
|
||||
}
|
||||
if (dropped) {
|
||||
*dropped = true;
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
|
||||
if (dropped) {
|
||||
*dropped = false;
|
||||
}
|
||||
|
||||
// Find the correct position using upper_bound to maintain a stable order
|
||||
auto it = std::upper_bound(queue.begin(), queue.end(), p, CompareMeshPacketFunc);
|
||||
queue.insert(it, p); // Insert packet at the found position
|
||||
|
||||
@ -19,8 +19,10 @@ class MeshPacketQueue
|
||||
public:
|
||||
explicit MeshPacketQueue(size_t _maxLen);
|
||||
|
||||
/** enqueue a packet, return false if full */
|
||||
bool enqueue(meshtastic_MeshPacket *p);
|
||||
/** enqueue a packet, return false if full
|
||||
* @param dropped Optional pointer to a bool that will be set to true if a packet was dropped
|
||||
*/
|
||||
bool enqueue(meshtastic_MeshPacket *p, bool *dropped = nullptr);
|
||||
|
||||
/** return true if the queue is empty */
|
||||
bool empty();
|
||||
|
||||
@ -56,10 +56,6 @@
|
||||
#include <WiFiOTA.h>
|
||||
#endif
|
||||
|
||||
// stringify
|
||||
#define xstr(s) str(s)
|
||||
#define str(s) #s
|
||||
|
||||
NodeDB *nodeDB = nullptr;
|
||||
|
||||
// we have plenty of ram so statically alloc this tempbuf (for now)
|
||||
@ -260,6 +256,8 @@ NodeDB::NodeDB()
|
||||
owner.role = config.device.role;
|
||||
// Ensure macaddr is set to our macaddr as it will be copied in our info below
|
||||
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
|
||||
// Ensure owner.id is always derived from the node number
|
||||
snprintf(owner.id, sizeof(owner.id), "!%08x", getNodeNum());
|
||||
|
||||
if (!config.has_security) {
|
||||
config.has_security = true;
|
||||
@ -1695,6 +1693,9 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
|
||||
}
|
||||
#endif
|
||||
|
||||
// Always ensure user.id is derived from nodeId, regardless of what was received
|
||||
snprintf(p.id, sizeof(p.id), "!%08x", nodeId);
|
||||
|
||||
// Both of info->user and p start as filled with zero so I think this is okay
|
||||
auto lite = TypeConversions::ConvertToUserLite(p);
|
||||
bool changed = memcmp(&info->user, &lite, sizeof(info->user)) || (info->channel != channelIndex);
|
||||
|
||||
@ -65,8 +65,10 @@ class RF95Interface : public RadioLibInterface
|
||||
*/
|
||||
virtual void configHardwareForSend() override;
|
||||
|
||||
uint32_t getPacketTime(uint32_t pl, bool received) override { return computePacketTime(*lora, pl, received); }
|
||||
|
||||
private:
|
||||
/** Some boards require GPIO control of tx vs rx paths */
|
||||
void setTransmitEnable(bool txon);
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -230,33 +230,7 @@ The band is from 902 to 928 MHz. It mentions channel number and its respective c
|
||||
separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts at 903.08 MHz center frequency.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Calculate airtime per
|
||||
* https://www.rs-online.com/designspark/rel-assets/ds-assets/uploads/knowledge-items/application-notes-for-the-internet-of-things/LoRa%20Design%20Guide.pdf
|
||||
* section 4
|
||||
*
|
||||
* @return num msecs for the packet
|
||||
*/
|
||||
uint32_t RadioInterface::getPacketTime(uint32_t pl)
|
||||
{
|
||||
float bandwidthHz = bw * 1000.0f;
|
||||
bool headDisable = false; // we currently always use the header
|
||||
float tSym = (1 << sf) / bandwidthHz;
|
||||
|
||||
bool lowDataOptEn = tSym > 16e-3 ? true : false; // Needed if symbol time is >16ms
|
||||
|
||||
float tPreamble = (preambleLength + 4.25f) * tSym;
|
||||
float numPayloadSym =
|
||||
8 + max(ceilf(((8.0f * pl - 4 * sf + 28 + 16 - 20 * headDisable) / (4 * (sf - 2 * lowDataOptEn))) * cr), 0.0f);
|
||||
float tPayload = numPayloadSym * tSym;
|
||||
float tPacket = tPreamble + tPayload;
|
||||
|
||||
uint32_t msecs = tPacket * 1000;
|
||||
|
||||
return msecs;
|
||||
}
|
||||
|
||||
uint32_t RadioInterface::getPacketTime(const meshtastic_MeshPacket *p)
|
||||
uint32_t RadioInterface::getPacketTime(const meshtastic_MeshPacket *p, bool received)
|
||||
{
|
||||
uint32_t pl = 0;
|
||||
if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag) {
|
||||
@ -265,7 +239,7 @@ uint32_t RadioInterface::getPacketTime(const meshtastic_MeshPacket *p)
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded);
|
||||
pl = numbytes + sizeof(PacketHeader);
|
||||
}
|
||||
return getPacketTime(pl);
|
||||
return getPacketTime(pl, received);
|
||||
}
|
||||
|
||||
/** The delay to use for retransmitting dropped packets */
|
||||
@ -624,8 +598,7 @@ void RadioInterface::applyModemConfig()
|
||||
saveFreq(freq + loraConfig.frequency_offset);
|
||||
|
||||
slotTimeMsec = computeSlotTimeMsec();
|
||||
preambleTimeMsec = getPacketTime((uint32_t)0);
|
||||
maxPacketTimeMsec = getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN + sizeof(PacketHeader));
|
||||
preambleTimeMsec = preambleLength * (pow_of_2(sf) / bw);
|
||||
|
||||
LOG_INFO("Radio freq=%.3f, config.lora.frequency_offset=%.3f", freq, loraConfig.frequency_offset);
|
||||
LOG_INFO("Set radio: region=%s, name=%s, config=%u, ch=%d, power=%d", myRegion->name, channelName, loraConfig.modem_preset,
|
||||
@ -635,7 +608,7 @@ void RadioInterface::applyModemConfig()
|
||||
LOG_INFO("numChannels: %d x %.3fkHz", numChannels, bw);
|
||||
LOG_INFO("channel_num: %d", channel_num + 1);
|
||||
LOG_INFO("frequency: %f", getFreq());
|
||||
LOG_INFO("Slot time: %u msec", slotTimeMsec);
|
||||
LOG_INFO("Slot time: %u msec, preamble time: %u msec", slotTimeMsec, preambleTimeMsec);
|
||||
}
|
||||
|
||||
/** Slottime is the time to detect a transmission has started, consisting of:
|
||||
|
||||
@ -87,9 +87,8 @@ class RadioInterface
|
||||
const uint8_t NUM_SYM_CAD = 2; // Number of symbols used for CAD, 2 is the default since RadioLib 6.3.0 as per AN1200.48
|
||||
const uint8_t NUM_SYM_CAD_24GHZ = 4; // Number of symbols used for CAD in 2.4 GHz, 4 is recommended in AN1200.22 of SX1280
|
||||
uint32_t slotTimeMsec = computeSlotTimeMsec();
|
||||
uint16_t preambleLength = 16; // 8 is default, but we use longer to increase the amount of sleep time when receiving
|
||||
uint32_t preambleTimeMsec = 165; // calculated on startup, this is the default for LongFast
|
||||
uint32_t maxPacketTimeMsec = 3246; // calculated on startup, this is the default for LongFast
|
||||
uint16_t preambleLength = 16; // 8 is default, but we use longer to increase the amount of sleep time when receiving
|
||||
uint32_t preambleTimeMsec = 165; // calculated on startup, this is the default for LongFast
|
||||
const uint32_t PROCESSING_TIME_MSEC =
|
||||
4500; // time to construct, process and construct a packet again (empirically determined)
|
||||
const uint8_t CWmin = 3; // minimum CWsize
|
||||
@ -202,8 +201,8 @@ class RadioInterface
|
||||
*
|
||||
* @return num msecs for the packet
|
||||
*/
|
||||
uint32_t getPacketTime(const meshtastic_MeshPacket *p);
|
||||
uint32_t getPacketTime(uint32_t totalPacketLen);
|
||||
uint32_t getPacketTime(const meshtastic_MeshPacket *p, bool received = false);
|
||||
virtual uint32_t getPacketTime(uint32_t totalPacketLen, bool received = false) = 0;
|
||||
|
||||
/**
|
||||
* Get the channel we saved.
|
||||
|
||||
@ -116,16 +116,21 @@ bool RadioLibInterface::receiveDetected(uint16_t irq, ulong syncWordHeaderValidF
|
||||
if (detected) {
|
||||
if (!activeReceiveStart) {
|
||||
activeReceiveStart = millis();
|
||||
} else if (!Throttle::isWithinTimespanMs(activeReceiveStart, 2 * preambleTimeMsec) && !(irq & syncWordHeaderValidFlag)) {
|
||||
// The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
|
||||
activeReceiveStart = 0;
|
||||
LOG_DEBUG("Ignore false preamble detection");
|
||||
return false;
|
||||
} else if (!Throttle::isWithinTimespanMs(activeReceiveStart, maxPacketTimeMsec)) {
|
||||
// We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
|
||||
activeReceiveStart = 0;
|
||||
LOG_DEBUG("Ignore false header detection");
|
||||
return false;
|
||||
} else if (!Throttle::isWithinTimespanMs(activeReceiveStart, 2 * preambleTimeMsec)) {
|
||||
if (!(irq & syncWordHeaderValidFlag)) {
|
||||
// The HEADER_VALID flag should be set by now if it was really a packet, so ignore PREAMBLE_DETECTED flag
|
||||
activeReceiveStart = 0;
|
||||
LOG_DEBUG("Ignore false preamble detection");
|
||||
return false;
|
||||
} else {
|
||||
uint32_t maxPacketTimeMsec = getPacketTime(meshtastic_Constants_DATA_PAYLOAD_LEN + sizeof(PacketHeader));
|
||||
if (!Throttle::isWithinTimespanMs(activeReceiveStart, maxPacketTimeMsec)) {
|
||||
// We should have gotten an RX_DONE IRQ by now if it was really a packet, so ignore HEADER_VALID flag
|
||||
activeReceiveStart = 0;
|
||||
LOG_DEBUG("Ignore false header detection");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return detected;
|
||||
@ -172,7 +177,12 @@ ErrorCode RadioLibInterface::send(meshtastic_MeshPacket *p)
|
||||
printPacket("enqueue for send", p);
|
||||
|
||||
LOG_DEBUG("txGood=%d,txRelay=%d,rxGood=%d,rxBad=%d", txGood, txRelay, rxGood, rxBad);
|
||||
ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
bool dropped = false;
|
||||
ErrorCode res = txQueue.enqueue(p, &dropped) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
|
||||
if (dropped) {
|
||||
txDrop++;
|
||||
}
|
||||
|
||||
if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks
|
||||
packetPool.release(p);
|
||||
@ -354,11 +364,15 @@ void RadioLibInterface::clampToLateRebroadcastWindow(NodeNum from, PacketId id)
|
||||
meshtastic_MeshPacket *p = txQueue.remove(from, id, true, false);
|
||||
if (p) {
|
||||
p->tx_after = millis() + getTxDelayMsecWeightedWorst(p->rx_snr);
|
||||
if (txQueue.enqueue(p)) {
|
||||
bool dropped = false;
|
||||
if (txQueue.enqueue(p, &dropped)) {
|
||||
LOG_DEBUG("Move existing queued packet to the late rebroadcast window %dms from now", p->tx_after - millis());
|
||||
} else {
|
||||
packetPool.release(p);
|
||||
}
|
||||
if (dropped) {
|
||||
txDrop++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -411,8 +425,6 @@ void RadioLibInterface::completeSending()
|
||||
|
||||
void RadioLibInterface::handleReceiveInterrupt()
|
||||
{
|
||||
uint32_t xmitMsec;
|
||||
|
||||
// when this is called, we should be in receive mode - if we are not, just jump out instead of bombing. Possible Race
|
||||
// Condition?
|
||||
if (!isReceiving) {
|
||||
@ -425,12 +437,12 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
// read the number of actually received bytes
|
||||
size_t length = iface->getPacketLength();
|
||||
|
||||
xmitMsec = getPacketTime(length);
|
||||
uint32_t rxMsec = getPacketTime(length, true);
|
||||
|
||||
#ifndef DISABLE_WELCOME_UNSET
|
||||
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
LOG_WARN("lora rx disabled: Region unset");
|
||||
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
|
||||
airTime->logAirtime(RX_ALL_LOG, rxMsec);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@ -446,7 +458,7 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
radioBuffer.header.to, radioBuffer.header.from, radioBuffer.header.flags);
|
||||
rxBad++;
|
||||
|
||||
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
|
||||
airTime->logAirtime(RX_ALL_LOG, rxMsec);
|
||||
|
||||
} else {
|
||||
// Skip the 4 headers that are at the beginning of the rxBuf
|
||||
@ -456,7 +468,7 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
if (payloadLen < 0) {
|
||||
LOG_WARN("Ignore received packet too short");
|
||||
rxBad++;
|
||||
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
|
||||
airTime->logAirtime(RX_ALL_LOG, rxMsec);
|
||||
} else {
|
||||
rxGood++;
|
||||
// altered packet with "from == 0" can do Remote Node Administration without permission
|
||||
@ -494,7 +506,7 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
|
||||
printPacket("Lora RX", mp);
|
||||
|
||||
airTime->logAirtime(RX_LOG, xmitMsec);
|
||||
airTime->logAirtime(RX_LOG, rxMsec);
|
||||
|
||||
deliverToReceiver(mp);
|
||||
}
|
||||
|
||||
@ -61,6 +61,17 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE);
|
||||
|
||||
protected:
|
||||
ModemType_t modemType = RADIOLIB_MODEM_LORA;
|
||||
DataRate_t getDataRate() const { return {.lora = {.spreadingFactor = sf, .bandwidth = bw, .codingRate = cr}}; }
|
||||
PacketConfig_t getPacketConfig() const
|
||||
{
|
||||
return {.lora = {.preambleLength = preambleLength,
|
||||
.implicitHeader = false,
|
||||
.crcEnabled = true,
|
||||
// We use auto LDRO, meaning it is enabled if the symbol time is >= 16msec
|
||||
.ldrOptimize = (1 << sf) / bw >= 16}};
|
||||
}
|
||||
|
||||
/**
|
||||
* We use a meshtastic sync word, but hashed with the Channel name. For releases before 1.2 we used 0x12 (or for very old
|
||||
* loads 0x14) Note: do not use 0x34 - that is reserved for lorawan
|
||||
@ -105,6 +116,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
* Debugging counts
|
||||
*/
|
||||
uint32_t rxBad = 0, rxGood = 0, txGood = 0, txRelay = 0;
|
||||
uint16_t txDrop = 0;
|
||||
|
||||
public:
|
||||
RadioLibInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||
@ -209,6 +221,36 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
*/
|
||||
virtual void setStandby();
|
||||
|
||||
/**
|
||||
* Derive packet time either for a received (using header info) or a transmitted packet
|
||||
*/
|
||||
template <typename T> uint32_t computePacketTime(T &lora, uint32_t pl, bool received)
|
||||
{
|
||||
if (received) {
|
||||
// First get the actual coding rate and CRC status from the received packet
|
||||
uint8_t rxCR;
|
||||
bool hasCRC;
|
||||
lora.getLoRaRxHeaderInfo(&rxCR, &hasCRC);
|
||||
// Go from raw header value to denominator
|
||||
if (rxCR < 5) {
|
||||
rxCR += 4;
|
||||
} else if (rxCR == 7) {
|
||||
rxCR = 8;
|
||||
}
|
||||
|
||||
// Received packet configuration must be the same as configured, except for coding rate and CRC
|
||||
DataRate_t dr = getDataRate();
|
||||
dr.lora.codingRate = rxCR;
|
||||
|
||||
PacketConfig_t pc = getPacketConfig();
|
||||
pc.lora.crcEnabled = hasCRC;
|
||||
|
||||
return lora.calculateTimeOnAir(modemType, dr, pc, pl) / 1000;
|
||||
}
|
||||
|
||||
return lora.getTimeOnAir(pl) / 1000;
|
||||
}
|
||||
|
||||
const char *radioLibErr = "RadioLib err=";
|
||||
|
||||
/**
|
||||
|
||||
@ -76,7 +76,7 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
If we don't add this, we will likely retransmit too early.
|
||||
*/
|
||||
for (auto i = pending.begin(); i != pending.end(); i++) {
|
||||
i->second.nextTxMsec += iface->getPacketTime(p);
|
||||
i->second.nextTxMsec += iface->getPacketTime(p, true);
|
||||
}
|
||||
|
||||
return isBroadcast(p->to) ? FloodingRouter::shouldFilterReceived(p) : NextHopRouter::shouldFilterReceived(p);
|
||||
|
||||
@ -52,7 +52,7 @@ template <typename T> bool SX126xInterface<T>::init()
|
||||
pinMode(SX126X_POWER_EN, OUTPUT);
|
||||
#endif
|
||||
|
||||
#ifdef HELTEC_V4
|
||||
#if defined(USE_GC1109_PA)
|
||||
pinMode(LORA_PA_POWER, OUTPUT);
|
||||
digitalWrite(LORA_PA_POWER, HIGH);
|
||||
|
||||
@ -352,7 +352,7 @@ template <typename T> bool SX126xInterface<T>::sleep()
|
||||
digitalWrite(SX126X_POWER_EN, LOW);
|
||||
#endif
|
||||
|
||||
#ifdef HELTEC_V4
|
||||
#if defined(USE_GC1109_PA)
|
||||
/*
|
||||
* Do not switch the power on and off frequently.
|
||||
* After turning off LORA_PA_EN, the power consumption has dropped to the uA level.
|
||||
@ -367,7 +367,7 @@ template <typename T> bool SX126xInterface<T>::sleep()
|
||||
/** Some boards require GPIO control of tx vs rx paths */
|
||||
template <typename T> void SX126xInterface<T>::setTransmitEnable(bool txon)
|
||||
{
|
||||
#ifdef HELTEC_V4
|
||||
#if defined(USE_GC1109_PA)
|
||||
digitalWrite(LORA_PA_POWER, HIGH);
|
||||
digitalWrite(LORA_PA_EN, HIGH);
|
||||
digitalWrite(LORA_PA_TX_EN, txon ? 1 : 0);
|
||||
|
||||
@ -72,6 +72,8 @@ template <class T> class SX126xInterface : public RadioLibInterface
|
||||
|
||||
virtual void setStandby() override;
|
||||
|
||||
uint32_t getPacketTime(uint32_t pl, bool received) override { return computePacketTime(lora, pl, received); }
|
||||
|
||||
private:
|
||||
/** Some boards require GPIO control of tx vs rx paths */
|
||||
void setTransmitEnable(bool txon);
|
||||
|
||||
@ -67,4 +67,6 @@ template <class T> class SX128xInterface : public RadioLibInterface
|
||||
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) override;
|
||||
|
||||
virtual void setStandby() override;
|
||||
|
||||
uint32_t getPacketTime(uint32_t pl, bool received) override { return computePacketTime(lora, pl, received); }
|
||||
};
|
||||
|
||||
@ -554,10 +554,8 @@ void AdminModule::handleSetOwner(const meshtastic_User &o)
|
||||
changed |= strcmp(owner.short_name, o.short_name);
|
||||
strncpy(owner.short_name, o.short_name, sizeof(owner.short_name));
|
||||
}
|
||||
if (*o.id) {
|
||||
changed |= strcmp(owner.id, o.id);
|
||||
strncpy(owner.id, o.id, sizeof(owner.id));
|
||||
}
|
||||
snprintf(owner.id, sizeof(owner.id), "!%08x", nodeDB->getNodeNum());
|
||||
|
||||
if (owner.is_licensed != o.is_licensed) {
|
||||
changed = 1;
|
||||
owner.is_licensed = o.is_licensed;
|
||||
@ -784,11 +782,24 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
if (myRegion->dutyCycle < 100) {
|
||||
config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit
|
||||
}
|
||||
// Compare the entire string, we are sure of the length as a topic has never been set
|
||||
if (strcmp(moduleConfig.mqtt.root, default_mqtt_root) == 0) {
|
||||
sprintf(moduleConfig.mqtt.root, "%s/%s", default_mqtt_root, myRegion->name);
|
||||
changes = SEGMENT_CONFIG | SEGMENT_MODULECONFIG;
|
||||
}
|
||||
}
|
||||
if (config.lora.region != myRegion->code) {
|
||||
// Region has changed so check whether there is a regulatory one we should be using instead.
|
||||
// Additionally as a side-effect, assume a new value under myRegion
|
||||
initRegion();
|
||||
|
||||
if (strncmp(moduleConfig.mqtt.root, default_mqtt_root, strlen(default_mqtt_root)) == 0) {
|
||||
// Default root is in use, so subscribe to the appropriate MQTT topic for this region
|
||||
sprintf(moduleConfig.mqtt.root, "%s/%s", default_mqtt_root, myRegion->name);
|
||||
}
|
||||
|
||||
changes = SEGMENT_CONFIG | SEGMENT_MODULECONFIG;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_Config_bluetooth_tag:
|
||||
|
||||
@ -442,7 +442,7 @@ ExternalNotificationModule::ExternalNotificationModule()
|
||||
|
||||
ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
if (moduleConfig.external_notification.enabled && !isMuted) {
|
||||
if (moduleConfig.external_notification.enabled && !isSilenced) {
|
||||
#ifdef T_WATCH_S3
|
||||
drv.setWaveform(0, 75);
|
||||
drv.setWaveform(1, 56);
|
||||
@ -453,12 +453,13 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
||||
// Check if the message contains a bell character. Don't do this loop for every pin, just once.
|
||||
auto &p = mp.decoded;
|
||||
bool containsBell = false;
|
||||
for (int i = 0; i < p.payload.size; i++) {
|
||||
for (size_t i = 0; i < p.payload.size; i++) {
|
||||
if (p.payload.bytes[i] == ASCII_BELL) {
|
||||
containsBell = true;
|
||||
}
|
||||
}
|
||||
|
||||
meshtastic_Channel ch = channels.getByIndex(mp.channel ? mp.channel : channels.getPrimaryIndex());
|
||||
if (moduleConfig.external_notification.alert_bell) {
|
||||
if (containsBell) {
|
||||
LOG_INFO("externalNotificationModule - Notification Bell");
|
||||
@ -509,7 +510,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleConfig.external_notification.alert_message) {
|
||||
if (moduleConfig.external_notification.alert_message && !ch.settings.mute) {
|
||||
LOG_INFO("externalNotificationModule - Notification Module");
|
||||
isNagging = true;
|
||||
setExternalState(0, true);
|
||||
@ -520,7 +521,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleConfig.external_notification.alert_message_vibra) {
|
||||
if (moduleConfig.external_notification.alert_message_vibra && !ch.settings.mute) {
|
||||
LOG_INFO("externalNotificationModule - Notification Module (Vibra)");
|
||||
isNagging = true;
|
||||
setExternalState(1, true);
|
||||
@ -531,25 +532,32 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleConfig.external_notification.alert_message_buzzer) {
|
||||
if (moduleConfig.external_notification.alert_message_buzzer && !ch.settings.mute) {
|
||||
LOG_INFO("externalNotificationModule - Notification Module (Buzzer)");
|
||||
isNagging = true;
|
||||
if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) {
|
||||
setExternalState(2, true);
|
||||
} else {
|
||||
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
|
||||
(!isBroadcast(mp.to) && isToUs(&mp))) {
|
||||
// Buzz if buzzer mode is not in DIRECT_MSG_ONLY or is DM to us
|
||||
isNagging = true;
|
||||
if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) {
|
||||
setExternalState(2, true);
|
||||
} else {
|
||||
#ifdef HAS_I2S
|
||||
if (moduleConfig.external_notification.use_i2s_as_buzzer) {
|
||||
audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone));
|
||||
} else
|
||||
if (moduleConfig.external_notification.use_i2s_as_buzzer) {
|
||||
audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone));
|
||||
} else
|
||||
#endif
|
||||
if (moduleConfig.external_notification.use_pwm) {
|
||||
rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone);
|
||||
if (moduleConfig.external_notification.use_pwm) {
|
||||
rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone);
|
||||
}
|
||||
}
|
||||
if (moduleConfig.external_notification.nag_timeout) {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
|
||||
} else {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
|
||||
}
|
||||
}
|
||||
if (moduleConfig.external_notification.nag_timeout) {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
|
||||
} else {
|
||||
nagCycleCutoff = millis() + moduleConfig.external_notification.output_ms;
|
||||
// Don't beep if buzzer mode is "direct messages only" and it is no direct message
|
||||
LOG_INFO("Message buzzer was suppressed because buzzer mode DIRECT_MSG_ONLY");
|
||||
}
|
||||
}
|
||||
setIntervalFromNow(0); // run once so we know if we should do something
|
||||
|
||||
@ -43,8 +43,8 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
|
||||
void setExternalState(uint8_t index = 0, bool on = false);
|
||||
bool getExternal(uint8_t index = 0);
|
||||
|
||||
void setMute(bool mute) { isMuted = mute; }
|
||||
bool getMute() { return isMuted; }
|
||||
void setMute(bool mute) { isSilenced = mute; }
|
||||
bool getMute() { return isSilenced; }
|
||||
|
||||
bool canBuzz();
|
||||
bool nagging();
|
||||
@ -67,7 +67,7 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
|
||||
|
||||
bool isNagging = false;
|
||||
|
||||
bool isMuted = false;
|
||||
bool isSilenced = false;
|
||||
|
||||
virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
|
||||
meshtastic_AdminMessage *request,
|
||||
|
||||
@ -148,7 +148,8 @@ void setupModules()
|
||||
}
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_ATAK
|
||||
if (IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TAK, meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)) {
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK ||
|
||||
config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) {
|
||||
atakPluginModule = new AtakPluginModule();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -30,14 +30,32 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
||||
|
||||
bool wasBroadcast = isBroadcast(mp.to);
|
||||
|
||||
// LOG_DEBUG("did encode");
|
||||
// if user has changed while packet was not for us, inform phone
|
||||
if (hasChanged && !wasBroadcast && !isToUs(&mp))
|
||||
service->sendToPhone(packetPool.allocCopy(mp));
|
||||
if (hasChanged && !wasBroadcast && !isToUs(&mp)) {
|
||||
auto packetCopy = packetPool.allocCopy(mp); // Keep a copy of the packet for later analysis
|
||||
|
||||
// Re-encode the user protobuf, as we have stripped out the user.id
|
||||
packetCopy->decoded.payload.size = pb_encode_to_bytes(
|
||||
packetCopy->decoded.payload.bytes, sizeof(packetCopy->decoded.payload.bytes), &meshtastic_User_msg, &p);
|
||||
|
||||
service->sendToPhone(packetCopy);
|
||||
}
|
||||
|
||||
// LOG_DEBUG("did handleReceived");
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
void NodeInfoModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic_User *p)
|
||||
{
|
||||
// Coerce user.id to be derived from the node number
|
||||
snprintf(p->id, sizeof(p->id), "!%08x", getFrom(&mp));
|
||||
|
||||
// Re-encode the altered protobuf back into the packet
|
||||
mp.decoded.payload.size =
|
||||
pb_encode_to_bytes(mp.decoded.payload.bytes, sizeof(mp.decoded.payload.bytes), &meshtastic_User_msg, p);
|
||||
}
|
||||
|
||||
void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t channel, bool _shorterTimeout)
|
||||
{
|
||||
// cancel any not yet sent (now stale) position packets
|
||||
@ -95,6 +113,8 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
|
||||
u.public_key.size = 0;
|
||||
}
|
||||
|
||||
// Clear the user.id field since it should be derived from node number on the receiving end
|
||||
u.id[0] = '\0';
|
||||
LOG_INFO("Send owner %s/%s/%s", u.id, u.long_name, u.short_name);
|
||||
lastSentToMesh = millis();
|
||||
return allocDataProtobuf(u);
|
||||
|
||||
@ -30,6 +30,9 @@ class NodeInfoModule : public ProtobufModule<meshtastic_User>, private concurren
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_User *p) override;
|
||||
|
||||
/** Called to alter received User protobuf */
|
||||
virtual void alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic_User *p) override;
|
||||
|
||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||
* so that subclasses can (optionally) send a response back to the original sender. */
|
||||
virtual meshtastic_MeshPacket *allocReply() override;
|
||||
|
||||
@ -121,6 +121,7 @@ meshtastic_Telemetry DeviceTelemetryModule::getLocalStatsTelemetry()
|
||||
telemetry.variant.local_stats.num_packets_rx = RadioLibInterface::instance->rxGood + RadioLibInterface::instance->rxBad;
|
||||
telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad;
|
||||
telemetry.variant.local_stats.num_tx_relay = RadioLibInterface::instance->txRelay;
|
||||
telemetry.variant.local_stats.num_tx_dropped = RadioLibInterface::instance->txDrop;
|
||||
}
|
||||
#ifdef ARCH_PORTDUINO
|
||||
if (SimRadio::instance) {
|
||||
@ -128,6 +129,7 @@ meshtastic_Telemetry DeviceTelemetryModule::getLocalStatsTelemetry()
|
||||
telemetry.variant.local_stats.num_packets_rx = SimRadio::instance->rxGood + SimRadio::instance->rxBad;
|
||||
telemetry.variant.local_stats.num_packets_rx_bad = SimRadio::instance->rxBad;
|
||||
telemetry.variant.local_stats.num_tx_relay = SimRadio::instance->txRelay;
|
||||
telemetry.variant.local_stats.num_tx_dropped = SimRadio::instance->txDrop;
|
||||
}
|
||||
#else
|
||||
telemetry.variant.local_stats.heap_total_bytes = memGet.getHeapSize();
|
||||
|
||||
@ -201,6 +201,8 @@
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_V4
|
||||
#elif defined(M5STACK_UNITC6L)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_M5STACK_C6L
|
||||
#elif defined(HELTEC_WIRELESS_TRACKER_V2)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_WIRELESS_TRACKER_V2
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@ -151,3 +151,6 @@
|
||||
// No serial ports on this board - ONLY use segger in memory console
|
||||
#define USE_SEGGER
|
||||
#endif
|
||||
|
||||
// Detect if running in ISR context (ARM Cortex-M4)
|
||||
#define xPortInIsrContext() ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) == 0 ? pdFALSE : pdTRUE)
|
||||
|
||||
@ -13,7 +13,12 @@ ErrorCode SimRadio::send(meshtastic_MeshPacket *p)
|
||||
{
|
||||
printPacket("enqueuing for send", p);
|
||||
|
||||
ErrorCode res = txQueue.enqueue(p) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
bool dropped = false;
|
||||
ErrorCode res = txQueue.enqueue(p, &dropped) ? ERRNO_OK : ERRNO_UNKNOWN;
|
||||
|
||||
if (dropped) {
|
||||
txDrop++;
|
||||
}
|
||||
|
||||
if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks
|
||||
packetPool.release(p);
|
||||
@ -182,7 +187,7 @@ void SimRadio::onNotify(uint32_t notification)
|
||||
assert(txp);
|
||||
startSend(txp);
|
||||
// Packet has been sent, count it toward our TX airtime utilization.
|
||||
uint32_t xmitMsec = getPacketTime(txp);
|
||||
uint32_t xmitMsec = RadioInterface::getPacketTime(txp);
|
||||
airTime->logAirtime(TX_LOG, xmitMsec);
|
||||
|
||||
notifyLater(xmitMsec, ISR_TX, false); // Model the time it is busy sending
|
||||
@ -252,7 +257,7 @@ void SimRadio::startReceive(meshtastic_MeshPacket *p)
|
||||
if (isActivelyReceiving()) {
|
||||
LOG_WARN("Collision detected, dropping current and previous packet!");
|
||||
rxBad++;
|
||||
airTime->logAirtime(RX_ALL_LOG, getPacketTime(receivingPacket));
|
||||
airTime->logAirtime(RX_ALL_LOG, getPacketTime(receivingPacket, true));
|
||||
packetPool.release(receivingPacket);
|
||||
receivingPacket = nullptr;
|
||||
return;
|
||||
@ -270,7 +275,7 @@ void SimRadio::startReceive(meshtastic_MeshPacket *p)
|
||||
}
|
||||
isReceiving = true;
|
||||
receivingPacket = packetPool.allocCopy(*p);
|
||||
uint32_t airtimeMsec = getPacketTime(p);
|
||||
uint32_t airtimeMsec = getPacketTime(p, true);
|
||||
notifyLater(airtimeMsec, ISR_RX, false); // Model the time it is busy receiving
|
||||
#else
|
||||
isReceiving = true;
|
||||
@ -311,7 +316,7 @@ void SimRadio::handleReceiveInterrupt()
|
||||
|
||||
printPacket("Lora RX", mp);
|
||||
|
||||
airTime->logAirtime(RX_LOG, getPacketTime(mp));
|
||||
airTime->logAirtime(RX_LOG, RadioInterface::getPacketTime(mp, true));
|
||||
|
||||
deliverToReceiver(mp);
|
||||
}
|
||||
@ -332,4 +337,29 @@ int16_t SimRadio::readData(uint8_t *data, size_t len)
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate airtime per
|
||||
* https://www.rs-online.com/designspark/rel-assets/ds-assets/uploads/knowledge-items/application-notes-for-the-internet-of-things/LoRa%20Design%20Guide.pdf
|
||||
* section 4
|
||||
*
|
||||
* @return num msecs for the packet
|
||||
*/
|
||||
uint32_t SimRadio::getPacketTime(uint32_t pl, bool received)
|
||||
{
|
||||
float bandwidthHz = bw * 1000.0f;
|
||||
bool headDisable = false; // we currently always use the header
|
||||
float tSym = (1 << sf) / bandwidthHz;
|
||||
|
||||
bool lowDataOptEn = tSym > 16e-3 ? true : false; // Needed if symbol time is >16ms
|
||||
|
||||
float tPreamble = (preambleLength + 4.25f) * tSym;
|
||||
float numPayloadSym =
|
||||
8 + max(ceilf(((8.0f * pl - 4 * sf + 28 + 16 - 20 * headDisable) / (4 * (sf - 2 * lowDataOptEn))) * cr), 0.0f);
|
||||
float tPayload = numPayloadSym * tSym;
|
||||
float tPacket = tPreamble + tPayload;
|
||||
|
||||
uint32_t msecs = tPacket * 1000;
|
||||
return msecs;
|
||||
}
|
||||
@ -52,6 +52,7 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr
|
||||
* Debugging counts
|
||||
*/
|
||||
uint32_t rxBad = 0, rxGood = 0, txGood = 0, txRelay = 0;
|
||||
uint16_t txDrop = 0;
|
||||
|
||||
protected:
|
||||
/// are _trying_ to receive a packet currently (note - we might just be waiting for one)
|
||||
@ -88,6 +89,8 @@ class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThr
|
||||
/**
|
||||
* If a send was in progress finish it and return the buffer to the pool */
|
||||
void completeSending();
|
||||
|
||||
virtual uint32_t getPacketTime(uint32_t pl, bool received = false) override;
|
||||
};
|
||||
|
||||
extern SimRadio *simRadio;
|
||||
@ -35,4 +35,7 @@
|
||||
#define HW_VENDOR meshtastic_HardwareModel_RP2040_FEATHER_RFM95
|
||||
#elif defined(PRIVATE_HW)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_PRIVATE_HW
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Detect if running in ISR context (ARM Cortex-M33 / RISC-V)
|
||||
#define xPortInIsrContext() (__get_current_exception() == 0 ? pdFALSE : pdTRUE)
|
||||
|
||||
@ -553,7 +553,7 @@ void enableLoraInterrupt()
|
||||
gpio_pullup_en((gpio_num_t)LORA_CS);
|
||||
#endif
|
||||
|
||||
#ifdef HELTEC_V4
|
||||
#if defined(USE_GC1109_PA)
|
||||
gpio_pullup_en((gpio_num_t)LORA_PA_POWER);
|
||||
gpio_pullup_en((gpio_num_t)LORA_PA_EN);
|
||||
gpio_pulldown_en((gpio_num_t)LORA_PA_TX_EN);
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
#define SX126X_DIO2_AS_RF_SWITCH
|
||||
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||
|
||||
#define USE_GC1109_PA // We have a GC1109 power amplifier+attenuator
|
||||
#define LORA_PA_POWER 7 // power en
|
||||
#define LORA_PA_EN 2
|
||||
#define LORA_PA_TX_EN 46 // enable tx
|
||||
|
||||
71
variants/esp32s3/heltec_wireless_tracker_v2/pins_arduino.h
Normal file
71
variants/esp32s3/heltec_wireless_tracker_v2/pins_arduino.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define DISPLAY_HEIGHT 80
|
||||
#define DISPLAY_WIDTH 160
|
||||
|
||||
#define USB_VID 0x303a
|
||||
#define USB_PID 0x1001
|
||||
|
||||
static const uint8_t LED_BUILTIN = 18;
|
||||
#define BUILTIN_LED LED_BUILTIN // backward compatibility
|
||||
#define LED_BUILTIN LED_BUILTIN
|
||||
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
static const uint8_t SDA = 5;
|
||||
static const uint8_t SCL = 6;
|
||||
|
||||
static const uint8_t SS = 8;
|
||||
static const uint8_t MOSI = 10;
|
||||
static const uint8_t MISO = 11;
|
||||
static const uint8_t SCK = 9;
|
||||
|
||||
static const uint8_t A0 = 1;
|
||||
static const uint8_t A1 = 2;
|
||||
static const uint8_t A2 = 3;
|
||||
static const uint8_t A3 = 4;
|
||||
static const uint8_t A4 = 5;
|
||||
static const uint8_t A5 = 6;
|
||||
static const uint8_t A6 = 7;
|
||||
static const uint8_t A7 = 8;
|
||||
static const uint8_t A8 = 9;
|
||||
static const uint8_t A9 = 10;
|
||||
static const uint8_t A10 = 11;
|
||||
static const uint8_t A11 = 12;
|
||||
static const uint8_t A12 = 13;
|
||||
static const uint8_t A13 = 14;
|
||||
static const uint8_t A14 = 15;
|
||||
static const uint8_t A15 = 16;
|
||||
static const uint8_t A16 = 17;
|
||||
static const uint8_t A17 = 18;
|
||||
static const uint8_t A18 = 19;
|
||||
static const uint8_t A19 = 20;
|
||||
|
||||
static const uint8_t T1 = 1;
|
||||
static const uint8_t T2 = 2;
|
||||
static const uint8_t T3 = 3;
|
||||
static const uint8_t T4 = 4;
|
||||
static const uint8_t T5 = 5;
|
||||
static const uint8_t T6 = 6;
|
||||
static const uint8_t T7 = 7;
|
||||
static const uint8_t T8 = 8;
|
||||
static const uint8_t T9 = 9;
|
||||
static const uint8_t T10 = 10;
|
||||
static const uint8_t T11 = 11;
|
||||
static const uint8_t T12 = 12;
|
||||
static const uint8_t T13 = 13;
|
||||
static const uint8_t T14 = 14;
|
||||
|
||||
static const uint8_t Vext = 3;
|
||||
static const uint8_t LED = 18;
|
||||
|
||||
static const uint8_t RST_LoRa = 12;
|
||||
static const uint8_t BUSY_LoRa = 13;
|
||||
static const uint8_t DIO0 = 14;
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
14
variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini
Normal file
14
variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini
Normal file
@ -0,0 +1,14 @@
|
||||
[env:heltec-wireless-tracker-v2]
|
||||
extends = esp32s3_base
|
||||
board = heltec_wireless_tracker_v2
|
||||
board_build.partitions = default_8MB.csv
|
||||
upload_protocol = esptool
|
||||
|
||||
build_flags =
|
||||
${esp32s3_base.build_flags}
|
||||
-I variants/esp32s3/heltec_wireless_tracker_v2
|
||||
-D HELTEC_WIRELESS_TRACKER_V2
|
||||
-D GPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely.
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
lovyan03/LovyanGFX@^1.2.0
|
||||
78
variants/esp32s3/heltec_wireless_tracker_v2/variant.h
Normal file
78
variants/esp32s3/heltec_wireless_tracker_v2/variant.h
Normal file
@ -0,0 +1,78 @@
|
||||
#define LED_PIN 18
|
||||
|
||||
#define _VARIANT_HELTEC_WIRELESS_TRACKER
|
||||
|
||||
// I2C
|
||||
#define I2C_SDA SDA
|
||||
#define I2C_SCL SCL
|
||||
|
||||
// ST7735S TFT LCD
|
||||
#define ST7735S 1 // there are different (sub-)versions of ST7735
|
||||
#define ST7735_CS 38
|
||||
#define ST7735_RS 40 // DC
|
||||
#define ST7735_SDA 42 // MOSI
|
||||
#define ST7735_SCK 41
|
||||
#define ST7735_RESET 39
|
||||
#define ST7735_MISO -1
|
||||
#define ST7735_BUSY -1
|
||||
#define TFT_BL 21
|
||||
#define ST7735_SPI_HOST SPI3_HOST
|
||||
#define SPI_FREQUENCY 40000000
|
||||
#define SPI_READ_FREQUENCY 16000000
|
||||
#define SCREEN_ROTATE
|
||||
#define TFT_HEIGHT DISPLAY_WIDTH
|
||||
#define TFT_WIDTH DISPLAY_HEIGHT
|
||||
#define TFT_OFFSET_X 24
|
||||
#define TFT_OFFSET_Y 0
|
||||
#define TFT_INVERT false
|
||||
#define SCREEN_TRANSITION_FRAMERATE 3 // fps
|
||||
#define DISPLAY_FORCE_SMALL_FONTS
|
||||
|
||||
#define VEXT_ENABLE 3 // active HIGH - powers the GPS, GPS LNA and OLED
|
||||
#define VEXT_ON_VALUE HIGH
|
||||
#define BUTTON_PIN 0
|
||||
|
||||
#define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_CHANNEL ADC1_GPIO1_CHANNEL
|
||||
#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider
|
||||
#define ADC_MULTIPLIER 4.9 * 1.045
|
||||
#define ADC_CTRL 2 // active HIGH, powers the voltage divider.
|
||||
#define ADC_USE_PULLUP // Use internal pullup/pulldown instead of actively driving the output
|
||||
|
||||
#undef GPS_RX_PIN
|
||||
#undef GPS_TX_PIN
|
||||
#define GPS_RX_PIN 33
|
||||
#define GPS_TX_PIN 34
|
||||
#define PIN_GPS_RESET 35
|
||||
#define PIN_GPS_PPS 36
|
||||
// #define PIN_GPS_EN 3 // Uncomment to power off the GPS with triple-click on Tracker v2, though we'll also lose the
|
||||
// display.
|
||||
|
||||
#define GPS_RESET_MODE LOW
|
||||
#define GPS_UC6580
|
||||
#define GPS_BAUDRATE 115200
|
||||
|
||||
#define USE_SX1262
|
||||
#define LORA_DIO0 -1 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 12
|
||||
#define LORA_DIO1 14 // SX1262 IRQ
|
||||
#define LORA_DIO2 13 // SX1262 BUSY
|
||||
#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TCXO is enabled
|
||||
|
||||
#define LORA_SCK 9
|
||||
#define LORA_MISO 11
|
||||
#define LORA_MOSI 10
|
||||
#define LORA_CS 8
|
||||
|
||||
#define SX126X_CS LORA_CS
|
||||
#define SX126X_DIO1 LORA_DIO1
|
||||
#define SX126X_BUSY LORA_DIO2
|
||||
#define SX126X_RESET LORA_RESET
|
||||
|
||||
#define SX126X_DIO2_AS_RF_SWITCH
|
||||
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||
|
||||
#define USE_GC1109_PA // We have a GC1109 power amplifier+attenuator
|
||||
#define LORA_PA_POWER 7 // power en
|
||||
#define LORA_PA_EN 4
|
||||
#define LORA_PA_TX_EN 46 // enable tx
|
||||
@ -15,7 +15,7 @@ build_flags = ${esp32s3_base.build_flags}
|
||||
-D SDCARD_USE_SPI1
|
||||
-D ENABLE_ROTARY_PULLUP
|
||||
-D ENABLE_BUTTON_PULLUP
|
||||
-D HALF_STEP
|
||||
-D ROTARY_BUXTRONICS
|
||||
|
||||
lib_deps = ${esp32s3_base.lib_deps}
|
||||
lovyan03/LovyanGFX@1.2.7
|
||||
@ -26,7 +26,7 @@ lib_deps = ${esp32s3_base.lib_deps}
|
||||
lewisxhe/SensorLib@0.3.1
|
||||
https://github.com/pschatzmann/arduino-audio-driver/archive/refs/tags/v0.1.3.zip
|
||||
https://github.com/mverch67/BQ27220/archive/07d92be846abd8a0258a50c23198dac0858b22ed.zip
|
||||
https://github.com/mverch67/RotaryEncoder/archive/25a59d5745a6645536f921427d80b08e78f886d4.zip
|
||||
https://github.com/mverch67/RotaryEncoder/archive/da958a21389cbcd485989705df602a33e092dd88.zip
|
||||
|
||||
[env:tlora-pager-tft]
|
||||
board_level = extra
|
||||
|
||||
@ -6,7 +6,8 @@ board = gat562_mesh_trial_tracker
|
||||
board_check = true
|
||||
build_flags = ${nrf52840_base.build_flags}
|
||||
-I variants/nrf52840/gat562_mesh_trial_tracker
|
||||
-D GAT562_MESH_TRIAL_TRACKER
|
||||
;-D GAT562_MESH_TRIAL_TRACKER
|
||||
-D PRIVATE_HW
|
||||
-DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely.
|
||||
-DRADIOLIB_EXCLUDE_SX128X=1
|
||||
-DRADIOLIB_EXCLUDE_SX127X=1
|
||||
|
||||
@ -140,11 +140,12 @@ static const uint8_t A0 = PIN_A0;
|
||||
#define HAS_GPS 1
|
||||
// #define PIN_GPS_REINIT (32 + 5) // An output to reset L76K GPS. As per datasheet, low for > 100ms will reset the L76K
|
||||
|
||||
#define PIN_GPS_STANDBY (32 + 10) // An output to wake GPS, low means allow sleep, high means force wake
|
||||
// Seems to be missing on this new board
|
||||
#define PIN_GPS_PPS (0 + 29) // Pulse per second input from the GPS
|
||||
#define GPS_TX_PIN (32 + 15) // This is for bits going TOWARDS the CPU
|
||||
#define GPS_RX_PIN (32 + 13) // This is for bits going TOWARDS the GPS
|
||||
#define PIN_GPS_EN (32 + 11) // GPS power
|
||||
#define GPS_EN_ACTIVE 1
|
||||
#define PIN_GPS_STANDBY (32 + 13) // wakeup pin
|
||||
#define PIN_GPS_PPS (32 + 15)
|
||||
#define GPS_TX_PIN (32 + 10) // L76K module RX PIN
|
||||
#define GPS_RX_PIN (0 + 29) // L76K module TX PIN
|
||||
|
||||
#define GPS_THREAD_INTERVAL 50
|
||||
|
||||
@ -204,4 +205,4 @@ static const uint8_t A0 = PIN_A0;
|
||||
* Arduino objects - C++ only
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue
Block a user