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
co-authored by GitHub Jake-B
parent 64e95554bb
commit c0afe92a7f
17 changed files with 92 additions and 195 deletions
+3 -2
View File
@@ -1,7 +1,8 @@
#ifdef CAN_RECLOCK_I2C #ifdef CAN_RECLOCK_I2C
#include "ScanI2CTwoWire.h" #include "ScanI2CTwoWire.h"
uint32_t reClockI2C(uint32_t desiredClock, TwoWire *i2cBus) { uint32_t reClockI2C(uint32_t desiredClock, TwoWire *i2cBus)
{
uint32_t currentClock; uint32_t currentClock;
@@ -31,7 +32,7 @@ uint32_t reClockI2C(uint32_t desiredClock, TwoWire *i2cBus) {
return 0; return 0;
#endif #endif
if (currentClock != desiredClock){ if (currentClock != desiredClock) {
LOG_DEBUG("Changing I2C clock to %u", desiredClock); LOG_DEBUG("Changing I2C clock to %u", desiredClock);
i2cBus->setClock(desiredClock); i2cBus->setClock(desiredClock);
} }
+3 -3
View File
@@ -53,7 +53,7 @@
#endif #endif
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI
#include <WiFiOTA.h> #include <MeshtasticOTA.h>
#endif #endif
NodeDB *nodeDB = nullptr; NodeDB *nodeDB = nullptr;
@@ -756,8 +756,8 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
config.display.compass_orientation = COMPASS_ORIENTATION; config.display.compass_orientation = COMPASS_ORIENTATION;
#endif #endif
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI
if (WiFiOTA::isUpdated()) { if (MeshtasticOTA::isUpdated()) {
WiFiOTA::recoverConfig(&config.network); MeshtasticOTA::recoverConfig(&config.network);
} }
#endif #endif
+13 -17
View File
@@ -9,11 +9,8 @@
#include "meshUtils.h" #include "meshUtils.h"
#include <FSCommon.h> #include <FSCommon.h>
#include <ctype.h> // for better whitespace handling #include <ctype.h> // for better whitespace handling
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
#include "BleOta.h"
#endif
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI
#include "WiFiOTA.h" #include "MeshtasticOTA.h"
#endif #endif
#include "Router.h" #include "Router.h"
#include "configuration.h" #include "configuration.h"
@@ -236,28 +233,27 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
reboot(r->reboot_seconds); reboot(r->reboot_seconds);
break; break;
} }
case meshtastic_AdminMessage_reboot_ota_seconds_tag: { case meshtastic_AdminMessage_ota_request_tag: {
int32_t s = r->reboot_ota_seconds;
#if defined(ARCH_ESP32) #if defined(ARCH_ESP32)
#if !MESHTASTIC_EXCLUDE_BLUETOOTH if (r->ota_request.ota_hash.size != 32) {
if (!BleOta::getOtaAppVersion().isEmpty()) {
suppressRebootBanner = true; suppressRebootBanner = true;
if (screen) LOG_INFO("OTA Failed: Invalid `ota_hash` provided");
screen->startFirmwareUpdateScreen(); break;
BleOta::switchToOtaApp();
LOG_INFO("Rebooting to BLE OTA");
} }
#endif
#if !MESHTASTIC_EXCLUDE_WIFI meshtastic_OTAMode mode = r->ota_request.reboot_ota_mode;
if (WiFiOTA::trySwitchToOTA()) { if (MeshtasticOTA::trySwitchToOTA()) {
LOG_INFO("OTA Requested");
suppressRebootBanner = true; suppressRebootBanner = true;
if (screen) if (screen)
screen->startFirmwareUpdateScreen(); screen->startFirmwareUpdateScreen();
WiFiOTA::saveConfig(&config.network); MeshtasticOTA::saveConfig(&config.network, mode, r->ota_request.ota_hash.bytes);
LOG_INFO("Rebooting to WiFi OTA"); LOG_INFO("Rebooting to WiFi OTA");
} else {
LOG_INFO("WIFI OTA Failed");
} }
#endif #endif
#endif int s = 1; // Reboot in 1 second, hard coded
LOG_INFO("Reboot in %d seconds", s); LOG_INFO("Reboot in %d seconds", s);
rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000); rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000);
break; break;
+21 -24
View File
@@ -3,26 +3,25 @@
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "Default.h"
#include "AirQualityTelemetry.h" #include "AirQualityTelemetry.h"
#include "Default.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "RTC.h" #include "RTC.h"
#include "Router.h" #include "Router.h"
#include "Sensor/AddI2CSensorTemplate.h"
#include "UnitConversions.h" #include "UnitConversions.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h" #include "graphics/SharedUIDisplay.h"
#include "graphics/images.h" #include "graphics/images.h"
#include "graphics/ScreenFonts.h"
#include "main.h" #include "main.h"
#include "sleep.h" #include "sleep.h"
#include <Throttle.h> #include <Throttle.h>
#include "Sensor/AddI2CSensorTemplate.h"
// Sensors // Sensors
#include "Sensor/PMSA003ISensor.h" #include "Sensor/PMSA003ISensor.h"
void AirQualityTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner) void AirQualityTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
{ {
if (!moduleConfig.telemetry.air_quality_enabled && !AIR_QUALITY_TELEMETRY_MODULE_ENABLE) { if (!moduleConfig.telemetry.air_quality_enabled && !AIR_QUALITY_TELEMETRY_MODULE_ENABLE) {
@@ -57,7 +56,7 @@ int32_t AirQualityTelemetryModule::runOnce()
uint32_t result = UINT32_MAX; uint32_t result = UINT32_MAX;
if (!(moduleConfig.telemetry.air_quality_enabled || moduleConfig.telemetry.air_quality_screen_enabled || if (!(moduleConfig.telemetry.air_quality_enabled || moduleConfig.telemetry.air_quality_screen_enabled ||
AIR_QUALITY_TELEMETRY_MODULE_ENABLE)) { AIR_QUALITY_TELEMETRY_MODULE_ENABLE)) {
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it // If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
return disable(); return disable();
@@ -74,7 +73,6 @@ int32_t AirQualityTelemetryModule::runOnce()
if (!sensors.empty()) { if (!sensors.empty()) {
result = DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; result = DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
} }
} }
// it's possible to have this module enabled, only for displaying values on the screen. // it's possible to have this module enabled, only for displaying values on the screen.
@@ -95,27 +93,26 @@ int32_t AirQualityTelemetryModule::runOnce()
} }
if (((lastSentToMesh == 0) || if (((lastSentToMesh == 0) ||
!Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled( !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
moduleConfig.telemetry.air_quality_interval, moduleConfig.telemetry.air_quality_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) && default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) { airTime->isTxAllowedAirUtil()) {
sendTelemetry(); sendTelemetry();
lastSentToMesh = millis(); lastSentToMesh = millis();
} else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) && } else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) &&
(service->isToPhoneQueueEmpty())) { (service->isToPhoneQueueEmpty())) {
// Just send to phone when it's not our time to send to mesh yet // Just send to phone when it's not our time to send to mesh yet
// Only send while queue is empty (phone assumed connected) // Only send while queue is empty (phone assumed connected)
sendTelemetry(NODENUM_BROADCAST, true); sendTelemetry(NODENUM_BROADCAST, true);
lastSentToPhone = millis(); lastSentToPhone = millis();
} }
// Send to sleep sensors that consume power // Send to sleep sensors that consume power
LOG_INFO("Sending sensors to sleep"); LOG_INFO("Sending sensors to sleep");
for (TelemetrySensor *sensor : sensors) { for (TelemetrySensor *sensor : sensors) {
sensor->sleep(); sensor->sleep();
} }
} }
return min(sendToPhoneIntervalMs, result); return min(sendToPhoneIntervalMs, result);
} }
@@ -161,8 +158,8 @@ void AirQualityTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSta
const auto &m = telemetry.variant.air_quality_metrics; const auto &m = telemetry.variant.air_quality_metrics;
// Check if any telemetry field has valid data // Check if any telemetry field has valid data
bool hasAny = m.has_pm10_standard || m.has_pm25_standard || m.has_pm100_standard || m.has_pm10_environmental || m.has_pm25_environmental || bool hasAny = m.has_pm10_standard || m.has_pm25_standard || m.has_pm100_standard || m.has_pm10_environmental ||
m.has_pm100_environmental; m.has_pm25_environmental || m.has_pm100_environmental;
if (!hasAny) { if (!hasAny) {
display->drawString(x, currentY, "No Telemetry"); display->drawString(x, currentY, "No Telemetry");
@@ -296,10 +293,10 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
m.time = getTime(); m.time = getTime();
if (getAirQualityTelemetry(&m)) { if (getAirQualityTelemetry(&m)) {
LOG_INFO("Send: pm10_standard=%u, pm25_standard=%u, pm100_standard=%u, \ LOG_INFO("Send: pm10_standard=%u, pm25_standard=%u, pm100_standard=%u, \
pm10_environmental=%u, pm25_environmental=%u, pm100_environmental=%u", \ pm10_environmental=%u, pm25_environmental=%u, pm100_environmental=%u",
m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard, \ m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard,
m.variant.air_quality_metrics.pm100_standard, m.variant.air_quality_metrics.pm10_environmental, \ m.variant.air_quality_metrics.pm100_standard, m.variant.air_quality_metrics.pm10_environmental,
m.variant.air_quality_metrics.pm25_environmental, m.variant.air_quality_metrics.pm100_environmental); m.variant.air_quality_metrics.pm25_environmental, m.variant.air_quality_metrics.pm100_environmental);
meshtastic_MeshPacket *p = allocDataProtobuf(m); meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest; p->to = dest;
@@ -341,8 +338,8 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
} }
AdminMessageHandleResult AirQualityTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp, AdminMessageHandleResult AirQualityTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
meshtastic_AdminMessage *request, meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response) meshtastic_AdminMessage *response)
{ {
AdminMessageHandleResult result = AdminMessageHandleResult::NOT_HANDLED; AdminMessageHandleResult result = AdminMessageHandleResult::NOT_HANDLED;
+2 -2
View File
@@ -16,8 +16,8 @@
#include <OLEDDisplayUi.h> #include <OLEDDisplayUi.h>
class AirQualityTelemetryModule : private concurrency::OSThread, class AirQualityTelemetryModule : private concurrency::OSThread,
public ScanI2CConsumer, public ScanI2CConsumer,
public ProtobufModule<meshtastic_Telemetry> public ProtobufModule<meshtastic_Telemetry>
{ {
CallbackObserver<AirQualityTelemetryModule, const meshtastic::Status *> nodeStatusObserver = CallbackObserver<AirQualityTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<AirQualityTelemetryModule, const meshtastic::Status *>(this, CallbackObserver<AirQualityTelemetryModule, const meshtastic::Status *>(this,
@@ -141,9 +141,9 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
#include "Sensor/AddI2CSensorTemplate.h"
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#include <Throttle.h> #include <Throttle.h>
#include "Sensor/AddI2CSensorTemplate.h"
void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner) void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
{ {
@@ -1,10 +1,10 @@
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR #if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#include <forward_list>
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
#include "detect/ScanI2C.h" #include "detect/ScanI2C.h"
#include "detect/ScanI2CTwoWire.h" #include "detect/ScanI2CTwoWire.h"
#include <Wire.h> #include <Wire.h>
#include <forward_list>
static std::forward_list<TelemetrySensor *> sensors; static std::forward_list<TelemetrySensor *> sensors;
@@ -2,17 +2,14 @@
#if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR #if !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
#include "../detect/reClockI2C.h"
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "PMSA003ISensor.h" #include "PMSA003ISensor.h"
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
#include "../detect/reClockI2C.h"
#include <Wire.h> #include <Wire.h>
PMSA003ISensor::PMSA003ISensor() PMSA003ISensor::PMSA003ISensor() : TelemetrySensor(meshtastic_TelemetrySensorType_PMSA003I, "PMSA003I") {}
: TelemetrySensor(meshtastic_TelemetrySensorType_PMSA003I, "PMSA003I")
{
}
bool PMSA003ISensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) bool PMSA003ISensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{ {
@@ -26,7 +23,7 @@ bool PMSA003ISensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
#if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C) #if defined(PMSA003I_I2C_CLOCK_SPEED) && defined(CAN_RECLOCK_I2C)
uint32_t currentClock = reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus); uint32_t currentClock = reClockI2C(PMSA003I_I2C_CLOCK_SPEED, _bus);
if (!currentClock){ if (!currentClock) {
LOG_WARN("PMSA003I can't be used at this clock speed"); LOG_WARN("PMSA003I can't be used at this clock speed");
return false; return false;
} }
@@ -51,7 +48,7 @@ bool PMSA003ISensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement) bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
if(!isActive()){ if (!isActive()) {
LOG_WARN("PMSA003I is not active"); LOG_WARN("PMSA003I is not active");
return false; return false;
} }
@@ -79,9 +76,7 @@ bool PMSA003ISensor::getMetrics(meshtastic_Telemetry *measurement)
return false; return false;
} }
auto read16 = [](uint8_t *data, uint8_t idx) -> uint16_t { auto read16 = [](uint8_t *data, uint8_t idx) -> uint16_t { return (data[idx] << 8) | data[idx + 1]; };
return (data[idx] << 8) | data[idx + 1];
};
computedChecksum = 0; computedChecksum = 0;
@@ -141,7 +136,6 @@ bool PMSA003ISensor::isActive()
return state == State::ACTIVE; return state == State::ACTIVE;
} }
void PMSA003ISensor::sleep() void PMSA003ISensor::sleep()
{ {
#ifdef PMSA003I_ENABLE_PIN #ifdef PMSA003I_ENABLE_PIN
@@ -6,12 +6,12 @@
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
#define PMSA003I_I2C_CLOCK_SPEED 100000 #define PMSA003I_I2C_CLOCK_SPEED 100000
#define PMSA003I_FRAME_LENGTH 32 #define PMSA003I_FRAME_LENGTH 32
#define PMSA003I_WARMUP_MS 30000 #define PMSA003I_WARMUP_MS 30000
class PMSA003ISensor : public TelemetrySensor class PMSA003ISensor : public TelemetrySensor
{ {
public: public:
PMSA003ISensor(); PMSA003ISensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override; virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override; virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
@@ -20,7 +20,7 @@ public:
virtual void sleep() override; virtual void sleep() override;
virtual uint32_t wakeUp() override; virtual uint32_t wakeUp() override;
private: private:
enum class State { IDLE, ACTIVE }; enum class State { IDLE, ACTIVE };
State state = State::ACTIVE; State state = State::ACTIVE;
@@ -28,7 +28,7 @@ private:
uint16_t receivedChecksum = 0; uint16_t receivedChecksum = 0;
uint8_t buffer[PMSA003I_FRAME_LENGTH]{}; uint8_t buffer[PMSA003I_FRAME_LENGTH]{};
TwoWire * _bus{}; TwoWire *_bus{};
uint8_t _address{}; uint8_t _address{};
}; };
@@ -59,7 +59,7 @@ class TelemetrySensor
// TODO: delete after migration // TODO: delete after migration
bool hasSensor() { return nodeTelemetrySensorsMap[sensorType].first > 0; } bool hasSensor() { return nodeTelemetrySensorsMap[sensorType].first > 0; }
// Functions to sleep / wakeup sensors that support it // Functions to sleep / wakeup sensors that support it
virtual void sleep() {}; virtual void sleep(){};
virtual uint32_t wakeUp() { return 0; } virtual uint32_t wakeUp() { return 0; }
// Return active by default, override per sensor // Return active by default, override per sensor
virtual bool isActive() { return true; } virtual bool isActive() { return true; }
-68
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;
}
-20
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
@@ -1,13 +1,13 @@
#include "WiFiOTA.h" #include "MeshtasticOTA.h"
#include "configuration.h" #include "configuration.h"
#include <Preferences.h> #include <Preferences.h>
#include <esp_ota_ops.h> #include <esp_ota_ops.h>
namespace WiFiOTA namespace MeshtasticOTA
{ {
static const char *nvsNamespace = "ota-wifi"; static const char *nvsNamespace = "MeshtasticOTA";
static const char *appProjectName = "OTA-WiFi"; static const char *appProjectName = "MeshtasticOTA";
static bool updated = false; 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)); 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"); LOG_INFO("Saving WiFi settings for upcoming OTA update");
Preferences prefs; Preferences prefs;
prefs.begin(nvsNamespace); prefs.begin(nvsNamespace);
prefs.putUChar("method", method);
prefs.putBytes("ota_hash", ota_hash, 32);
prefs.putString("ssid", network->wifi_ssid); prefs.putString("ssid", network->wifi_ssid);
prefs.putString("psk", network->wifi_psk); prefs.putString("psk", network->wifi_psk);
prefs.putBool("updated", false); 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) 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; 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 false;
}
return true; return true;
} }
@@ -89,4 +95,4 @@ const char *getVersion()
return app_desc.version; return app_desc.version;
} }
} // namespace WiFiOTA } // namespace MeshtasticOTA
+18
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
-18
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
+5 -14
View File
@@ -5,11 +5,10 @@
#include "main.h" #include "main.h"
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
#include "BleOta.h"
#include "nimble/NimbleBluetooth.h" #include "nimble/NimbleBluetooth.h"
#endif #endif
#include <WiFiOTA.h> #include <MeshtasticOTA.h>
#if HAS_WIFI #if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h" #include "mesh/wifi/WiFiAPClient.h"
@@ -144,22 +143,14 @@ void esp32Setup()
preferences.putUInt("hwVendor", HW_VENDOR); preferences.putUInt("hwVendor", HW_VENDOR);
preferences.end(); preferences.end();
LOG_DEBUG("Number of Device Reboots: %d", rebootCounter); 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 #if !MESHTASTIC_EXCLUDE_WIFI
String version = WiFiOTA::getVersion(); String version = MeshtasticOTA::getVersion();
if (version.isEmpty()) { if (version.isEmpty()) {
LOG_INFO("No WiFi OTA firmware available"); LOG_INFO("MeshtasticOTA firmware not available");
} else { } 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 #endif
// enableModemSleep(); // enableModemSleep();