Meshtastic unified OTA (#9231)

* Initial commit of combined BLE and WiFi OTA

* Incorporate ota_hash in AdminMessage protobuf

* OTA protobuf changes

* Trunk fmt

---------

Co-authored-by: Jake-B <jake-b@users.noreply.github.com>
This commit is contained in:
Ben Meadors
2026-01-14 20:54:31 -06:00
committed by GitHub
parent 64e95554bb
commit c0afe92a7f
17 changed files with 92 additions and 195 deletions

View File

@@ -1,68 +0,0 @@
#include "BleOta.h"
#include "Arduino.h"
#include <cctype>
#include <esp_ota_ops.h>
#include <string>
static bool isMeshtasticOtaProject(const esp_app_desc_t &desc)
{
std::string name(desc.project_name);
return name.find("Meshtastic") != std::string::npos && name.find("OTA") != std::string::npos;
}
const esp_partition_t *BleOta::findEspOtaAppPartition()
{
esp_app_desc_t app_desc;
esp_err_t ret = ESP_ERR_INVALID_ARG;
// Try standard OTA slots first (app0 / app1)
const esp_partition_t *part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, nullptr);
if (part) {
ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc));
}
if (!part || ret != ESP_OK || !isMeshtasticOtaProject(app_desc)) {
part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, nullptr);
if (part) {
ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc));
}
}
// Fallback: look by partition label "app1" in case table uses custom labels
if ((!part || ret != ESP_OK || !isMeshtasticOtaProject(app_desc))) {
part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, "app1");
if (part) {
ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc));
}
}
if (part && ret == ESP_OK && isMeshtasticOtaProject(app_desc)) {
return part;
}
return nullptr;
}
String BleOta::getOtaAppVersion()
{
const esp_partition_t *part = findEspOtaAppPartition();
if (!part) {
return String();
}
esp_app_desc_t app_desc;
esp_err_t ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc));
String version;
if (ret == ESP_OK) {
version = app_desc.version;
}
return version;
}
bool BleOta::switchToOtaApp()
{
bool success = false;
const esp_partition_t *part = findEspOtaAppPartition();
if (part) {
success = (ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_set_boot_partition(part)) == ESP_OK);
}
return success;
}

View File

@@ -1,20 +0,0 @@
#ifndef BLEOTA_H
#define BLEOTA_H
#include <Arduino.h>
#include <functional>
class BleOta
{
public:
explicit BleOta(){};
static String getOtaAppVersion();
static bool switchToOtaApp();
private:
String mUserAgent;
static const esp_partition_t *findEspOtaAppPartition();
};
#endif // BLEOTA_H

View File

@@ -1,13 +1,13 @@
#include "WiFiOTA.h"
#include "MeshtasticOTA.h"
#include "configuration.h"
#include <Preferences.h>
#include <esp_ota_ops.h>
namespace WiFiOTA
namespace MeshtasticOTA
{
static const char *nvsNamespace = "ota-wifi";
static const char *appProjectName = "OTA-WiFi";
static const char *nvsNamespace = "MeshtasticOTA";
static const char *appProjectName = "MeshtasticOTA";
static bool updated = false;
@@ -43,12 +43,14 @@ void recoverConfig(meshtastic_Config_NetworkConfig *network)
strncpy(network->wifi_psk, psk.c_str(), sizeof(network->wifi_psk));
}
void saveConfig(meshtastic_Config_NetworkConfig *network)
void saveConfig(meshtastic_Config_NetworkConfig *network, meshtastic_OTAMode method, uint8_t *ota_hash)
{
LOG_INFO("Saving WiFi settings for upcoming OTA update");
Preferences prefs;
prefs.begin(nvsNamespace);
prefs.putUChar("method", method);
prefs.putBytes("ota_hash", ota_hash, 32);
prefs.putString("ssid", network->wifi_ssid);
prefs.putString("psk", network->wifi_psk);
prefs.putBool("updated", false);
@@ -62,10 +64,14 @@ const esp_partition_t *getAppPartition()
bool getAppDesc(const esp_partition_t *part, esp_app_desc_t *app_desc)
{
if (esp_ota_get_partition_description(part, app_desc) != ESP_OK)
if (esp_ota_get_partition_description(part, app_desc) != ESP_OK) {
LOG_INFO("esp_ota_get_partition_description failed");
return false;
if (strcmp(app_desc->project_name, appProjectName) != 0)
}
if (strcmp(app_desc->project_name, appProjectName) != 0) {
LOG_INFO("app_desc->project_name == 0");
return false;
}
return true;
}
@@ -89,4 +95,4 @@ const char *getVersion()
return app_desc.version;
}
} // namespace WiFiOTA
} // namespace MeshtasticOTA

View File

@@ -0,0 +1,18 @@
#ifndef MESHTASTICOTA_H
#define MESHTASTICOTA_H
#include "mesh-pb-constants.h"
#include <Arduino.h>
namespace MeshtasticOTA
{
void initialize();
bool isUpdated();
void recoverConfig(meshtastic_Config_NetworkConfig *network);
void saveConfig(meshtastic_Config_NetworkConfig *network, meshtastic_OTAMode method, uint8_t *ota_hash);
bool trySwitchToOTA();
const char *getVersion();
} // namespace MeshtasticOTA
#endif // MESHTASTICOTA_H

View File

@@ -1,18 +0,0 @@
#ifndef WIFIOTA_H
#define WIFIOTA_H
#include "mesh-pb-constants.h"
#include <Arduino.h>
namespace WiFiOTA
{
void initialize();
bool isUpdated();
void recoverConfig(meshtastic_Config_NetworkConfig *network);
void saveConfig(meshtastic_Config_NetworkConfig *network);
bool trySwitchToOTA();
const char *getVersion();
} // namespace WiFiOTA
#endif // WIFIOTA_H

View File

@@ -5,11 +5,10 @@
#include "main.h"
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
#include "BleOta.h"
#include "nimble/NimbleBluetooth.h"
#endif
#include <WiFiOTA.h>
#include <MeshtasticOTA.h>
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
@@ -144,22 +143,14 @@ void esp32Setup()
preferences.putUInt("hwVendor", HW_VENDOR);
preferences.end();
LOG_DEBUG("Number of Device Reboots: %d", rebootCounter);
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
String BLEOTA = BleOta::getOtaAppVersion();
if (BLEOTA.isEmpty()) {
LOG_INFO("No BLE OTA firmware available");
} else {
LOG_INFO("BLE OTA firmware version %s", BLEOTA.c_str());
}
#endif
#if !MESHTASTIC_EXCLUDE_WIFI
String version = WiFiOTA::getVersion();
String version = MeshtasticOTA::getVersion();
if (version.isEmpty()) {
LOG_INFO("No WiFi OTA firmware available");
LOG_INFO("MeshtasticOTA firmware not available");
} else {
LOG_INFO("WiFi OTA firmware version %s", version.c_str());
LOG_INFO("MeshtasticOTA firmware version %s", version.c_str());
}
WiFiOTA::initialize();
MeshtasticOTA::initialize();
#endif
// enableModemSleep();