diff --git a/src/input/RotaryEncoderImpl.cpp b/src/input/RotaryEncoderImpl.cpp
index d3fcbbf9d..7d638dd71 100644
--- a/src/input/RotaryEncoderImpl.cpp
+++ b/src/input/RotaryEncoderImpl.cpp
@@ -40,10 +40,7 @@ bool RotaryEncoderImpl::init()
int32_t RotaryEncoderImpl::runOnce()
{
- InputEvent e;
- e.inputEvent = INPUT_BROKER_NONE;
- e.source = this->originName;
-
+ InputEvent e{originName, INPUT_BROKER_NONE, 0, 0, 0};
static uint32_t lastPressed = millis();
if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) {
if (lastPressed + 200 < millis()) {
@@ -70,7 +67,7 @@ int32_t RotaryEncoderImpl::runOnce()
this->notifyObservers(&e);
}
- return 20;
+ return 10;
}
#endif
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 6667f9f17..8d576f008 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1506,6 +1506,9 @@ extern meshtastic_DeviceMetadata getDeviceMetadata()
deviceMetadata.hw_model = HW_VENDOR;
deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled;
deviceMetadata.excluded_modules = meshtastic_ExcludedModules_EXCLUDED_NONE;
+#if MESHTASTIC_EXCLUDE_MQTT
+ deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_MQTT_CONFIG;
+#endif
#if MESHTASTIC_EXCLUDE_REMOTEHARDWARE
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_REMOTEHARDWARE_CONFIG;
#endif
@@ -1528,10 +1531,21 @@ extern meshtastic_DeviceMetadata getDeviceMetadata()
#if NO_EXT_GPIO && NO_GPS || MESHTASTIC_EXCLUDE_SERIAL
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_SERIAL_CONFIG;
#endif
-#ifndef ARCH_ESP32
+#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_PAXCOUNTER
+ // PAXCOUNTER is only supported on ESP32 due to memory constraints
+#else
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_PAXCOUNTER_CONFIG;
#endif
-#if !defined(HAS_RGB_LED) && !RAK_4631
+#if MESHTASTIC_EXCLUDE_STOREFORWARD
+ deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_STOREFORWARD_CONFIG;
+#endif
+#if MESHTASTIC_EXCLUDE_RANGETEST
+ deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_RANGETEST_CONFIG;
+#endif
+#if MESHTASTIC_EXCLUDE_NEIGHBORINFO
+ deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_NEIGHBORINFO_CONFIG;
+#endif
+#if (!defined(HAS_RGB_LED) && !defined(RAK_4631)) || defined(MESHTASTIC_EXCLUDE_AMBIENTLIGHTING)
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_AMBIENTLIGHTING_CONFIG;
#endif
diff --git a/src/mesh/NextHopRouter.cpp b/src/mesh/NextHopRouter.cpp
index 794b25aa6..7ceca2195 100644
--- a/src/mesh/NextHopRouter.cpp
+++ b/src/mesh/NextHopRouter.cpp
@@ -74,10 +74,11 @@ void NextHopRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtast
if (p->from != 0) {
meshtastic_NodeInfoLite *origTx = nodeDB->getMeshNode(p->from);
if (origTx) {
- // Either relayer of ACK was also a relayer of the packet, or we were the relayer and the ACK came directly from
- // the destination
+ // Either relayer of ACK was also a relayer of the packet, or we were the *only* relayer and the ACK came directly
+ // from the destination
if (wasRelayer(p->relay_node, p->decoded.request_id, p->to) ||
- (wasRelayer(ourRelayID, p->decoded.request_id, p->to) && p->hop_start != 0 && p->hop_start == p->hop_limit)) {
+ (p->hop_start != 0 && p->hop_start == p->hop_limit &&
+ wasSoleRelayer(ourRelayID, p->decoded.request_id, p->to))) {
if (origTx->next_hop != p->relay_node) { // Not already set
LOG_INFO("Update next hop of 0x%x to 0x%x based on ACK/reply", p->from, p->relay_node);
origTx->next_hop = p->relay_node;
diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 2ab6fda59..52a18a53f 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -775,7 +775,9 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.version = DEVICESTATE_CUR_VER;
moduleConfig.has_mqtt = true;
+#if !MESHTASTIC_EXCLUDE_RANGETEST
moduleConfig.has_range_test = true;
+#endif
moduleConfig.has_serial = true;
moduleConfig.has_store_forward = true;
moduleConfig.has_telemetry = true;
@@ -841,6 +843,12 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.canned_message.inputbroker_event_press = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
#endif
moduleConfig.has_canned_message = true;
+#if !MESHTASTIC_EXCLUDE_AUDIO
+ moduleConfig.has_audio = true;
+#endif
+#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
+ moduleConfig.has_paxcounter = true;
+#endif
#if USERPREFS_MQTT_ENABLED && !MESHTASTIC_EXCLUDE_MQTT
moduleConfig.mqtt.enabled = true;
#endif
@@ -883,12 +891,14 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.detection_sensor.detection_trigger_type = meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH;
moduleConfig.detection_sensor.minimum_broadcast_secs = 45;
+#if !MESHTASTIC_EXCLUDE_AMBIENTLIGHTING
moduleConfig.has_ambient_lighting = 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
initModuleConfigIntervals();
}
@@ -1428,15 +1438,25 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat)
moduleConfig.has_canned_message = true;
moduleConfig.has_external_notification = true;
moduleConfig.has_mqtt = true;
+#if !MESHTASTIC_EXCLUDE_RANGETEST
moduleConfig.has_range_test = true;
+#endif
moduleConfig.has_serial = true;
+#if !MESHTASTIC_EXCLUDE_STOREFORWARD
moduleConfig.has_store_forward = true;
+#endif
moduleConfig.has_telemetry = true;
moduleConfig.has_neighbor_info = true;
moduleConfig.has_detection_sensor = true;
+#if !MESHTASTIC_EXCLUDE_AMBIENTLIGHTING
moduleConfig.has_ambient_lighting = true;
+#endif
+#if !MESHTASTIC_EXCLUDE_AUDIO
moduleConfig.has_audio = true;
+#endif
+#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
moduleConfig.has_paxcounter = true;
+#endif
success &=
saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig);
diff --git a/src/mesh/PacketHistory.cpp b/src/mesh/PacketHistory.cpp
index 3902c1057..735386d79 100644
--- a/src/mesh/PacketHistory.cpp
+++ b/src/mesh/PacketHistory.cpp
@@ -294,7 +294,7 @@ void PacketHistory::insert(const PacketRecord &r)
/* Check if a certain node was a relayer of a packet in the history given an ID and sender
* @return true if node was indeed a relayer, false if not */
-bool PacketHistory::wasRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender)
+bool PacketHistory::wasRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender, bool *wasSole)
{
if (!initOk()) {
LOG_ERROR("PacketHistory - wasRelayer: NOT INITIALIZED!");
@@ -322,27 +322,42 @@ bool PacketHistory::wasRelayer(const uint8_t relayer, const uint32_t id, const N
found->sender, found->id, found->next_hop, millis() - found->rxTimeMsec, found->relayed_by[0], found->relayed_by[1],
found->relayed_by[2], relayer);
#endif
- return wasRelayer(relayer, *found);
+ return wasRelayer(relayer, *found, wasSole);
}
/* Check if a certain node was a relayer of a packet in the history given iterator
* @return true if node was indeed a relayer, false if not */
-bool PacketHistory::wasRelayer(const uint8_t relayer, const PacketRecord &r)
+bool PacketHistory::wasRelayer(const uint8_t relayer, const PacketRecord &r, bool *wasSole)
{
- for (uint8_t i = 0; i < NUM_RELAYERS; i++) {
+ bool found = false;
+ bool other_present = false;
+
+ for (uint8_t i = 0; i < NUM_RELAYERS; ++i) {
if (r.relayed_by[i] == relayer) {
-#if VERBOSE_PACKET_HISTORY
- LOG_DEBUG("Packet History - was rel.PR.: s=%08x id=%08x rls=%02x %02x %02x / rl=%02x? YES", r.sender, r.id,
- r.relayed_by[0], r.relayed_by[1], r.relayed_by[2], relayer);
-#endif
- return true;
+ found = true;
+ } else if (r.relayed_by[i] != 0) {
+ other_present = true;
}
}
+
+ if (wasSole) {
+ *wasSole = (found && !other_present);
+ }
+
#if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - was rel.PR.: s=%08x id=%08x rls=%02x %02x %02x / rl=%02x? NO", r.sender, r.id, r.relayed_by[0],
r.relayed_by[1], r.relayed_by[2], relayer);
#endif
- return false;
+
+ return found;
+}
+
+// Check if a certain node was the *only* relayer of a packet in the history given an ID and sender
+bool PacketHistory::wasSoleRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender)
+{
+ bool wasSole = false;
+ wasRelayer(relayer, id, sender, &wasSole);
+ return wasSole;
}
// Remove a relayer from the list of relayers of a packet in the history given an ID and sender
diff --git a/src/mesh/PacketHistory.h b/src/mesh/PacketHistory.h
index 9f14a4cf0..4b53c8f6a 100644
--- a/src/mesh/PacketHistory.h
+++ b/src/mesh/PacketHistory.h
@@ -34,8 +34,9 @@ class PacketHistory
void insert(const PacketRecord &r); // Insert or replace a packet record in the history
/* Check if a certain node was a relayer of a packet in the history given iterator
+ * If wasSole is not nullptr, it will be set to true if the relayer was the only relayer of that packet
* @return true if node was indeed a relayer, false if not */
- bool wasRelayer(const uint8_t relayer, const PacketRecord &r);
+ bool wasRelayer(const uint8_t relayer, const PacketRecord &r, bool *wasSole = nullptr);
PacketHistory(const PacketHistory &); // non construction-copyable
PacketHistory &operator=(const PacketHistory &); // non copyable
@@ -54,8 +55,12 @@ class PacketHistory
bool *weWereNextHop = nullptr);
/* Check if a certain node was a relayer of a packet in the history given an ID and sender
+ * If wasSole is not nullptr, it will be set to true if the relayer was the only relayer of that packet
* @return true if node was indeed a relayer, false if not */
- bool wasRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender);
+ bool wasRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender, bool *wasSole = nullptr);
+
+ // Check if a certain node was the *only* relayer of a packet in the history given an ID and sender
+ bool wasSoleRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender);
// Remove a relayer from the list of relayers of a packet in the history given an ID and sender
void removeRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender);
diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp
index a3a8a2087..d11eff9e7 100644
--- a/src/mesh/PhoneAPI.cpp
+++ b/src/mesh/PhoneAPI.cpp
@@ -34,6 +34,21 @@
// Flag to indicate a heartbeat was received and we should send queue status
bool heartbeatReceived = false;
+// Helper function to skip excluded module configs and advance state
+size_t PhoneAPI::skipExcludedModuleConfig(uint8_t *buf)
+{
+ config_state++;
+ if (config_state > (_meshtastic_AdminMessage_ModuleConfigType_MAX + 1)) {
+ if (config_nonce == SPECIAL_NONCE_ONLY_CONFIG) {
+ state = STATE_SEND_FILEMANIFEST;
+ } else {
+ state = STATE_SEND_OTHER_NODEINFOS;
+ }
+ config_state = 0;
+ }
+ return getFromRadio(buf);
+}
+
PhoneAPI::PhoneAPI()
{
lastContactMsec = millis();
@@ -354,20 +369,35 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.moduleConfig.payload_variant.serial = moduleConfig.serial;
break;
case meshtastic_ModuleConfig_external_notification_tag:
+#if !(NO_EXT_GPIO || MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION)
LOG_DEBUG("Send module config: ext notification");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_external_notification_tag;
fromRadioScratch.moduleConfig.payload_variant.external_notification = moduleConfig.external_notification;
break;
+#else
+ LOG_DEBUG("External Notification module excluded from build, skipping");
+ return skipExcludedModuleConfig(buf);
+#endif
case meshtastic_ModuleConfig_store_forward_tag:
+#if !MESHTASTIC_EXCLUDE_STOREFORWARD
LOG_DEBUG("Send module config: store forward");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_store_forward_tag;
fromRadioScratch.moduleConfig.payload_variant.store_forward = moduleConfig.store_forward;
break;
+#else
+ LOG_DEBUG("Store & Forward module excluded from build, skipping");
+ return skipExcludedModuleConfig(buf);
+#endif
case meshtastic_ModuleConfig_range_test_tag:
+#if !MESHTASTIC_EXCLUDE_RANGETEST
LOG_DEBUG("Send module config: range test");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_range_test_tag;
fromRadioScratch.moduleConfig.payload_variant.range_test = moduleConfig.range_test;
break;
+#else
+ LOG_DEBUG("Range Test module excluded from build, skipping");
+ return skipExcludedModuleConfig(buf);
+#endif
case meshtastic_ModuleConfig_telemetry_tag:
LOG_DEBUG("Send module config: telemetry");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_telemetry_tag;
@@ -379,10 +409,15 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.moduleConfig.payload_variant.canned_message = moduleConfig.canned_message;
break;
case meshtastic_ModuleConfig_audio_tag:
+#if !MESHTASTIC_EXCLUDE_AUDIO
LOG_DEBUG("Send module config: audio");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_audio_tag;
fromRadioScratch.moduleConfig.payload_variant.audio = moduleConfig.audio;
break;
+#else
+ LOG_DEBUG("Audio module excluded from build, skipping");
+ return skipExcludedModuleConfig(buf);
+#endif
case meshtastic_ModuleConfig_remote_hardware_tag:
LOG_DEBUG("Send module config: remote hardware");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_remote_hardware_tag;
@@ -399,15 +434,25 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.moduleConfig.payload_variant.detection_sensor = moduleConfig.detection_sensor;
break;
case meshtastic_ModuleConfig_ambient_lighting_tag:
+#if !MESHTASTIC_EXCLUDE_AMBIENTLIGHTING
LOG_DEBUG("Send module config: ambient lighting");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_ambient_lighting_tag;
fromRadioScratch.moduleConfig.payload_variant.ambient_lighting = moduleConfig.ambient_lighting;
break;
+#else
+ LOG_DEBUG("Ambient Lighting module excluded from build, skipping");
+ return skipExcludedModuleConfig(buf);
+#endif
case meshtastic_ModuleConfig_paxcounter_tag:
+#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
LOG_DEBUG("Send module config: paxcounter");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_paxcounter_tag;
fromRadioScratch.moduleConfig.payload_variant.paxcounter = moduleConfig.paxcounter;
break;
+#else
+ LOG_DEBUG("Paxcounter module excluded from build, skipping");
+ return skipExcludedModuleConfig(buf);
+#endif
default:
LOG_ERROR("Unknown module config type %d", config_state);
}
diff --git a/src/mesh/PhoneAPI.h b/src/mesh/PhoneAPI.h
index 0d7772d17..6b4bb6fc1 100644
--- a/src/mesh/PhoneAPI.h
+++ b/src/mesh/PhoneAPI.h
@@ -172,4 +172,7 @@ class PhoneAPI
/// If the mesh service tells us fromNum has changed, tell the phone
virtual int onNotify(uint32_t newValue) override;
+
+ /// Helper function to skip excluded module configs and advance state
+ size_t skipExcludedModuleConfig(uint8_t *buf);
};
diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h
index 67d461611..59e55db3f 100644
--- a/src/mesh/generated/meshtastic/config.pb.h
+++ b/src/mesh/generated/meshtastic/config.pb.h
@@ -64,7 +64,12 @@ typedef enum _meshtastic_Config_DeviceConfig_Role {
in areas not already covered by other routers, or to bridge around problematic terrain,
but should not be given priority over other routers in order to avoid unnecessaraily
consuming hops. */
- meshtastic_Config_DeviceConfig_Role_ROUTER_LATE = 11
+ meshtastic_Config_DeviceConfig_Role_ROUTER_LATE = 11,
+ /* Description: Treats packets from or to favorited nodes as ROUTER, and all other packets as CLIENT.
+ Technical Details: Used for stronger attic/roof nodes to distribute messages more widely
+ from weaker, indoor, or less-well-positioned nodes. Recommended for users with multiple nodes
+ where one CLIENT_BASE acts as a more powerful base station, such as an attic/roof node. */
+ meshtastic_Config_DeviceConfig_Role_CLIENT_BASE = 12
} meshtastic_Config_DeviceConfig_Role;
/* Defines the device's behavior for how messages are rebroadcast */
@@ -646,8 +651,8 @@ extern "C" {
/* Helper constants for enums */
#define _meshtastic_Config_DeviceConfig_Role_MIN meshtastic_Config_DeviceConfig_Role_CLIENT
-#define _meshtastic_Config_DeviceConfig_Role_MAX meshtastic_Config_DeviceConfig_Role_ROUTER_LATE
-#define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_ROUTER_LATE+1))
+#define _meshtastic_Config_DeviceConfig_Role_MAX meshtastic_Config_DeviceConfig_Role_CLIENT_BASE
+#define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_CLIENT_BASE+1))
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN meshtastic_Config_DeviceConfig_RebroadcastMode_ALL
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY
diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h
index ce3722aa7..2a4e77870 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.h
+++ b/src/mesh/generated/meshtastic/mesh.pb.h
@@ -272,6 +272,8 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_HELTEC_MESH_SOLAR = 108,
/* Lilygo T-Echo Lite */
meshtastic_HardwareModel_T_ECHO_LITE = 109,
+ /* New Heltec LoRA32 with ESP32-S3 CPU */
+ meshtastic_HardwareModel_HELTEC_V4 = 110,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */
diff --git a/src/mesh/http/ContentHandler.cpp b/src/mesh/http/ContentHandler.cpp
index 42ebb8417..74953d8fc 100644
--- a/src/mesh/http/ContentHandler.cpp
+++ b/src/mesh/http/ContentHandler.cpp
@@ -342,6 +342,11 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
res->print(value->Stringify().c_str());
delete value;
+
+ // Clean up the fileList to prevent memory leak
+ for (auto *val : fileList) {
+ delete val;
+ }
}
void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
@@ -610,33 +615,38 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->println("");
}
+ // Helper lambda to create JSON array and clean up memory properly
+ auto createJSONArrayFromLog = [](uint32_t *logArray, int count) -> JSONValue * {
+ JSONArray tempArray;
+ for (int i = 0; i < count; i++) {
+ tempArray.push_back(new JSONValue((int)logArray[i]));
+ }
+ JSONValue *result = new JSONValue(tempArray);
+ // Clean up original array to prevent memory leak
+ for (auto *val : tempArray) {
+ delete val;
+ }
+ return result;
+ };
+
// data->airtime->tx_log
- JSONArray txLogValues;
uint32_t *logArray;
logArray = airTime->airtimeReport(TX_LOG);
- for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
- txLogValues.push_back(new JSONValue((int)logArray[i]));
- }
+ JSONValue *txLogJsonValue = createJSONArrayFromLog(logArray, airTime->getPeriodsToLog());
// data->airtime->rx_log
- JSONArray rxLogValues;
logArray = airTime->airtimeReport(RX_LOG);
- for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
- rxLogValues.push_back(new JSONValue((int)logArray[i]));
- }
+ JSONValue *rxLogJsonValue = createJSONArrayFromLog(logArray, airTime->getPeriodsToLog());
// data->airtime->rx_all_log
- JSONArray rxAllLogValues;
logArray = airTime->airtimeReport(RX_ALL_LOG);
- for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
- rxAllLogValues.push_back(new JSONValue((int)logArray[i]));
- }
+ JSONValue *rxAllLogJsonValue = createJSONArrayFromLog(logArray, airTime->getPeriodsToLog());
// data->airtime
JSONObject jsonObjAirtime;
- jsonObjAirtime["tx_log"] = new JSONValue(txLogValues);
- jsonObjAirtime["rx_log"] = new JSONValue(rxLogValues);
- jsonObjAirtime["rx_all_log"] = new JSONValue(rxAllLogValues);
+ jsonObjAirtime["tx_log"] = txLogJsonValue;
+ jsonObjAirtime["rx_log"] = rxLogJsonValue;
+ jsonObjAirtime["rx_all_log"] = rxAllLogJsonValue;
jsonObjAirtime["channel_utilization"] = new JSONValue(airTime->channelUtilizationPercent());
jsonObjAirtime["utilization_tx"] = new JSONValue(airTime->utilizationTXPercent());
jsonObjAirtime["seconds_since_boot"] = new JSONValue(int(airTime->getSecondsSinceBoot()));
@@ -765,6 +775,11 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res)
JSONValue *value = new JSONValue(jsonObjOuter);
res->print(value->Stringify().c_str());
delete value;
+
+ // Clean up the nodesArray to prevent memory leak
+ for (auto *val : nodesArray) {
+ delete val;
+ }
}
/*
@@ -955,5 +970,10 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
JSONValue *value = new JSONValue(jsonObjOuter);
res->print(value->Stringify().c_str());
delete value;
+
+ // Clean up the networkObjs to prevent memory leak
+ for (auto *val : networkObjs) {
+ delete val;
+ }
}
#endif
\ No newline at end of file
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index 407003f7e..78c101765 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -1040,19 +1040,32 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
res.get_module_config_response.payload_variant.serial = moduleConfig.serial;
break;
case meshtastic_AdminMessage_ModuleConfigType_EXTNOTIF_CONFIG:
+#if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION
LOG_INFO("Get module config: External Notification");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_external_notification_tag;
res.get_module_config_response.payload_variant.external_notification = moduleConfig.external_notification;
+#else
+ LOG_DEBUG("External Notification module excluded from build, skipping config");
+#endif
break;
case meshtastic_AdminMessage_ModuleConfigType_STOREFORWARD_CONFIG:
+#if !MESHTASTIC_EXCLUDE_STOREFORWARD
LOG_INFO("Get module config: Store & Forward");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_store_forward_tag;
res.get_module_config_response.payload_variant.store_forward = moduleConfig.store_forward;
+#else
+ LOG_DEBUG("Store & Forward module excluded from build, skipping config");
+#endif
break;
case meshtastic_AdminMessage_ModuleConfigType_RANGETEST_CONFIG:
+#if !MESHTASTIC_EXCLUDE_RANGETEST
LOG_INFO("Get module config: Range Test");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_range_test_tag;
res.get_module_config_response.payload_variant.range_test = moduleConfig.range_test;
+#else
+ LOG_DEBUG("Range Test module excluded from build, skipping config");
+ // Don't set payload variant - will result in empty response
+#endif
break;
case meshtastic_AdminMessage_ModuleConfigType_TELEMETRY_CONFIG:
LOG_INFO("Get module config: Telemetry");
@@ -1065,9 +1078,13 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
res.get_module_config_response.payload_variant.canned_message = moduleConfig.canned_message;
break;
case meshtastic_AdminMessage_ModuleConfigType_AUDIO_CONFIG:
+#if !MESHTASTIC_EXCLUDE_AUDIO
LOG_INFO("Get module config: Audio");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_audio_tag;
res.get_module_config_response.payload_variant.audio = moduleConfig.audio;
+#else
+ LOG_DEBUG("Audio module excluded from build, skipping config");
+#endif
break;
case meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG:
LOG_INFO("Get module config: Remote Hardware");
@@ -1080,19 +1097,31 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
res.get_module_config_response.payload_variant.neighbor_info = moduleConfig.neighbor_info;
break;
case meshtastic_AdminMessage_ModuleConfigType_DETECTIONSENSOR_CONFIG:
+#if !(NO_EXT_GPIO || MESHTASTIC_EXCLUDE_DETECTIONSENSOR)
LOG_INFO("Get module config: Detection Sensor");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_detection_sensor_tag;
res.get_module_config_response.payload_variant.detection_sensor = moduleConfig.detection_sensor;
+#else
+ LOG_DEBUG("Detection Sensor module excluded from build, skipping config");
+#endif
break;
case meshtastic_AdminMessage_ModuleConfigType_AMBIENTLIGHTING_CONFIG:
+#if !MESHTASTIC_EXCLUDE_AMBIENTLIGHTING
LOG_INFO("Get module config: Ambient Lighting");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_ambient_lighting_tag;
res.get_module_config_response.payload_variant.ambient_lighting = moduleConfig.ambient_lighting;
+#else
+ LOG_DEBUG("Ambient Lighting module excluded from build, skipping config");
+#endif
break;
case meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG:
+#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
LOG_INFO("Get module config: Paxcounter");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_paxcounter_tag;
res.get_module_config_response.payload_variant.paxcounter = moduleConfig.paxcounter;
+#else
+ LOG_DEBUG("Paxcounter module excluded from build, skipping config");
+#endif
break;
}
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp
index c90d9250f..8ac160f8b 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.cpp
+++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp
@@ -30,7 +30,8 @@
namespace graphics
{
-extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool battery_only);
+extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool force_no_invert,
+ bool show_date);
}
#if __has_include()
#include "Sensor/AHT10.h"
diff --git a/src/modules/Telemetry/PowerTelemetry.cpp b/src/modules/Telemetry/PowerTelemetry.cpp
index 35409edef..479861a2e 100644
--- a/src/modules/Telemetry/PowerTelemetry.cpp
+++ b/src/modules/Telemetry/PowerTelemetry.cpp
@@ -24,7 +24,8 @@
namespace graphics
{
-extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool battery_only);
+extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool force_no_invert,
+ bool show_date);
}
int32_t PowerTelemetryModule::runOnce()
diff --git a/src/sleep.cpp b/src/sleep.cpp
index bff318900..83597e349 100644
--- a/src/sleep.cpp
+++ b/src/sleep.cpp
@@ -431,15 +431,6 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_LOW_LEVEL); // pmu irq
#endif
-#ifdef T_LORA_PAGER
- LOG_DEBUG("power down XL9555 io");
- io.digitalWrite(EXPANDS_DRV_EN, LOW);
- io.digitalWrite(EXPANDS_AMP_EN, LOW);
- io.digitalWrite(EXPANDS_KB_EN, LOW);
- io.digitalWrite(EXPANDS_SD_EN, LOW);
- io.digitalWrite(EXPANDS_GPIO_EN, LOW);
-#endif
-
auto res = esp_sleep_enable_gpio_wakeup();
if (res != ESP_OK) {
LOG_ERROR("esp_sleep_enable_gpio_wakeup result %d", res);
@@ -480,14 +471,6 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
gpio_wakeup_disable((gpio_num_t)RF95_IRQ);
}
#endif
-#ifdef T_LORA_PAGER
- LOG_DEBUG("power up XL9555 io");
- io.digitalWrite(EXPANDS_DRV_EN, HIGH);
- io.digitalWrite(EXPANDS_AMP_EN, HIGH);
- io.digitalWrite(EXPANDS_KB_EN, HIGH);
- io.digitalWrite(EXPANDS_SD_EN, HIGH);
- io.digitalWrite(EXPANDS_GPIO_EN, HIGH);
-#endif
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
notifyLightSleepEnd.notifyObservers(cause); // Button interrupts are reattached here
diff --git a/variants/esp32/diy/9m2ibr_aprs_lora_tracker/platformio.ini b/variants/esp32/diy/9m2ibr_aprs_lora_tracker/platformio.ini
new file mode 100644
index 000000000..809599212
--- /dev/null
+++ b/variants/esp32/diy/9m2ibr_aprs_lora_tracker/platformio.ini
@@ -0,0 +1,12 @@
+; 9M2IBR APRS LoRa Tracker: ESP32-WROOM-32 + EBYTE E22-400M30S
+; https://shopee.com.my/product/1095224/21692283917
+[env:9m2ibr_aprs_lora_tracker]
+extends = esp32_base
+board = esp32doit-devkit-v1
+board_level = extra
+build_flags =
+ ${esp32_base.build_flags}
+ -D PRIVATE_HW
+ -D EBYTE_E22
+ -D EBYTE_E22_900M30S ; Assume Tx power curve is identical to 900M30S as there is no documentation
+ -I variants/esp32/diy/9m2ibr_aprs_lora_tracker
diff --git a/variants/esp32/diy/9m2ibr_aprs_lora_tracker/variant.h b/variants/esp32/diy/9m2ibr_aprs_lora_tracker/variant.h
new file mode 100644
index 000000000..037933140
--- /dev/null
+++ b/variants/esp32/diy/9m2ibr_aprs_lora_tracker/variant.h
@@ -0,0 +1,74 @@
+/*
+
+ 9M2IBR APRS LoRa Tracker: ESP32-WROOM-32 + EBYTE E22-400M30S
+ https://shopee.com.my/product/1095224/21692283917
+
+ Originally developed for LoRa_APRS_iGate and GPIO is similar to
+ https://github.com/richonguzman/LoRa_APRS_iGate/blob/main/variants/ESP32_DIY_1W_LoRa_Mesh_V1_2/board_pinout.h
+
+*/
+
+// OLED (may be different controllers depending on screen size)
+#define I2C_SDA 21
+#define I2C_SCL 22
+#define HAS_SCREEN 1 // Generates randomized BLE pin
+
+// GNSS: Ai-Thinker GP-02 BDS/GNSS module
+#define GPS_RX_PIN 16
+#define GPS_TX_PIN 17
+
+// Button
+#define BUTTON_PIN 15 // Right side button - if not available, set device.button_gpio to 0 from Meshtastic client
+
+// LEDs
+#define LED_PIN 13 // Tx LED
+#define USER_LED 2 // Rx LED
+
+// Buzzer
+#define PIN_BUZZER 33
+
+// Battery sense
+#define BATTERY_PIN 35
+#define ADC_MULTIPLIER 2.01 // 100k + 100k, and add 1% tolerance
+#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
+#define BATTERY_SENSE_RESOLUTION_BITS ADC_RESOLUTION
+
+// SPI
+#define LORA_SCK 18
+#define LORA_MISO 19
+#define LORA_MOSI 23
+
+// LoRa
+#define LORA_CS 5
+#define LORA_DIO0 26 // a No connect on the SX1262/SX1268 module
+#define LORA_RESET 27 // RST for SX1276, and for SX1262/SX1268
+#define LORA_DIO1 12 // IRQ for SX1262/SX1268
+#define LORA_DIO2 RADIOLIB_NC // BUSY for SX1262/SX1268
+#define LORA_DIO3 // NC, but used as TCXO supply by E22 module
+#define LORA_RXEN 32 // RF switch RX (and E22 LNA) control by ESP32 GPIO
+#define LORA_TXEN 25 // RF switch TX (and E22 PA) control by ESP32 GPIO
+
+// RX/TX for RFM95/SX127x
+#define RF95_RXEN LORA_RXEN
+#define RF95_TXEN LORA_TXEN
+// #define RF95_TCXO
+
+// common pinouts for SX126X modules
+#define SX126X_CS 5
+#define SX126X_DIO1 LORA_DIO1
+#define SX126X_BUSY LORA_DIO2
+#define SX126X_RESET LORA_RESET
+#define SX126X_RXEN LORA_RXEN
+#define SX126X_TXEN LORA_TXEN
+
+// Support alternative modules if soldered in place of E22
+#define USE_RF95 // RFM95/SX127x
+#define USE_SX1262
+#define USE_SX1268
+#define USE_LLCC68
+
+// E22 TCXO support
+#ifdef EBYTE_E22
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL
+#endif
diff --git a/variants/esp32/heltec_wireless_bridge/platformio.ini b/variants/esp32/heltec_wireless_bridge/platformio.ini
index 60e686f9e..93c3e3394 100644
--- a/variants/esp32/heltec_wireless_bridge/platformio.ini
+++ b/variants/esp32/heltec_wireless_bridge/platformio.ini
@@ -1,6 +1,7 @@
[env:heltec-wireless-bridge]
;build_type = debug ; to make it possible to step through our jtag debugger
extends = esp32_base
+board_level = extra
board = heltec_wifi_lora_32
build_flags =
${esp32_base.build_flags}
diff --git a/variants/esp32/trackerd/platformio.ini b/variants/esp32/trackerd/platformio.ini
index 3c2726a3c..00c14fad2 100644
--- a/variants/esp32/trackerd/platformio.ini
+++ b/variants/esp32/trackerd/platformio.ini
@@ -1,5 +1,6 @@
[env:trackerd]
extends = esp32_base
+board_level = extra
board = pico32
board_build.f_flash = 80000000L
diff --git a/variants/esp32s3/picomputer-s3/platformio.ini b/variants/esp32s3/picomputer-s3/platformio.ini
index d5847959b..4131cc30a 100644
--- a/variants/esp32s3/picomputer-s3/platformio.ini
+++ b/variants/esp32s3/picomputer-s3/platformio.ini
@@ -2,7 +2,7 @@
extends = esp32s3_base
board = bpi_picow_esp32_s3
board_check = true
-board_build.partitions = default_8MB.csv
+board_build.partitions = partition-table-8MB.csv
;OpenOCD flash method
;upload_protocol = esp-builtin
;Normal method
@@ -26,6 +26,7 @@ extends = env:picomputer-s3
build_flags =
${env:picomputer-s3.build_flags}
+ -D MESHTASTIC_EXCLUDE_WEBSERVER=1
-D INPUTDRIVER_MATRIX_TYPE=1
-D USE_PIN_BUZZER=PIN_BUZZER
-D USE_SX127x
diff --git a/variants/esp32s3/rak_wismesh_tap_v2/platformio.ini b/variants/esp32s3/rak_wismesh_tap_v2/platformio.ini
index 8b86e0217..de4714efa 100644
--- a/variants/esp32s3/rak_wismesh_tap_v2/platformio.ini
+++ b/variants/esp32s3/rak_wismesh_tap_v2/platformio.ini
@@ -70,6 +70,7 @@ build_flags =
${ft5x06.build_flags}
-D LGFX_SCREEN_WIDTH=240
-D LGFX_SCREEN_HEIGHT=320
+ -D DISPLAY_SIZE=320x240 ; landscape mode
-D LGFX_PANEL=ST7789
-D LGFX_ROTATION=1
-D LGFX_TOUCH_X_MIN=0
diff --git a/variants/esp32s3/seeed-sensecap-indicator/platformio.ini b/variants/esp32s3/seeed-sensecap-indicator/platformio.ini
index f408054cf..25ec3ebfc 100644
--- a/variants/esp32s3/seeed-sensecap-indicator/platformio.ini
+++ b/variants/esp32s3/seeed-sensecap-indicator/platformio.ini
@@ -6,7 +6,7 @@ platform_packages =
board = seeed-sensecap-indicator
board_check = true
-board_build.partitions = default_8MB.csv
+board_build.partitions = partition-table-8MB.csv
upload_protocol = esptool
build_flags = ${esp32_base.build_flags}
diff --git a/variants/esp32s3/tlora-pager/platformio.ini b/variants/esp32s3/tlora-pager/platformio.ini
index b16e516a7..312d46259 100644
--- a/variants/esp32s3/tlora-pager/platformio.ini
+++ b/variants/esp32s3/tlora-pager/platformio.ini
@@ -26,7 +26,7 @@ lib_deps = ${esp32s3_base.lib_deps}
lewisxhe/SensorLib@0.3.1
https://github.com/pschatzmann/arduino-audio-driver/archive/refs/tags/v0.1.3.zip
https://github.com/mverch67/BQ27220/archive/07d92be846abd8a0258a50c23198dac0858b22ed.zip
- https://github.com/mverch67/RotaryEncoder
+ https://github.com/mverch67/RotaryEncoder/archive/25a59d5745a6645536f921427d80b08e78f886d4.zip
[env:tlora-pager-tft]
board_level = extra
diff --git a/variants/esp32s3/unphone/platformio.ini b/variants/esp32s3/unphone/platformio.ini
index 476858ff5..f17a27e17 100644
--- a/variants/esp32s3/unphone/platformio.ini
+++ b/variants/esp32s3/unphone/platformio.ini
@@ -3,7 +3,7 @@
[env:unphone]
extends = esp32s3_base
board = unphone
-board_build.partitions = default_8MB.csv
+board_build.partitions = partition-table-8MB.csv
upload_speed = 921600
monitor_speed = 115200
monitor_filters = esp32_exception_decoder
@@ -20,6 +20,7 @@ build_flags =
-D UNPHONE_LORA=0
-D UNPHONE_FACTORY_MODE=0
-D USE_SX127x
+ -D SDCARD_CS=43
build_src_filter =
${esp32s3_base.build_src_filter}
@@ -41,6 +42,7 @@ build_flags =
-D HAS_SCREEN=1
-D HAS_TFT=1
-D HAS_SDCARD
+ -D SDCARD_CS=43
-D DISPLAY_SET_RESOLUTION
-D RAM_SIZE=6144
-D LV_CACHE_DEF_SIZE=2097152
diff --git a/variants/esp32s3/unphone/variant.h b/variants/esp32s3/unphone/variant.h
index e186b5740..366b49233 100644
--- a/variants/esp32s3/unphone/variant.h
+++ b/variants/esp32s3/unphone/variant.h
@@ -52,7 +52,6 @@
#undef GPS_TX_PIN
#define SD_SPI_FREQUENCY 25000000
-#define SDCARD_CS 43
#define LED_PIN 13 // the red part of the RGB LED
#define LED_STATE_ON 0 // State when LED is lit
diff --git a/variants/nrf52840/gat562_mesh_trial_tracker/platformio.ini b/variants/nrf52840/gat562_mesh_trial_tracker/platformio.ini
index 72ac6320d..5c1047aae 100644
--- a/variants/nrf52840/gat562_mesh_trial_tracker/platformio.ini
+++ b/variants/nrf52840/gat562_mesh_trial_tracker/platformio.ini
@@ -1,6 +1,7 @@
; The very slick RAK wireless RAK 4631 / 4630 board - Unified firmware for 5005/19003, with or without OLED RAK 1921
[env:gat562_mesh_trial_tracker]
extends = nrf52840_base
+board_level = extra
board = gat562_mesh_trial_tracker
board_check = true
build_flags = ${nrf52840_base.build_flags}
diff --git a/variants/nrf52840/heltec_mesh_node_t114/variant.h b/variants/nrf52840/heltec_mesh_node_t114/variant.h
index b71106a53..7e82733aa 100644
--- a/variants/nrf52840/heltec_mesh_node_t114/variant.h
+++ b/variants/nrf52840/heltec_mesh_node_t114/variant.h
@@ -208,7 +208,7 @@ No longer populated on PCB
#undef AREF_VOLTAGE
#define AREF_VOLTAGE 3.0
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
-#define ADC_MULTIPLIER (4.99F)
+#define ADC_MULTIPLIER (4.916F)
#define HAS_RTC 0
#ifdef __cplusplus
diff --git a/variants/nrf52840/meshlink/platformio.ini b/variants/nrf52840/meshlink/platformio.ini
index 8216a704a..466362242 100644
--- a/variants/nrf52840/meshlink/platformio.ini
+++ b/variants/nrf52840/meshlink/platformio.ini
@@ -4,6 +4,7 @@
[env:meshlink]
extends = nrf52840_base
board = meshlink
+board_level = extra
;board_check = true
build_flags = ${nrf52840_base.build_flags}
-I variants/nrf52840/meshlink
diff --git a/variants/nrf52840/meshlink_eink/platformio.ini b/variants/nrf52840/meshlink_eink/platformio.ini
index a48a9e695..af5a0040e 100644
--- a/variants/nrf52840/meshlink_eink/platformio.ini
+++ b/variants/nrf52840/meshlink_eink/platformio.ini
@@ -4,6 +4,7 @@
[env:meshlink_eink]
extends = nrf52840_base
board = meshlink
+board_level = extra
;board_check = true
build_flags = ${nrf52840_base.build_flags}
-I variants/nrf52840/meshlink_eink
diff --git a/variants/rp2040/ec_catsniffer/platformio.ini b/variants/rp2040/ec_catsniffer/platformio.ini
index acf19d757..b70eff6d7 100644
--- a/variants/rp2040/ec_catsniffer/platformio.ini
+++ b/variants/rp2040/ec_catsniffer/platformio.ini
@@ -1,6 +1,7 @@
[env:catsniffer]
extends = rp2040_base
board = rpipico
+board_level = extra
upload_protocol = picotool
build_flags =
${rp2040_base.build_flags}