Compare commits

..

16 Commits

Author SHA1 Message Date
Ben Meadors
53231ae4b1 Added toggable config and default for larger screens to enable / hide bubbles on chat messages (#9560)
* Added toggable config and default for largeer screens to enable / hide bubbles on chat messages

* Refactor message bubble rendering logic for improved layout and consistency

* Move osk_found initialization for trackball/encoder devices before module setup to fix missing keyboard for L1

* Utilize current checks for consistency

* Reverted last changes

---------

Co-authored-by: Jason P <applewiz@mac.com>
Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
2026-02-07 15:41:31 -06:00
github-actions[bot]
5280caf9d8 Update protobufs (#9559)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2026-02-07 06:24:56 -06:00
Colby Dillion
ba016fd91a Fix hop_limit upgrade detection (#9550)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-02-07 06:19:25 -06:00
renovate[bot]
e2cf401ad3 Update meshtastic/device-ui digest to 6c75195 (#9553)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-07 06:16:30 -06:00
github-actions[bot]
f73d18384d Upgrade trunk (#9547)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2026-02-06 06:22:34 -06:00
github-actions[bot]
11bb2ee84e Upgrade trunk (#9368)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2026-02-05 05:53:48 -06:00
renovate[bot]
a324c4af10 Update meshtastic-esp8266-oled-ssd1306 digest to 21e484f (#9533)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-02-04 19:30:12 -06:00
renovate[bot]
5df5ab2790 Update Adafruit MPU6050 to v2.2.8 (#9534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-04 17:19:11 -06:00
renovate[bot]
74ea6206d9 Update NeoPixel to v1.15.3 (#9530)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-04 17:13:44 -06:00
renovate[bot]
b238744445 Update Adafruit MPU6050 to v2.2.7 (#9525)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-04 17:12:01 -06:00
Ben Meadors
be5f0a9ade Implement UDP multicast handler start/stop to ensure proper lifecycle (#9524)
* Implement UDP multicast handler start/stop to ensure proper lifecycle

* Add close method to AsyncUDP and improve UDP multicast handler lifecycle management

* Guard portduino
2026-02-04 11:15:38 -06:00
Jason P
b008c7a170 Fix config.display.use_long_node_name not saving (#9522) 2026-02-03 12:21:51 -06:00
Eric Sesterhenn
c8a9cdc148 Make sure we always return a value in NodeDB::restorePreferences() (#9516)
In case FScom is not defined there is no return statement. This
moves the return outside of the ifdef to make sure a defined
value is returned.
2026-02-03 06:23:49 -06:00
Jonathan Bennett
644fa5b54e Power off control pin on Thinknode m5 during deepsleep and add RTC (#9510)
* Power off control pin on Thinknode m5 during deepsleep

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Finish HAS_RTC cleanup

* Add RTC for Thinknode M5

* Don't double-init Wire

* Specify the RTC chip directly rather than use SensorRtcHelper.
Saves a bit of flash, and avoid mis-detection

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-03 06:07:33 -06:00
Ben Meadors
e9d4485bb5 Merge remote-tracking branch 'origin/develop' 2026-01-30 13:56:11 -06:00
Ben Meadors
004179c045 Fix OTA filename determination to use unified format for ESP32 (#9488) 2026-01-30 13:46:11 -06:00
168 changed files with 982 additions and 1040 deletions

View File

@@ -8,18 +8,18 @@ plugins:
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- checkov@3.2.497
- renovate@42.84.2
- prettier@3.8.0
- trufflehog@3.92.5
- checkov@3.2.500
- renovate@43.4.0
- prettier@3.8.1
- trufflehog@3.93.0
- yamllint@1.38.0
- bandit@1.9.3
- trivy@0.68.2
- trivy@0.69.1
- taplo@0.10.0
- ruff@0.14.13
- ruff@0.15.0
- isort@7.0.0
- markdownlint@0.47.0
- oxipng@10.0.0
- oxipng@10.1.0
- svgo@4.0.0
- actionlint@1.7.10
- flake8@7.3.0

View File

@@ -156,16 +156,8 @@ IF %BPS_RESET% EQU 1 (
SET "PROGNAME=!FILENAME:.factory.bin=!"
CALL :LOG_MESSAGE DEBUG "Computed PROGNAME: !PROGNAME!"
IF "__!MCU!__" == "__esp32s3__" (
@REM We are working with ESP32-S3
SET "OTA_FILENAME=bleota-s3.bin"
) ELSE IF "__!MCU!__" == "__esp32c3__" (
@REM We are working with ESP32-C3
SET "OTA_FILENAME=bleota-c3.bin"
) ELSE (
@REM Everything else
SET "OTA_FILENAME=bleota.bin"
)
@REM Determine OTA filename based on MCU type (unified OTA format)
SET "OTA_FILENAME=mt-!MCU!-ota.bin"
CALL :LOG_MESSAGE DEBUG "Set OTA_FILENAME to: !OTA_FILENAME!"
@REM Set SPIFFS filename with "littlefs-" prefix.

View File

@@ -131,14 +131,8 @@ if [[ -f "$FILENAME" && "$FILENAME" == *.factory.bin ]]; then
exit 1
fi
# Determine OTA filename based on MCU type
if [ "$MCU" == "esp32s3" ]; then
OTAFILE=bleota-s3.bin
elif [ "$MCU" == "esp32c3" ]; then
OTAFILE=bleota-c3.bin
else
OTAFILE=bleota.bin
fi
# Determine OTA filename based on MCU type (unified OTA format)
OTAFILE="mt-${MCU}-ota.bin"
# Set SPIFFS filename with "littlefs-" prefix.
SPIFFSFILE="littlefs-${PROGNAME/firmware-/}.bin"

View File

@@ -59,14 +59,8 @@ BuildRequires: pkgconfig(libbsd-overlay)
Requires: systemd-udev
# Declare that this package provides the user/group it creates in %pre
# Required for Fedora 43+ which tracks users/groups as RPM dependencies
Provides: user(%{meshtasticd_user})
Provides: group(%{meshtasticd_user})
Provides: group(spi)
%description
Meshtastic daemon. Meshtastic is an off-grid
Meshtastic daemon for controlling Meshtastic devices. Meshtastic is an off-grid
text communication platform that uses inexpensive LoRa radios.
%prep

View File

@@ -50,7 +50,6 @@ build_flags = -Wno-missing-field-initializers
-DRADIOLIB_EXCLUDE_APRS=1
-DRADIOLIB_EXCLUDE_LORAWAN=1
-DMESHTASTIC_EXCLUDE_DROPZONE=1
-DMESHTASTIC_EXCLUDE_REPLYBOT=1
-DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1
-DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
@@ -67,7 +66,7 @@ monitor_speed = 115200
monitor_filters = direct
lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/b34c6817c25d6faabb3a8a162b5d14fb75395433.zip
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/21e484f409cde18d44012caef84c244eb5ca28f3.zip
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
@@ -121,7 +120,7 @@ lib_deps =
[device-ui_base]
lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/63967a4a557d33d56fc5746f9128200dde2d88c5.zip
https://github.com/meshtastic/device-ui/archive/6c75195e9987b7a49563232234f2f868dd343cae.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]
@@ -145,7 +144,7 @@ lib_deps =
# renovate: datasource=custom.pio depName=Adafruit INA219 packageName=adafruit/library/Adafruit INA219
adafruit/Adafruit INA219@1.2.3
# renovate: datasource=custom.pio depName=Adafruit MPU6050 packageName=adafruit/library/Adafruit MPU6050
adafruit/Adafruit MPU6050@2.2.6
adafruit/Adafruit MPU6050@2.2.8
# renovate: datasource=custom.pio depName=Adafruit LIS3DH packageName=adafruit/library/Adafruit LIS3DH
adafruit/Adafruit LIS3DH@1.3.0
# renovate: datasource=custom.pio depName=Adafruit AHTX0 packageName=adafruit/library/Adafruit AHTX0

View File

@@ -1,24 +1,19 @@
#ifndef AMBIENTLIGHTINGTHREAD_H
#define AMBIENTLIGHTINGTHREAD_H
#include "Observer.h"
#include "configuration.h"
#include "detect/ScanI2C.h"
#include "sleep.h"
#ifdef HAS_NCP5623
#include <NCP5623.h>
static NCP5623 rgb;
#include <graphics/RAKled.h>
NCP5623 rgb;
#endif
#ifdef HAS_LP5562
#include <graphics/NomadStarLED.h>
static LP5562 rgbw;
LP5562 rgbw;
#endif
#ifdef HAS_NEOPIXEL
#include <Adafruit_NeoPixel.h>
static Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_DATA, NEOPIXEL_TYPE);
#include <graphics/NeoPixel.h>
Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_DATA, NEOPIXEL_TYPE);
#endif
#ifdef UNPHONE
@@ -26,10 +21,10 @@ static Adafruit_NeoPixel pixels(NEOPIXEL_COUNT, NEOPIXEL_DATA, NEOPIXEL_TYPE);
extern unPhone unphone;
#endif
namespace concurrency
{
class AmbientLightingThread : public concurrency::OSThread
{
friend class StatusLEDModule; // Let the LEDStatusModule trigger the ambient lighting for notifications and battery status.
friend class ExternalNotificationModule; // Let the ExternalNotificationModule trigger the ambient lighting for notifications.
public:
explicit AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLighting")
{
@@ -41,15 +36,14 @@ class AmbientLightingThread : public concurrency::OSThread
moduleConfig.ambient_lighting.led_state = true;
#endif
#endif
#if AMBIENT_LIGHTING_TEST
// define to enable test
moduleConfig.ambient_lighting.led_state = true;
moduleConfig.ambient_lighting.current = 10;
// Uncomment to test module
// moduleConfig.ambient_lighting.led_state = true;
// moduleConfig.ambient_lighting.current = 10;
// Default to a color based on our node number
moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
#endif
// moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
// moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
// moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
_type = type;
if (_type == ScanI2C::DeviceType::NONE) {
@@ -59,6 +53,11 @@ class AmbientLightingThread : public concurrency::OSThread
}
#endif
#ifdef HAS_RGB_LED
if (!moduleConfig.ambient_lighting.led_state) {
LOG_DEBUG("AmbientLighting Disable due to moduleConfig.ambient_lighting.led_state OFF");
disable();
return;
}
LOG_DEBUG("AmbientLighting init");
#ifdef HAS_NCP5623
if (_type == ScanI2C::NCP5623) {
@@ -78,13 +77,7 @@ class AmbientLightingThread : public concurrency::OSThread
pixels.clear(); // Set all pixel colors to 'off'
pixels.setBrightness(moduleConfig.ambient_lighting.current);
#endif
if (!moduleConfig.ambient_lighting.led_state) {
LOG_DEBUG("AmbientLighting Disable due to moduleConfig.ambient_lighting.led_state OFF");
disable();
return;
}
setLighting(moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
setLighting();
#endif
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
}
@@ -98,8 +91,7 @@ class AmbientLightingThread : public concurrency::OSThread
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
if ((_type == ScanI2C::NCP5623 || _type == ScanI2C::LP5562) && moduleConfig.ambient_lighting.led_state) {
#endif
setLighting(moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
setLighting();
return 30000; // 30 seconds to reset from any animations that may have been running from Ext. Notification
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
}
@@ -156,53 +148,65 @@ class AmbientLightingThread : public concurrency::OSThread
return 0;
}
protected:
void setLighting(float current, uint8_t red, uint8_t green, uint8_t blue)
void setLighting()
{
#ifdef HAS_NCP5623
rgb.setCurrent(current);
rgb.setRed(red);
rgb.setGreen(green);
rgb.setBlue(blue);
LOG_DEBUG("Init NCP5623 Ambient light w/ current=%d, red=%d, green=%d, blue=%d", current, red, green, blue);
rgb.setCurrent(moduleConfig.ambient_lighting.current);
rgb.setRed(moduleConfig.ambient_lighting.red);
rgb.setGreen(moduleConfig.ambient_lighting.green);
rgb.setBlue(moduleConfig.ambient_lighting.blue);
LOG_DEBUG("Init NCP5623 Ambient light w/ current=%d, red=%d, green=%d, blue=%d",
moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif
#ifdef HAS_LP5562
rgbw.setCurrent(current);
rgbw.setRed(red);
rgbw.setGreen(green);
rgbw.setBlue(blue);
LOG_DEBUG("Init LP5562 Ambient light w/ current=%d, red=%d, green=%d, blue=%d", current, red, green, blue);
rgbw.setCurrent(moduleConfig.ambient_lighting.current);
rgbw.setRed(moduleConfig.ambient_lighting.red);
rgbw.setGreen(moduleConfig.ambient_lighting.green);
rgbw.setBlue(moduleConfig.ambient_lighting.blue);
LOG_DEBUG("Init LP5562 Ambient light w/ current=%d, red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.current,
moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif
#ifdef HAS_NEOPIXEL
pixels.fill(pixels.Color(red, green, blue), 0, NEOPIXEL_COUNT);
pixels.fill(pixels.Color(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
moduleConfig.ambient_lighting.blue),
0, NEOPIXEL_COUNT);
// RadioMaster Bandit has addressable LED at the two buttons
// this allow us to set different lighting for them in variant.h file.
#ifdef RADIOMASTER_900_BANDIT
#if defined(BUTTON1_COLOR) && defined(BUTTON1_COLOR_INDEX)
pixels.fill(BUTTON1_COLOR, BUTTON1_COLOR_INDEX, 1);
#endif
#if defined(BUTTON2_COLOR) && defined(BUTTON2_COLOR_INDEX)
pixels.fill(BUTTON2_COLOR, BUTTON2_COLOR_INDEX, 1);
#endif
#endif
pixels.show();
// LOG_DEBUG("Init NeoPixel Ambient light w/ brightness(current)=%d, red=%d, green=%d, blue=%d",
// current, red, green, blue);
// moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
// moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif
#ifdef RGBLED_CA
analogWrite(RGBLED_RED, 255 - red);
analogWrite(RGBLED_GREEN, 255 - green);
analogWrite(RGBLED_BLUE, 255 - blue);
LOG_DEBUG("Init Ambient light RGB Common Anode w/ red=%d, green=%d, blue=%d", red, green, blue);
analogWrite(RGBLED_RED, 255 - moduleConfig.ambient_lighting.red);
analogWrite(RGBLED_GREEN, 255 - moduleConfig.ambient_lighting.green);
analogWrite(RGBLED_BLUE, 255 - moduleConfig.ambient_lighting.blue);
LOG_DEBUG("Init Ambient light RGB Common Anode w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red,
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#elif defined(RGBLED_RED)
analogWrite(RGBLED_RED, red);
analogWrite(RGBLED_GREEN, green);
analogWrite(RGBLED_BLUE, blue);
LOG_DEBUG("Init Ambient light RGB Common Cathode w/ red=%d, green=%d, blue=%d", red, green, blue);
analogWrite(RGBLED_RED, moduleConfig.ambient_lighting.red);
analogWrite(RGBLED_GREEN, moduleConfig.ambient_lighting.green);
analogWrite(RGBLED_BLUE, moduleConfig.ambient_lighting.blue);
LOG_DEBUG("Init Ambient light RGB Common Cathode w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red,
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif
#ifdef UNPHONE
unphone.rgb(red, green, blue);
LOG_DEBUG("Init unPhone Ambient light w/ red=%d, green=%d, blue=%d", red, green, blue);
unphone.rgb(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
moduleConfig.ambient_lighting.blue);
LOG_DEBUG("Init unPhone Ambient light w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red,
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif
}
};
#endif // AMBIENTLIGHTINGTHREAD_H
} // namespace concurrency

View File

@@ -89,14 +89,22 @@ class BluetoothStatus : public Status
case ConnectionState::CONNECTED:
LOG_DEBUG("BluetoothStatus CONNECTED");
#ifdef BLE_LED
digitalWrite(BLE_LED, LED_STATE_ON);
#ifdef BLE_LED_INVERTED
digitalWrite(BLE_LED, LOW);
#else
digitalWrite(BLE_LED, HIGH);
#endif
#endif
break;
case ConnectionState::DISCONNECTED:
LOG_DEBUG("BluetoothStatus DISCONNECTED");
#ifdef BLE_LED
digitalWrite(BLE_LED, LED_STATE_OFF);
#ifdef BLE_LED_INVERTED
digitalWrite(BLE_LED, HIGH);
#else
digitalWrite(BLE_LED, LOW);
#endif
#endif
break;
}

66
src/Led.cpp Normal file
View File

@@ -0,0 +1,66 @@
#include "Led.h"
#include "PowerMon.h"
#include "main.h"
#include "power.h"
GpioVirtPin ledForceOn, ledBlink;
#if defined(LED_PIN)
// Most boards have a GPIO for LED control
static GpioHwPin ledRawHwPin(LED_PIN);
#else
static GpioVirtPin ledRawHwPin; // Dummy pin for no hardware
#endif
#if LED_STATE_ON == 0
static GpioVirtPin ledHwPin;
static GpioNotTransformer ledInverter(&ledHwPin, &ledRawHwPin);
#else
static GpioPin &ledHwPin = ledRawHwPin;
#endif
#if defined(HAS_PMU)
/**
* A GPIO controlled by the PMU
*/
class GpioPmuPin : public GpioPin
{
public:
void set(bool value)
{
if (pmu_found && PMU) {
// blink the axp led
PMU->setChargingLedMode(value ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
}
}
} ledPmuHwPin;
// In some cases we need to drive a PMU LED and a normal LED
static GpioSplitter ledFinalPin(&ledHwPin, &ledPmuHwPin);
#else
static GpioPin &ledFinalPin = ledHwPin;
#endif
#ifdef USE_POWERMON
/**
* We monitor changes to the LED drive output because we use that as a sanity test in our power monitor stuff.
*/
class MonitoredLedPin : public GpioPin
{
public:
void set(bool value)
{
if (powerMon) {
if (value)
powerMon->setState(meshtastic_PowerMon_State_LED_On);
else
powerMon->clearState(meshtastic_PowerMon_State_LED_On);
}
ledFinalPin.set(value);
}
} monitoredLedPin;
#else
static GpioPin &monitoredLedPin = ledFinalPin;
#endif
static GpioBinaryTransformer ledForcer(&ledForceOn, &ledBlink, &monitoredLedPin, GpioBinaryTransformer::Or);

7
src/Led.h Normal file
View File

@@ -0,0 +1,7 @@
#include "GpioLogic.h"
#include "configuration.h"
/**
* ledForceOn and ledForceOff both override the normal ledBlinker behavior (which is controlled by main)
*/
extern GpioVirtPin ledForceOn, ledBlink;

View File

@@ -459,8 +459,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
// if it's not HIGH - check the battery
#endif
// If we have an EXT_PWR_DETECT pin and it indicates no external power, believe it.
return false;
// technically speaking this should work for all(?) NRF52 boards
// but needs testing across multiple devices. NRF52 USB would not even work if

View File

@@ -9,13 +9,13 @@
*/
#include "PowerFSM.h"
#include "Default.h"
#include "Led.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerMon.h"
#include "configuration.h"
#include "graphics/Screen.h"
#include "main.h"
#include "modules/StatusLEDModule.h"
#include "sleep.h"
#include "target_specific.h"
@@ -103,7 +103,7 @@ static void lsIdle()
uint32_t sleepTime = SLEEP_TIME;
powerMon->setState(meshtastic_PowerMon_State_CPU_LightSleep);
statusLEDModule->setPowerLED(false);
ledBlink.set(false); // Never leave led on while in light sleep
esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL);
powerMon->clearState(meshtastic_PowerMon_State_CPU_LightSleep);
@@ -111,7 +111,7 @@ static void lsIdle()
case ESP_SLEEP_WAKEUP_TIMER:
// Normal case: timer expired, we should just go back to sleep ASAP
statusLEDModule->setPowerLED(true);
ledBlink.set(true); // briefly turn on led
wakeCause2 = doLightSleep(100); // leave led on for 1ms
secsSlept += sleepTime;
@@ -146,7 +146,7 @@ static void lsIdle()
}
} else {
// Time to stop sleeping!
statusLEDModule->setPowerLED(false);
ledBlink.set(false);
LOG_INFO("Reached ls_secs, service loop()");
powerFSM.trigger(EVENT_WAKE_TIMER);
}

View File

@@ -72,11 +72,13 @@ RTCSetResult readFromRTC()
#elif defined(PCF8563_RTC) || defined(PCF85063_RTC)
#if defined(PCF8563_RTC)
if (rtc_found.address == PCF8563_RTC) {
SensorPCF8563 rtc;
#elif defined(PCF85063_RTC)
if (rtc_found.address == PCF85063_RTC) {
SensorPCF85063 rtc;
#endif
uint32_t now = millis();
SensorRtcHelper rtc;
#if WIRE_INTERFACES_COUNT == 2
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);
@@ -240,10 +242,12 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
#elif defined(PCF8563_RTC) || defined(PCF85063_RTC)
#if defined(PCF8563_RTC)
if (rtc_found.address == PCF8563_RTC) {
SensorPCF8563 rtc;
#elif defined(PCF85063_RTC)
if (rtc_found.address == PCF85063_RTC) {
SensorPCF85063 rtc;
#endif
SensorRtcHelper rtc;
#if WIRE_INTERFACES_COUNT == 2
rtc.begin(rtc_found.port == ScanI2C::I2CPort::WIRE1 ? Wire1 : Wire);

4
src/graphics/NeoPixel.h Normal file
View File

@@ -0,0 +1,4 @@
#ifdef HAS_NEOPIXEL
#include <Adafruit_NeoPixel.h>
extern Adafruit_NeoPixel pixels;
#endif

5
src/graphics/RAKled.h Normal file
View File

@@ -0,0 +1,5 @@
#ifdef HAS_NCP5623
#include <NCP5623.h>
extern NCP5623 rgb;
#endif

View File

@@ -221,6 +221,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
if (rtc_sec > 0) {
// === Build Time String ===
long hms = (rtc_sec % SEC_PER_DAY + SEC_PER_DAY) % SEC_PER_DAY;
int hour, minute, second;
graphics::decomposeTime(rtc_sec, hour, minute, second);
snprintf(timeStr, sizeof(timeStr), "%d:%02d", hour, minute);

View File

@@ -58,7 +58,7 @@ BannerOverlayOptions createStaticBannerOptions(const char *message, const MenuOp
} // namespace
menuHandler::screenMenus menuHandler::menuQueue = menu_none;
menuHandler::screenMenus menuHandler::menuQueue = MenuNone;
uint32_t menuHandler::pickedNodeNum = 0;
bool test_enabled = false;
uint8_t test_count = 0;
@@ -66,7 +66,7 @@ uint8_t test_count = 0;
void menuHandler::loraMenu()
{
static const char *optionsArray[] = {"Back", "Device Role", "Radio Preset", "Frequency Slot", "LoRa Region"};
enum optionsNumbers { Back = 0, device_role_picker = 1, radio_preset_picker = 2, frequency_slot = 3, lora_picker = 4 };
enum optionsNumbers { Back = 0, DeviceRolePicker = 1, RadioPresetPicker = 2, FrequencySlot = 3, LoraPicker = 4 };
BannerOverlayOptions bannerOptions;
bannerOptions.message = "LoRa Actions";
bannerOptions.optionsArrayPtr = optionsArray;
@@ -74,14 +74,14 @@ void menuHandler::loraMenu()
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) {
// No action
} else if (selected == device_role_picker) {
menuHandler::menuQueue = menuHandler::device_role_picker;
} else if (selected == radio_preset_picker) {
menuHandler::menuQueue = menuHandler::radio_preset_picker;
} else if (selected == frequency_slot) {
menuHandler::menuQueue = menuHandler::frequency_slot;
} else if (selected == lora_picker) {
menuHandler::menuQueue = menuHandler::lora_picker;
} else if (selected == DeviceRolePicker) {
menuHandler::menuQueue = menuHandler::DeviceRolePicker;
} else if (selected == RadioPresetPicker) {
menuHandler::menuQueue = menuHandler::RadioPresetPicker;
} else if (selected == FrequencySlot) {
menuHandler::menuQueue = menuHandler::FrequencySlot;
} else if (selected == LoraPicker) {
menuHandler::menuQueue = menuHandler::LoraPicker;
}
};
screen->showOverlayBanner(bannerOptions);
@@ -102,7 +102,7 @@ void menuHandler::OnboardMessage()
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 2;
bannerOptions.bannerCallback = [](int selected) -> void {
menuHandler::menuQueue = menuHandler::no_timeout_lora_picker;
menuHandler::menuQueue = menuHandler::NoTimeoutLoraPicker;
screen->runNow();
};
screen->showOverlayBanner(bannerOptions);
@@ -216,7 +216,7 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::DeviceRolePicker()
void menuHandler::deviceRolePicker()
{
static const char *optionsArray[] = {"Back", "Client", "Client Mute", "Lost and Found", "Tracker"};
enum optionsNumbers {
@@ -232,7 +232,7 @@ void menuHandler::DeviceRolePicker()
bannerOptions.optionsCount = 5;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) {
menuHandler::menuQueue = menuHandler::lora_Menu;
menuHandler::menuQueue = menuHandler::LoraMenu;
screen->runNow();
return;
} else if (selected == devicerole_client) {
@@ -300,7 +300,7 @@ void menuHandler::FrequencySlotPicker()
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) {
menuHandler::menuQueue = menuHandler::lora_Menu;
menuHandler::menuQueue = menuHandler::LoraMenu;
screen->runNow();
return;
}
@@ -313,7 +313,7 @@ void menuHandler::FrequencySlotPicker()
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::RadioPresetPicker()
void menuHandler::radioPresetPicker()
{
static const RadioPresetOption presetOptions[] = {
{"Back", OptionsAction::Back},
@@ -333,7 +333,7 @@ void menuHandler::RadioPresetPicker()
auto bannerOptions =
createStaticBannerOptions("Radio Preset", presetOptions, presetLabels, [](const RadioPresetOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuHandler::menuQueue = menuHandler::lora_Menu;
menuHandler::menuQueue = menuHandler::LoraMenu;
screen->runNow();
return;
}
@@ -352,7 +352,7 @@ void menuHandler::RadioPresetPicker()
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::TwelveHourPicker()
void menuHandler::twelveHourPicker()
{
static const char *optionsArray[] = {"Back", "12-hour", "24-hour"};
enum optionsNumbers { Back = 0, twelve = 1, twentyfour = 2 };
@@ -362,7 +362,7 @@ void menuHandler::TwelveHourPicker()
bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) {
menuHandler::menuQueue = menuHandler::clock_menu;
menuHandler::menuQueue = menuHandler::ClockMenu;
screen->runNow();
} else if (selected == twelve) {
config.display.use_12h_clock = true;
@@ -390,7 +390,7 @@ void menuHandler::showConfirmationBanner(const char *message, std::function<void
screen->showOverlayBanner(confirmBanner);
}
void menuHandler::ClockFacePicker()
void menuHandler::clockFacePicker()
{
static const ClockFaceOption clockFaceOptions[] = {
{"Back", OptionsAction::Back},
@@ -404,7 +404,7 @@ void menuHandler::ClockFacePicker()
auto bannerOptions = createStaticBannerOptions("Which Face?", clockFaceOptions, clockFaceLabels,
[](const ClockFaceOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuHandler::menuQueue = menuHandler::clock_menu;
menuHandler::menuQueue = menuHandler::ClockMenu;
screen->runNow();
return;
}
@@ -456,7 +456,7 @@ void menuHandler::TZPicker()
auto bannerOptions = createStaticBannerOptions(
"Pick Timezone", timezoneOptions, timezoneLabels, [](const TimezoneOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuHandler::menuQueue = menuHandler::clock_menu;
menuHandler::menuQueue = menuHandler::ClockMenu;
screen->runNow();
return;
}
@@ -503,13 +503,13 @@ void menuHandler::clockMenu()
bannerOptions.optionsCount = 4;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Clock) {
menuHandler::menuQueue = menuHandler::clock_face_picker;
menuHandler::menuQueue = menuHandler::ClockFacePicker;
screen->runNow();
} else if (selected == Time) {
menuHandler::menuQueue = menuHandler::twelve_hour_picker;
menuHandler::menuQueue = menuHandler::TwelveHourPicker;
screen->runNow();
} else if (selected == Timezone) {
menuHandler::menuQueue = menuHandler::TZ_picker;
menuHandler::menuQueue = menuHandler::TzPicker;
screen->runNow();
}
};
@@ -572,12 +572,12 @@ void menuHandler::messageResponseMenu()
LOG_DEBUG("[ReplyCtx] mode=%d ch=%d peer=0x%08x", (int)mode, ch, (unsigned int)peer);
if (selected == ViewMode) {
menuHandler::menuQueue = menuHandler::message_viewmode_menu;
menuHandler::menuQueue = menuHandler::MessageViewModeMenu;
screen->runNow();
// Reply submenu
} else if (selected == ReplyMenu) {
menuHandler::menuQueue = menuHandler::reply_menu;
menuHandler::menuQueue = menuHandler::ReplyMenu;
screen->runNow();
} else if (selected == MuteChannel) {
@@ -589,7 +589,7 @@ void menuHandler::messageResponseMenu()
}
} else if (selected == DeleteMenu) {
menuHandler::menuQueue = menuHandler::delete_messages_menu;
menuHandler::menuQueue = menuHandler::DeleteMessagesMenu;
screen->runNow();
#ifdef HAS_I2S
@@ -649,7 +649,7 @@ void menuHandler::replyMenu()
uint32_t peer = graphics::MessageRenderer::getThreadPeer();
if (selected == Back) {
menuHandler::menuQueue = menuHandler::message_response_menu;
menuHandler::menuQueue = menuHandler::MessageResponseMenu;
screen->runNow();
return;
}
@@ -737,7 +737,7 @@ void menuHandler::deleteMessagesMenu()
uint32_t peer = graphics::MessageRenderer::getThreadPeer();
if (selected == Back) {
menuHandler::menuQueue = menuHandler::message_response_menu;
menuHandler::menuQueue = menuHandler::MessageResponseMenu;
screen->runNow();
return;
}
@@ -901,7 +901,7 @@ void menuHandler::messageViewModeMenu()
bannerOptions.bannerCallback = [=](int selected) -> void {
LOG_DEBUG("messageViewModeMenu: selected=%d", selected);
if (selected == -1) {
menuHandler::menuQueue = menuHandler::message_response_menu;
menuHandler::menuQueue = menuHandler::MessageResponseMenu;
screen->runNow();
} else if (selected == -2) {
graphics::MessageRenderer::setThreadMode(graphics::MessageRenderer::ThreadMode::ALL);
@@ -1083,23 +1083,23 @@ void menuHandler::systemBaseMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Notifications) {
menuHandler::menuQueue = menuHandler::buzzermodemenupicker;
menuHandler::menuQueue = menuHandler::BuzzerModeMenuPicker;
screen->runNow();
} else if (selected == ScreenOptions) {
menuHandler::menuQueue = menuHandler::screen_options_menu;
menuHandler::menuQueue = menuHandler::ScreenOptionsMenu;
screen->runNow();
} else if (selected == PowerMenu) {
menuHandler::menuQueue = menuHandler::power_menu;
menuHandler::menuQueue = menuHandler::PowerMenu;
screen->runNow();
} else if (selected == Test) {
menuHandler::menuQueue = menuHandler::test_menu;
menuHandler::menuQueue = menuHandler::TestMenu;
screen->runNow();
} else if (selected == Bluetooth) {
menuQueue = bluetooth_toggle_menu;
menuQueue = BluetoothToggleMenu;
screen->runNow();
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
} else if (selected == WiFiToggle) {
menuQueue = wifi_toggle_menu;
menuQueue = WifiToggleMenu;
screen->runNow();
#endif
} else if (selected == Back && !test_enabled) {
@@ -1177,7 +1177,7 @@ void menuHandler::favoriteBaseMenu()
evt.action = UIFrameEvent::Action::SWITCH_TO_TEXTMESSAGE;
screen->handleUIFrameEvent(&evt);
} else if (selected == Remove) {
menuHandler::menuQueue = menuHandler::remove_favorite;
menuHandler::menuQueue = menuHandler::RemoveFavorite;
screen->runNow();
} else if (selected == TraceRoute) {
if (traceRouteModule) {
@@ -1238,15 +1238,15 @@ void menuHandler::positionBaseMenu()
auto action = static_cast<PositionAction>(option.value);
switch (action) {
case PositionAction::GpsToggle:
menuQueue = gps_toggle_menu;
menuQueue = GpsToggleMenu;
screen->runNow();
break;
case PositionAction::GpsFormat:
menuQueue = gps_format_menu;
menuQueue = GpsFormatMenu;
screen->runNow();
break;
case PositionAction::CompassMenu:
menuQueue = compass_point_north_menu;
menuQueue = CompassPointNorthMenu;
screen->runNow();
break;
case PositionAction::CompassCalibrate:
@@ -1255,15 +1255,15 @@ void menuHandler::positionBaseMenu()
}
break;
case PositionAction::GPSSmartPosition:
menuQueue = gps_smart_position_menu;
menuQueue = GpsSmartPositionMenu;
screen->runNow();
break;
case PositionAction::GPSUpdateInterval:
menuQueue = gps_update_interval_menu;
menuQueue = GpsUpdateIntervalMenu;
screen->runNow();
break;
case PositionAction::GPSPositionBroadcast:
menuQueue = gps_position_broadcast_menu;
menuQueue = GpsPositionBroadcastMenu;
screen->runNow();
break;
}
@@ -1303,13 +1303,13 @@ void menuHandler::nodeListMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == NodePicker) {
menuQueue = NodePicker_menu;
menuQueue = NodePickerMenu;
screen->runNow();
} else if (selected == Reset) {
menuQueue = reset_node_db_menu;
menuQueue = ResetNodeDbMenu;
screen->runNow();
} else if (selected == NodeNameLength) {
menuHandler::menuQueue = menuHandler::node_name_length_menu;
menuHandler::menuQueue = menuHandler::NodeNameLengthMenu;
screen->runNow();
}
};
@@ -1330,12 +1330,12 @@ void menuHandler::NodePicker()
menuHandler::pickedNodeNum = nodenum;
// Keep UI favorite context in sync (used elsewhere for some node-based actions)
graphics::UIRenderer::currentFavoriteNodeNum = nodenum;
menuQueue = Manage_Node_menu;
menuQueue = ManageNodeMenu;
screen->runNow();
});
}
void menuHandler::ManageNodeMenu()
void menuHandler::manageNodeMenu()
{
// If we don't have a node selected yet, go fast exit
auto node = nodeDB->getMeshNode(menuHandler::pickedNodeNum);
@@ -1391,7 +1391,7 @@ void menuHandler::ManageNodeMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) {
menuQueue = node_base_menu;
menuQueue = NodeBaseMenu;
screen->runNow();
return;
}
@@ -1483,7 +1483,7 @@ void menuHandler::nodeNameLengthMenu()
auto bannerOptions = createStaticBannerOptions("Node Name Length", nodeNameOptions, nodeNameLabels,
[](const NodeNameOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuQueue = node_base_menu;
menuQueue = NodeBaseMenu;
screen->runNow();
return;
}
@@ -1498,6 +1498,7 @@ void menuHandler::nodeNameLengthMenu()
config.display.use_long_node_name = option.value;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
LOG_INFO("Setting names to %s", option.value ? "long" : "short");
});
@@ -1528,7 +1529,7 @@ void menuHandler::resetNodeDBMenu()
nodeDB->resetNodes(1);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
} else if (selected == 0) {
menuQueue = node_base_menu;
menuQueue = NodeBaseMenu;
screen->runNow();
}
};
@@ -1550,7 +1551,7 @@ void menuHandler::compassNorthMenu()
auto bannerOptions = createStaticBannerOptions("North Directions?", compassOptions, compassLabels,
[](const CompassOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuQueue = position_base_menu;
menuQueue = PositionBaseMenu;
screen->runNow();
return;
}
@@ -1595,7 +1596,7 @@ void menuHandler::GPSToggleMenu()
auto bannerOptions =
createStaticBannerOptions("Toggle GPS", gpsToggleOptions, toggleLabels, [](const GPSToggleOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuQueue = position_base_menu;
menuQueue = PositionBaseMenu;
screen->runNow();
return;
}
@@ -1660,7 +1661,7 @@ void menuHandler::GPSFormatMenu()
auto onSelection = [](const GPSFormatOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuQueue = position_base_menu;
menuQueue = PositionBaseMenu;
screen->runNow();
return;
}
@@ -1715,7 +1716,7 @@ void menuHandler::GPSSmartPositionMenu()
bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) {
menuQueue = position_base_menu;
menuQueue = PositionBaseMenu;
screen->runNow();
} else if (selected == 1) {
config.position.position_broadcast_smart_enabled = true;
@@ -1744,7 +1745,7 @@ void menuHandler::GPSUpdateIntervalMenu()
bannerOptions.optionsCount = 16;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) {
menuQueue = position_base_menu;
menuQueue = PositionBaseMenu;
screen->runNow();
} else if (selected == 1) {
config.position.gps_update_interval = 8;
@@ -1832,7 +1833,7 @@ void menuHandler::GPSPositionBroadcastMenu()
bannerOptions.optionsCount = 17;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) {
menuQueue = position_base_menu;
menuQueue = PositionBaseMenu;
screen->runNow();
} else if (selected == 1) {
config.position.position_broadcast_secs = 60;
@@ -1915,7 +1916,7 @@ void menuHandler::GPSPositionBroadcastMenu()
#endif
void menuHandler::BluetoothToggleMenu()
void menuHandler::bluetoothToggleMenu()
{
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
BannerOverlayOptions bannerOptions;
@@ -2043,7 +2044,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
auto bannerOptions = createStaticBannerOptions(
"Select Screen Color", colorOptions, colorLabels, [display](const ScreenColorOption &option, int) -> void {
if (option.action == OptionsAction::Back) {
menuQueue = system_base_menu;
menuQueue = SystemBaseMenu;
screen->runNow();
return;
}
@@ -2138,7 +2139,7 @@ void menuHandler::rebootMenu()
messageStore.saveToFlash();
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
} else {
menuQueue = power_menu;
menuQueue = PowerMenu;
screen->runNow();
}
};
@@ -2160,7 +2161,7 @@ void menuHandler::shutdownMenu()
InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SHUTDOWN, .kbchar = 0, .touchX = 0, .touchY = 0};
inputBroker->injectInputEvent(&event);
} else {
menuQueue = power_menu;
menuQueue = PowerMenu;
screen->runNow();
}
};
@@ -2221,14 +2222,14 @@ void menuHandler::testMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == NumberPicker) {
menuQueue = number_test;
menuQueue = NumberTest;
screen->runNow();
} else if (selected == ShowChirpy) {
screen->toggleFrameVisibility("chirpy");
screen->setFrames(Screen::FOCUS_SYSTEM);
} else {
menuQueue = system_base_menu;
menuQueue = SystemBaseMenu;
screen->runNow();
}
};
@@ -2252,7 +2253,7 @@ void menuHandler::wifiBaseMenu()
bannerOptions.optionsCount = 2;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Wifi_toggle) {
menuQueue = wifi_toggle_menu;
menuQueue = WifiToggleMenu;
screen->runNow();
}
};
@@ -2301,9 +2302,9 @@ void menuHandler::screenOptionsMenu()
hasSupportBrightness = false;
#endif
enum optionsNumbers { Back, Brightness, ScreenColor, FrameToggles, DisplayUnits };
static const char *optionsArray[5] = {"Back"};
static int optionsEnumArray[5] = {Back};
enum optionsNumbers { Back, Brightness, ScreenColor, FrameToggles, DisplayUnits, MessageBubbles };
static const char *optionsArray[6] = {"Back"};
static int optionsEnumArray[6] = {Back};
int options = 1;
// Only show brightness for B&W displays
@@ -2325,6 +2326,9 @@ void menuHandler::screenOptionsMenu()
optionsArray[options] = "Display Units";
optionsEnumArray[options++] = DisplayUnits;
optionsArray[options] = "Message Bubbles";
optionsEnumArray[options++] = MessageBubbles;
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Display Options";
bannerOptions.optionsArrayPtr = optionsArray;
@@ -2332,10 +2336,10 @@ void menuHandler::screenOptionsMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Brightness) {
menuHandler::menuQueue = menuHandler::brightness_picker;
menuHandler::menuQueue = menuHandler::BrightnessPicker;
screen->runNow();
} else if (selected == ScreenColor) {
menuHandler::menuQueue = menuHandler::tftcolormenupicker;
menuHandler::menuQueue = menuHandler::TftColorMenuPicker;
screen->runNow();
} else if (selected == FrameToggles) {
menuHandler::menuQueue = menuHandler::FrameToggles;
@@ -2343,8 +2347,11 @@ void menuHandler::screenOptionsMenu()
} else if (selected == DisplayUnits) {
menuHandler::menuQueue = menuHandler::DisplayUnits;
screen->runNow();
} else if (selected == MessageBubbles) {
menuHandler::menuQueue = menuHandler::MessageBubblesMenu;
screen->runNow();
} else {
menuQueue = system_base_menu;
menuQueue = SystemBaseMenu;
screen->runNow();
}
};
@@ -2380,16 +2387,16 @@ void menuHandler::powerMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Reboot) {
menuHandler::menuQueue = menuHandler::reboot_menu;
menuHandler::menuQueue = menuHandler::RebootMenu;
screen->runNow();
} else if (selected == Shutdown) {
menuHandler::menuQueue = menuHandler::shutdown_menu;
menuHandler::menuQueue = menuHandler::ShutdownMenu;
screen->runNow();
} else if (selected == MUI) {
menuHandler::menuQueue = menuHandler::mui_picker;
menuHandler::menuQueue = menuHandler::MuiPicker;
screen->runNow();
} else {
menuQueue = system_base_menu;
menuQueue = SystemBaseMenu;
screen->runNow();
}
};
@@ -2427,7 +2434,7 @@ void menuHandler::keyVerificationFinalPrompt()
}
}
void menuHandler::FrameToggles_menu()
void menuHandler::frameTogglesMenu()
{
enum optionsNumbers {
Finish,
@@ -2571,7 +2578,7 @@ void menuHandler::FrameToggles_menu()
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::DisplayUnits_menu()
void menuHandler::displayUnitsMenu()
{
enum optionsNumbers { Back, MetricUnits, ImperialUnits };
@@ -2592,7 +2599,34 @@ void menuHandler::DisplayUnits_menu()
config.display.units = meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL;
service->reloadConfig(SEGMENT_CONFIG);
} else {
menuHandler::menuQueue = menuHandler::screen_options_menu;
menuHandler::menuQueue = menuHandler::ScreenOptionsMenu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::messageBubblesMenu()
{
enum optionsNumbers { Back, ShowBubbles, HideBubbles };
static const char *optionsArray[] = {"Back", "Show Bubbles", "Hide Bubbles"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Message Bubbles";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.InitialSelected = config.display.enable_message_bubbles ? 1 : 2;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == ShowBubbles) {
config.display.enable_message_bubbles = true;
service->reloadConfig(SEGMENT_CONFIG);
LOG_INFO("Message bubbles enabled");
} else if (selected == HideBubbles) {
config.display.enable_message_bubbles = false;
service->reloadConfig(SEGMENT_CONFIG);
LOG_INFO("Message bubbles disabled");
} else {
menuHandler::menuQueue = menuHandler::ScreenOptionsMenu;
screen->runNow();
}
};
@@ -2601,153 +2635,156 @@ void menuHandler::DisplayUnits_menu()
void menuHandler::handleMenuSwitch(OLEDDisplay *display)
{
if (menuQueue != menu_none)
if (menuQueue != MenuNone)
test_count = 0;
switch (menuQueue) {
case menu_none:
case MenuNone:
break;
case lora_Menu:
case LoraMenu:
loraMenu();
break;
case lora_picker:
case LoraPicker:
LoraRegionPicker();
break;
case device_role_picker:
DeviceRolePicker();
case DeviceRolePicker:
deviceRolePicker();
break;
case radio_preset_picker:
RadioPresetPicker();
case RadioPresetPicker:
radioPresetPicker();
break;
case frequency_slot:
case FrequencySlot:
FrequencySlotPicker();
break;
case no_timeout_lora_picker:
case NoTimeoutLoraPicker:
LoraRegionPicker(0);
break;
case TZ_picker:
case TzPicker:
TZPicker();
break;
case twelve_hour_picker:
TwelveHourPicker();
case TwelveHourPicker:
twelveHourPicker();
break;
case clock_face_picker:
ClockFacePicker();
case ClockFacePicker:
clockFacePicker();
break;
case clock_menu:
case ClockMenu:
clockMenu();
break;
case system_base_menu:
case SystemBaseMenu:
systemBaseMenu();
break;
case position_base_menu:
case PositionBaseMenu:
positionBaseMenu();
break;
case node_base_menu:
case NodeBaseMenu:
nodeListMenu();
break;
#if !MESHTASTIC_EXCLUDE_GPS
case gps_toggle_menu:
case GpsToggleMenu:
GPSToggleMenu();
break;
case gps_format_menu:
case GpsFormatMenu:
GPSFormatMenu();
break;
case gps_smart_position_menu:
case GpsSmartPositionMenu:
GPSSmartPositionMenu();
break;
case gps_update_interval_menu:
case GpsUpdateIntervalMenu:
GPSUpdateIntervalMenu();
break;
case gps_position_broadcast_menu:
case GpsPositionBroadcastMenu:
GPSPositionBroadcastMenu();
break;
#endif
case compass_point_north_menu:
case CompassPointNorthMenu:
compassNorthMenu();
break;
case reset_node_db_menu:
case ResetNodeDbMenu:
resetNodeDBMenu();
break;
case buzzermodemenupicker:
case BuzzerModeMenuPicker:
BuzzerModeMenu();
break;
case mui_picker:
case MuiPicker:
switchToMUIMenu();
break;
case tftcolormenupicker:
case TftColorMenuPicker:
TFTColorPickerMenu(display);
break;
case brightness_picker:
case BrightnessPicker:
BrightnessPickerMenu();
break;
case node_name_length_menu:
case NodeNameLengthMenu:
nodeNameLengthMenu();
break;
case reboot_menu:
case RebootMenu:
rebootMenu();
break;
case shutdown_menu:
case ShutdownMenu:
shutdownMenu();
break;
case NodePicker_menu:
case NodePickerMenu:
NodePicker();
break;
case Manage_Node_menu:
ManageNodeMenu();
case ManageNodeMenu:
manageNodeMenu();
break;
case remove_favorite:
case RemoveFavorite:
removeFavoriteMenu();
break;
case trace_route_menu:
case TraceRouteMenu:
traceRouteMenu();
break;
case test_menu:
case TestMenu:
testMenu();
break;
case number_test:
case NumberTest:
numberTest();
break;
case wifi_toggle_menu:
case WifiToggleMenu:
wifiToggleMenu();
break;
case key_verification_init:
case KeyVerificationInit:
keyVerificationInitMenu();
break;
case key_verification_final_prompt:
case KeyVerificationFinalPrompt:
keyVerificationFinalPrompt();
break;
case bluetooth_toggle_menu:
BluetoothToggleMenu();
case BluetoothToggleMenu:
bluetoothToggleMenu();
break;
case screen_options_menu:
case ScreenOptionsMenu:
screenOptionsMenu();
break;
case power_menu:
case PowerMenu:
powerMenu();
break;
case FrameToggles:
FrameToggles_menu();
frameTogglesMenu();
break;
case DisplayUnits:
DisplayUnits_menu();
displayUnitsMenu();
break;
case throttle_message:
case ThrottleMessage:
screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
break;
case message_response_menu:
case MessageResponseMenu:
messageResponseMenu();
break;
case reply_menu:
case ReplyMenu:
replyMenu();
break;
case delete_messages_menu:
case DeleteMessagesMenu:
deleteMessagesMenu();
break;
case message_viewmode_menu:
case MessageViewModeMenu:
messageViewModeMenu();
break;
case MessageBubblesMenu:
messageBubblesMenu();
break;
}
menuQueue = menu_none;
menuQueue = MenuNone;
}
void menuHandler::saveUIConfig()

View File

@@ -8,53 +8,54 @@ class menuHandler
{
public:
enum screenMenus {
menu_none,
lora_Menu,
lora_picker,
device_role_picker,
radio_preset_picker,
frequency_slot,
no_timeout_lora_picker,
TZ_picker,
twelve_hour_picker,
clock_face_picker,
clock_menu,
position_base_menu,
node_base_menu,
gps_toggle_menu,
gps_format_menu,
gps_smart_position_menu,
gps_update_interval_menu,
gps_position_broadcast_menu,
compass_point_north_menu,
reset_node_db_menu,
buzzermodemenupicker,
mui_picker,
tftcolormenupicker,
brightness_picker,
reboot_menu,
shutdown_menu,
NodePicker_menu,
Manage_Node_menu,
remove_favorite,
test_menu,
number_test,
wifi_toggle_menu,
bluetooth_toggle_menu,
screen_options_menu,
power_menu,
system_base_menu,
key_verification_init,
key_verification_final_prompt,
trace_route_menu,
throttle_message,
message_response_menu,
message_viewmode_menu,
reply_menu,
delete_messages_menu,
node_name_length_menu,
MenuNone,
LoraMenu,
LoraPicker,
DeviceRolePicker,
RadioPresetPicker,
FrequencySlot,
NoTimeoutLoraPicker,
TzPicker,
TwelveHourPicker,
ClockFacePicker,
ClockMenu,
PositionBaseMenu,
NodeBaseMenu,
GpsToggleMenu,
GpsFormatMenu,
GpsSmartPositionMenu,
GpsUpdateIntervalMenu,
GpsPositionBroadcastMenu,
CompassPointNorthMenu,
ResetNodeDbMenu,
BuzzerModeMenuPicker,
MuiPicker,
TftColorMenuPicker,
BrightnessPicker,
RebootMenu,
ShutdownMenu,
NodePickerMenu,
ManageNodeMenu,
RemoveFavorite,
TestMenu,
NumberTest,
WifiToggleMenu,
BluetoothToggleMenu,
ScreenOptionsMenu,
PowerMenu,
SystemBaseMenu,
KeyVerificationInit,
KeyVerificationFinalPrompt,
TraceRouteMenu,
ThrottleMessage,
MessageResponseMenu,
MessageViewModeMenu,
ReplyMenu,
DeleteMessagesMenu,
NodeNameLengthMenu,
FrameToggles,
DisplayUnits
DisplayUnits,
MessageBubblesMenu
};
static screenMenus menuQueue;
static uint32_t pickedNodeNum; // node selected by NodePicker for ManageNodeMenu
@@ -62,15 +63,15 @@ class menuHandler
static void OnboardMessage();
static void LoraRegionPicker(uint32_t duration = 30000);
static void loraMenu();
static void DeviceRolePicker();
static void RadioPresetPicker();
static void deviceRolePicker();
static void radioPresetPicker();
static void FrequencySlotPicker();
static void handleMenuSwitch(OLEDDisplay *display);
static void showConfirmationBanner(const char *message, std::function<void()> onConfirm);
static void clockMenu();
static void TZPicker();
static void TwelveHourPicker();
static void ClockFacePicker();
static void twelveHourPicker();
static void clockFacePicker();
static void messageResponseMenu();
static void messageViewModeMenu();
static void replyMenu();
@@ -95,7 +96,7 @@ class menuHandler
static void rebootMenu();
static void shutdownMenu();
static void NodePicker();
static void ManageNodeMenu();
static void manageNodeMenu();
static void addFavoriteMenu();
static void removeFavoriteMenu();
static void traceRouteMenu();
@@ -106,15 +107,16 @@ class menuHandler
static void screenOptionsMenu();
static void powerMenu();
static void nodeNameLengthMenu();
static void FrameToggles_menu();
static void DisplayUnits_menu();
static void frameTogglesMenu();
static void displayUnitsMenu();
static void messageBubblesMenu();
static void textMessageMenu();
private:
static void saveUIConfig();
static void keyVerificationInitMenu();
static void keyVerificationFinalPrompt();
static void BluetoothToggleMenu();
static void bluetoothToggleMenu();
};
/* Generic Menu Options designations */

View File

@@ -527,8 +527,12 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
constexpr int BUBBLE_MIN_W = 24;
constexpr int BUBBLE_TEXT_INDENT = 2;
// Check if bubbles are enabled
const bool showBubbles = config.display.enable_message_bubbles;
const int textIndent = showBubbles ? (BUBBLE_PAD_X + BUBBLE_TEXT_INDENT) : LEFT_MARGIN;
// Derived widths
const int leftTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - (BUBBLE_PAD_X * 2);
const int leftTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - (showBubbles ? (BUBBLE_PAD_X * 2) : 0);
const int rightTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - SCROLLBAR_WIDTH;
// Title string depending on mode
@@ -796,114 +800,105 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
}
}
// Draw bubbles
for (size_t bi = 0; bi < blocks.size(); ++bi) {
const auto &b = blocks[bi];
if (b.start >= cachedLines.size() || b.end >= cachedLines.size() || b.start > b.end)
continue;
// Draw bubbles (only if enabled)
if (showBubbles) {
for (size_t bi = 0; bi < blocks.size(); ++bi) {
const auto &b = blocks[bi];
if (b.start >= cachedLines.size() || b.end >= cachedLines.size() || b.start > b.end)
continue;
int visualTop = lineTop[b.start];
int visualTop = lineTop[b.start];
int topY;
if (isHeader[b.start]) {
// Header start
constexpr int BUBBLE_PAD_TOP_HEADER = 1; // try 1 or 2
topY = visualTop - BUBBLE_PAD_TOP_HEADER;
} else {
// Body start
bool thisLineHasEmote = false;
for (int e = 0; e < numEmotes; ++e) {
if (cachedLines[b.start].find(emotes[e].label) != std::string::npos) {
thisLineHasEmote = true;
break;
int topY;
if (isHeader[b.start]) {
// Header start
constexpr int BUBBLE_PAD_TOP_HEADER = 1; // try 1 or 2
topY = visualTop - BUBBLE_PAD_TOP_HEADER;
} else {
// Body start
bool thisLineHasEmote = false;
for (int e = 0; e < numEmotes; ++e) {
if (cachedLines[b.start].find(emotes[e].label) != std::string::npos) {
thisLineHasEmote = true;
break;
}
}
if (thisLineHasEmote) {
constexpr int EMOTE_PADDING_ABOVE = 4;
visualTop -= EMOTE_PADDING_ABOVE;
}
topY = visualTop - BUBBLE_PAD_Y;
}
if (thisLineHasEmote) {
constexpr int EMOTE_PADDING_ABOVE = 4;
visualTop -= EMOTE_PADDING_ABOVE;
int visualBottom = getDrawnLinePixelBottom(lineTop[b.end], cachedLines[b.end], isHeader[b.end]);
int bottomY = visualBottom + BUBBLE_PAD_Y;
if (bi + 1 < blocks.size()) {
int nextHeaderIndex = (int)blocks[bi + 1].start;
int nextTop = lineTop[nextHeaderIndex];
int maxBottom = nextTop - 1 - bubbleGapY;
if (bottomY > maxBottom)
bottomY = maxBottom;
}
topY = visualTop - BUBBLE_PAD_Y;
}
int visualBottom = getDrawnLinePixelBottom(lineTop[b.end], cachedLines[b.end], isHeader[b.end]);
int bottomY = visualBottom + BUBBLE_PAD_Y;
if (bi + 1 < blocks.size()) {
int nextHeaderIndex = (int)blocks[bi + 1].start;
int nextTop = lineTop[nextHeaderIndex];
int maxBottom = nextTop - 1 - bubbleGapY;
if (bottomY > maxBottom)
bottomY = maxBottom;
}
if (bottomY <= topY + 2)
continue;
if (bottomY <= topY + 2)
continue;
if (bottomY < contentTop || topY > contentBottom - 1)
continue;
if (bottomY < contentTop || topY > contentBottom - 1)
continue;
int maxLineW = 0;
int maxLineW = 0;
for (size_t i = b.start; i <= b.end; ++i) {
int w = 0;
if (isHeader[i]) {
w = display->getStringWidth(cachedLines[i].c_str());
if (b.mine)
w += 12; // room for ACK/NACK/relay mark
} else {
w = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes);
for (size_t i = b.start; i <= b.end; ++i) {
int w = 0;
if (isHeader[i]) {
w = display->getStringWidth(cachedLines[i].c_str());
if (b.mine)
w += 12; // room for ACK/NACK/relay mark
} else {
w = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes);
}
if (w > maxLineW)
maxLineW = w;
}
if (w > maxLineW)
maxLineW = w;
}
int bubbleW = std::max(BUBBLE_MIN_W, maxLineW + (BUBBLE_PAD_X * 2));
int bubbleH = (bottomY - topY) + 1;
int bubbleX = 0;
if (b.mine) {
bubbleX = rightEdge - bubbleW;
} else {
bubbleX = x;
}
if (bubbleX < x)
bubbleX = x;
if (bubbleX + bubbleW > rightEdge)
bubbleW = std::max(1, rightEdge - bubbleX);
if (bubbleW > 1 && bubbleH > 1) {
int x1 = bubbleX + bubbleW - 1;
int y1 = topY + bubbleH - 1;
int bubbleW = std::max(BUBBLE_MIN_W, maxLineW + (textIndent * 2));
int bubbleH = (bottomY - topY) + 1;
int bubbleX = 0;
if (b.mine) {
// Send Message (Right side)
display->drawRect(x1 + 2 - bubbleW, y1 - bubbleH, bubbleW, bubbleH);
// Top Right Corner
display->drawRect(x1 - 1, topY, 2, 1);
display->drawRect(x1, topY, 1, 2);
// Bottom Right Corner
display->drawRect(x1 - 1, bottomY - 2, 2, 1);
display->drawRect(x1, bottomY - 3, 1, 2);
// Knock the corners off to make a bubble
display->setColor(BLACK);
display->drawRect(x1 - bubbleW + 2, topY - 1, 1, 1);
display->drawRect(x1 - bubbleW + 2, bottomY - 1, 1, 1);
display->setColor(WHITE);
bubbleX = rightEdge - bubbleW;
} else {
// Received Message (Left Side)
display->drawRect(bubbleX, topY, bubbleW + 1, bubbleH);
// Top Left Corner
display->drawRect(bubbleX + 1, topY + 1, 2, 1);
display->drawRect(bubbleX + 1, topY + 1, 1, 2);
// Bottom Left Corner
display->drawRect(bubbleX + 1, bottomY - 1, 2, 1);
display->drawRect(bubbleX + 1, bottomY - 2, 1, 2);
// Knock the corners off to make a bubble
display->setColor(BLACK);
display->drawRect(bubbleX + bubbleW, topY, 1, 1);
display->drawRect(bubbleX + bubbleW, bottomY, 1, 1);
display->setColor(WHITE);
bubbleX = x;
}
if (bubbleX < x)
bubbleX = x;
if (bubbleX + bubbleW > rightEdge)
bubbleW = std::max(1, rightEdge - bubbleX);
// Draw rounded rectangle bubble
if (bubbleW > BUBBLE_RADIUS * 2 && bubbleH > BUBBLE_RADIUS * 2) {
const int r = BUBBLE_RADIUS;
const int bx = bubbleX;
const int by = topY;
const int bw = bubbleW;
const int bh = bubbleH;
// Draw the 4 corner arcs using drawCircleQuads
display->drawCircleQuads(bx + r, by + r, r, 0x2); // Top-left
display->drawCircleQuads(bx + bw - r - 1, by + r, r, 0x1); // Top-right
display->drawCircleQuads(bx + r, by + bh - r - 1, r, 0x4); // Bottom-left
display->drawCircleQuads(bx + bw - r - 1, by + bh - r - 1, r, 0x8); // Bottom-right
// Draw the 4 edges between corners
display->drawHorizontalLine(bx + r, by, bw - 2 * r); // Top edge
display->drawHorizontalLine(bx + r, by + bh - 1, bw - 2 * r); // Bottom edge
display->drawVerticalLine(bx, by + r, bh - 2 * r); // Left edge
display->drawVerticalLine(bx + bw - 1, by + r, bh - 2 * r); // Right edge
} else if (bubbleW > 1 && bubbleH > 1) {
// Fallback to simple rectangle for very small bubbles
display->drawRect(bubbleX, topY, bubbleW, bubbleH);
}
}
}
} // end if (showBubbles)
// Render visible lines
int lineY = yOffset;
@@ -916,11 +911,11 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
int headerX;
if (isMine[i]) {
// push header left to avoid overlap with scrollbar
headerX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - w - BUBBLE_TEXT_INDENT;
headerX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - w - (showBubbles ? textIndent : 0);
if (headerX < LEFT_MARGIN)
headerX = LEFT_MARGIN;
} else {
headerX = x + BUBBLE_PAD_X + BUBBLE_TEXT_INDENT;
headerX = x + textIndent;
}
display->drawString(headerX, lineY, cachedLines[i].c_str());
@@ -960,14 +955,13 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
if (isMine[i]) {
// Calculate actual rendered width including emotes
int renderedWidth = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes);
int rightX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - renderedWidth - BUBBLE_TEXT_INDENT;
int rightX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - renderedWidth - (showBubbles ? textIndent : 0);
if (rightX < LEFT_MARGIN)
rightX = LEFT_MARGIN;
drawStringWithEmotes(display, rightX, lineY, cachedLines[i], emotes, numEmotes);
} else {
drawStringWithEmotes(display, x + BUBBLE_PAD_X + BUBBLE_TEXT_INDENT, lineY, cachedLines[i], emotes,
numEmotes);
drawStringWithEmotes(display, x + textIndent, lineY, cachedLines[i], emotes, numEmotes);
}
}
}

View File

@@ -1,5 +1,3 @@
#include "graphics/niche/InkHUD/Tile.h"
#include <cstdint>
#ifdef MESHTASTIC_INCLUDE_INKHUD
#include "./Applet.h"
@@ -787,16 +785,6 @@ void InkHUD::Applet::drawHeader(std::string text)
drawPixel(x, 0, BLACK);
drawPixel(x, headerDivY, BLACK); // Dotted 50%
}
// Dither near battery
if (settings->optionalFeatures.batteryIcon) {
constexpr uint16_t ditherSizePx = 4;
Tile *batteryTile = ((Applet *)inkhud->getSystemApplet("BatteryIcon"))->getTile();
const uint16_t batteryTileLeft = batteryTile->getLeft();
const uint16_t batteryTileTop = batteryTile->getTop();
const uint16_t batteryTileHeight = batteryTile->getHeight();
hatchRegion(batteryTileLeft - ditherSizePx, batteryTileTop, ditherSizePx, batteryTileHeight, 2, WHITE);
}
}
// Get the height of the standard applet header

View File

@@ -48,27 +48,37 @@ int InkHUD::BatteryIconApplet::onPowerStatusUpdate(const meshtastic::Status *sta
void InkHUD::BatteryIconApplet::onRender(bool full)
{
// Clear the region beneath the tile, including the border
// Fill entire tile
// - size of icon controlled by size of tile
int16_t l = 0;
int16_t t = 0;
uint16_t w = width();
int16_t h = height();
// Clear the region beneath the tile
// Most applets are drawing onto an empty frame buffer and don't need to do this
// We do need to do this with the battery though, as it is an "overlay"
fillRect(0, 0, width(), height(), WHITE);
fillRect(l, t, w, h, WHITE);
// Vertical centerline
const int16_t m = t + (h / 2);
// =====================
// Draw battery outline
// =====================
// Positive terminal "bump"
const int16_t &bumpL = l;
const uint16_t bumpH = h / 2;
const int16_t bumpT = m - (bumpH / 2);
constexpr uint16_t bumpW = 2;
const int16_t &bumpL = 1;
const uint16_t bumpH = (height() - 2) / 2;
const int16_t bumpT = (1 + ((height() - 2) / 2)) - (bumpH / 2);
fillRect(bumpL, bumpT, bumpW, bumpH, BLACK);
// Main body of battery
const int16_t bodyL = 1 + bumpW;
const int16_t &bodyT = 1;
const int16_t &bodyH = height() - 2; // Handle top/bottom padding
const int16_t bodyW = (width() - 1) - bumpW; // Handle 1px left pad
const int16_t bodyL = bumpL + bumpW;
const int16_t &bodyT = t;
const int16_t &bodyH = h;
const int16_t bodyW = w - bumpW;
drawRect(bodyL, bodyT, bodyW, bodyH, BLACK);
// Erase join between bump and body
@@ -79,13 +89,12 @@ void InkHUD::BatteryIconApplet::onRender(bool full)
// ===================
constexpr int16_t slicePad = 2;
int16_t sliceL = bodyL + slicePad;
const int16_t sliceL = bodyL + slicePad;
const int16_t sliceT = bodyT + slicePad;
const uint16_t sliceH = bodyH - (slicePad * 2);
uint16_t sliceW = bodyW - (slicePad * 2);
sliceW = (sliceW * socRounded) / 100; // Apply percentage
sliceL += ((bodyW - (slicePad * 2)) - sliceW); // Shift slice to the battery's negative terminal, correcting drain direction
sliceW = (sliceW * socRounded) / 100; // Apply percentage
hatchRegion(sliceL, sliceT, sliceW, sliceH, 2, BLACK);
drawRect(sliceL, sliceT, sliceW, sliceH, BLACK);

View File

@@ -510,10 +510,10 @@ void InkHUD::WindowManager::placeSystemTiles()
const uint16_t batteryIconWidth = batteryIconHeight * 1.8;
inkhud->getSystemApplet("BatteryIcon")
->getTile()
->setRegion(inkhud->width() - batteryIconWidth - 1, // x
1, // y
batteryIconWidth + 1, // width
batteryIconHeight + 2); // height
->setRegion(inkhud->width() - batteryIconWidth, // x
2, // y
batteryIconWidth, // width
batteryIconHeight); // height
// Note: the tiles of placeholder and menu applets are manipulated specially
// - menuApplet borrows user tiles

View File

@@ -7,13 +7,13 @@
#include "NodeDB.h"
#include "PowerFSM.h"
#include "PowerMon.h"
#include "RadioLibInterface.h"
#include "ReliableRouter.h"
#include "airtime.h"
#include "buzz.h"
#include "power/PowerHAL.h"
#include "FSCommon.h"
#include "Led.h"
#include "RTC.h"
#include "SPILock.h"
#include "Throttle.h"
@@ -29,6 +29,7 @@
#include <Wire.h>
#endif
#include "detect/einkScan.h"
#include "graphics/RAKled.h"
#include "graphics/Screen.h"
#include "main.h"
#include "mesh/generated/meshtastic/config.pb.h"
@@ -192,8 +193,6 @@ bool kb_found = false;
// global bool to record that on-screen keyboard (OSK) is present
bool osk_found = false;
unsigned long last_listen = 0;
// The I2C address of the RTC Module (if found)
ScanI2C::DeviceAddress rtc_found = ScanI2C::ADDRESS_NONE;
// The I2C address of the Accelerometer (if found)
@@ -243,8 +242,26 @@ const char *getDeviceName()
return name;
}
// TODO remove from main.cpp
static int32_t ledBlinker()
{
// Still set up the blinking (heartbeat) interval but skip code path below, so LED will blink if
// config.device.led_heartbeat_disabled is changed
if (config.device.led_heartbeat_disabled)
return 1000;
static bool ledOn;
ledOn ^= 1;
ledBlink.set(ledOn);
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 1 : 1000);
}
uint32_t timeLastPowered = 0;
static Periodic *ledPeriodic;
static OSThread *powerFSMthread;
static OSThread *ambientLightingThread;
@@ -282,16 +299,21 @@ void earlyInitVariant() {}
// blink user led in 3 flashes sequence to indicate what is happening
void waitUntilPowerLevelSafe()
{
#ifdef LED_PIN
pinMode(LED_PIN, OUTPUT);
#endif
while (powerHAL_isPowerLevelSafe() == false) {
#ifdef LED_POWER
#ifdef LED_PIN
// 3x: blink for 300 ms, pause for 300 ms
for (int i = 0; i < 3; i++) {
digitalWrite(LED_POWER, LED_STATE_ON);
digitalWrite(LED_PIN, LED_STATE_ON);
delay(300);
digitalWrite(LED_POWER, LED_STATE_OFF);
digitalWrite(LED_PIN, LED_STATE_OFF);
delay(300);
}
#endif
@@ -315,11 +337,6 @@ void setup()
// initialize power HAL layer as early as possible
powerHAL_init();
#ifdef LED_POWER
pinMode(LED_POWER, OUTPUT);
digitalWrite(LED_POWER, LED_STATE_ON);
#endif
// prevent booting if device is in power failure mode
// boot sequence will follow when battery level raises to safe mode
waitUntilPowerLevelSafe();
@@ -332,6 +349,11 @@ void setup()
digitalWrite(PIN_POWER_EN, HIGH);
#endif
#ifdef LED_POWER
pinMode(LED_POWER, OUTPUT);
digitalWrite(LED_POWER, LED_STATE_ON);
#endif
#ifdef LED_NOTIFICATION
pinMode(LED_NOTIFICATION, OUTPUT);
digitalWrite(LED_NOTIFICATION, HIGH ^ LED_STATE_ON);
@@ -344,7 +366,11 @@ void setup()
#ifdef BLE_LED
pinMode(BLE_LED, OUTPUT);
digitalWrite(BLE_LED, LED_STATE_OFF);
#ifdef BLE_LED_INVERTED
digitalWrite(BLE_LED, HIGH);
#else
digitalWrite(BLE_LED, LOW);
#endif
#endif
concurrency::hasBeenSetup = true;
@@ -462,6 +488,14 @@ void setup()
OSThread::setup();
// TODO make this ifdef based on defined pins and move from main.cpp
#if defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M2)
// The ThinkNodes have their own blink logic
// ledPeriodic = new Periodic("Blink", elecrowLedBlinker);
#else
ledPeriodic = new Periodic("Blink", ledBlinker);
#endif
fsInit();
#if !MESHTASTIC_EXCLUDE_I2C
@@ -686,6 +720,13 @@ void setup()
setupSDCard();
#endif
// LED init
#ifdef LED_PIN
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LED_STATE_ON); // turn on for now
#endif
// Hello
printInfo();
#ifdef BUILD_EPOCH
@@ -793,7 +834,7 @@ void setup()
SPI.begin();
#endif
#else
// ESP32
// ESP32
#if defined(HW_SPI1_DEVICE)
SPI1.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
LOG_DEBUG("SPI1.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)", LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
@@ -888,6 +929,13 @@ void setup()
service = new MeshService();
service->init();
// Set osk_found for trackball/encoder devices BEFORE setupModules so CannedMessageModule can detect it
#if defined(HAS_TRACKBALL) || (defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2)
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true;
#endif
#endif
// Now that the mesh service is created, create any modules
setupModules();
@@ -919,6 +967,12 @@ void setup()
setupNicheGraphics();
#endif
#ifdef LED_PIN
// Turn LED off after boot, if heartbeat by config
if (config.device.led_heartbeat_disabled)
digitalWrite(LED_PIN, HIGH ^ LED_STATE_ON);
#endif
// Do this after service.init (because that clears error_code)
#ifdef HAS_PMU
if (!pmu_found)
@@ -972,12 +1026,6 @@ void setup()
#endif
#endif
#if defined(HAS_TRACKBALL) || (defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2)
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true;
#endif
#endif
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER
// Start web server thread.
webServerThread = new WebServerThread();
@@ -1121,12 +1169,6 @@ void loop()
#endif
power->powerCommandsCheck();
if (RadioLibInterface::instance != nullptr && !Throttle::isWithinTimespanMs(last_listen, 1000 * 60) &&
!(RadioLibInterface::instance->isSending() || RadioLibInterface::instance->isActivelyReceiving())) {
RadioLibInterface::instance->startReceive();
LOG_DEBUG("attempting AGC reset");
}
#ifdef DEBUG_STACK
static uint32_t lastPrint = 0;
if (!Throttle::isWithinTimespanMs(lastPrint, 10 * 1000L)) {

View File

@@ -33,7 +33,6 @@ extern ScanI2C::DeviceAddress cardkb_found;
extern uint8_t kb_model;
extern bool kb_found;
extern bool osk_found;
extern unsigned long last_listen;
extern ScanI2C::DeviceAddress rtc_found;
extern ScanI2C::DeviceAddress accelerometer_found;
extern ScanI2C::FoundDevice rgb_found;

View File

@@ -574,6 +574,10 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR;
#endif
#if defined(TFT_WIDTH) && defined(TFT_HEIGHT) && (TFT_WIDTH >= 200 || TFT_HEIGHT >= 200)
config.display.enable_message_bubbles = true;
#endif
#ifdef USERPREFS_CONFIG_DEVICE_ROLE
// Restrict ROUTER*, LOST AND FOUND roles for security reasons
if (IS_ONE_OF(USERPREFS_CONFIG_DEVICE_ROLE, meshtastic_Config_DeviceConfig_Role_ROUTER,
@@ -1404,15 +1408,6 @@ void NodeDB::loadFromDisk()
if (portduino_config.has_configDisplayMode) {
config.display.displaymode = (_meshtastic_Config_DisplayConfig_DisplayMode)portduino_config.configDisplayMode;
}
if (portduino_config.has_statusMessage) {
moduleConfig.has_statusmessage = true;
strncpy(moduleConfig.statusmessage.node_status, portduino_config.statusMessage.c_str(),
sizeof(moduleConfig.statusmessage.node_status));
moduleConfig.statusmessage.node_status[sizeof(moduleConfig.statusmessage.node_status) - 1] = '\0';
}
if (portduino_config.enable_UDP) {
config.network.enabled_protocols = true;
}
#endif
}
@@ -1553,7 +1548,6 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat)
moduleConfig.has_ambient_lighting = true;
moduleConfig.has_audio = true;
moduleConfig.has_paxcounter = true;
moduleConfig.has_statusmessage = true;
success &=
saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig);

View File

@@ -514,8 +514,6 @@ void RadioLibInterface::handleReceiveInterrupt()
void RadioLibInterface::startReceive()
{
// Note the updated timestamp, to avoid unneeded AGC resets
last_listen = millis();
isReceiving = true;
powerMon->setState(meshtastic_PowerMon_State_Lora_RXOn);
}

View File

@@ -505,6 +505,8 @@ typedef struct _meshtastic_Config_DisplayConfig {
/* If false (default), the device will use short names for various display screens.
If true, node names will show in long format */
bool use_long_node_name;
/* If true, the device will display message bubbles on screen. */
bool enable_message_bubbles;
} meshtastic_Config_DisplayConfig;
/* Lora Config */
@@ -732,7 +734,7 @@ extern "C" {
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, "", 0, 0}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0, 0}
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0, 0, 0}
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0}
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
#define meshtastic_Config_SecurityConfig_init_default {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0}
@@ -743,7 +745,7 @@ extern "C" {
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, "", 0, 0}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0, 0}
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_DeprecatedGpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN, 0, 0, 0}
#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0}
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
#define meshtastic_Config_SecurityConfig_init_zero {{0, {0}}, {0, {0}}, 0, {{0, {0}}, {0, {0}}, {0, {0}}}, 0, 0, 0, 0}
@@ -811,6 +813,7 @@ extern "C" {
#define meshtastic_Config_DisplayConfig_compass_orientation_tag 11
#define meshtastic_Config_DisplayConfig_use_12h_clock_tag 12
#define meshtastic_Config_DisplayConfig_use_long_node_name_tag 13
#define meshtastic_Config_DisplayConfig_enable_message_bubbles_tag 14
#define meshtastic_Config_LoRaConfig_use_preset_tag 1
#define meshtastic_Config_LoRaConfig_modem_preset_tag 2
#define meshtastic_Config_LoRaConfig_bandwidth_tag 3
@@ -957,7 +960,8 @@ X(a, STATIC, SINGULAR, BOOL, heading_bold, 9) \
X(a, STATIC, SINGULAR, BOOL, wake_on_tap_or_motion, 10) \
X(a, STATIC, SINGULAR, UENUM, compass_orientation, 11) \
X(a, STATIC, SINGULAR, BOOL, use_12h_clock, 12) \
X(a, STATIC, SINGULAR, BOOL, use_long_node_name, 13)
X(a, STATIC, SINGULAR, BOOL, use_long_node_name, 13) \
X(a, STATIC, SINGULAR, BOOL, enable_message_bubbles, 14)
#define meshtastic_Config_DisplayConfig_CALLBACK NULL
#define meshtastic_Config_DisplayConfig_DEFAULT NULL
@@ -1035,7 +1039,7 @@ extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg;
#define MESHTASTIC_MESHTASTIC_CONFIG_PB_H_MAX_SIZE meshtastic_Config_size
#define meshtastic_Config_BluetoothConfig_size 10
#define meshtastic_Config_DeviceConfig_size 100
#define meshtastic_Config_DisplayConfig_size 34
#define meshtastic_Config_DisplayConfig_size 36
#define meshtastic_Config_LoRaConfig_size 85
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
#define meshtastic_Config_NetworkConfig_size 204

View File

@@ -361,7 +361,7 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg;
/* Maximum encoded size of messages (where known) */
/* meshtastic_NodeDatabase_size depends on runtime parameters */
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_BackupPreferences_size
#define meshtastic_BackupPreferences_size 2362
#define meshtastic_BackupPreferences_size 2364
#define meshtastic_ChannelFile_size 718
#define meshtastic_DeviceState_size 1737
#define meshtastic_NodeInfoLite_size 196

View File

@@ -193,7 +193,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size
#define meshtastic_LocalConfig_size 749
#define meshtastic_LocalConfig_size 751
#define meshtastic_LocalModuleConfig_size 758
#ifdef __cplusplus

View File

@@ -9,6 +9,7 @@
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
#include "Led.h"
#include "SPILock.h"
#include "power.h"
#include "serialization/JSON.h"
@@ -91,6 +92,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
ResourceNode *nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload);
ResourceNode *nodeJsonScanNetworks = new ResourceNode("/json/scanNetworks", "GET", &handleScanNetworks);
ResourceNode *nodeJsonBlinkLED = new ResourceNode("/json/blink", "POST", &handleBlinkLED);
ResourceNode *nodeJsonReport = new ResourceNode("/json/report", "GET", &handleReport);
ResourceNode *nodeJsonNodes = new ResourceNode("/json/nodes", "GET", &handleNodes);
ResourceNode *nodeJsonFsBrowseStatic = new ResourceNode("/json/fs/browse/static", "GET", &handleFsBrowseStatic);
@@ -108,6 +110,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
secureServer->registerNode(nodeRestart);
secureServer->registerNode(nodeFormUpload);
secureServer->registerNode(nodeJsonScanNetworks);
secureServer->registerNode(nodeJsonBlinkLED);
secureServer->registerNode(nodeJsonFsBrowseStatic);
secureServer->registerNode(nodeJsonDelete);
secureServer->registerNode(nodeJsonReport);
@@ -130,6 +133,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
insecureServer->registerNode(nodeRestart);
insecureServer->registerNode(nodeFormUpload);
insecureServer->registerNode(nodeJsonScanNetworks);
insecureServer->registerNode(nodeJsonBlinkLED);
insecureServer->registerNode(nodeJsonFsBrowseStatic);
insecureServer->registerNode(nodeJsonDelete);
insecureServer->registerNode(nodeJsonReport);
@@ -900,6 +904,45 @@ void handleRestart(HTTPRequest *req, HTTPResponse *res)
webServerThread->requestRestart = (millis() / 1000) + 5;
}
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
{
res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "POST");
ResourceParameters *params = req->getParams();
std::string blink_target;
if (!params->getQueryParameter("blink_target", blink_target)) {
// if no blink_target was supplied in the URL parameters of the
// POST request, then assume we should blink the LED
blink_target = "LED";
}
if (blink_target == "LED") {
uint8_t count = 10;
while (count > 0) {
ledBlink.set(true);
delay(50);
ledBlink.set(false);
delay(50);
count = count - 1;
}
} else {
#if HAS_SCREEN
if (screen)
screen->blink();
#endif
}
JSONObject jsonObjOuter;
jsonObjOuter["status"] = new JSONValue("ok");
JSONValue *value = new JSONValue(jsonObjOuter);
std::string jsonString = value->Stringify();
res->print(jsonString.c_str());
delete value;
}
void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
{
res->setHeader("Content-Type", "application/json");

View File

@@ -11,6 +11,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res);
void handleScanNetworks(HTTPRequest *req, HTTPResponse *res);
void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res);
void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res);
void handleReport(HTTPRequest *req, HTTPResponse *res);
void handleNodes(HTTPRequest *req, HTTPResponse *res);
void handleUpdateFs(HTTPRequest *req, HTTPResponse *res);

View File

@@ -22,10 +22,14 @@
class UdpMulticastHandler final
{
public:
UdpMulticastHandler() { udpIpAddress = IPAddress(224, 0, 0, 69); }
UdpMulticastHandler() : isRunning(false) { udpIpAddress = IPAddress(224, 0, 0, 69); }
void start()
{
if (isRunning) {
LOG_DEBUG("UDP multicast already running");
return;
}
if (udp.listenMulticast(udpIpAddress, UDP_MULTICAST_DEFAUL_PORT, 64)) {
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO)
LOG_DEBUG("UDP Listening on IP: %u.%u.%u.%u:%u", udpIpAddress[0], udpIpAddress[1], udpIpAddress[2], udpIpAddress[3],
@@ -34,13 +38,29 @@ class UdpMulticastHandler final
LOG_DEBUG("UDP Listening on IP: %s", WiFi.localIP().toString().c_str());
#endif
udp.onPacket([this](AsyncUDPPacket packet) { onReceive(packet); });
isRunning = true;
} else {
LOG_DEBUG("Failed to listen on UDP");
}
}
void stop()
{
if (!isRunning) {
return;
}
LOG_DEBUG("Stopping UDP multicast");
#if defined(ARCH_ESP32) || defined(ARCH_NRF52)
udp.close();
#endif
isRunning = false;
}
void onReceive(AsyncUDPPacket packet)
{
if (!isRunning) {
return;
}
size_t packetLength = packet.length();
#if defined(ARCH_NRF52)
IPAddress ip = packet.remoteIP();
@@ -67,7 +87,7 @@ class UdpMulticastHandler final
bool onSend(const meshtastic_MeshPacket *mp)
{
if (!mp || !udp) {
if (!isRunning || !mp || !udp) {
return false;
}
#if defined(ARCH_NRF52)
@@ -92,5 +112,6 @@ class UdpMulticastHandler final
private:
IPAddress udpIpAddress;
AsyncUDP udp;
bool isRunning;
};
#endif // HAS_UDP_MULTICAST

View File

@@ -391,6 +391,11 @@ static void WiFiEvent(WiFiEvent_t event)
LOG_INFO("Disconnected from WiFi access point");
#ifdef WIFI_LED
digitalWrite(WIFI_LED, LOW);
#endif
#if HAS_UDP_MULTICAST
if (udpHandler) {
udpHandler->stop();
}
#endif
if (!isReconnecting) {
WiFi.disconnect(false, true);
@@ -417,6 +422,11 @@ static void WiFiEvent(WiFiEvent_t event)
break;
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
LOG_INFO("Lost IP address and IP address is reset to 0");
#if HAS_UDP_MULTICAST
if (udpHandler) {
udpHandler->stop();
}
#endif
if (!isReconnecting) {
WiFi.disconnect(false, true);
syslog.disable();

View File

@@ -643,6 +643,12 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
accelerometerThread->enabled = true;
accelerometerThread->start();
}
#endif
#ifdef LED_PIN
// Turn LED off if heartbeat by config
if (c.payload_variant.device.led_heartbeat_disabled) {
digitalWrite(LED_PIN, HIGH ^ LED_STATE_ON);
}
#endif
if (config.device.button_gpio == c.payload_variant.device.button_gpio &&
config.device.buzzer_gpio == c.payload_variant.device.buzzer_gpio &&
@@ -899,11 +905,10 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c)
{
bool shouldReboot = true;
// If we are in an open transaction or configuring MQTT or Serial (which have validation), defer disabling Bluetooth
// Otherwise, disable Bluetooth to prevent the phone from interfering with the config
if (!hasOpenEditTransaction && !IS_ONE_OF(c.which_payload_variant, meshtastic_ModuleConfig_mqtt_tag,
meshtastic_ModuleConfig_serial_tag, meshtastic_ModuleConfig_statusmessage_tag)) {
if (!hasOpenEditTransaction &&
!IS_ONE_OF(c.which_payload_variant, meshtastic_ModuleConfig_mqtt_tag, meshtastic_ModuleConfig_serial_tag)) {
disableBluetooth();
}
@@ -995,14 +1000,8 @@ bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c)
moduleConfig.has_paxcounter = true;
moduleConfig.paxcounter = c.payload_variant.paxcounter;
break;
case meshtastic_ModuleConfig_statusmessage_tag:
LOG_INFO("Set module config: StatusMessage");
moduleConfig.has_statusmessage = true;
moduleConfig.statusmessage = c.payload_variant.statusmessage;
shouldReboot = false;
break;
}
saveChanges(SEGMENT_MODULECONFIG, shouldReboot);
saveChanges(SEGMENT_MODULECONFIG);
return true;
}
@@ -1181,11 +1180,6 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_paxcounter_tag;
res.get_module_config_response.payload_variant.paxcounter = moduleConfig.paxcounter;
break;
case meshtastic_AdminMessage_ModuleConfigType_STATUSMESSAGE_CONFIG:
LOG_INFO("Get module config: StatusMessage");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_statusmessage_tag;
res.get_module_config_response.payload_variant.statusmessage = moduleConfig.statusmessage;
break;
}
// NOTE: The phone app needs to know the ls_secsvalue so it can properly expect sleep behavior.

View File

@@ -24,8 +24,24 @@
#include "mesh/generated/meshtastic/rtttl.pb.h"
#include <Arduino.h>
#ifdef HAS_NCP5623
#include <graphics/RAKled.h>
#endif
#ifdef HAS_LP5562
#include <graphics/NomadStarLED.h>
#endif
#ifdef HAS_NEOPIXEL
#include <graphics/NeoPixel.h>
#endif
#ifdef UNPHONE
#include "unPhone.h"
extern unPhone unphone;
#endif
#if defined(HAS_RGB_LED)
#include "AmbientLightingThread.h"
uint8_t red = 0;
uint8_t green = 0;
uint8_t blue = 0;
@@ -107,6 +123,32 @@ int32_t ExternalNotificationModule::runOnce()
green = (colorState & 2) ? brightnessValues[brightnessIndex] : 0; // Green enabled on colorState = 2,3,6,7
blue = (colorState & 1) ? (brightnessValues[brightnessIndex] * 1.5) : 0; // Blue enabled on colorState = 1,3,5,7
white = (colorState & 12) ? brightnessValues[brightnessIndex] : 0;
#ifdef HAS_NCP5623
if (rgb_found.type == ScanI2C::NCP5623) {
rgb.setColor(red, green, blue);
}
#endif
#ifdef HAS_LP5562
if (rgb_found.type == ScanI2C::LP5562) {
rgbw.setColor(red, green, blue, white);
}
#endif
#ifdef RGBLED_CA
analogWrite(RGBLED_RED, 255 - red); // CA type needs reverse logic
analogWrite(RGBLED_GREEN, 255 - green);
analogWrite(RGBLED_BLUE, 255 - blue);
#elif defined(RGBLED_RED)
analogWrite(RGBLED_RED, red);
analogWrite(RGBLED_GREEN, green);
analogWrite(RGBLED_BLUE, blue);
#endif
#ifdef HAS_NEOPIXEL
pixels.fill(pixels.Color(red, green, blue), 0, NEOPIXEL_COUNT);
pixels.show();
#endif
#ifdef UNPHONE
unphone.rgb(red, green, blue);
#endif
if (ascending) { // fade in
brightnessIndex++;
if (brightnessIndex == (sizeof(brightnessValues) - 1)) {
@@ -213,9 +255,34 @@ void ExternalNotificationModule::setExternalState(uint8_t index, bool on)
blue = 0;
white = 0;
}
ambientLightingThread.setLighting(moduleConfig.ambient_lighting.current, red, green, blue);
#endif
#ifdef HAS_NCP5623
if (rgb_found.type == ScanI2C::NCP5623) {
rgb.setColor(red, green, blue);
}
#endif
#ifdef HAS_LP5562
if (rgb_found.type == ScanI2C::LP5562) {
rgbw.setColor(red, green, blue, white);
}
#endif
#ifdef RGBLED_CA
analogWrite(RGBLED_RED, 255 - red); // CA type needs reverse logic
analogWrite(RGBLED_GREEN, 255 - green);
analogWrite(RGBLED_BLUE, 255 - blue);
#elif defined(RGBLED_RED)
analogWrite(RGBLED_RED, red);
analogWrite(RGBLED_GREEN, green);
analogWrite(RGBLED_BLUE, blue);
#endif
#ifdef HAS_NEOPIXEL
pixels.fill(pixels.Color(red, green, blue), 0, NEOPIXEL_COUNT);
pixels.show();
#endif
#ifdef UNPHONE
unphone.rgb(red, green, blue);
#endif
#ifdef HAS_DRV2605
if (on) {
drv.go();
@@ -340,6 +407,33 @@ ExternalNotificationModule::ExternalNotificationModule()
LOG_INFO("Use Pin %i in PWM mode", config.device.buzzer_gpio);
}
}
#ifdef HAS_NCP5623
if (rgb_found.type == ScanI2C::NCP5623) {
rgb.begin();
rgb.setCurrent(10);
}
#endif
#ifdef HAS_LP5562
if (rgb_found.type == ScanI2C::LP5562) {
rgbw.begin();
rgbw.setCurrent(20);
}
#endif
#ifdef RGBLED_RED
pinMode(RGBLED_RED, OUTPUT); // set up the RGB led pins
pinMode(RGBLED_GREEN, OUTPUT);
pinMode(RGBLED_BLUE, OUTPUT);
#endif
#ifdef RGBLED_CA
analogWrite(RGBLED_RED, 255); // with a common anode type, logic is reversed
analogWrite(RGBLED_GREEN, 255); // so we want to initialise with lights off
analogWrite(RGBLED_BLUE, 255);
#endif
#ifdef HAS_NEOPIXEL
pixels.begin(); // Initialise the pixel(s)
pixels.clear(); // Set all pixel colors to 'off'
pixels.setBrightness(moduleConfig.ambient_lighting.current);
#endif
} else {
LOG_INFO("External Notification Module Disabled");
disable();

View File

@@ -5,11 +5,6 @@
#include "configuration.h"
#include "input/InputBroker.h"
#ifdef HAS_RGB_LED
#include "AmbientLightingThread.h"
extern AmbientLightingThread ambientLightingThread;
#endif
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(CONFIG_IDF_TARGET_ESP32C6)
#include <NonBlockingRtttl.h>
#else

View File

@@ -123,7 +123,7 @@ bool KeyVerificationModule::sendInitialRequest(NodeNum remoteNode)
// generate nonce
updateState();
if (currentState != KEY_VERIFICATION_IDLE) {
IF_SCREEN(graphics::menuHandler::menuQueue = graphics::menuHandler::throttle_message;)
IF_SCREEN(graphics::menuHandler::menuQueue = graphics::menuHandler::ThrottleMessage;)
return false;
}
currentNonce = random();
@@ -259,7 +259,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
p->priority = meshtastic_MeshPacket_Priority_HIGH;
service->sendToMesh(p, RX_SRC_LOCAL, true);
currentState = KEY_VERIFICATION_SENDER_AWAITING_USER;
IF_SCREEN(screen->requestMenu(graphics::menuHandler::key_verification_final_prompt);)
IF_SCREEN(screen->requestMenu(graphics::menuHandler::KeyVerificationFinalPrompt);)
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
cn->level = meshtastic_LogRecord_Level_WARNING;
sprintf(cn->message, "Final confirmation for outgoing manual key verification %s", message);

View File

@@ -1,11 +1,8 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_INPUTBROKER
#include "buzz/BuzzerFeedbackThread.h"
#include "modules/SystemCommandsModule.h"
#endif
#include "modules/StatusLEDModule.h"
#if !MESHTASTIC_EXCLUDE_REPLYBOT
#include "ReplyBotModule.h"
#include "modules/SystemCommandsModule.h"
#endif
#if !MESHTASTIC_EXCLUDE_PKI
#include "KeyVerificationModule.h"
@@ -93,9 +90,6 @@
#if !MESHTASTIC_EXCLUDE_DROPZONE
#include "modules/DropzoneModule.h"
#endif
#if !MESHTASTIC_EXCLUDE_STATUS
#include "modules/StatusMessageModule.h"
#endif
#if defined(HAS_HARDWARE_WATCHDOG)
#include "watchdog/watchdogThread.h"
@@ -112,10 +106,10 @@ void setupModules()
buzzerFeedbackThread = new BuzzerFeedbackThread();
}
#endif
#if defined(LED_CHARGE) || defined(LED_PAIRING)
statusLEDModule = new StatusLEDModule();
#if !MESHTASTIC_EXCLUDE_REPLYBOT
new ReplyBotModule();
#endif
#if !MESHTASTIC_EXCLUDE_ADMIN
adminModule = new AdminModule();
#endif
@@ -156,9 +150,6 @@ void setupModules()
#if !MESHTASTIC_EXCLUDE_DROPZONE
dropzoneModule = new DropzoneModule();
#endif
#if !MESHTASTIC_EXCLUDE_STATUS
statusMessageModule = new StatusMessageModule();
#endif
#if !MESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE
new GenericThreadModule();
#endif

View File

@@ -1,4 +1,5 @@
#include "PowerStressModule.h"
#include "Led.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerMon.h"
@@ -77,12 +78,10 @@ int32_t PowerStressModule::runOnce()
switch (p.cmd) {
case meshtastic_PowerStressMessage_Opcode_LED_ON:
// FIXME - implement
// ledForceOn.set(true);
ledForceOn.set(true);
break;
case meshtastic_PowerStressMessage_Opcode_LED_OFF:
// FIXME - implement
// ledForceOn.set(false);
ledForceOn.set(false);
break;
case meshtastic_PowerStressMessage_Opcode_GPS_ON:
// FIXME - implement

View File

@@ -1,183 +0,0 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_REPLYBOT
/*
* ReplyBotModule.cpp
*
* This module implements a simple reply bot for the Meshtastic firmware. It listens for
* specific text commands ("/ping", "/hello" and "/test") delivered either via a direct
* message (DM) or a broadcast on the primary channel. When a supported command is
* received the bot responds with a short status message that includes the hop count
* (minimum number of relays), RSSI and SNR of the received packet. To avoid spamming
* the network it enforces a persender cooldown between responses. By default the
* module is enabled; define MESHTASTIC_EXCLUDE_REPLYBOT at build time to exclude it
* entirely. See the official firmware documentation for guidance on adding modules.
*/
#include "Channels.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "ReplyBotModule.h"
#include "mesh/MeshTypes.h"
#include <Arduino.h>
#include <cctype>
#include <cstring>
//
// Rate limiting data structures
//
// Each sender is tracked in a small ring buffer. When a message arrives from a
// sender we check the last time we responded to them. If the difference is
// less than the configured cooldown (different values for DM vs broadcast)
// the message is ignored; otherwise we update the last response time and
// proceed with replying.
struct ReplyBotCooldownEntry {
uint32_t from = 0;
uint32_t lastMs = 0;
};
static constexpr uint8_t REPLYBOT_COOLDOWN_SLOTS = 8; // ring buffer size
static constexpr uint32_t REPLYBOT_DM_COOLDOWN_MS = 15 * 1000; // 15 seconds for DMs
static constexpr uint32_t REPLYBOT_LF_COOLDOWN_MS = 60 * 1000; // 60 seconds for LongFast broadcasts
static ReplyBotCooldownEntry replybotCooldown[REPLYBOT_COOLDOWN_SLOTS];
static uint8_t replybotCooldownIdx = 0;
// Return true if a reply should be ratelimited for this sender, updating the
// entry table as needed.
static bool replybotRateLimited(uint32_t from, uint32_t cooldownMs)
{
const uint32_t now = millis();
for (auto &e : replybotCooldown) {
if (e.from == from) {
// Found existing entry; check if cooldown expired
if ((uint32_t)(now - e.lastMs) < cooldownMs) {
return true;
}
e.lastMs = now;
return false;
}
}
// No entry found insert new sender into the ring
replybotCooldown[replybotCooldownIdx].from = from;
replybotCooldown[replybotCooldownIdx].lastMs = now;
replybotCooldownIdx = (replybotCooldownIdx + 1) % REPLYBOT_COOLDOWN_SLOTS;
return false;
}
// Constructor registers a single text port and marks the module promiscuous
// so that broadcast messages on the primary channel are visible.
ReplyBotModule::ReplyBotModule() : SinglePortModule("replybot", meshtastic_PortNum_TEXT_MESSAGE_APP)
{
isPromiscuous = true;
}
void ReplyBotModule::setup()
{
// In future we may add a protobuf configuration; for now the module is
// always enabled when compiled in.
}
// Determine whether we want to process this packet. We only care about
// plain text messages addressed to our port.
bool ReplyBotModule::wantPacket(const meshtastic_MeshPacket *p)
{
return (p && p->decoded.portnum == ourPortNum);
}
ProcessMessage ReplyBotModule::handleReceived(const meshtastic_MeshPacket &mp)
{
// Accept only direct messages to us or broadcasts on the Primary channel
// (regardless of modem preset: LongFast, MediumFast, etc).
const uint32_t ourNode = nodeDB->getNodeNum();
const bool isDM = (mp.to == ourNode);
const bool isPrimaryChannel = (mp.channel == channels.getPrimaryIndex()) && isBroadcast(mp.to);
if (!isDM && !isPrimaryChannel) {
return ProcessMessage::CONTINUE;
}
// Ignore empty payloads
if (mp.decoded.payload.size == 0) {
return ProcessMessage::CONTINUE;
}
// Copy payload into a nullterminated buffer
char buf[260];
memset(buf, 0, sizeof(buf));
size_t n = mp.decoded.payload.size;
if (n > sizeof(buf) - 1)
n = sizeof(buf) - 1;
memcpy(buf, mp.decoded.payload.bytes, n);
// React only to supported slash commands
if (!isCommand(buf)) {
return ProcessMessage::CONTINUE;
}
// Apply rate limiting per sender depending on DM/broadcast
const uint32_t cooldownMs = isDM ? REPLYBOT_DM_COOLDOWN_MS : REPLYBOT_LF_COOLDOWN_MS;
if (replybotRateLimited(mp.from, cooldownMs)) {
return ProcessMessage::CONTINUE;
}
// Compute hop count indicator if the relay_node is nonzero we know
// there was at least one relay. Some firmware builds support a hop_start
// field which could be used for more accurate counts, but here we use
// the available relay_node flag only.
// int hopsAway = mp.hop_start - mp.hop_limit;
int hopsAway = getHopsAway(mp);
// Normalize RSSI: if positive adjust down by 200 to align with typical values
int rssi = mp.rx_rssi;
if (rssi > 0) {
rssi -= 200;
}
float snr = mp.rx_snr;
// Build the reply message and send it back via DM
char reply[96];
snprintf(reply, sizeof(reply), "🎙️ Mic Check : %d Hops away | RSSI %d | SNR %.1f", hopsAway, rssi, snr);
sendDm(mp, reply);
return ProcessMessage::CONTINUE;
}
// Check if the message starts with one of the supported commands. Leading
// whitespace is skipped and commands must be followed by endofstring or
// whitespace.
bool ReplyBotModule::isCommand(const char *msg) const
{
if (!msg)
return false;
while (*msg == ' ' || *msg == '\t')
msg++;
auto isEndOrSpace = [](char c) { return c == '\0' || std::isspace(static_cast<unsigned char>(c)); };
if (strncmp(msg, "/ping", 5) == 0 && isEndOrSpace(msg[5]))
return true;
if (strncmp(msg, "/hello", 6) == 0 && isEndOrSpace(msg[6]))
return true;
if (strncmp(msg, "/test", 5) == 0 && isEndOrSpace(msg[5]))
return true;
return false;
}
// Send a direct message back to the originating node.
void ReplyBotModule::sendDm(const meshtastic_MeshPacket &rx, const char *text)
{
if (!text)
return;
meshtastic_MeshPacket *p = allocDataPacket();
p->to = rx.from;
p->channel = rx.channel;
p->want_ack = false;
p->decoded.want_response = false;
size_t len = strlen(text);
if (len > sizeof(p->decoded.payload.bytes)) {
len = sizeof(p->decoded.payload.bytes);
}
p->decoded.payload.size = len;
memcpy(p->decoded.payload.bytes, text, len);
service->sendToMesh(p);
}
#endif // MESHTASTIC_EXCLUDE_REPLYBOT

View File

@@ -1,19 +0,0 @@
#pragma once
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_REPLYBOT
#include "SinglePortModule.h"
#include "mesh/generated/meshtastic/mesh.pb.h"
class ReplyBotModule : public SinglePortModule
{
public:
ReplyBotModule();
void setup() override;
bool wantPacket(const meshtastic_MeshPacket *p) override;
ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
protected:
bool isCommand(const char *msg) const;
void sendDm(const meshtastic_MeshPacket &rx, const char *text);
};
#endif // MESHTASTIC_EXCLUDE_REPLYBOT

View File

@@ -13,10 +13,8 @@ StatusLEDModule::StatusLEDModule() : concurrency::OSThread("StatusLEDModule")
{
bluetoothStatusObserver.observe(&bluetoothStatus->onNewStatus);
powerStatusObserver.observe(&powerStatus->onNewStatus);
#if !MESHTASTIC_EXCLUDE_INPUTBROKER
if (inputBroker)
inputObserver.observe(inputBroker);
#endif
}
int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
@@ -64,22 +62,19 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
}
return 0;
};
#if !MESHTASTIC_EXCLUDE_INPUTBROKER
int StatusLEDModule::handleInputEvent(const InputEvent *event)
{
lastUserbuttonTime = millis();
return 0;
}
#endif
int32_t StatusLEDModule::runOnce()
{
my_interval = 1000;
if (power_state == charging) {
#ifndef POWER_LED_HARDWARE_BLINKS_WHILE_CHARGING
CHARGE_LED_state = !CHARGE_LED_state;
#endif
} else if (power_state == charged) {
CHARGE_LED_state = LED_STATE_ON;
} else if (power_state == critical) {
@@ -99,15 +94,7 @@ int32_t StatusLEDModule::runOnce()
}
} else {
if (doing_fast_blink) {
CHARGE_LED_state = LED_STATE_OFF;
doing_fast_blink = false;
my_interval = 999;
} else {
CHARGE_LED_state = LED_STATE_ON;
doing_fast_blink = true;
my_interval = 1;
}
CHARGE_LED_state = LED_STATE_OFF;
}
if (!config.bluetooth.enabled || PAIRING_LED_starttime + 30 * 1000 < millis() || doing_fast_blink) {
@@ -125,11 +112,6 @@ int32_t StatusLEDModule::runOnce()
PAIRING_LED_state = LED_STATE_ON;
}
// Override if disabled in config
if (config.device.led_heartbeat_disabled) {
CHARGE_LED_state = LED_STATE_OFF;
}
#ifdef Battery_LED_1
bool chargeIndicatorLED1 = LED_STATE_OFF;
bool chargeIndicatorLED2 = LED_STATE_OFF;
bool chargeIndicatorLED3 = LED_STATE_OFF;
@@ -144,38 +126,14 @@ int32_t StatusLEDModule::runOnce()
if (powerStatus && powerStatus->getBatteryChargePercent() >= 75)
chargeIndicatorLED4 = LED_STATE_ON;
}
#endif
#if defined(HAS_PMU)
if (pmu_found && PMU) {
// blink the axp led
PMU->setChargingLedMode(CHARGE_LED_state ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
}
#endif
#ifdef PCA_LED_POWER
io.digitalWrite(PCA_LED_POWER, CHARGE_LED_state);
#endif
#ifdef PCA_LED_ENABLE
io.digitalWrite(PCA_LED_ENABLE, CHARGE_LED_state);
#endif
#ifdef LED_POWER
digitalWrite(LED_POWER, CHARGE_LED_state);
#ifdef LED_CHARGE
digitalWrite(LED_CHARGE, CHARGE_LED_state);
#endif
#ifdef LED_PAIRING
digitalWrite(LED_PAIRING, PAIRING_LED_state);
#endif
#ifdef RGB_LED_POWER
if (!config.device.led_heartbeat_disabled) {
if (CHARGE_LED_state == LED_STATE_ON) {
ambientLightingThread.setLighting(10, 255, 0, 0);
} else {
ambientLightingThread.setLighting(0, 0, 0, 0);
}
}
#endif
#ifdef Battery_LED_1
digitalWrite(Battery_LED_1, chargeIndicatorLED1);
#endif
@@ -191,43 +149,3 @@ int32_t StatusLEDModule::runOnce()
return (my_interval);
}
void StatusLEDModule::setPowerLED(bool LEDon)
{
#if defined(HAS_PMU)
if (pmu_found && PMU) {
// blink the axp led
PMU->setChargingLedMode(LEDon ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
}
#endif
if (LEDon)
LEDon = LED_STATE_ON;
else
LEDon = LED_STATE_OFF;
#ifdef PCA_LED_POWER
io.digitalWrite(PCA_LED_POWER, LEDon);
#endif
#ifdef PCA_LED_ENABLE
io.digitalWrite(PCA_LED_ENABLE, LEDon);
#endif
#ifdef LED_POWER
digitalWrite(LED_POWER, LEDon);
#endif
#ifdef LED_PAIRING
digitalWrite(LED_PAIRING, LEDon);
#endif
#ifdef Battery_LED_1
digitalWrite(Battery_LED_1, LEDon);
#endif
#ifdef Battery_LED_2
digitalWrite(Battery_LED_2, LEDon);
#endif
#ifdef Battery_LED_3
digitalWrite(Battery_LED_3, LEDon);
#endif
#ifdef Battery_LED_4
digitalWrite(Battery_LED_4, LEDon);
#endif
}

View File

@@ -5,14 +5,10 @@
#include "PowerStatus.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
#include "main.h"
#include "input/InputBroker.h"
#include <Arduino.h>
#include <functional>
#if !MESHTASTIC_EXCLUDE_INPUTBROKER
#include "input/InputBroker.h"
#endif
class StatusLEDModule : private concurrency::OSThread
{
bool slowTrack = false;
@@ -21,11 +17,8 @@ class StatusLEDModule : private concurrency::OSThread
StatusLEDModule();
int handleStatusUpdate(const meshtastic::Status *);
#if !MESHTASTIC_EXCLUDE_INPUTBROKER
int handleInputEvent(const InputEvent *arg);
#endif
void setPowerLED(bool);
int handleInputEvent(const InputEvent *arg);
protected:
unsigned int my_interval = 1000; // interval in millisconds
@@ -35,10 +28,8 @@ class StatusLEDModule : private concurrency::OSThread
CallbackObserver<StatusLEDModule, const meshtastic::Status *>(this, &StatusLEDModule::handleStatusUpdate);
CallbackObserver<StatusLEDModule, const meshtastic::Status *> powerStatusObserver =
CallbackObserver<StatusLEDModule, const meshtastic::Status *>(this, &StatusLEDModule::handleStatusUpdate);
#if !MESHTASTIC_EXCLUDE_INPUTBROKER
CallbackObserver<StatusLEDModule, const InputEvent *> inputObserver =
CallbackObserver<StatusLEDModule, const InputEvent *>(this, &StatusLEDModule::handleInputEvent);
#endif
private:
bool CHARGE_LED_state = LED_STATE_OFF;
@@ -59,7 +50,3 @@ class StatusLEDModule : private concurrency::OSThread
};
extern StatusLEDModule *statusLEDModule;
#ifdef RGB_LED_POWER
#include "AmbientLightingThread.h"
extern AmbientLightingThread ambientLightingThread;
#endif

View File

@@ -1,41 +0,0 @@
#if !MESHTASTIC_EXCLUDE_STATUS
#include "StatusMessageModule.h"
#include "MeshService.h"
#include "ProtobufModule.h"
StatusMessageModule *statusMessageModule;
int32_t StatusMessageModule::runOnce()
{
if (moduleConfig.has_statusmessage && moduleConfig.statusmessage.node_status[0] != '\0') {
// create and send message with the status message set
meshtastic_StatusMessage ourStatus = meshtastic_StatusMessage_init_zero;
strncpy(ourStatus.status, moduleConfig.statusmessage.node_status, sizeof(ourStatus.status));
ourStatus.status[sizeof(ourStatus.status) - 1] = '\0'; // ensure null termination
meshtastic_MeshPacket *p = allocDataPacket();
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes),
meshtastic_StatusMessage_fields, &ourStatus);
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
p->channel = 0;
service->sendToMesh(p);
}
return 1000 * 12 * 60 * 60;
}
ProcessMessage StatusMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
{
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
meshtastic_StatusMessage incomingMessage;
if (pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_StatusMessage_fields,
&incomingMessage)) {
LOG_INFO("Received a NodeStatus message %s", incomingMessage.status);
}
}
return ProcessMessage::CONTINUE;
}
#endif

