Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,43 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
#include <string>
#endif

#ifdef ARCH_ESP32
#ifdef DEBUG_PARTITION_TABLE
#include "esp_partition.h"

void printPartitionTable()
{
printf("\n--- Partition Table ---\n");
Comment thread
thebentern marked this conversation as resolved.
// 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"

Expand Down Expand Up @@ -648,7 +685,11 @@ void setup()
sensor_detected = true;
#endif
}

#ifdef ARCH_ESP32
#ifdef DEBUG_PARTITION_TABLE
printPartitionTable();
Comment thread
thebentern marked this conversation as resolved.
#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) {
Expand Down
64 changes: 58 additions & 6 deletions src/modules/AdminModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Comment thread
thebentern marked this conversation as resolved.

// 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.");
Comment thread
thebentern marked this conversation as resolved.
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
Expand Down Expand Up @@ -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
Expand Down
9 changes: 6 additions & 3 deletions src/modules/AdminModule.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include <sys/types.h>

#pragma once
#ifdef ESP_PLATFORM
#include <esp_ota_ops.h>
#endif
#include "ProtobufModule.h"
#include <sys/types.h>
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
Expand Down Expand Up @@ -71,7 +73,8 @@ class AdminModule : public ProtobufModule<meshtastic_AdminMessage>, 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 =
Expand Down
43 changes: 35 additions & 8 deletions src/platform/esp32/MeshtasticOTA.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
#include "MeshtasticOTA.h"
#include "configuration.h"
#ifdef ESP_PLATFORM
#include <Preferences.h>
#include <esp_ota_ops.h>
#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;

Expand Down Expand Up @@ -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);
Comment thread
thebentern marked this conversation as resolved.
return false;
}

return true;
}

Expand Down
10 changes: 9 additions & 1 deletion src/platform/esp32/MeshtasticOTA.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@

#include "mesh-pb-constants.h"
#include <Arduino.h>
#ifdef ESP_PLATFORM
#include <esp_ota_ops.h>
#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();
Expand Down
2 changes: 2 additions & 0 deletions variants/esp32c6/m5stack_unitc6l/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down