Merge origin/main into test/config-records

Both sides added a helper at the end of test/e2e/test_settings.py
(boot_time here, header on main); both are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cjkas 2026-10-29 18:02:56 +01:00
commit afbfac5181
20 changed files with 149 additions and 40 deletions

View file

@ -7,6 +7,7 @@
#include "Utils.h"
#include "Secrets.h"
#include "esp_chip_info.h"
#include "esp_ota_ops.h"
#include "esp_log.h"
static const char *TAG = "Config";
@ -134,6 +135,11 @@ bool ConfigSettings::begin() {
case esp_chip_model_t::CHIP_ESP32C3:
strcpy(this->chipModel, "c3");
break;
#ifdef CONFIG_IDF_TARGET_ESP32C5
case esp_chip_model_t::CHIP_ESP32C5:
strcpy(this->chipModel, "c5");
break;
#endif
#ifdef CONFIG_IDF_TARGET_ESP32C6
case esp_chip_model_t::CHIP_ESP32C6:
strcpy(this->chipModel, "c6");
@ -144,6 +150,7 @@ bool ConfigSettings::begin() {
break;
}
ESP_LOGD(TAG, "Chip Model ESP32-%s", this->chipModel);
this->hasOtaSlot = esp_ota_get_next_update_partition(nullptr) != nullptr;
this->fwVersion.parse(FW_VERSION);
uint64_t mac = ESP.getEfuseMac();
for(int i=0; i<17; i=i+8) {

View file

@ -163,6 +163,9 @@ class ConfigSettings: BaseSettings {
char serverId[10] = "";
char hostname[32] = "ESPSomfyRTS";
char chipModel[10] = "ESP32";
// Whether the partition table has a second app slot for /updateFirmware
// to write. Builds with one 3 MB slot (C5, C6, debug) are flashed over USB.
bool hasOtaSlot = true;
conn_types_t connType = conn_types_t::unset;
appver_t fwVersion;
appver_t appVersion;

View file

@ -5,8 +5,8 @@
#include <sdkconfig.h>
#endif
// Ethernet needs the RMII MAC, which only the original ESP32 has. The C3, S3
// and C6 build without it.
// Ethernet needs the RMII MAC, which only the original ESP32 has. The C3, C5,
// S3 and C6 build without it.
#if defined(CONFIG_ETH_USE_ESP32_EMAC) && CONFIG_ETH_USE_ESP32_EMAC
#define HAS_ETHERNET 1
#else

View file

@ -169,6 +169,7 @@
FIELD(hostname, "hn", 's', 0) \
FIELD(model, "md", 's', 0) \
FIELD(chipModel, "cp", 's', 0) \
FIELD(hasOtaSlot, "os", 'b', 1) \
FIELD(connType, "ct", 's', 0) \
/* memory, wifi and ethernet */ \
FIELD(freeHeap, "fr", 'i', 0) \

View file

@ -474,13 +474,28 @@ void transceiver_config_t::load() {
this->CSNPin = 34;
break;
case esp_chip_model_t::CHIP_ESP32C3:
this->TXPin = 13;
this->RXPin = 12;
this->MOSIPin = 16;
this->MISOPin = 17;
this->SCKPin = 15;
this->CSNPin = 14;
// The framework's default SPI pins (variants/esp32c3). 12-17 hold the
// SPI flash, and 2, 8 and 9 are strapping pins.
this->TXPin = 10;
this->RXPin = 3;
this->MOSIPin = 6;
this->MISOPin = 5;
this->SCKPin = 4;
this->CSNPin = 7;
break;
#ifdef CONFIG_IDF_TARGET_ESP32C5
case esp_chip_model_t::CHIP_ESP32C5:
// The framework's default SPI pins (variants/esp32c5). TX and RX take
// 4 and 5, the LP UART's pins, which nothing here uses; 2, 3, 7 and
// 25-28 are strapping pins and 11-22 hold UART0, USB and the flash.
this->TXPin = 4;
this->RXPin = 5;
this->MOSIPin = 8;
this->MISOPin = 9;
this->SCKPin = 10;
this->CSNPin = 6;
break;
#endif
#ifdef CONFIG_IDF_TARGET_ESP32C6
case esp_chip_model_t::CHIP_ESP32C6:
// Pinout applicable for the ESP32-C6-WROOM-1 module (ESP32-C6-DevKitC-1-N4)

View file

@ -85,7 +85,7 @@ const char *wifiAuthModeName(uint8_t authMode);
bool otaProven(bool networkUp, unsigned long uptimeMs, unsigned long minUptimeMs);
// A release asset's name: "SomfyController.<kind>.esp32<chipModel>.bin", kind
// "ino" for the firmware or "littlefs" for the web UI image, chipModel as in
// ConfigSettings ("" for the ESP32, "s3", "c3", "c6").
// ConfigSettings ("" for the ESP32, "s3", "c3", "c5", "c6").
void releaseAssetName(char *buf, size_t size, const char *kind, const char *chipModel);
// A coredump backtrace as " 0x%08x" per frame. bt holds maxFrames entries and
// depth is how many the dump claims are valid.

View file

@ -164,15 +164,21 @@ void registerHttpRoutes(AsyncWebServer &server) {
// updateFirmware - file upload
server.on(AsyncURIMatcher::exact("/updateFirmware"), HTTP_POST,
[](AsyncWebServerRequest *request) {
// Nothing was written or stopped, so there is nothing to reboot for.
if(!settings.hasOtaSlot) {
request->send(400, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"This device has no OTA slot, so its firmware is flashed over USB.\"}"));
return;
}
// errorString() is one of the updater's fixed messages, none with a quote.
if(Update.hasError())
request->send(500, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Error updating firmware: \"}");
request->send(500, _encoding_json, String("{\"status\":\"ERROR\",\"desc\":\"Error updating firmware: ") + Update.errorString() + "\"}");
else
request->send(200, _encoding_json, "{\"status\":\"SUCCESS\",\"desc\":\"Successfully updated firmware\"}");
rebootDelay.reboot = true;
rebootDelay.rebootTime = millis() + 500;
},
[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
if(!uploadAllowed(request, index)) return;
if(!uploadAllowed(request, index) || !settings.hasOtaSlot) return;
if(index == 0) {
webServer.uploadSuccess = false;
ESP_LOGI(TAG, "Update: %s", filename.c_str());
@ -207,7 +213,7 @@ void registerHttpRoutes(AsyncWebServer &server) {
server.on(AsyncURIMatcher::exact("/updateApplication"), HTTP_POST,
[](AsyncWebServerRequest *request) {
if(Update.hasError())
request->send(500, _encoding_json, "{\"status\":\"ERROR\",\"desc\":\"Error updating application: \"}");
request->send(500, _encoding_json, String("{\"status\":\"ERROR\",\"desc\":\"Error updating application: ") + Update.errorString() + "\"}");
else
request->send(200, _encoding_json, "{\"status\":\"SUCCESS\",\"desc\":\"Successfully updated application\"}");
rebootDelay.reboot = true;

View file

@ -77,6 +77,7 @@ static void writeHeader(ProtoWriter &w) {
w.add(proto_field_t::hostname, settings.hostname);
w.add(proto_field_t::model, "ESPSomfyRTS");
w.add(proto_field_t::chipModel, settings.chipModel);
w.add(proto_field_t::hasOtaSlot, settings.hasOtaSlot);
w.add(proto_field_t::fw, settings.fwVersion.name);
w.add(proto_field_t::connType, net.connTypeName());
}