View File

@@ -1,35 +0,0 @@
#pragma once
#if !MESHTASTIC_EXCLUDE_STATUS
#include "SinglePortModule.h"
#include "configuration.h"
class StatusMessageModule : public SinglePortModule, private concurrency::OSThread
{
public:
/** Constructor
* name is for debugging output
*/
StatusMessageModule()
: SinglePortModule("statusMessage", meshtastic_PortNum_NODE_STATUS_APP), concurrency::OSThread("StatusMessage")
{
if (moduleConfig.has_statusmessage && moduleConfig.statusmessage.node_status[0] != '\0') {
this->setInterval(2 * 60 * 1000);
} else {
this->setInterval(1000 * 12 * 60 * 60);
}
// TODO: If we have a string, set the initial delay (15 minutes maybe)
}
virtual int32_t runOnce() override;
protected:
/** Called to handle a particular incoming message
*/
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
private:
};
extern StatusMessageModule *statusMessageModule;
#endif

View File

@@ -757,7 +757,11 @@ void NimbleBluetooth::deinit()
isDeInit = true;
#ifdef BLE_LED
digitalWrite(BLE_LED, LED_STATE_OFF);
#ifdef BLE_LED_INVERTED
digitalWrite(BLE_LED, HIGH);
#else
digitalWrite(BLE_LED, LOW);
#endif
#endif
#ifndef NIMBLE_TWO
NimBLEDevice::deinit();

