diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml
index 12e6696c0..9d563a39a 100644
--- a/.trunk/trunk.yaml
+++ b/.trunk/trunk.yaml
@@ -9,14 +9,14 @@ plugins:
lint:
enabled:
- checkov@3.2.497
- - renovate@42.81.8
+ - renovate@42.84.2
- prettier@3.8.0
- - trufflehog@3.92.4
+ - trufflehog@3.92.5
- yamllint@1.38.0
- - bandit@1.9.2
+ - bandit@1.9.3
- trivy@0.68.2
- taplo@0.10.0
- - ruff@0.14.11
+ - ruff@0.14.13
- isort@7.0.0
- markdownlint@0.47.0
- oxipng@10.0.0
@@ -26,7 +26,7 @@ lint:
- hadolint@2.14.0
- shfmt@3.6.0
- shellcheck@0.11.0
- - black@25.12.0
+ - black@26.1.0
- git-diff-check
- gitleaks@8.30.0
- clang-format@16.0.3
diff --git a/platformio.ini b/platformio.ini
index 4c19136af..7ba2c4166 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -119,7 +119,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/5a870c623a4e9ab7a7abe3d02950536f107d1a31.zip
+ https://github.com/meshtastic/device-ui/archive/3480b731d28b10d73414cf0dd7975bff745de8cf.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]
@@ -212,3 +212,30 @@ lib_deps =
sensirion/Sensirion Core@0.7.2
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
sensirion/Sensirion I2C SCD4x@1.1.0
+; Same as environmental_extra but without BSEC (saves ~3.5KB DRAM for original ESP32 targets)
+[environmental_extra_no_bsec]
+lib_deps =
+ # renovate: datasource=custom.pio depName=Adafruit BMP3XX packageName=adafruit/library/Adafruit BMP3XX Library
+ adafruit/Adafruit BMP3XX Library@2.1.6
+ # renovate: datasource=custom.pio depName=Adafruit MAX1704X packageName=adafruit/library/Adafruit MAX1704X
+ adafruit/Adafruit MAX1704X@1.0.3
+ # renovate: datasource=custom.pio depName=Adafruit SHTC3 packageName=adafruit/library/Adafruit SHTC3 Library
+ adafruit/Adafruit SHTC3 Library@1.0.2
+ # renovate: datasource=custom.pio depName=Adafruit LPS2X packageName=adafruit/library/Adafruit LPS2X
+ adafruit/Adafruit LPS2X@2.0.6
+ # renovate: datasource=custom.pio depName=Adafruit SHT31 packageName=adafruit/library/Adafruit SHT31 Library
+ adafruit/Adafruit SHT31 Library@2.2.2
+ # renovate: datasource=custom.pio depName=Adafruit VEML7700 packageName=adafruit/library/Adafruit VEML7700 Library
+ adafruit/Adafruit VEML7700 Library@2.1.6
+ # renovate: datasource=custom.pio depName=Adafruit SHT4x packageName=adafruit/library/Adafruit SHT4x Library
+ adafruit/Adafruit SHT4x Library@1.0.5
+ # renovate: datasource=custom.pio depName=SparkFun Qwiic Scale NAU7802 packageName=sparkfun/library/SparkFun Qwiic Scale NAU7802 Arduino Library
+ sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6
+ # renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
+ closedcube/ClosedCube OPT3001@1.1.2
+ # renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
+ https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
+ # renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
+ sensirion/Sensirion Core@0.7.2
+ # renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
+ sensirion/Sensirion I2C SCD4x@1.1.0
\ No newline at end of file
diff --git a/protobufs b/protobufs
index 4b9f104a1..77c8329a5 160000
--- a/protobufs
+++ b/protobufs
@@ -1 +1 @@
-Subproject commit 4b9f104a18ea43b1b2091ee2b48899fe43ad8a0b
+Subproject commit 77c8329a59a9c96a61c447b5d5f1a52ca583e4f2
diff --git a/src/configuration.h b/src/configuration.h
index eb258651c..f7b438272 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -155,6 +155,10 @@ along with this program. If not, see .
#endif
// Default system gain to 0 if not defined
+#ifndef NUM_PA_POINTS
+#define NUM_PA_POINTS 1
+#endif
+
#ifndef TX_GAIN_LORA
#define TX_GAIN_LORA 0
#endif
@@ -445,18 +449,6 @@ along with this program. If not, see .
#endif
#endif
-// BME680 BSEC2 support detection
-#if !defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
-#if defined(RAK_4631) || defined(TBEAM_V10)
-
-#define MESHTASTIC_BME680_BSEC2_SUPPORTED 1
-#define MESHTASTIC_BME680_HEADER
-#else
-#define MESHTASTIC_BME680_BSEC2_SUPPORTED 0
-#define MESHTASTIC_BME680_HEADER
-#endif // defined(RAK_4631)
-#endif // !defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
-
// -----------------------------------------------------------------------------
// Global switches to turn off features for a minimized build
// -----------------------------------------------------------------------------
diff --git a/src/main.cpp b/src/main.cpp
index 88282c837..c1096a240 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -105,6 +105,43 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
#include
#endif
+#ifdef ARCH_ESP32
+#ifdef DEBUG_PARTITION_TABLE
+#include "esp_partition.h"
+
+void printPartitionTable()
+{
+ printf("\n--- Partition Table ---\n");
+ // Print Column Headers
+ printf("| %-16s | %-4s | %-7s | %-10s | %-10s |\n", "Label", "Type", "Subtype", "Offset", "Size");
+ printf("|------------------|------|---------|------------|------------|\n");
+
+ // Create an iterator to find ALL partitions (Type ANY, Subtype ANY)
+ esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
+
+ // Loop through the iterator
+ if (it != NULL) {
+ do {
+ const esp_partition_t *part = esp_partition_get(it);
+
+ // Print details: Label, Type (Hex), Subtype (Hex), Offset (Hex), Size (Hex)
+ printf("| %-16s | 0x%02x | 0x%02x | 0x%08x | 0x%08x |\n", part->label, part->type, part->subtype, part->address,
+ part->size);
+
+ // Move to next partition
+ it = esp_partition_next(it);
+ } while (it != NULL);
+
+ // Release the iterator memory
+ esp_partition_iterator_release(it);
+ } else {
+ printf("No partitions found.\n");
+ }
+ printf("-----------------------\n");
+}
+#endif // DEBUG_PARTITION_TABLE
+#endif // ARCH_ESP32
+
#if HAS_BUTTON || defined(ARCH_PORTDUINO)
#include "input/ButtonThread.h"
@@ -648,7 +685,11 @@ void setup()
sensor_detected = true;
#endif
}
-
+#ifdef ARCH_ESP32
+#ifdef DEBUG_PARTITION_TABLE
+ printPartitionTable();
+#endif
+#endif // ARCH_ESP32
#ifdef ARCH_ESP32
// Don't init display if we don't have one or we are waking headless due to a timer event
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER) {
diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp
index aaaca719e..d0097a46a 100644
--- a/src/mesh/RadioInterface.cpp
+++ b/src/mesh/RadioInterface.cpp
@@ -666,18 +666,24 @@ void RadioInterface::limitPower(int8_t loraMaxPower)
power = maxPower;
}
-#ifndef NUM_PA_POINTS
- if (TX_GAIN_LORA > 0 && !devicestate.owner.is_licensed) {
- LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, TX_GAIN_LORA);
- power -= TX_GAIN_LORA;
- }
+#ifdef ARCH_PORTDUINO
+ size_t num_pa_points = portduino_config.num_pa_points;
+ const uint16_t *tx_gain = portduino_config.tx_gain_lora;
#else
- if (!devicestate.owner.is_licensed) {
+ size_t num_pa_points = NUM_PA_POINTS;
+ const uint16_t tx_gain[NUM_PA_POINTS] = {TX_GAIN_LORA};
+#endif
+
+ if (num_pa_points == 1) {
+ if (tx_gain[0] > 0 && !devicestate.owner.is_licensed) {
+ LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, tx_gain[0]);
+ power -= tx_gain[0];
+ }
+ } else if (!devicestate.owner.is_licensed) {
// we have an array of PA gain values. Find the highest power setting that works.
- const uint16_t tx_gain[NUM_PA_POINTS] = {TX_GAIN_LORA};
- for (int radio_dbm = 0; radio_dbm < NUM_PA_POINTS; radio_dbm++) {
+ for (int radio_dbm = 0; radio_dbm < num_pa_points; radio_dbm++) {
if (((radio_dbm + tx_gain[radio_dbm]) > power) ||
- ((radio_dbm == (NUM_PA_POINTS - 1)) && ((radio_dbm + tx_gain[radio_dbm]) <= power))) {
+ ((radio_dbm == (num_pa_points - 1)) && ((radio_dbm + tx_gain[radio_dbm]) <= power))) {
// we've exceeded the power limit, or hit the max we can do
LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", power, tx_gain[radio_dbm]);
power -= tx_gain[radio_dbm];
@@ -685,7 +691,7 @@ void RadioInterface::limitPower(int8_t loraMaxPower)
}
}
}
-#endif
+
if (power > loraMaxPower) // Clamp power to maximum defined level
power = loraMaxPower;
diff --git a/src/mesh/generated/meshtastic/admin.pb.h b/src/mesh/generated/meshtastic/admin.pb.h
index 26b4343e9..efdead91b 100644
--- a/src/mesh/generated/meshtastic/admin.pb.h
+++ b/src/mesh/generated/meshtastic/admin.pb.h
@@ -77,7 +77,9 @@ typedef enum _meshtastic_AdminMessage_ModuleConfigType {
/* TODO: REPLACE */
meshtastic_AdminMessage_ModuleConfigType_DETECTIONSENSOR_CONFIG = 11,
/* TODO: REPLACE */
- meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG = 12
+ meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG = 12,
+ /* TODO: REPLACE */
+ meshtastic_AdminMessage_ModuleConfigType_STATUSMESSAGE_CONFIG = 13
} meshtastic_AdminMessage_ModuleConfigType;
typedef enum _meshtastic_AdminMessage_BackupLocation {
@@ -323,8 +325,8 @@ extern "C" {
#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG+1))
#define _meshtastic_AdminMessage_ModuleConfigType_MIN meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG
-#define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG
-#define _meshtastic_AdminMessage_ModuleConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ModuleConfigType)(meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG+1))
+#define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_STATUSMESSAGE_CONFIG
+#define _meshtastic_AdminMessage_ModuleConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ModuleConfigType)(meshtastic_AdminMessage_ModuleConfigType_STATUSMESSAGE_CONFIG+1))
#define _meshtastic_AdminMessage_BackupLocation_MIN meshtastic_AdminMessage_BackupLocation_FLASH
#define _meshtastic_AdminMessage_BackupLocation_MAX meshtastic_AdminMessage_BackupLocation_SD
diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h
index 409805d24..57e7df8fc 100644
--- a/src/mesh/generated/meshtastic/deviceonly.pb.h
+++ b/src/mesh/generated/meshtastic/deviceonly.pb.h
@@ -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 2279
+#define meshtastic_BackupPreferences_size 2362
#define meshtastic_ChannelFile_size 718
#define meshtastic_DeviceState_size 1737
#define meshtastic_NodeInfoLite_size 196
diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h
index 2b44d0c9a..f11b13419 100644
--- a/src/mesh/generated/meshtastic/localonly.pb.h
+++ b/src/mesh/generated/meshtastic/localonly.pb.h
@@ -87,6 +87,9 @@ typedef struct _meshtastic_LocalModuleConfig {
/* Paxcounter Config */
bool has_paxcounter;
meshtastic_ModuleConfig_PaxcounterConfig paxcounter;
+ /* StatusMessage Config */
+ bool has_statusmessage;
+ meshtastic_ModuleConfig_StatusMessageConfig statusmessage;
} meshtastic_LocalModuleConfig;
@@ -96,9 +99,9 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0, false, meshtastic_Config_SecurityConfig_init_default}
-#define meshtastic_LocalModuleConfig_init_default {false, meshtastic_ModuleConfig_MQTTConfig_init_default, false, meshtastic_ModuleConfig_SerialConfig_init_default, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_default, false, meshtastic_ModuleConfig_StoreForwardConfig_init_default, false, meshtastic_ModuleConfig_RangeTestConfig_init_default, false, meshtastic_ModuleConfig_TelemetryConfig_init_default, false, meshtastic_ModuleConfig_CannedMessageConfig_init_default, 0, false, meshtastic_ModuleConfig_AudioConfig_init_default, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_default, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_default, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_default, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_default, false, meshtastic_ModuleConfig_PaxcounterConfig_init_default}
+#define meshtastic_LocalModuleConfig_init_default {false, meshtastic_ModuleConfig_MQTTConfig_init_default, false, meshtastic_ModuleConfig_SerialConfig_init_default, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_default, false, meshtastic_ModuleConfig_StoreForwardConfig_init_default, false, meshtastic_ModuleConfig_RangeTestConfig_init_default, false, meshtastic_ModuleConfig_TelemetryConfig_init_default, false, meshtastic_ModuleConfig_CannedMessageConfig_init_default, 0, false, meshtastic_ModuleConfig_AudioConfig_init_default, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_default, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_default, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_default, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_default, false, meshtastic_ModuleConfig_PaxcounterConfig_init_default, false, meshtastic_ModuleConfig_StatusMessageConfig_init_default}
#define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0, false, meshtastic_Config_SecurityConfig_init_zero}
-#define meshtastic_LocalModuleConfig_init_zero {false, meshtastic_ModuleConfig_MQTTConfig_init_zero, false, meshtastic_ModuleConfig_SerialConfig_init_zero, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero, false, meshtastic_ModuleConfig_StoreForwardConfig_init_zero, false, meshtastic_ModuleConfig_RangeTestConfig_init_zero, false, meshtastic_ModuleConfig_TelemetryConfig_init_zero, false, meshtastic_ModuleConfig_CannedMessageConfig_init_zero, 0, false, meshtastic_ModuleConfig_AudioConfig_init_zero, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_zero, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_zero, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_zero, false, meshtastic_ModuleConfig_PaxcounterConfig_init_zero}
+#define meshtastic_LocalModuleConfig_init_zero {false, meshtastic_ModuleConfig_MQTTConfig_init_zero, false, meshtastic_ModuleConfig_SerialConfig_init_zero, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero, false, meshtastic_ModuleConfig_StoreForwardConfig_init_zero, false, meshtastic_ModuleConfig_RangeTestConfig_init_zero, false, meshtastic_ModuleConfig_TelemetryConfig_init_zero, false, meshtastic_ModuleConfig_CannedMessageConfig_init_zero, 0, false, meshtastic_ModuleConfig_AudioConfig_init_zero, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_zero, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_zero, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_zero, false, meshtastic_ModuleConfig_PaxcounterConfig_init_zero, false, meshtastic_ModuleConfig_StatusMessageConfig_init_zero}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_LocalConfig_device_tag 1
@@ -124,6 +127,7 @@ extern "C" {
#define meshtastic_LocalModuleConfig_ambient_lighting_tag 12
#define meshtastic_LocalModuleConfig_detection_sensor_tag 13
#define meshtastic_LocalModuleConfig_paxcounter_tag 14
+#define meshtastic_LocalModuleConfig_statusmessage_tag 15
/* Struct field encoding specification for nanopb */
#define meshtastic_LocalConfig_FIELDLIST(X, a) \
@@ -161,7 +165,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, remote_hardware, 10) \
X(a, STATIC, OPTIONAL, MESSAGE, neighbor_info, 11) \
X(a, STATIC, OPTIONAL, MESSAGE, ambient_lighting, 12) \
X(a, STATIC, OPTIONAL, MESSAGE, detection_sensor, 13) \
-X(a, STATIC, OPTIONAL, MESSAGE, paxcounter, 14)
+X(a, STATIC, OPTIONAL, MESSAGE, paxcounter, 14) \
+X(a, STATIC, OPTIONAL, MESSAGE, statusmessage, 15)
#define meshtastic_LocalModuleConfig_CALLBACK NULL
#define meshtastic_LocalModuleConfig_DEFAULT NULL
#define meshtastic_LocalModuleConfig_mqtt_MSGTYPE meshtastic_ModuleConfig_MQTTConfig
@@ -177,6 +182,7 @@ X(a, STATIC, OPTIONAL, MESSAGE, paxcounter, 14)
#define meshtastic_LocalModuleConfig_ambient_lighting_MSGTYPE meshtastic_ModuleConfig_AmbientLightingConfig
#define meshtastic_LocalModuleConfig_detection_sensor_MSGTYPE meshtastic_ModuleConfig_DetectionSensorConfig
#define meshtastic_LocalModuleConfig_paxcounter_MSGTYPE meshtastic_ModuleConfig_PaxcounterConfig
+#define meshtastic_LocalModuleConfig_statusmessage_MSGTYPE meshtastic_ModuleConfig_StatusMessageConfig
extern const pb_msgdesc_t meshtastic_LocalConfig_msg;
extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
@@ -186,9 +192,9 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
#define meshtastic_LocalModuleConfig_fields &meshtastic_LocalModuleConfig_msg
/* Maximum encoded size of messages (where known) */
-#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalConfig_size
+#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size
#define meshtastic_LocalConfig_size 749
-#define meshtastic_LocalModuleConfig_size 675
+#define meshtastic_LocalModuleConfig_size 758
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/mesh/generated/meshtastic/mesh.pb.cpp b/src/mesh/generated/meshtastic/mesh.pb.cpp
index d8eee1203..7f1a738c6 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.cpp
+++ b/src/mesh/generated/meshtastic/mesh.pb.cpp
@@ -30,6 +30,9 @@ PB_BIND(meshtastic_StoreForwardPlusPlus, meshtastic_StoreForwardPlusPlus, 2)
PB_BIND(meshtastic_Waypoint, meshtastic_Waypoint, AUTO)
+PB_BIND(meshtastic_StatusMessage, meshtastic_StatusMessage, AUTO)
+
+
PB_BIND(meshtastic_MqttClientProxyMessage, meshtastic_MqttClientProxyMessage, 2)
diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h
index 68552ede5..aeae4bd84 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.h
+++ b/src/mesh/generated/meshtastic/mesh.pb.h
@@ -858,6 +858,11 @@ typedef struct _meshtastic_Waypoint {
uint32_t icon;
} meshtastic_Waypoint;
+/* Message for node status */
+typedef struct _meshtastic_StatusMessage {
+ char status[80];
+} meshtastic_StatusMessage;
+
typedef PB_BYTES_ARRAY_T(435) meshtastic_MqttClientProxyMessage_data_t;
/* This message will be proxied over the PhoneAPI for the client to deliver to the MQTT server */
typedef struct _meshtastic_MqttClientProxyMessage {
@@ -1402,6 +1407,7 @@ extern "C" {
+
#define meshtastic_MeshPacket_priority_ENUMTYPE meshtastic_MeshPacket_Priority
#define meshtastic_MeshPacket_delayed_ENUMTYPE meshtastic_MeshPacket_Delayed
#define meshtastic_MeshPacket_transport_mechanism_ENUMTYPE meshtastic_MeshPacket_TransportMechanism
@@ -1444,6 +1450,7 @@ extern "C" {
#define meshtastic_KeyVerification_init_default {0, {0, {0}}, {0, {0}}}
#define meshtastic_StoreForwardPlusPlus_init_default {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0}
#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0}
+#define meshtastic_StatusMessage_init_default {""}
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0, 0, 0, 0}
@@ -1476,6 +1483,7 @@ extern "C" {
#define meshtastic_KeyVerification_init_zero {0, {0, {0}}, {0, {0}}}
#define meshtastic_StoreForwardPlusPlus_init_zero {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0, 0}
#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0}
+#define meshtastic_StatusMessage_init_zero {""}
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0, 0, 0, 0}
@@ -1571,6 +1579,7 @@ extern "C" {
#define meshtastic_Waypoint_name_tag 6
#define meshtastic_Waypoint_description_tag 7
#define meshtastic_Waypoint_icon_tag 8
+#define meshtastic_StatusMessage_status_tag 1
#define meshtastic_MqttClientProxyMessage_topic_tag 1
#define meshtastic_MqttClientProxyMessage_data_tag 2
#define meshtastic_MqttClientProxyMessage_text_tag 3
@@ -1806,6 +1815,11 @@ X(a, STATIC, SINGULAR, FIXED32, icon, 8)
#define meshtastic_Waypoint_CALLBACK NULL
#define meshtastic_Waypoint_DEFAULT NULL
+#define meshtastic_StatusMessage_FIELDLIST(X, a) \
+X(a, STATIC, SINGULAR, STRING, status, 1)
+#define meshtastic_StatusMessage_CALLBACK NULL
+#define meshtastic_StatusMessage_DEFAULT NULL
+
#define meshtastic_MqttClientProxyMessage_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, topic, 1) \
X(a, STATIC, ONEOF, BYTES, (payload_variant,data,payload_variant.data), 2) \
@@ -2072,6 +2086,7 @@ extern const pb_msgdesc_t meshtastic_Data_msg;
extern const pb_msgdesc_t meshtastic_KeyVerification_msg;
extern const pb_msgdesc_t meshtastic_StoreForwardPlusPlus_msg;
extern const pb_msgdesc_t meshtastic_Waypoint_msg;
+extern const pb_msgdesc_t meshtastic_StatusMessage_msg;
extern const pb_msgdesc_t meshtastic_MqttClientProxyMessage_msg;
extern const pb_msgdesc_t meshtastic_MeshPacket_msg;
extern const pb_msgdesc_t meshtastic_NodeInfo_msg;
@@ -2106,6 +2121,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_KeyVerification_fields &meshtastic_KeyVerification_msg
#define meshtastic_StoreForwardPlusPlus_fields &meshtastic_StoreForwardPlusPlus_msg
#define meshtastic_Waypoint_fields &meshtastic_Waypoint_msg
+#define meshtastic_StatusMessage_fields &meshtastic_StatusMessage_msg
#define meshtastic_MqttClientProxyMessage_fields &meshtastic_MqttClientProxyMessage_msg
#define meshtastic_MeshPacket_fields &meshtastic_MeshPacket_msg
#define meshtastic_NodeInfo_fields &meshtastic_NodeInfo_msg
@@ -2161,6 +2177,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_QueueStatus_size 23
#define meshtastic_RouteDiscovery_size 256
#define meshtastic_Routing_size 259
+#define meshtastic_StatusMessage_size 81
#define meshtastic_StoreForwardPlusPlus_size 377
#define meshtastic_ToRadio_size 504
#define meshtastic_User_size 115
diff --git a/src/mesh/generated/meshtastic/module_config.pb.cpp b/src/mesh/generated/meshtastic/module_config.pb.cpp
index f262df6a3..bb57c3f2d 100644
--- a/src/mesh/generated/meshtastic/module_config.pb.cpp
+++ b/src/mesh/generated/meshtastic/module_config.pb.cpp
@@ -51,6 +51,9 @@ PB_BIND(meshtastic_ModuleConfig_CannedMessageConfig, meshtastic_ModuleConfig_Can
PB_BIND(meshtastic_ModuleConfig_AmbientLightingConfig, meshtastic_ModuleConfig_AmbientLightingConfig, AUTO)
+PB_BIND(meshtastic_ModuleConfig_StatusMessageConfig, meshtastic_ModuleConfig_StatusMessageConfig, AUTO)
+
+
PB_BIND(meshtastic_RemoteHardwarePin, meshtastic_RemoteHardwarePin, AUTO)
diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h
index dd0151e3f..46a7164d2 100644
--- a/src/mesh/generated/meshtastic/module_config.pb.h
+++ b/src/mesh/generated/meshtastic/module_config.pb.h
@@ -409,6 +409,12 @@ typedef struct _meshtastic_ModuleConfig_AmbientLightingConfig {
uint8_t blue;
} meshtastic_ModuleConfig_AmbientLightingConfig;
+/* StatusMessage config - Allows setting a status message for a node to periodically rebroadcast */
+typedef struct _meshtastic_ModuleConfig_StatusMessageConfig {
+ /* The actual status string */
+ char node_status[80];
+} meshtastic_ModuleConfig_StatusMessageConfig;
+
/* A GPIO pin definition for remote hardware module */
typedef struct _meshtastic_RemoteHardwarePin {
/* GPIO Pin number (must match Arduino) */
@@ -460,6 +466,8 @@ typedef struct _meshtastic_ModuleConfig {
meshtastic_ModuleConfig_DetectionSensorConfig detection_sensor;
/* TODO: REPLACE */
meshtastic_ModuleConfig_PaxcounterConfig paxcounter;
+ /* TODO: REPLACE */
+ meshtastic_ModuleConfig_StatusMessageConfig statusmessage;
} payload_variant;
} meshtastic_ModuleConfig;
@@ -515,6 +523,7 @@ extern "C" {
#define meshtastic_ModuleConfig_CannedMessageConfig_inputbroker_event_press_ENUMTYPE meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar
+
#define meshtastic_RemoteHardwarePin_type_ENUMTYPE meshtastic_RemoteHardwarePinType
@@ -534,6 +543,7 @@ extern "C" {
#define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
#define meshtastic_ModuleConfig_AmbientLightingConfig_init_default {0, 0, 0, 0, 0}
+#define meshtastic_ModuleConfig_StatusMessageConfig_init_default {""}
#define meshtastic_RemoteHardwarePin_init_default {0, "", _meshtastic_RemoteHardwarePinType_MIN}
#define meshtastic_ModuleConfig_init_zero {0, {meshtastic_ModuleConfig_MQTTConfig_init_zero}}
#define meshtastic_ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0, 0, "", 0, 0, false, meshtastic_ModuleConfig_MapReportSettings_init_zero}
@@ -550,6 +560,7 @@ extern "C" {
#define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
#define meshtastic_ModuleConfig_AmbientLightingConfig_init_zero {0, 0, 0, 0, 0}
+#define meshtastic_ModuleConfig_StatusMessageConfig_init_zero {""}
#define meshtastic_RemoteHardwarePin_init_zero {0, "", _meshtastic_RemoteHardwarePinType_MIN}
/* Field tags (for use in manual encoding/decoding) */
@@ -653,6 +664,7 @@ extern "C" {
#define meshtastic_ModuleConfig_AmbientLightingConfig_red_tag 3
#define meshtastic_ModuleConfig_AmbientLightingConfig_green_tag 4
#define meshtastic_ModuleConfig_AmbientLightingConfig_blue_tag 5
+#define meshtastic_ModuleConfig_StatusMessageConfig_node_status_tag 1
#define meshtastic_RemoteHardwarePin_gpio_pin_tag 1
#define meshtastic_RemoteHardwarePin_name_tag 2
#define meshtastic_RemoteHardwarePin_type_tag 3
@@ -672,6 +684,7 @@ extern "C" {
#define meshtastic_ModuleConfig_ambient_lighting_tag 11
#define meshtastic_ModuleConfig_detection_sensor_tag 12
#define meshtastic_ModuleConfig_paxcounter_tag 13
+#define meshtastic_ModuleConfig_statusmessage_tag 14
/* Struct field encoding specification for nanopb */
#define meshtastic_ModuleConfig_FIELDLIST(X, a) \
@@ -687,7 +700,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,remote_hardware,payload_vari
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,neighbor_info,payload_variant.neighbor_info), 10) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ambient_lighting,payload_variant.ambient_lighting), 11) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,detection_sensor,payload_variant.detection_sensor), 12) \
-X(a, STATIC, ONEOF, MESSAGE, (payload_variant,paxcounter,payload_variant.paxcounter), 13)
+X(a, STATIC, ONEOF, MESSAGE, (payload_variant,paxcounter,payload_variant.paxcounter), 13) \
+X(a, STATIC, ONEOF, MESSAGE, (payload_variant,statusmessage,payload_variant.statusmessage), 14)
#define meshtastic_ModuleConfig_CALLBACK NULL
#define meshtastic_ModuleConfig_DEFAULT NULL
#define meshtastic_ModuleConfig_payload_variant_mqtt_MSGTYPE meshtastic_ModuleConfig_MQTTConfig
@@ -703,6 +717,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,paxcounter,payload_variant.p
#define meshtastic_ModuleConfig_payload_variant_ambient_lighting_MSGTYPE meshtastic_ModuleConfig_AmbientLightingConfig
#define meshtastic_ModuleConfig_payload_variant_detection_sensor_MSGTYPE meshtastic_ModuleConfig_DetectionSensorConfig
#define meshtastic_ModuleConfig_payload_variant_paxcounter_MSGTYPE meshtastic_ModuleConfig_PaxcounterConfig
+#define meshtastic_ModuleConfig_payload_variant_statusmessage_MSGTYPE meshtastic_ModuleConfig_StatusMessageConfig
#define meshtastic_ModuleConfig_MQTTConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, enabled, 1) \
@@ -865,6 +880,11 @@ X(a, STATIC, SINGULAR, UINT32, blue, 5)
#define meshtastic_ModuleConfig_AmbientLightingConfig_CALLBACK NULL
#define meshtastic_ModuleConfig_AmbientLightingConfig_DEFAULT NULL
+#define meshtastic_ModuleConfig_StatusMessageConfig_FIELDLIST(X, a) \
+X(a, STATIC, SINGULAR, STRING, node_status, 1)
+#define meshtastic_ModuleConfig_StatusMessageConfig_CALLBACK NULL
+#define meshtastic_ModuleConfig_StatusMessageConfig_DEFAULT NULL
+
#define meshtastic_RemoteHardwarePin_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, gpio_pin, 1) \
X(a, STATIC, SINGULAR, STRING, name, 2) \
@@ -887,6 +907,7 @@ extern const pb_msgdesc_t meshtastic_ModuleConfig_RangeTestConfig_msg;
extern const pb_msgdesc_t meshtastic_ModuleConfig_TelemetryConfig_msg;
extern const pb_msgdesc_t meshtastic_ModuleConfig_CannedMessageConfig_msg;
extern const pb_msgdesc_t meshtastic_ModuleConfig_AmbientLightingConfig_msg;
+extern const pb_msgdesc_t meshtastic_ModuleConfig_StatusMessageConfig_msg;
extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
@@ -905,6 +926,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
#define meshtastic_ModuleConfig_TelemetryConfig_fields &meshtastic_ModuleConfig_TelemetryConfig_msg
#define meshtastic_ModuleConfig_CannedMessageConfig_fields &meshtastic_ModuleConfig_CannedMessageConfig_msg
#define meshtastic_ModuleConfig_AmbientLightingConfig_fields &meshtastic_ModuleConfig_AmbientLightingConfig_msg
+#define meshtastic_ModuleConfig_StatusMessageConfig_fields &meshtastic_ModuleConfig_StatusMessageConfig_msg
#define meshtastic_RemoteHardwarePin_fields &meshtastic_RemoteHardwarePin_msg
/* Maximum encoded size of messages (where known) */
@@ -921,6 +943,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
#define meshtastic_ModuleConfig_RangeTestConfig_size 12
#define meshtastic_ModuleConfig_RemoteHardwareConfig_size 96
#define meshtastic_ModuleConfig_SerialConfig_size 28
+#define meshtastic_ModuleConfig_StatusMessageConfig_size 81
#define meshtastic_ModuleConfig_StoreForwardConfig_size 24
#define meshtastic_ModuleConfig_TelemetryConfig_size 50
#define meshtastic_ModuleConfig_size 227
diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h
index 6b89c6a37..d31daa4b2 100644
--- a/src/mesh/generated/meshtastic/portnums.pb.h
+++ b/src/mesh/generated/meshtastic/portnums.pb.h
@@ -91,6 +91,11 @@ typedef enum _meshtastic_PortNum {
This module is specifically for Native Linux nodes, and provides a Git-style
chain of messages. */
meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP = 35,
+ /* Node Status module
+ ENCODING: protobuf
+ This module allows setting an extra string of status for a node.
+ Broadcasts on change and on a timer, possibly once a day. */
+ meshtastic_PortNum_NODE_STATUS_APP = 36,
/* Provides a hardware serial interface to send and receive from the Meshtastic network.
Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic
network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network.
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h
index dec89ba15..131dd9949 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.h
+++ b/src/mesh/generated/meshtastic/telemetry.pb.h
@@ -361,6 +361,8 @@ typedef struct _meshtastic_LocalStats {
uint32_t heap_free_bytes;
/* Number of packets that were dropped because the transmit queue was full. */
uint16_t num_tx_dropped;
+ /* Noise floor value measured in dBm */
+ int32_t noise_floor;
} meshtastic_LocalStats;
/* Health telemetry metrics */
@@ -458,7 +460,7 @@ extern "C" {
#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
-#define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_HealthMetrics_init_default {false, 0, false, 0, false, 0}
#define meshtastic_HostMetrics_init_default {0, 0, 0, false, 0, false, 0, 0, 0, 0, false, ""}
#define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}}
@@ -467,7 +469,7 @@ extern "C" {
#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
-#define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_HealthMetrics_init_zero {false, 0, false, 0, false, 0}
#define meshtastic_HostMetrics_init_zero {0, 0, 0, false, 0, false, 0, 0, 0, 0, false, ""}
#define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}}
@@ -556,6 +558,7 @@ extern "C" {
#define meshtastic_LocalStats_heap_total_bytes_tag 12
#define meshtastic_LocalStats_heap_free_bytes_tag 13
#define meshtastic_LocalStats_num_tx_dropped_tag 14
+#define meshtastic_LocalStats_noise_floor_tag 15
#define meshtastic_HealthMetrics_heart_bpm_tag 1
#define meshtastic_HealthMetrics_spO2_tag 2
#define meshtastic_HealthMetrics_temperature_tag 3
@@ -678,7 +681,8 @@ X(a, STATIC, SINGULAR, UINT32, num_tx_relay, 10) \
X(a, STATIC, SINGULAR, UINT32, num_tx_relay_canceled, 11) \
X(a, STATIC, SINGULAR, UINT32, heap_total_bytes, 12) \
X(a, STATIC, SINGULAR, UINT32, heap_free_bytes, 13) \
-X(a, STATIC, SINGULAR, UINT32, num_tx_dropped, 14)
+X(a, STATIC, SINGULAR, UINT32, num_tx_dropped, 14) \
+X(a, STATIC, SINGULAR, INT32, noise_floor, 15)
#define meshtastic_LocalStats_CALLBACK NULL
#define meshtastic_LocalStats_DEFAULT NULL
@@ -755,7 +759,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
#define meshtastic_EnvironmentMetrics_size 113
#define meshtastic_HealthMetrics_size 11
#define meshtastic_HostMetrics_size 264
-#define meshtastic_LocalStats_size 76
+#define meshtastic_LocalStats_size 87
#define meshtastic_Nau7802Config_size 16
#define meshtastic_PowerMetrics_size 81
#define meshtastic_Telemetry_size 272
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index 990ca0f46..1fda9bf13 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -235,22 +235,46 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
}
case meshtastic_AdminMessage_ota_request_tag: {
#if defined(ARCH_ESP32)
+ LOG_INFO("OTA Requested");
+
if (r->ota_request.ota_hash.size != 32) {
suppressRebootBanner = true;
- LOG_INFO("OTA Failed: Invalid `ota_hash` provided");
+ sendWarningAndLog("Cannot start OTA: Invalid `ota_hash` provided.");
break;
}
meshtastic_OTAMode mode = r->ota_request.reboot_ota_mode;
+ const char *mode_name = (mode == METHOD_OTA_BLE ? "BLE" : "WiFi");
+
+ // Check that we have an OTA partition
+ const esp_partition_t *part = MeshtasticOTA::getAppPartition();
+ if (part == NULL) {
+ suppressRebootBanner = true;
+ sendWarningAndLog("Cannot start OTA: Cannot find OTA Loader partition.");
+ break;
+ }
+
+ static esp_app_desc_t app_desc;
+ if (!MeshtasticOTA::getAppDesc(part, &app_desc)) {
+ suppressRebootBanner = true;
+ sendWarningAndLog("Cannot start OTA: Device does have a valid OTA Loader.");
+ break;
+ }
+
+ if (!MeshtasticOTA::checkOTACapability(&app_desc, mode)) {
+ suppressRebootBanner = true;
+ sendWarningAndLog("OTA Loader does not support %s", mode_name);
+ break;
+ }
+
if (MeshtasticOTA::trySwitchToOTA()) {
- LOG_INFO("OTA Requested");
suppressRebootBanner = true;
if (screen)
screen->startFirmwareUpdateScreen();
MeshtasticOTA::saveConfig(&config.network, mode, r->ota_request.ota_hash.bytes);
- LOG_INFO("Rebooting to WiFi OTA");
+ sendWarningAndLog("Rebooting to %s OTA", mode_name);
} else {
- LOG_INFO("WIFI OTA Failed");
+ sendWarningAndLog("Unable to switch to the OTA partition.");
}
#endif
int s = 1; // Reboot in 1 second, hard coded
@@ -1472,15 +1496,43 @@ void AdminModule::handleSendInputEvent(const meshtastic_AdminMessage_InputEvent
#endif
}
-void AdminModule::sendWarning(const char *message)
+void AdminModule::sendWarning(const char *format, ...)
{
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
+ if (!cn)
+ return;
+
cn->level = meshtastic_LogRecord_Level_WARNING;
cn->time = getValidTime(RTCQualityFromNet);
- strncpy(cn->message, message, sizeof(cn->message));
+
+ va_list args;
+ va_start(args, format);
+ // Format the arguments directly into the notification object
+ vsnprintf(cn->message, sizeof(cn->message), format, args);
+ va_end(args);
+
service->sendClientNotification(cn);
}
+void AdminModule::sendWarningAndLog(const char *format, ...)
+{
+ // We need a temporary buffer to hold the formatted text so we can log it
+ // Using 250 bytes as a safe upper limit for typical text notifications
+ char buf[250];
+
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buf, sizeof(buf), format, args);
+ va_end(args);
+
+ LOG_WARN(buf);
+ // 2. Call sendWarning
+ // SECURITY NOTE: We pass "%s", buf instead of just 'buf'.
+ // If 'buf' contained a % symbol (e.g. "Battery 50%"), passing it directly
+ // would crash sendWarning. "%s" treats it purely as text.
+ sendWarning("%s", buf);
+}
+
void disableBluetooth()
{
#if HAS_BLUETOOTH
diff --git a/src/modules/AdminModule.h b/src/modules/AdminModule.h
index 867751f49..c446887b3 100644
--- a/src/modules/AdminModule.h
+++ b/src/modules/AdminModule.h
@@ -1,7 +1,9 @@
-#include
-
#pragma once
+#ifdef ESP_PLATFORM
+#include
+#endif
#include "ProtobufModule.h"
+#include
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
@@ -71,7 +73,8 @@ class AdminModule : public ProtobufModule, public Obser
bool messageIsResponse(const meshtastic_AdminMessage *r);
bool messageIsRequest(const meshtastic_AdminMessage *r);
- void sendWarning(const char *message);
+ void sendWarning(const char *format, ...) __attribute__((format(printf, 2, 3)));
+ void sendWarningAndLog(const char *format, ...) __attribute__((format(printf, 2, 3)));
};
static constexpr const char *licensedModeMessage =
diff --git a/src/modules/StatusLEDModule.cpp b/src/modules/StatusLEDModule.cpp
index 33aa58127..fed035513 100644
--- a/src/modules/StatusLEDModule.cpp
+++ b/src/modules/StatusLEDModule.cpp
@@ -50,9 +50,10 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
break;
}
case meshtastic::BluetoothStatus::ConnectionState::CONNECTED: {
- ble_state = connected;
- PAIRING_LED_starttime = millis();
- break;
+ if (ble_state != connected) {
+ ble_state = connected;
+ PAIRING_LED_starttime = millis();
+ }
}
}
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp
index ec6fe4799..86a8606c2 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.cpp
+++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp
@@ -53,7 +53,7 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c
#include "Sensor/LTR390UVSensor.h"
#endif
-#if __has_include(MESHTASTIC_BME680_HEADER)
+#if __has_include() || __has_include()
#include "Sensor/BME680Sensor.h"
#endif
@@ -187,7 +187,7 @@ void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
#if __has_include()
addSensor(i2cScanner, ScanI2C::DeviceType::LTR390UV);
#endif
-#if __has_include(MESHTASTIC_BME680_HEADER)
+#if __has_include() || __has_include()
addSensor(i2cScanner, ScanI2C::DeviceType::BME_680);
#endif
#if __has_include()
diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.cpp b/src/modules/Telemetry/Sensor/BME680Sensor.cpp
index 22330ca75..3a1eb9532 100644
--- a/src/modules/Telemetry/Sensor/BME680Sensor.cpp
+++ b/src/modules/Telemetry/Sensor/BME680Sensor.cpp
@@ -1,6 +1,6 @@
#include "configuration.h"
-#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(MESHTASTIC_BME680_HEADER)
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && (__has_include() || __has_include())
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "BME680Sensor.h"
@@ -10,7 +10,7 @@
BME680Sensor::BME680Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME680, "BME680") {}
-#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
+#if __has_include()
int32_t BME680Sensor::runOnce()
{
if (!bme680.run()) {
@@ -18,13 +18,13 @@ int32_t BME680Sensor::runOnce()
}
return 35;
}
-#endif // defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
+#endif
bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
status = 0;
-#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
+#if __has_include()
if (!bme680.begin(dev->address.address, *bus))
checkStatus("begin");
@@ -56,7 +56,7 @@ bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
status = 1;
-#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
+#endif
initI2CSensor();
return status;
@@ -64,7 +64,7 @@ bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
-#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
+#if __has_include()
if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0)
return false;
@@ -98,11 +98,11 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
measurement->variant.environment_metrics.barometric_pressure = bme680->readPressure() / 100.0F;
measurement->variant.environment_metrics.gas_resistance = bme680->readGas() / 1000.0;
-#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
+#endif
return true;
}
-#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
+#if __has_include()
void BME680Sensor::loadState()
{
#ifdef FSCom
@@ -179,6 +179,6 @@ void BME680Sensor::checkStatus(const char *functionName)
else if (bme680.sensor.status > BME68X_OK)
LOG_WARN("%s BME68X code: %d", functionName, bme680.sensor.status);
}
-#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
+#endif
#endif
diff --git a/src/modules/Telemetry/Sensor/BME680Sensor.h b/src/modules/Telemetry/Sensor/BME680Sensor.h
index 9bef56e1e..eaeceb848 100644
--- a/src/modules/Telemetry/Sensor/BME680Sensor.h
+++ b/src/modules/Telemetry/Sensor/BME680Sensor.h
@@ -1,29 +1,29 @@
#include "configuration.h"
-#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(MESHTASTIC_BME680_HEADER)
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && (__has_include() || __has_include())
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
-#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
+#if __has_include()
#include
#include
#else
#include
#include
-#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
+#endif
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // That's 6 hours worth of millis()
-#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
+#if __has_include()
const uint8_t bsec_config[] = {
#include "config/bme680/bme680_iaq_33v_3s_4d/bsec_iaq.txt"
};
-#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
+#endif
class BME680Sensor : public TelemetrySensor
{
private:
-#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
+#if __has_include()
Bsec2 bme680;
#else
using BME680Ptr = std::unique_ptr;
@@ -31,10 +31,10 @@ class BME680Sensor : public TelemetrySensor
static BME680Ptr makeBME680(TwoWire *bus) { return std::make_unique(bus); }
BME680Ptr bme680;
-#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
+#endif
protected:
-#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
+#if __has_include()
const char *bsecConfigFileName = "/prefs/bsec.dat";
uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0};
uint8_t accuracy = 0;
@@ -51,13 +51,13 @@ class BME680Sensor : public TelemetrySensor
void loadState();
void updateState();
void checkStatus(const char *functionName);
-#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
+#endif
public:
BME680Sensor();
-#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
+#if __has_include()
virtual int32_t runOnce() override;
-#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
+#endif
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
diff --git a/src/platform/esp32/MeshtasticOTA.cpp b/src/platform/esp32/MeshtasticOTA.cpp
index b8cb052ef..4ca074723 100644
--- a/src/platform/esp32/MeshtasticOTA.cpp
+++ b/src/platform/esp32/MeshtasticOTA.cpp
@@ -1,13 +1,17 @@
#include "MeshtasticOTA.h"
#include "configuration.h"
+#ifdef ESP_PLATFORM
#include
#include
+#endif
namespace MeshtasticOTA
{
static const char *nvsNamespace = "MeshtasticOTA";
-static const char *appProjectName = "MeshtasticOTA";
+static const char *combinedAppProjectName = "MeshtasticOTA";
+static const char *bleOnlyAppProjectName = "MeshtasticOTA-BLE";
+static const char *wifiOnlyAppProjectName = "MeshtasticOTA-WiFi";
static bool updated = false;
@@ -68,21 +72,44 @@ bool getAppDesc(const esp_partition_t *part, esp_app_desc_t *app_desc)
LOG_INFO("esp_ota_get_partition_description failed");
return false;
}
- if (strcmp(app_desc->project_name, appProjectName) != 0) {
- LOG_INFO("app_desc->project_name == 0");
- return false;
- }
return true;
}
+bool checkOTACapability(esp_app_desc_t *app_desc, uint8_t method)
+{
+ // Combined loader supports all (both) transports, BLE and WiFi
+ if (strcmp(app_desc->project_name, combinedAppProjectName) == 0) {
+ LOG_INFO("OTA partition contains combined BLE/WiFi OTA Loader");
+ return true;
+ }
+ if (method == METHOD_OTA_BLE && strcmp(app_desc->project_name, bleOnlyAppProjectName) == 0) {
+ LOG_INFO("OTA partition contains BLE-only OTA Loader");
+ return true;
+ }
+ if (method == METHOD_OTA_WIFI && strcmp(app_desc->project_name, wifiOnlyAppProjectName) == 0) {
+ LOG_INFO("OTA partition contains WiFi-only OTA Loader");
+ return true;
+ }
+ LOG_INFO("OTA partition does not contain a known OTA loader");
+ return false;
+}
+
bool trySwitchToOTA()
{
const esp_partition_t *part = getAppPartition();
- esp_app_desc_t app_desc;
- if (!getAppDesc(part, &app_desc))
+
+ if (part == NULL) {
+ LOG_WARN("Unable to get app partition in preparation of OTA reboot");
return false;
- if (esp_ota_set_boot_partition(part) != ESP_OK)
+ }
+
+ uint8_t result = esp_ota_set_boot_partition(part);
+ // Partition and app checks should now be done in the AdminModule before this is called
+ if (result != ESP_OK) {
+ LOG_WARN("Unable to switch to OTA partiton. (Reason %d)", result);
return false;
+ }
+
return true;
}
diff --git a/src/platform/esp32/MeshtasticOTA.h b/src/platform/esp32/MeshtasticOTA.h
index 001eba039..7c158775f 100644
--- a/src/platform/esp32/MeshtasticOTA.h
+++ b/src/platform/esp32/MeshtasticOTA.h
@@ -3,12 +3,20 @@
#include "mesh-pb-constants.h"
#include
+#ifdef ESP_PLATFORM
+#include
+#endif
+
+#define METHOD_OTA_BLE 1
+#define METHOD_OTA_WIFI 2
namespace MeshtasticOTA
{
void initialize();
bool isUpdated();
-
+const esp_partition_t *getAppPartition();
+bool getAppDesc(const esp_partition_t *part, esp_app_desc_t *app_desc);
+bool checkOTACapability(esp_app_desc_t *app_desc, uint8_t method);
void recoverConfig(meshtastic_Config_NetworkConfig *network);
void saveConfig(meshtastic_Config_NetworkConfig *network, meshtastic_OTAMode method, uint8_t *ota_hash);
bool trySwitchToOTA();
diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index ec9bbedca..530cbe13e 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -649,6 +649,19 @@ bool loadConfig(const char *configPath)
if (yamlConfig["Lora"]["RF95_MAX_POWER"])
portduino_config.rf95_max_power = yamlConfig["Lora"]["RF95_MAX_POWER"].as(20);
+ if (yamlConfig["Lora"]["TX_GAIN_LORA"]) {
+ YAML::Node tx_gain_node = yamlConfig["Lora"]["TX_GAIN_LORA"];
+ if (tx_gain_node.IsSequence() && tx_gain_node.size() != 0) {
+ portduino_config.num_pa_points = min(tx_gain_node.size(), std::size(portduino_config.tx_gain_lora));
+ for (int i = 0; i < portduino_config.num_pa_points; i++) {
+ portduino_config.tx_gain_lora[i] = tx_gain_node[i].as();
+ }
+ } else {
+ portduino_config.num_pa_points = 1;
+ portduino_config.tx_gain_lora[0] = tx_gain_node.as(0);
+ }
+ }
+
if (portduino_config.lora_module != use_autoconf && portduino_config.lora_module != use_simradio &&
!portduino_config.force_simradio) {
portduino_config.dio2_as_rf_switch = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as(false);
diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h
index 3a6887421..eb4eb8925 100644
--- a/src/platform/portduino/PortduinoGlue.h
+++ b/src/platform/portduino/PortduinoGlue.h
@@ -91,6 +91,8 @@ extern struct portduino_config_struct {
int lora_usb_pid = 0x5512;
int lora_usb_vid = 0x1A86;
int spiSpeed = 2000000;
+ int num_pa_points = 1; // default to 1 point, with 0 gain
+ uint16_t tx_gain_lora[22] = {0};
pinMapping lora_cs_pin = {"Lora", "CS"};
pinMapping lora_irq_pin = {"Lora", "IRQ"};
pinMapping lora_busy_pin = {"Lora", "Busy"};
@@ -231,6 +233,17 @@ extern struct portduino_config_struct {
out << YAML::Key << "LR1120_MAX_POWER" << YAML::Value << lr1120_max_power;
if (rf95_max_power != 20)
out << YAML::Key << "RF95_MAX_POWER" << YAML::Value << rf95_max_power;
+
+ if (num_pa_points > 1) {
+ out << YAML::Key << "TX_GAIN_LORA" << YAML::Value << YAML::Flow << YAML::BeginSeq;
+ for (int i = 0; i < num_pa_points; i++) {
+ out << YAML::Value << tx_gain_lora[i];
+ }
+ out << YAML::EndSeq;
+ } else if (tx_gain_lora[0] != 0) {
+ out << YAML::Key << "TX_GAIN_LORA" << YAML::Value << tx_gain_lora[0];
+ }
+
out << YAML::Key << "DIO2_AS_RF_SWITCH" << YAML::Value << dio2_as_rf_switch;
if (dio3_tcxo_voltage != 0)
out << YAML::Key << "DIO3_TCXO_VOLTAGE" << YAML::Value << YAML::Precision(3) << (float)dio3_tcxo_voltage / 1000;
diff --git a/variants/esp32/esp32.ini b/variants/esp32/esp32.ini
index 5999bc098..502d937b0 100644
--- a/variants/esp32/esp32.ini
+++ b/variants/esp32/esp32.ini
@@ -7,3 +7,22 @@ custom_esp32_kind = esp32
build_flags =
${esp32_common.build_flags}
-DMESHTASTIC_EXCLUDE_AUDIO=1
+; Override lib_deps to use environmental_extra_no_bsec instead of environmental_extra
+; BSEC library uses ~3.5KB DRAM which causes overflow on original ESP32 targets
+lib_deps =
+ ${arduino_base.lib_deps}
+ ${networking_base.lib_deps}
+ ${networking_extra.lib_deps}
+ ${environmental_base.lib_deps}
+ ${environmental_extra_no_bsec.lib_deps}
+ ${radiolib_base.lib_deps}
+ # renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master
+ https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip
+ # renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
+ h2zero/NimBLE-Arduino@^1.4.3
+ # renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
+ https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
+ # renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
+ https://github.com/lewisxhe/XPowersLib/archive/v0.3.2.zip
+ # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
+ rweather/Crypto@0.4.0
\ No newline at end of file
diff --git a/variants/esp32c6/m5stack_unitc6l/platformio.ini b/variants/esp32c6/m5stack_unitc6l/platformio.ini
index 3054d342a..ed26598d2 100644
--- a/variants/esp32c6/m5stack_unitc6l/platformio.ini
+++ b/variants/esp32c6/m5stack_unitc6l/platformio.ini
@@ -10,6 +10,8 @@ custom_meshtastic_tags = M5Stack
extends = esp32c6_base
board = esp32-c6-devkitc-1
+board_upload.flash_size = 16MB
+board_build.partitions = default_16MB.csv
;OpenOCD flash method
;upload_protocol = esp-builtin
;Normal method
diff --git a/variants/stm32/russell/platformio.ini b/variants/stm32/russell/platformio.ini
new file mode 100644
index 000000000..0dd57a2c7
--- /dev/null
+++ b/variants/stm32/russell/platformio.ini
@@ -0,0 +1,21 @@
+; Russell is a board designed to mount on an ER34615/IFR32700 cell and go Up! on a balloon
+; Hardware repository: https://github.com/Meshtastic-Malaysia/russell
+; - RAK3172 STM32WLE5CCU6 MCU + integrated SX1262 LoRa
+; - CDtop CD-PA1010D GPS
+; - Bosch Sensortec BME280 sensor
+; - Consonance CN3158 LiFePO4 solar charger
+[env:russell]
+extends = stm32_base
+board = wiscore_rak3172
+board_level = extra
+board_upload.maximum_size = 233472 ; reserve the last 28KB for filesystem
+build_flags =
+ ${stm32_base.build_flags}
+ -Ivariants/stm32/russell
+ -DPRIVATE_HW
+lib_deps =
+ ${stm32_base.lib_deps}
+ # renovate: datasource=custom.pio depName=Adafruit BME280 packageName=adafruit/library/Adafruit BME280 Library
+ adafruit/Adafruit BME280 Library@2.3.0
+
+upload_port = stlink
diff --git a/variants/stm32/russell/rfswitch.h b/variants/stm32/russell/rfswitch.h
new file mode 100644
index 000000000..ec4829de6
--- /dev/null
+++ b/variants/stm32/russell/rfswitch.h
@@ -0,0 +1,7 @@
+// Pins from https://forum.rakwireless.com/t/rak3172-internal-schematic/4557/2
+// PB8, PC13
+
+static const RADIOLIB_PIN_TYPE rfswitch_pins[5] = {PB8, PC13, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
+
+static const Module::RfSwitchMode_t rfswitch_table[4] = {
+ {STM32WLx::MODE_IDLE, {LOW, LOW}}, {STM32WLx::MODE_RX, {HIGH, LOW}}, {STM32WLx::MODE_TX_HP, {LOW, HIGH}}, END_OF_MODE_TABLE};
diff --git a/variants/stm32/russell/variant.h b/variants/stm32/russell/variant.h
new file mode 100644
index 000000000..796302d34
--- /dev/null
+++ b/variants/stm32/russell/variant.h
@@ -0,0 +1,41 @@
+#ifndef _VARIANT_RUSSELL_
+#define _VARIANT_RUSSELL_
+
+#define USE_STM32WLx
+
+// I/O
+#define LED_PIN PA0 // Red LED
+#define LED_STATE_ON 1
+#define BUTTON_PIN PH3 // Shared with BOOT0
+#define BUTTON_NEED_PULLUP
+// Charger IC charge/standby pins are open-drain with no hardware pull-up:
+// Internal pull-up is needed on STM32 (TODO)
+// #define EXT_CHRG_DETECT PA5
+// #define EXT_PWR_DETECT PA4
+
+// Bosch Sensortec BME280
+#define HAS_SENSOR 1
+
+// CDtop CD-PA1010D
+#define ENABLE_HWSERIAL1
+#define PIN_SERIAL1_RX PB7
+#define PIN_SERIAL1_TX PB6
+#define HAS_GPS 1
+#define PIN_GPS_STANDBY PA15
+#define GPS_RX_PIN PB7
+#define GPS_TX_PIN PB6
+
+// LoRa
+/*
+ * RAK3172 (-20–85°C) -> No TCXO
+ * RAK3172-T (-40–85°C) -> 3.0V TCXO
+ * https://github.com/RAKWireless/RAK-STM32-RUI/blob/e5a28be8fab1a492bd9223dd425ca33a8a297d90/variants/WisDuo_RAK3172-T_Board/radio_conf.h#L91
+ */
+#define TCXO_OPTIONAL
+#define SX126X_DIO3_TCXO_VOLTAGE 3.0
+
+// Required to avoid Serial1 conflicts due to board definition here:
+// https://github.com/stm32duino/Arduino_Core_STM32/blob/main/variants/STM32WLxx/WL54CCU_WL55CCU_WLE4C(8-B-C)U_WLE5C(8-B-C)U/variant_RAK3172_MODULE.h
+#define RAK3172
+
+#endif