mirror of
https://github.com/meshtastic/firmware.git
synced 2025-08-04 12:44:40 +00:00
Merge branch 'master' into thinknode-m5
This commit is contained in:
commit
2906afad0d
@ -2,7 +2,7 @@
|
|||||||
extends = arduino_base
|
extends = arduino_base
|
||||||
platform =
|
platform =
|
||||||
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
|
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
|
||||||
platformio/ststm32@19.2.0
|
platformio/ststm32@19.3.0
|
||||||
platform_packages =
|
platform_packages =
|
||||||
# TODO renovate
|
# TODO renovate
|
||||||
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
|
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
|
PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
|
||||||
CHANGE_MODE=false
|
CHANGE_MODE=false
|
||||||
|
2
debian/meshtasticd.postinst
vendored
2
debian/meshtasticd.postinst
vendored
@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
# postinst script for meshtasticd
|
# postinst script for meshtasticd
|
||||||
#
|
#
|
||||||
# see: dh_installdeb(1)
|
# see: dh_installdeb(1)
|
||||||
|
2
debian/meshtasticd.postrm
vendored
2
debian/meshtasticd.postrm
vendored
@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
# postrm script for meshtasticd
|
# postrm script for meshtasticd
|
||||||
#
|
#
|
||||||
# see: dh_installdeb(1)
|
# see: dh_installdeb(1)
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 9bac2886f9344f25716921467a82e8b0326107cd
|
Subproject commit 1ecf94da9898ea0b8f2745bfe6bda2a8f2ca4073
|
@ -1511,7 +1511,7 @@ bool GPS::lookForTime()
|
|||||||
|
|
||||||
#ifdef GNSS_AIROHA
|
#ifdef GNSS_AIROHA
|
||||||
uint8_t fix = reader.fixQuality();
|
uint8_t fix = reader.fixQuality();
|
||||||
if (fix > 0) {
|
if (fix >= 1 && fix <= 5) {
|
||||||
if (lastFixStartMsec > 0) {
|
if (lastFixStartMsec > 0) {
|
||||||
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
||||||
return false;
|
return false;
|
||||||
@ -1566,7 +1566,7 @@ bool GPS::lookForLocation()
|
|||||||
#ifdef GNSS_AIROHA
|
#ifdef GNSS_AIROHA
|
||||||
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
|
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
|
||||||
uint8_t fix = reader.fixQuality();
|
uint8_t fix = reader.fixQuality();
|
||||||
if (fix > 0) {
|
if (fix >= 1 && fix <= 5) {
|
||||||
if (lastFixStartMsec > 0) {
|
if (lastFixStartMsec > 0) {
|
||||||
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -14,7 +14,9 @@
|
|||||||
#include "modules/AdminModule.h"
|
#include "modules/AdminModule.h"
|
||||||
#include "modules/CannedMessageModule.h"
|
#include "modules/CannedMessageModule.h"
|
||||||
#include "modules/KeyVerificationModule.h"
|
#include "modules/KeyVerificationModule.h"
|
||||||
|
|
||||||
#include "modules/TraceRouteModule.h"
|
#include "modules/TraceRouteModule.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
extern uint16_t TFT_MESH;
|
extern uint16_t TFT_MESH;
|
||||||
|
|
||||||
@ -118,6 +120,22 @@ void menuHandler::TwelveHourPicker()
|
|||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reusable confirmation prompt function
|
||||||
|
void menuHandler::showConfirmationBanner(const char *message, std::function<void()> onConfirm)
|
||||||
|
{
|
||||||
|
static const char *confirmOptions[] = {"No", "Yes"};
|
||||||
|
BannerOverlayOptions confirmBanner;
|
||||||
|
confirmBanner.message = message;
|
||||||
|
confirmBanner.optionsArrayPtr = confirmOptions;
|
||||||
|
confirmBanner.optionsCount = 2;
|
||||||
|
confirmBanner.bannerCallback = [onConfirm](int confirmSelected) -> void {
|
||||||
|
if (confirmSelected == 1) {
|
||||||
|
onConfirm();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
screen->showOverlayBanner(confirmBanner);
|
||||||
|
}
|
||||||
|
|
||||||
void menuHandler::ClockFacePicker()
|
void menuHandler::ClockFacePicker()
|
||||||
{
|
{
|
||||||
static const char *optionsArray[] = {"Back", "Digital", "Analog"};
|
static const char *optionsArray[] = {"Back", "Digital", "Analog"};
|
||||||
@ -294,7 +312,7 @@ void menuHandler::messageResponseMenu()
|
|||||||
|
|
||||||
void menuHandler::homeBaseMenu()
|
void menuHandler::homeBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Bluetooth, Sleep, enumEnd };
|
enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Sleep, enumEnd };
|
||||||
|
|
||||||
static const char *optionsArray[enumEnd] = {"Back"};
|
static const char *optionsArray[enumEnd] = {"Back"};
|
||||||
static int optionsEnumArray[enumEnd] = {Back};
|
static int optionsEnumArray[enumEnd] = {Back};
|
||||||
@ -316,8 +334,6 @@ void menuHandler::homeBaseMenu()
|
|||||||
optionsArray[options] = "New Freetext Msg";
|
optionsArray[options] = "New Freetext Msg";
|
||||||
optionsEnumArray[options++] = Freetext;
|
optionsEnumArray[options++] = Freetext;
|
||||||
}
|
}
|
||||||
optionsArray[options] = "Bluetooth Toggle";
|
|
||||||
optionsEnumArray[options++] = Bluetooth;
|
|
||||||
|
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "Home Action";
|
bannerOptions.message = "Home Action";
|
||||||
@ -342,9 +358,6 @@ void menuHandler::homeBaseMenu()
|
|||||||
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
|
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
|
||||||
} else if (selected == Freetext) {
|
} else if (selected == Freetext) {
|
||||||
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST);
|
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST);
|
||||||
} else if (selected == Bluetooth) {
|
|
||||||
menuQueue = bluetooth_toggle_menu;
|
|
||||||
screen->runNow();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
@ -381,7 +394,7 @@ void menuHandler::textMessageBaseMenu()
|
|||||||
|
|
||||||
void menuHandler::systemBaseMenu()
|
void menuHandler::systemBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Notifications, ScreenOptions, PowerMenu, Test, enumEnd };
|
enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, PowerMenu, Test, enumEnd };
|
||||||
static const char *optionsArray[enumEnd] = {"Back"};
|
static const char *optionsArray[enumEnd] = {"Back"};
|
||||||
static int optionsEnumArray[enumEnd] = {Back};
|
static int optionsEnumArray[enumEnd] = {Back};
|
||||||
int options = 1;
|
int options = 1;
|
||||||
@ -394,6 +407,9 @@ void menuHandler::systemBaseMenu()
|
|||||||
optionsEnumArray[options++] = ScreenOptions;
|
optionsEnumArray[options++] = ScreenOptions;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
optionsArray[options] = "Bluetooth Toggle";
|
||||||
|
optionsEnumArray[options++] = Bluetooth;
|
||||||
|
|
||||||
optionsArray[options] = "Reboot/Shutdown";
|
optionsArray[options] = "Reboot/Shutdown";
|
||||||
optionsEnumArray[options++] = PowerMenu;
|
optionsEnumArray[options++] = PowerMenu;
|
||||||
|
|
||||||
@ -420,6 +436,9 @@ void menuHandler::systemBaseMenu()
|
|||||||
} else if (selected == Test) {
|
} else if (selected == Test) {
|
||||||
menuHandler::menuQueue = menuHandler::test_menu;
|
menuHandler::menuQueue = menuHandler::test_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
|
} else if (selected == Bluetooth) {
|
||||||
|
menuQueue = bluetooth_toggle_menu;
|
||||||
|
screen->runNow();
|
||||||
} else if (selected == Back && !test_enabled) {
|
} else if (selected == Back && !test_enabled) {
|
||||||
test_count++;
|
test_count++;
|
||||||
if (test_count > 4) {
|
if (test_count > 4) {
|
||||||
|
@ -43,6 +43,7 @@ class menuHandler
|
|||||||
|
|
||||||
static void LoraRegionPicker(uint32_t duration = 30000);
|
static void LoraRegionPicker(uint32_t duration = 30000);
|
||||||
static void handleMenuSwitch(OLEDDisplay *display);
|
static void handleMenuSwitch(OLEDDisplay *display);
|
||||||
|
static void showConfirmationBanner(const char *message, std::function<void()> onConfirm);
|
||||||
static void clockMenu();
|
static void clockMenu();
|
||||||
static void TZPicker();
|
static void TZPicker();
|
||||||
static void TwelveHourPicker();
|
static void TwelveHourPicker();
|
||||||
|
@ -31,6 +31,9 @@
|
|||||||
#include "Throttle.h"
|
#include "Throttle.h"
|
||||||
#include <RTC.h>
|
#include <RTC.h>
|
||||||
|
|
||||||
|
// Flag to indicate a heartbeat was received and we should send queue status
|
||||||
|
bool heartbeatReceived = false;
|
||||||
|
|
||||||
PhoneAPI::PhoneAPI()
|
PhoneAPI::PhoneAPI()
|
||||||
{
|
{
|
||||||
lastContactMsec = millis();
|
lastContactMsec = millis();
|
||||||
@ -155,6 +158,7 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
|||||||
#endif
|
#endif
|
||||||
case meshtastic_ToRadio_heartbeat_tag:
|
case meshtastic_ToRadio_heartbeat_tag:
|
||||||
LOG_DEBUG("Got client heartbeat");
|
LOG_DEBUG("Got client heartbeat");
|
||||||
|
heartbeatReceived = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Ignore nop messages
|
// Ignore nop messages
|
||||||
@ -194,6 +198,17 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
// In case we send a FromRadio packet
|
// In case we send a FromRadio packet
|
||||||
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
||||||
|
|
||||||
|
// Respond to heartbeat by sending queue status
|
||||||
|
if (heartbeatReceived) {
|
||||||
|
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
||||||
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_queueStatus_tag;
|
||||||
|
fromRadioScratch.queueStatus = router->getQueueStatus();
|
||||||
|
heartbeatReceived = false;
|
||||||
|
size_t numbytes = pb_encode_to_bytes(buf, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch);
|
||||||
|
LOG_DEBUG("FromRadio=STATE_SEND_QUEUE_STATUS, numbytes=%u", numbytes);
|
||||||
|
return numbytes;
|
||||||
|
}
|
||||||
|
|
||||||
// Advance states as needed
|
// Advance states as needed
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case STATE_SEND_NOTHING:
|
case STATE_SEND_NOTHING:
|
||||||
|
@ -267,6 +267,9 @@ typedef enum _meshtastic_HardwareModel {
|
|||||||
meshtastic_HardwareModel_RAK3312 = 106,
|
meshtastic_HardwareModel_RAK3312 = 106,
|
||||||
/* Elecrow ThinkNode M5 https://www.elecrow.com/wiki/ThinkNode_M5_Meshtastic_LoRa_Signal_Transceiver_ESP32-S3.html */
|
/* Elecrow ThinkNode M5 https://www.elecrow.com/wiki/ThinkNode_M5_Meshtastic_LoRa_Signal_Transceiver_ESP32-S3.html */
|
||||||
meshtastic_HardwareModel_THINKNODE_M5 = 107,
|
meshtastic_HardwareModel_THINKNODE_M5 = 107,
|
||||||
|
/* MeshSolar is an integrated power management and communication solution designed for outdoor low-power devices.
|
||||||
|
https://heltec.org/project/meshsolar/ */
|
||||||
|
meshtastic_HardwareModel_HELTEC_MESH_SOLAR = 108,
|
||||||
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||||
------------------------------------------------------------------------------------------------------------------------------------------ */
|
------------------------------------------------------------------------------------------------------------------------------------------ */
|
||||||
|
@ -82,7 +82,10 @@ typedef enum _meshtastic_ModuleConfig_SerialConfig_Serial_Mode {
|
|||||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85 = 6,
|
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85 = 6,
|
||||||
/* VE.Direct is a serial protocol used by Victron Energy products
|
/* VE.Direct is a serial protocol used by Victron Energy products
|
||||||
https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable */
|
https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable */
|
||||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_VE_DIRECT = 7
|
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_VE_DIRECT = 7,
|
||||||
|
/* Used to configure and view some parameters of MeshSolar.
|
||||||
|
https://heltec.org/project/meshsolar/ */
|
||||||
|
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG = 8
|
||||||
} meshtastic_ModuleConfig_SerialConfig_Serial_Mode;
|
} meshtastic_ModuleConfig_SerialConfig_Serial_Mode;
|
||||||
|
|
||||||
/* TODO: REPLACE */
|
/* TODO: REPLACE */
|
||||||
@ -472,8 +475,8 @@ extern "C" {
|
|||||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Baud)(meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600+1))
|
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Baud)(meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600+1))
|
||||||
|
|
||||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN meshtastic_ModuleConfig_SerialConfig_Serial_Mode_DEFAULT
|
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN meshtastic_ModuleConfig_SerialConfig_Serial_Mode_DEFAULT
|
||||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MAX meshtastic_ModuleConfig_SerialConfig_Serial_Mode_VE_DIRECT
|
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MAX meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG
|
||||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Mode)(meshtastic_ModuleConfig_SerialConfig_Serial_Mode_VE_DIRECT+1))
|
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Mode)(meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG+1))
|
||||||
|
|
||||||
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE
|
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE
|
||||||
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MAX meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK
|
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MAX meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK
|
||||||
|
@ -235,6 +235,11 @@ bool isWifiAvailable()
|
|||||||
#ifdef USE_WS5500
|
#ifdef USE_WS5500
|
||||||
} else if (config.network.eth_enabled) {
|
} else if (config.network.eth_enabled) {
|
||||||
return true;
|
return true;
|
||||||
|
#endif
|
||||||
|
#ifndef ARCH_PORTDUINO
|
||||||
|
} else if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
// it's likely we have wifi now, but user intends to turn it off in config!
|
||||||
|
return true;
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -40,6 +40,9 @@ extern ScanI2C::DeviceAddress cardkb_found;
|
|||||||
extern bool graphics::isMuted;
|
extern bool graphics::isMuted;
|
||||||
|
|
||||||
static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto";
|
static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto";
|
||||||
|
static NodeNum lastDest = NODENUM_BROADCAST;
|
||||||
|
static uint8_t lastChannel = 0;
|
||||||
|
static bool lastDestSet = false;
|
||||||
|
|
||||||
meshtastic_CannedMessageModuleConfig cannedMessageModuleConfig;
|
meshtastic_CannedMessageModuleConfig cannedMessageModuleConfig;
|
||||||
|
|
||||||
@ -63,8 +66,18 @@ CannedMessageModule::CannedMessageModule()
|
|||||||
|
|
||||||
void CannedMessageModule::LaunchWithDestination(NodeNum newDest, uint8_t newChannel)
|
void CannedMessageModule::LaunchWithDestination(NodeNum newDest, uint8_t newChannel)
|
||||||
{
|
{
|
||||||
|
// Use the requested destination, unless it's "broadcast" and we have a previous node/channel
|
||||||
|
if (newDest == NODENUM_BROADCAST && lastDestSet) {
|
||||||
|
newDest = lastDest;
|
||||||
|
newChannel = lastChannel;
|
||||||
|
}
|
||||||
dest = newDest;
|
dest = newDest;
|
||||||
channel = newChannel;
|
channel = newChannel;
|
||||||
|
lastDest = dest;
|
||||||
|
lastChannel = channel;
|
||||||
|
lastDestSet = true;
|
||||||
|
|
||||||
|
// Rest of function unchanged...
|
||||||
// Always select the first real canned message on activation
|
// Always select the first real canned message on activation
|
||||||
int firstRealMsgIdx = 0;
|
int firstRealMsgIdx = 0;
|
||||||
for (int i = 0; i < messagesCount; ++i) {
|
for (int i = 0; i < messagesCount; ++i) {
|
||||||
@ -84,10 +97,28 @@ void CannedMessageModule::LaunchWithDestination(NodeNum newDest, uint8_t newChan
|
|||||||
notifyObservers(&e);
|
notifyObservers(&e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CannedMessageModule::LaunchRepeatDestination()
|
||||||
|
{
|
||||||
|
if (!lastDestSet) {
|
||||||
|
LaunchWithDestination(NODENUM_BROADCAST, 0);
|
||||||
|
} else {
|
||||||
|
LaunchWithDestination(lastDest, lastChannel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CannedMessageModule::LaunchFreetextWithDestination(NodeNum newDest, uint8_t newChannel)
|
void CannedMessageModule::LaunchFreetextWithDestination(NodeNum newDest, uint8_t newChannel)
|
||||||
{
|
{
|
||||||
|
// Use the requested destination, unless it's "broadcast" and we have a previous node/channel
|
||||||
|
if (newDest == NODENUM_BROADCAST && lastDestSet) {
|
||||||
|
newDest = lastDest;
|
||||||
|
newChannel = lastChannel;
|
||||||
|
}
|
||||||
dest = newDest;
|
dest = newDest;
|
||||||
channel = newChannel;
|
channel = newChannel;
|
||||||
|
lastDest = dest;
|
||||||
|
lastChannel = channel;
|
||||||
|
lastDestSet = true;
|
||||||
|
|
||||||
runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
|
runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
|
||||||
requestFocus();
|
requestFocus();
|
||||||
UIFrameEvent e;
|
UIFrameEvent e;
|
||||||
@ -479,6 +510,9 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
|
|||||||
if (destIndex < static_cast<int>(activeChannelIndices.size())) {
|
if (destIndex < static_cast<int>(activeChannelIndices.size())) {
|
||||||
dest = NODENUM_BROADCAST;
|
dest = NODENUM_BROADCAST;
|
||||||
channel = activeChannelIndices[destIndex];
|
channel = activeChannelIndices[destIndex];
|
||||||
|
lastDest = dest;
|
||||||
|
lastChannel = channel;
|
||||||
|
lastDestSet = true;
|
||||||
} else {
|
} else {
|
||||||
int nodeIndex = destIndex - static_cast<int>(activeChannelIndices.size());
|
int nodeIndex = destIndex - static_cast<int>(activeChannelIndices.size());
|
||||||
if (nodeIndex >= 0 && nodeIndex < static_cast<int>(filteredNodes.size())) {
|
if (nodeIndex >= 0 && nodeIndex < static_cast<int>(filteredNodes.size())) {
|
||||||
@ -486,6 +520,10 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
|
|||||||
if (selectedNode) {
|
if (selectedNode) {
|
||||||
dest = selectedNode->num;
|
dest = selectedNode->num;
|
||||||
channel = selectedNode->channel;
|
channel = selectedNode->channel;
|
||||||
|
// Already saves here, but for clarity, also:
|
||||||
|
lastDest = dest;
|
||||||
|
lastChannel = channel;
|
||||||
|
lastDestSet = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -595,8 +633,27 @@ bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bo
|
|||||||
// Normal canned message selection
|
// Normal canned message selection
|
||||||
if (runState == CANNED_MESSAGE_RUN_STATE_INACTIVE || runState == CANNED_MESSAGE_RUN_STATE_DISABLED) {
|
if (runState == CANNED_MESSAGE_RUN_STATE_INACTIVE || runState == CANNED_MESSAGE_RUN_STATE_DISABLED) {
|
||||||
} else {
|
} else {
|
||||||
|
// Show confirmation dialog before sending canned message
|
||||||
|
NodeNum destNode = dest;
|
||||||
|
ChannelIndex chan = channel;
|
||||||
|
#if CANNED_MESSAGE_ADD_CONFIRMATION
|
||||||
|
graphics::menuHandler::showConfirmationBanner("Send message?", [this, destNode, chan, current]() {
|
||||||
|
this->sendText(destNode, chan, current, false);
|
||||||
|
payload = runState;
|
||||||
|
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||||
|
currentMessageIndex = -1;
|
||||||
|
|
||||||
|
// Notify UI to regenerate frame set and redraw
|
||||||
|
UIFrameEvent e;
|
||||||
|
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||||
|
notifyObservers(&e);
|
||||||
|
screen->forceDisplay();
|
||||||
|
});
|
||||||
|
#else
|
||||||
payload = runState;
|
payload = runState;
|
||||||
runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT;
|
runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT;
|
||||||
|
#endif
|
||||||
|
// Do not immediately set runState; wait for confirmation
|
||||||
handled = true;
|
handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -827,6 +884,9 @@ int CannedMessageModule::handleEmotePickerInput(const InputEvent *event)
|
|||||||
|
|
||||||
void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const char *message, bool wantReplies)
|
void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const char *message, bool wantReplies)
|
||||||
{
|
{
|
||||||
|
lastDest = dest;
|
||||||
|
lastChannel = channel;
|
||||||
|
lastDestSet = true;
|
||||||
// === Prepare packet ===
|
// === Prepare packet ===
|
||||||
meshtastic_MeshPacket *p = allocDataPacket();
|
meshtastic_MeshPacket *p = allocDataPacket();
|
||||||
p->to = dest;
|
p->to = dest;
|
||||||
@ -1711,7 +1771,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
|||||||
// Text: split by words and wrap inside word if needed
|
// Text: split by words and wrap inside word if needed
|
||||||
String text = token.second;
|
String text = token.second;
|
||||||
pos = 0;
|
pos = 0;
|
||||||
while (pos < text.length()) {
|
while (pos < static_cast<int>(text.length())) {
|
||||||
// Find next space (or end)
|
// Find next space (or end)
|
||||||
int spacePos = text.indexOf(' ', pos);
|
int spacePos = text.indexOf(' ', pos);
|
||||||
int endPos = (spacePos == -1) ? text.length() : spacePos + 1; // Include space
|
int endPos = (spacePos == -1) ? text.length() : spacePos + 1; // Include space
|
||||||
|
@ -59,6 +59,7 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
|
|||||||
CannedMessageModule();
|
CannedMessageModule();
|
||||||
|
|
||||||
void LaunchWithDestination(NodeNum, uint8_t newChannel = 0);
|
void LaunchWithDestination(NodeNum, uint8_t newChannel = 0);
|
||||||
|
void LaunchRepeatDestination();
|
||||||
void LaunchFreetextWithDestination(NodeNum, uint8_t newChannel = 0);
|
void LaunchFreetextWithDestination(NodeNum, uint8_t newChannel = 0);
|
||||||
|
|
||||||
// === Emote Picker navigation ===
|
// === Emote Picker navigation ===
|
||||||
|
@ -162,6 +162,8 @@ static const uint8_t SCL = PIN_WIRE_SCL;
|
|||||||
|
|
||||||
#define CANNED_MESSAGE_MODULE_ENABLE 1
|
#define CANNED_MESSAGE_MODULE_ENABLE 1
|
||||||
|
|
||||||
|
#define CANNED_MESSAGE_ADD_CONFIRMATION 1
|
||||||
|
|
||||||
// trackball
|
// trackball
|
||||||
#define HAS_TRACKBALL 1
|
#define HAS_TRACKBALL 1
|
||||||
#define TB_UP 25
|
#define TB_UP 25
|
||||||
|
Loading…
Reference in New Issue
Block a user