View File

@@ -24,6 +24,11 @@
#include <nvs.h>
#include <nvs_flash.h>
// Weak empty variant shutdown prep function.
// May be redefined by variant files.
void variant_shutdown() __attribute__((weak));
void variant_shutdown() {}
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
void setBluetoothEnable(bool enable)
{
@@ -249,6 +254,7 @@ void cpuDeepSleep(uint32_t msecToWake)
#endif // #end ESP32S3_WAKE_TYPE
#endif
variant_shutdown();
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);

View File

@@ -36,6 +36,13 @@ bool AsyncUDP::writeTo(const uint8_t *data, size_t len, IPAddress ip, uint16_t p
return udp.endPacket();
}
void AsyncUDP::close()
{
udp.stop();
localPort = 0;
_onPacket = nullptr;
}
// AsyncUDPPacket
AsyncUDPPacket::AsyncUDPPacket(EthernetUDP &source) : _udp(source), _remoteIP(source.remoteIP()), _remotePort(source.remotePort())
{

View File

@@ -22,6 +22,7 @@ class AsyncUDP : public Print, private concurrency::OSThread
bool listenMulticast(IPAddress multicastIP, uint16_t port, uint8_t ttl = 64);
bool writeTo(const uint8_t *data, size_t len, IPAddress ip, uint16_t port);
void close();
size_t write(uint8_t b) override;
size_t write(const uint8_t *data, size_t len) override;

View File

@@ -158,7 +158,7 @@
#endif
#ifdef PIN_LED1
#define LED_POWER PIN_LED1 // LED1 on nrf52840-DK
#define LED_PIN PIN_LED1 // LED1 on nrf52840-DK
#endif
#ifdef PIN_BUTTON1

View File

@@ -46,7 +46,7 @@
uint16_t getVDDVoltage();
// Weak empty variant initialization function.
// Weak empty variant shutdown prep function.
// May be redefined by variant files.
void variant_shutdown() __attribute__((weak));
void variant_shutdown() {}

View File

@@ -872,7 +872,6 @@ bool loadConfig(const char *configPath)
}
if (yamlConfig["Config"]) {
portduino_config.has_config_overrides = true;
if (yamlConfig["Config"]["DisplayMode"]) {
portduino_config.has_configDisplayMode = true;
if ((yamlConfig["Config"]["DisplayMode"]).as<std::string>("") == "TWOCOLOR") {
@@ -885,13 +884,6 @@ bool loadConfig(const char *configPath)
portduino_config.configDisplayMode = meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT;
}
}
if (yamlConfig["Config"]["StatusMessage"]) {
portduino_config.has_statusMessage = true;
portduino_config.statusMessage = (yamlConfig["Config"]["StatusMessage"]).as<std::string>("");
}
if ((yamlConfig["Config"]["EnableUDP"]).as<bool>(false)) {
portduino_config.enable_UDP = true;
}
}
if (yamlConfig["General"]) {

View File

@@ -177,12 +177,8 @@ extern struct portduino_config_struct {
int hostMetrics_channel = 0;
// config
bool has_config_overrides = false;
int configDisplayMode = 0;
bool has_configDisplayMode = false;
std::string statusMessage = "";
bool has_statusMessage = false;
bool enable_UDP = false;
// General
std::string mac_address = "";
@@ -509,30 +505,21 @@ extern struct portduino_config_struct {
}
// config
if (has_config_overrides) {
if (has_configDisplayMode) {
out << YAML::Key << "Config" << YAML::Value << YAML::BeginMap;
if (has_configDisplayMode) {
switch (configDisplayMode) {
case meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR:
out << YAML::Key << "DisplayMode" << YAML::Value << "TWOCOLOR";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_INVERTED:
out << YAML::Key << "DisplayMode" << YAML::Value << "INVERTED";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_COLOR:
out << YAML::Key << "DisplayMode" << YAML::Value << "COLOR";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT:
out << YAML::Key << "DisplayMode" << YAML::Value << "DEFAULT";
break;
}
}
if (has_statusMessage) {
out << YAML::Key << "StatusMessage" << YAML::Value << statusMessage;
}
if (enable_UDP) {
out << YAML::Key << "EnableUDP" << YAML::Value << true;
switch (configDisplayMode) {
case meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR:
out << YAML::Key << "DisplayMode" << YAML::Value << "TWOCOLOR";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_INVERTED:
out << YAML::Key << "DisplayMode" << YAML::Value << "INVERTED";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_COLOR:
out << YAML::Key << "DisplayMode" << YAML::Value << "COLOR";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT:
out << YAML::Key << "DisplayMode" << YAML::Value << "DEFAULT";
break;
}
out << YAML::EndMap; // Config

View File

@@ -5,6 +5,7 @@
#endif
#include "Default.h"
#include "Led.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
@@ -12,7 +13,6 @@
#include "detect/LoRaRadioType.h"
#include "error.h"
#include "main.h"
#include "modules/StatusLEDModule.h"
#include "sleep.h"
#include "target_specific.h"
@@ -268,7 +268,8 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN
digitalWrite(PIN_WD_EN, LOW);
#endif
#endif
statusLEDModule->setPowerLED(false);
ledBlink.set(false);
#ifdef RESET_OLED
digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power
#endif

View File

@@ -16,4 +16,4 @@ upload_speed = 460800
lib_deps =
${esp32_base.lib_deps}
# renovate: datasource=custom.pio depName=NeoPixel packageName=adafruit/library/Adafruit NeoPixel
adafruit/Adafruit NeoPixel@1.15.2
adafruit/Adafruit NeoPixel@1.15.3

View File

@@ -14,7 +14,7 @@
#define LORA_CS 5
#define RF95_FAN_EN 17
// This is a LED_WS2812 not a standard LED
// #define LED_PIN 16 // This is a LED_WS2812 not a standard LED
#define HAS_NEOPIXEL // Enable the use of neopixels
#define NEOPIXEL_COUNT 1 // How many neopixels are connected
#define NEOPIXEL_DATA 16 // gpio pin used to send data to the neopixels

View File

@@ -20,7 +20,7 @@
#define LORA_DIO2
#define LORA_DIO3
#define LED_POWER 16 // green - blue is at 17
#define LED_PIN 16 // green - blue is at 17
#define BUTTON_PIN 25
#define BUTTON_NEED_PULLUP

View File

@@ -23,6 +23,8 @@
#define SX126X_TXEN RADIOLIB_NC
#define SX126X_RXEN RADIOLIB_NC
// Status
// #define LED_PIN 1
// External notification
// FIXME: Check if EXT_NOTIFY_OUT actualy has any effect and removes the need for setting the external notication pin in the
// app/preferences

View File

@@ -21,8 +21,8 @@
#define BUTTON_PIN 15 // Right side button - if not available, set device.button_gpio to 0 from Meshtastic client
// LEDs
#define LED_POWER 13 // Tx LED
#define USER_LED 2 // Rx LED
#define LED_PIN 13 // Tx LED
#define USER_LED 2 // Rx LED
// Buzzer
#define PIN_BUZZER 33

View File

@@ -15,7 +15,7 @@
#define ADC_MULTIPLIER 1.85 // (R1 = 470k, R2 = 680k)
#define EXT_PWR_DETECT 4 // Pin to detect connected external power source for LILYGO® TTGO T-Energy T18 and other DIY boards
#define EXT_NOTIFY_OUT 12 // Overridden default pin to use for Ext Notify Module (#975).
#define LED_POWER 2 // add status LED (compatible with core-pcb and DIY targets)
#define LED_PIN 2 // add status LED (compatible with core-pcb and DIY targets)
// Radio
#define USE_SX1262 // E22-900M30S uses SX1262

View File

@@ -15,7 +15,7 @@
#define ADC_MULTIPLIER 1.85 // (R1 = 470k, R2 = 680k)
#define EXT_PWR_DETECT 4 // Pin to detect connected external power source for LILYGO® TTGO T-Energy T18 and other DIY boards
#define EXT_NOTIFY_OUT 12 // Overridden default pin to use for Ext Notify Module (#975).
#define LED_POWER 2 // add status LED (compatible with core-pcb and DIY targets)
#define LED_PIN 2 // add status LED (compatible with core-pcb and DIY targets)
#define LORA_DIO0 26 // a No connect on the SX1262/SX1268 module
#define LORA_RESET 23 // RST for SX1276, and for SX1262/SX1268

View File

@@ -3,7 +3,7 @@
// HACKBOX LoRa IO Kit
// Uses a ESP-32-WROOM and a RA-01SH (SX1262) LoRa Board
#define LED_POWER 2 // LED
#define LED_PIN 2 // LED
#define LED_STATE_ON 1 // State when LED is lit
#define HAS_SCREEN 0

View File

@@ -12,7 +12,7 @@
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
#define LED_POWER 25 // If defined we will blink this LED
#define LED_PIN 25 // If defined we will blink this LED
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
#define USE_RF95

View File

@@ -18,7 +18,7 @@
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
#define LED_POWER 25 // If defined we will blink this LED
#define LED_PIN 25 // If defined we will blink this LED
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
#define USE_RF95

View File

@@ -13,7 +13,7 @@
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
#define LED_POWER 25 // If defined we will blink this LED
#define LED_PIN 25 // If defined we will blink this LED
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
#define USE_RF95

View File

@@ -15,7 +15,7 @@
#undef GPS_TX_PIN
// Green / Lora = PIN 22 / GPIO2, Yellow / Wifi = PIN 23 / GPIO0, Blue / BLE = PIN 25 / GPIO16
#define LED_POWER 22
#define LED_PIN 22
#define WIFI_LED 23
#define BLE_LED 25

View File

@@ -1,7 +1,7 @@
#define I2C_SCL SCL
#define I2C_SDA SDA
#define LED_POWER LED
#define LED_PIN LED
// active low, powers the Battery reader, but no lora antenna boost (?)
// #define VEXT_ENABLE Vext

View File

@@ -11,7 +11,7 @@
// Green LED
#define LED_STATE_ON 1 // State when LED is lit
#define LED_POWER 10
#define LED_PIN 10
// PCF8563 RTC Module
#define PCF8563_RTC 0x51

View File

@@ -37,7 +37,7 @@
/*
LED PIN setup.
*/
#define LED_POWER 15
#define LED_PIN 15
/*
Five way button when using ADC.

View File

@@ -43,7 +43,7 @@ static const uint8_t SCK = 33;
#undef GPS_TX_PIN
#define GPS_TX_PIN (TX1)
#define LED_POWER LED_BLUE
#define LED_PIN LED_BLUE
#define PIN_VBAT WB_A0
#define BATTERY_PIN PIN_VBAT

View File

@@ -9,7 +9,7 @@
#define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module.
#define LED_STATE_ON 0 // State when LED is lit
#define LED_POWER 4 // Newer tbeams (1.1) have an extra led on GPIO4
#define LED_PIN 4 // Newer tbeams (1.1) have an extra led on GPIO4
// TTGO uses a common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if
// not found then probe for SX1262
@@ -49,7 +49,7 @@
#undef EXT_NOTIFY_OUT
#undef LED_STATE_ON
#undef LED_POWER
#undef LED_PIN
#define HAS_CST226SE 1
#define HAS_TOUCHSCREEN 1

View File

@@ -5,7 +5,7 @@
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
#define VEXT_ON_VALUE LOW
#define LED_POWER 2 // If defined we will blink this LED
#define LED_PIN 2 // If defined we will blink this LED
#define BUTTON_PIN 0 // If defined, this will be used for user button presses
#define BUTTON_NEED_PULLUP
#define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module.

View File

@@ -7,7 +7,7 @@
#define RESET_OLED 16 // If defined, this pin will be used to reset the display controller
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
#define LED_POWER 25 // If defined we will blink this LED
#define LED_PIN 25 // If defined we will blink this LED
#define BUTTON_PIN 36
#define BUTTON_NEED_PULLUP

View File

@@ -5,7 +5,7 @@
#define I2C_SCL 22
#define VEXT_ENABLE 21 // active low, powers the oled display and the lora antenna boost
#define LED_POWER 25 // If defined we will blink this LED
#define LED_PIN 25 // If defined we will blink this LED
#define BUTTON_PIN \
0 // If defined, this will be used for user button presses, if your board doesn't have a physical switch, you can wire one
// between this pin and ground

View File

@@ -22,4 +22,4 @@ build_flags =
${env:tlora-v2-1-1_6.build_flags}
-DBUTTON_PIN=0
-DPIN_BUZZER=25
-DLED_POWER=-1
-DLED_PIN=-1

View File

@@ -8,10 +8,10 @@
#define I2C_SDA 21 // I2C pins for this board
#define I2C_SCL 22
#if defined(LED_POWER) && LED_POWER == -1
#undef LED_POWER
#if defined(LED_PIN) && LED_PIN == -1
#undef LED_PIN
#else
#define LED_POWER 25 // If defined we will blink this LED
#define LED_PIN 25 // If defined we will blink this LED
#endif
#define USE_RF95

View File

@@ -7,5 +7,4 @@ build_flags =
-D TLORA_V2_1_16
-I variants/esp32/tlora_v2_1_16
-D LORA_TCXO_GPIO=33
-ULED_BUILTIN
upload_speed = 115200
upload_speed = 115200

View File

@@ -6,7 +6,7 @@
#define I2C_SDA 21 // I2C pins for this board
#define I2C_SCL 22
#define LED_POWER 25 // If defined we will blink this LED
#define LED_PIN 25 // If defined we will blink this LED
#define BUTTON_PIN 12 // If defined, this will be used for user button presses,
#define BUTTON_NEED_PULLUP

View File

@@ -8,7 +8,7 @@
#define GPS_RX_PIN 9
#define GPS_TX_PIN 10
#define LED_POWER 13 // 13 red, 2 blue, 15 red
#define LED_PIN 13 // 13 red, 2 blue, 15 red
#define BUTTON_PIN 0
#define BUTTON_NEED_PULLUP

View File

@@ -26,6 +26,7 @@
#undef GPS_TX_PIN
#define NO_GPS 1
#define HAS_GPS 0
#define NO_SCREEN
#define HAS_SCREEN 0
// Default SPI1 will be mapped to the display

View File

@@ -4,7 +4,7 @@
#define I2C_SCL SCL
#define BUTTON_PIN 9 // BOOT button
#define LED_POWER 30 // RGB LED
#define LED_PIN 30 // RGB LED
#define USE_RF95
#define LORA_SCK 4

View File

@@ -3,7 +3,7 @@
// Hackerboxes LoRa ESP32-C3 OLED Kit
// Uses a ESP32-C3 OLED Board and a RA-01SH (SX1262) LoRa Board
#define LED_POWER 8 // LED
#define LED_PIN 8 // LED
#define LED_STATE_ON 1 // State when LED is lit
#define HAS_SCREEN 0

View File

@@ -3,7 +3,7 @@
// LED pin on HT-DEV-ESP_V2 and HT-DEV-ESP_V3
// https://resource.heltec.cn/download/HT-CT62/HT-CT62_Reference_Design.pdf
// https://resource.heltec.cn/download/HT-DEV-ESP/HT-DEV-ESP_V3_Sch.pdf
#define LED_POWER 2 // LED
#define LED_PIN 2 // LED
#define LED_STATE_ON 1 // State when LED is lit
#define HAS_SCREEN 0

View File

@@ -7,4 +7,4 @@ build_flags =
-I variants/esp32c3/heltec_hru_3601
lib_deps = ${esp32c3_base.lib_deps}
# renovate: datasource=custom.pio depName=NeoPixel packageName=adafruit/library/Adafruit NeoPixel
adafruit/Adafruit NeoPixel@1.15.2
adafruit/Adafruit NeoPixel@1.15.3

View File

@@ -24,7 +24,7 @@ build_unflags =
lib_deps =
${esp32c6_base.lib_deps}
# renovate: datasource=custom.pio depName=NeoPixel packageName=adafruit/library/Adafruit NeoPixel
adafruit/Adafruit NeoPixel@1.15.2
adafruit/Adafruit NeoPixel@1.15.3
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
h2zero/NimBLE-Arduino@2.3.7
build_flags =

View File

@@ -1,7 +1,7 @@
#define I2C_SDA 8 // I2C pins for this board
#define I2C_SCL 9
#define LED_POWER 7 // If defined we will blink this LED
#define LED_PIN 7 // If defined we will blink this LED
#define LED_STATE_ON 0 // State when LED is lit
#define USE_SX1262

View File

@@ -1,7 +1,7 @@
#define I2C_SDA 34 // I2C pins for this board
#define I2C_SCL 36
#define LED_POWER 15 // If defined we will blink this LED
#define LED_PIN 15 // If defined we will blink this LED
#define HAS_NEOPIXEL // Enable the use of neopixels
#define NEOPIXEL_COUNT 3 // How many neopixels are connected

View File

@@ -1,7 +1,7 @@
// EByte EoRA-Hub
// Uses E80 (LR1121) LoRa module
#define LED_POWER 35
#define LED_PIN 35
// Button - user interface
#define BUTTON_PIN 0 // BOOT button

View File

@@ -1,5 +1,5 @@
// LED - status indication
#define LED_POWER 37
#define LED_PIN 37
// Button - user interface
#define BUTTON_PIN 0 // This is the BOOT button, and it has its own pull-up resistor

View File

@@ -100,7 +100,7 @@
*/
// Status
#define LED_POWER 1
#define LED_PIN 1
#define LED_STATE_ON 1 // State when LED is lit
// External notification
// FIXME: Check if EXT_NOTIFY_OUT actualy has any effect and removes the need for setting the external notication pin in the

View File

@@ -1,5 +1,5 @@
// Status
#define LED_POWER 1
#define LED_PIN 1
#define PIN_BUTTON1 47 // 功能键
#define PIN_BUTTON2 4 // 电源键

View File

@@ -34,3 +34,5 @@ lib_deps = ${esp32s3_base.lib_deps}
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
# renovate: datasource=custom.pio depName=PCA9557-arduino packageName=maxpromer/library/PCA9557-arduino
maxpromer/PCA9557-arduino@1.0.0
# renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/SensorLib@0.3.4

View File

@@ -1,18 +1,17 @@
#include "variant.h"
#include <PCA9557.h>
PCA9557 io(0x18, &Wire);
PCA9557 io(0x18, &Wire1);
void earlyInitVariant()
{
Wire.begin(48, 47);
Wire1.begin(48, 47);
io.pinMode(PCA_PIN_EINK_EN, OUTPUT);
io.pinMode(PCA_PIN_POWER_EN, OUTPUT);
io.pinMode(PCA_LED_POWER, OUTPUT);
io.pinMode(PCA_LED_USER, OUTPUT);
io.pinMode(PCA_LED_ENABLE, OUTPUT);
io.digitalWrite(PCA_PIN_POWER_EN, HIGH);
io.digitalWrite(PCA_LED_USER, LOW);
io.digitalWrite(PCA_LED_ENABLE, LOW);
}
void variant_shutdown()
{
io.digitalWrite(PCA_PIN_POWER_EN, LOW);
}

View File

@@ -8,10 +8,8 @@
// LED
// Both of these are on the GPIO expander
#define PCA_LED_USER 1 // the Blue LED
#define PCA_LED_ENABLE 2 // the power supply to the LEDs, in an OR arrangement with VBUS power
#define PCA_LED_POWER 3 // the Red LED? Seems to have hardware logic to blink when USB is plugged in.
#define POWER_LED_HARDWARE_BLINKS_WHILE_CHARGING
#define PCA_LED_USER 1 // the Blue LED
#define PCA_LED_POWER 3 // the Red LED? Seems to have hardware logic to blink when USB is plugged in.
// USB_CHECK
#define EXT_PWR_DETECT 12
@@ -32,6 +30,9 @@
#define I2C_SCL 1
#define I2C_SDA 2
// PCF8563 RTC Module
#define PCF8563_RTC 0x51
// GPS pins
#define GPS_SWITH 10
#define HAS_GPS 1

View File

@@ -11,7 +11,7 @@
#define I2C_SDA 12
#define I2C_SCL 14
#define LED_POWER 46
#define LED_PIN 46
#define LED_STATE_ON 0 // State when LED is litted
// #define BUTTON_PIN 15 // Pico OLED 1.3 User key 0 - removed User key 1 (17)

View File

@@ -26,7 +26,7 @@
// #define GPS_RX_PIN 44
// #define GPS_TX_PIN 43
#define LED_POWER 41
#define LED_PIN 41
#define BUTTON_PIN 2
#define BUTTON_NEED_PULLUP

View File

@@ -13,7 +13,7 @@ lib_deps =
# renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2
zinggjm/GxEPD2@1.6.6
# renovate: datasource=custom.pio depName=NeoPixel packageName=adafruit/library/Adafruit NeoPixel
adafruit/Adafruit NeoPixel@1.15.2
adafruit/Adafruit NeoPixel@1.15.3
build_unflags =
${esp32s3_base.build_unflags}
-DARDUINO_USB_MODE=1

View File

@@ -11,7 +11,7 @@
#define I2C_SDA 18 // 1 // I2C pins for this board
#define I2C_SCL 17 // 2
// #define LED_POWER 38 // This is a RGB LED not a standard LED
// #define LED_PIN 38 // This is a RGB LED not a standard LED
#define HAS_NEOPIXEL // Enable the use of neopixels
#define NEOPIXEL_COUNT 1 // How many neopixels are connected
#define NEOPIXEL_DATA 38 // gpio pin used to send data to the neopixels

View File

@@ -11,7 +11,7 @@ upload_speed = 921600
lib_deps =
${esp32s3_base.lib_deps}
# renovate: datasource=custom.pio depName=NeoPixel packageName=adafruit/library/Adafruit NeoPixel
adafruit/Adafruit NeoPixel@1.15.2
adafruit/Adafruit NeoPixel@1.15.3
build_unflags =
${esp32s3_base.build_unflags}
-DARDUINO_USB_MODE=1

View File

@@ -11,7 +11,7 @@
#define I2C_SDA 18 // 1 // I2C pins for this board
#define I2C_SCL 17 // 2
// #define LED_POWER 38 // This is a RGB LED not a standard LED
// #define LED_PIN 38 // This is a RGB LED not a standard LED
#define HAS_NEOPIXEL // Enable the use of neopixels
#define NEOPIXEL_COUNT 1 // How many neopixels are connected
#define NEOPIXEL_DATA 38 // gpio pin used to send data to the neopixels

Some files were not shown because too many files have changed in this diff Show More