From 220c8dca561d3a9ee2b7c93293684adf429b53ca Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Sun, 27 Jul 2025 15:36:23 +0200
Subject: [PATCH] Change Hosted MCU code to driver
---
tasmota/tasmota.ino | 5 -
tasmota/tasmota_support/support_command.ino | 41 ----
.../tasmota_support/support_hosted_mcu.ino | 104 ----------
tasmota/tasmota_support/support_tasmota.ino | 28 ---
.../xdrv_84_esp32_hosted.ino | 193 ++++++++++++++++++
5 files changed, 193 insertions(+), 178 deletions(-)
delete mode 100644 tasmota/tasmota_support/support_hosted_mcu.ino
create mode 100644 tasmota/tasmota_xdrv_driver/xdrv_84_esp32_hosted.ino
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index 6ba81d416..a8f92e795 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -269,11 +269,6 @@ struct TasmotaGlobal_t {
GpioOptionABits gpio_optiona; // GPIO Option_A flags
void *log_buffer_mutex; // Control access to log buffer
-#ifdef CONFIG_ESP_WIFI_REMOTE_ENABLED
- char *hosted_ota_url; // ESP32-P4 hosted OTA URL
- int hosted_ota_state_flag; // ESP32-P4 hosted OTA initiated flag
-#endif // CONFIG_ESP_WIFI_REMOTE_ENABLED
-
power_t power; // Current copy of Settings->power
power_t power_latching; // Current state of single pin latching power
power_t rel_inverted; // Relay inverted flag (1 = (0 = On, 1 = Off))
diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino
index a43c12f86..14bec975e 100644
--- a/tasmota/tasmota_support/support_command.ino
+++ b/tasmota/tasmota_support/support_command.ino
@@ -22,9 +22,6 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix
D_SO_WIFINOSLEEP "|"
// Other commands
D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_RESTART "|"
-#ifdef CONFIG_ESP_WIFI_REMOTE_ENABLED
- D_CMND_HOSTEDOTA "|"
-#endif // CONFIG_ESP_WIFI_REMOTE_ENABLED
#ifndef FIRMWARE_MINIMAL
D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_POWERLOCK "|" D_CMND_TIMEDPOWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_SLEEP "|"
D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_STATETEXT "|" D_CMND_SAVEDATA "|"
@@ -74,9 +71,6 @@ SO_SYNONYMS(kTasmotaSynonyms,
void (* const TasmotaCommand[])(void) PROGMEM = {
&CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, &CmndSeriallog, &CmndRestart,
-#ifdef CONFIG_ESP_WIFI_REMOTE_ENABLED
- &CmdHostedOta,
-#endif // CONFIG_ESP_WIFI_REMOTE_ENABLED
#ifndef FIRMWARE_MINIMAL
&CmndBacklog, &CmndDelay, &CmndPower, &CmndPowerLock, &CmndTimedPower, &CmndStatus, &CmndState, &CmndSleep,
&CmndPowerOnState, &CmndPulsetime, &CmndBlinktime, &CmndBlinkcount, &CmndStateText, &CmndSavedata,
@@ -1341,41 +1335,6 @@ void CmndOtaUrl(void)
ResponseCmndChar(SettingsText(SET_OTAURL));
}
-#ifdef CONFIG_ESP_WIFI_REMOTE_ENABLED
-void CmdHostedOta() {
- /*
- If OtaUrl = "https://ota.tasmota.com/tasmota32/tasmota32p4.bin"
- Then use "https://ota.tasmota.com/tasmota32/coprocessor/network_adapter_" CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET ".bin"
- As an option allow user to enter URL like:
- HostedOta https://ota.tasmota.com/tasmota32/coprocessor/network_adapter_esp32c6.bin
- HostedOta https://ota.tasmota.com/tasmota32/coprocessor/v2.0.14/network_adapter_esp32c6.bin
- Or allow user to enter required version like:
- HostedOta v2.0.17
- */
- TasmotaGlobal.hosted_ota_url = (char*)calloc(200, sizeof(char));
- if (!TasmotaGlobal.hosted_ota_url) { return; } // Unable to allocate memory
- if (XdrvMailbox.data_len > 15) {
- strlcpy(TasmotaGlobal.hosted_ota_url, XdrvMailbox.data, 200);
- } else {
- // Replace https://ota.tasmota.com/tasmota32/tasmota32p4.bin with https://ota.tasmota.com/tasmota32/coprocessor/network_adapter_esp32c6.bin
- char ota_url[TOPSZ];
- strlcpy(TasmotaGlobal.hosted_ota_url, GetOtaUrl(ota_url, sizeof(ota_url)), 200);
- char *bch = strrchr(TasmotaGlobal.hosted_ota_url, '/'); // Only consider filename after last backslash
- if (bch == nullptr) { bch = TasmotaGlobal.hosted_ota_url; } // No path found so use filename only
- *bch = '\0'; // full_ota_url = https://ota.tasmota.com/tasmota32
- char version[16] = { 0 };
- if (XdrvMailbox.data_len) {
- snprintf_P(version, sizeof(version), PSTR("/%s"), XdrvMailbox.data);
- }
- snprintf_P(TasmotaGlobal.hosted_ota_url, 200, PSTR("%s/coprocessor%s/network_adapter_" CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET ".bin"),
- TasmotaGlobal.hosted_ota_url, version);
- }
- TasmotaGlobal.hosted_ota_state_flag = 1;
- Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"),
- XdrvMailbox.command, GetHostedMCUFwVersion().c_str(), TasmotaGlobal.hosted_ota_url);
-}
-#endif // CONFIG_ESP_WIFI_REMOTE_ENABLED
-
void CmndSeriallog(void)
{
if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) {
diff --git a/tasmota/tasmota_support/support_hosted_mcu.ino b/tasmota/tasmota_support/support_hosted_mcu.ino
deleted file mode 100644
index f31fda743..000000000
--- a/tasmota/tasmota_support/support_hosted_mcu.ino
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- support_hosted_mcu.ino - eeprom support for Tasmota
-
- Copyright (C) 2025 Theo Arends & Christian Baars
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-*/
-
-#ifdef CONFIG_ESP_WIFI_REMOTE_ENABLED
-
-#include "esp_hosted.h"
-#include "esp_hosted_api_types.h"
-#include "esp_hosted_ota.h"
-
-String GetHostedMCU(void) {
- // Function is not yet implemented in Arduino Core so emulate it here
- if (0 == strcasecmp_P(CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET, PSTR("esp32c6"))) {
- return String("ESP32-C6");
- }
- return String("Unknown");
-}
-
-int GetFwVersionNumber(void) {
- // Function is not yet implemented in Arduino Core so emulate it here
- return 0x0200000E; // v2.0.14
-}
-
-int GetHostedMCUFwVersionNumber(void) {
- static int version = -1;
-
- if (!esp_hosted_is_config_valid()) {
- return 0;
- }
- if (-1 == version) {
- version = 6; // v0.0.6
- esp_hosted_coprocessor_fwver_t ver_info;
- esp_err_t err = esp_hosted_get_coprocessor_fwversion(&ver_info); // This takes almost 4 seconds on > 24;
- uint8_t minor1 = version >> 16;
- uint16_t patch1 = version;
- char data[40];
- snprintf_P(data, sizeof(data), PSTR("%d.%d.%d"), major1, minor1, patch1);
- return String(data);
-}
-
-int OTAHostedMCU(const char* image_url) {
- AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HST: About to OTA update with %s"), image_url);
- int result = esp_hosted_slave_ota(image_url);
- AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HST: Done with result %d"), result);
- return result;
-}
-
-void HostedMCUStatus(void) {
- // Execute after HostedMCU is init by WiFi.mode()
- static bool once_shown = false;
-
- if (once_shown) { return; }
- if (esp_hosted_is_config_valid()) {
- once_shown = true;
- char config[128] = { 0 };
- struct esp_hosted_transport_config *pconfig;
- if (ESP_TRANSPORT_OK == esp_hosted_transport_get_config(&pconfig)) {
- if (pconfig->transport_in_use == H_TRANSPORT_SDIO) {
- struct esp_hosted_sdio_config *psdio_config;
- if (ESP_TRANSPORT_OK == esp_hosted_sdio_get_config(&psdio_config)) {
- snprintf_P(config, sizeof(config), PSTR(" using GPIO%02d(CLK), GPIO%02d(CMD), GPIO%02d(D0), GPIO%02d(D1), GPIO%02d(D2), GPIO%02d(D3) and GPIO%02d(RST)"),
- psdio_config->pin_clk.pin, psdio_config->pin_cmd.pin, psdio_config->pin_d0.pin, psdio_config->pin_d1.pin, psdio_config->pin_d2.pin, psdio_config->pin_d3.pin, psdio_config->pin_reset.pin);
- }
- }
- }
- AddLog(LOG_LEVEL_INFO, PSTR("HST: Hosted MCU %s v%s%s"),
- GetHostedMCU().c_str(), GetHostedMCUFwVersion().c_str(), config);
- }
-}
-
-#endif // CONFIG_ESP_WIFI_REMOTE_ENABLED
\ No newline at end of file
diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino
index 39cf3d58a..33f961e28 100644
--- a/tasmota/tasmota_support/support_tasmota.ino
+++ b/tasmota/tasmota_support/support_tasmota.ino
@@ -1517,34 +1517,6 @@ void Every250mSeconds(void)
AllowInterrupts(1);
}
}
-
-#ifdef CONFIG_ESP_WIFI_REMOTE_ENABLED
- if (TasmotaGlobal.hosted_ota_state_flag && CommandsReady()) {
- TasmotaGlobal.hosted_ota_state_flag--;
-/*
- if (2 == TasmotaGlobal.hosted_ota_state_flag) {
- SettingsSave(0);
- }
-*/
- if (TasmotaGlobal.hosted_ota_state_flag <= 0) {
- // Blocking
- int ret = OTAHostedMCU(TasmotaGlobal.hosted_ota_url);
- free(TasmotaGlobal.hosted_ota_url);
- TasmotaGlobal.hosted_ota_url = nullptr;
- Response_P(PSTR("{\"" D_CMND_HOSTEDOTA "\":\""));
- if (ret == ESP_OK) {
- // next lines are questionable, because currently the system will reboot immediately on succesful upgrade
- ResponseAppend_P(PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING));
- TasmotaGlobal.restart_flag = 5; // Allow time for webserver to update console
- } else {
- ResponseAppend_P(PSTR(D_JSON_FAILED " %d\"}"), ret);
- }
- ResponseAppend_P(PSTR("\"}"));
- MqttPublishPrefixTopicRulesProcess_P(STAT, PSTR(D_CMND_HOSTEDOTA));
- }
- }
-#endif // CONFIG_ESP_WIFI_REMOTE_ENABLED
-
break;
case 1: // Every x.25 second
if (MidnightNow()) {
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_84_esp32_hosted.ino b/tasmota/tasmota_xdrv_driver/xdrv_84_esp32_hosted.ino
new file mode 100644
index 000000000..d16c43b4d
--- /dev/null
+++ b/tasmota/tasmota_xdrv_driver/xdrv_84_esp32_hosted.ino
@@ -0,0 +1,193 @@
+/*
+ xdrv_84_esp32_hosted.ino - ESP32 Hosted MCU support for Tasmota
+
+ SPDX-FileCopyrightText: 2025 Theo Arends
+
+ SPDX-License-Identifier: GPL-3.0-only
+*/
+
+#ifdef ESP32
+#ifdef CONFIG_ESP_WIFI_REMOTE_ENABLED
+/*********************************************************************************************\
+ * Support for Hosted MCU to be used on ESP32-H2 and ESP32-P4
+\*********************************************************************************************/
+
+#define XDRV_84 84
+
+#include "esp_hosted.h"
+#include "esp_hosted_api_types.h"
+#include "esp_hosted_ota.h"
+
+struct Hosted_t {
+ char *hosted_ota_url; // Hosted MCU OTA URL
+ int hosted_ota_state_flag; // Hosted MCU OTA initiated flag
+} Hosted;
+
+/*********************************************************************************************/
+
+String GetHostedMCU(void) {
+ // Function is not yet implemented in Arduino Core so emulate it here
+ if (0 == strcasecmp_P(CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET, PSTR("esp32c6"))) {
+ return String("ESP32-C6");
+ }
+ return String("Unknown");
+}
+
+int GetFwVersionNumber(void) {
+ // Function is not yet implemented in Arduino Core so emulate it here
+ return 0x0200000E; // v2.0.14
+}
+
+int GetHostedMCUFwVersionNumber(void) {
+ static int version = -1;
+
+ if (!esp_hosted_is_config_valid()) {
+ return 0;
+ }
+ if (-1 == version) {
+ version = 6; // v0.0.6
+ esp_hosted_coprocessor_fwver_t ver_info;
+ esp_err_t err = esp_hosted_get_coprocessor_fwversion(&ver_info); // This takes almost 4 seconds on > 24;
+ uint8_t minor1 = version >> 16;
+ uint16_t patch1 = version;
+ char data[40];
+ snprintf_P(data, sizeof(data), PSTR("%d.%d.%d"), major1, minor1, patch1);
+ return String(data);
+}
+
+void HostedMCUStatus(void) {
+ // Execute after HostedMCU is init by WiFi.mode()
+ static bool once_shown = false;
+
+ if (once_shown) { return; }
+ if (esp_hosted_is_config_valid()) {
+ once_shown = true;
+ char config[128] = { 0 };
+ struct esp_hosted_transport_config *pconfig;
+ if (ESP_TRANSPORT_OK == esp_hosted_transport_get_config(&pconfig)) {
+ if (pconfig->transport_in_use == H_TRANSPORT_SDIO) {
+ struct esp_hosted_sdio_config *psdio_config;
+ if (ESP_TRANSPORT_OK == esp_hosted_sdio_get_config(&psdio_config)) {
+ snprintf_P(config, sizeof(config), PSTR(" using GPIO%02d(CLK), GPIO%02d(CMD), GPIO%02d(D0), GPIO%02d(D1), GPIO%02d(D2), GPIO%02d(D3) and GPIO%02d(RST)"),
+ psdio_config->pin_clk.pin, psdio_config->pin_cmd.pin, psdio_config->pin_d0.pin, psdio_config->pin_d1.pin, psdio_config->pin_d2.pin, psdio_config->pin_d3.pin, psdio_config->pin_reset.pin);
+ }
+ }
+ }
+ AddLog(LOG_LEVEL_INFO, PSTR("HST: Hosted MCU %s v%s%s"),
+ GetHostedMCU().c_str(), GetHostedMCUFwVersion().c_str(), config);
+ }
+}
+
+/*********************************************************************************************\
+ * Every second
+\*********************************************************************************************/
+
+void HostedMCUEverySecond(void) {
+ if (Hosted.hosted_ota_state_flag && CommandsReady()) {
+ Hosted.hosted_ota_state_flag--;
+/*
+ if (2 == Hosted.hosted_ota_state_flag) {
+ SettingsSave(0);
+ }
+*/
+ if (Hosted.hosted_ota_state_flag <= 0) {
+ // Blocking
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HST: About to OTA update with %s"), Hosted.hosted_ota_url);
+ int ret = esp_hosted_slave_ota(Hosted.hosted_ota_url);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HST: Done with result %d"), ret);
+ free(Hosted.hosted_ota_url);
+ Hosted.hosted_ota_url = nullptr;
+ Response_P(PSTR("{\"" D_CMND_HOSTEDOTA "\":\""));
+ if (ret == ESP_OK) {
+ // next lines are questionable, because currently the system will reboot immediately on succesful upgrade
+ ResponseAppend_P(PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING));
+ TasmotaGlobal.restart_flag = 5; // Allow time for webserver to update console
+ } else {
+ ResponseAppend_P(PSTR(D_JSON_FAILED " %d\"}"), ret);
+ }
+ ResponseAppend_P(PSTR("\"}"));
+ MqttPublishPrefixTopicRulesProcess_P(STAT, PSTR(D_CMND_HOSTEDOTA));
+ }
+ }
+}
+
+/*********************************************************************************************\
+ * Commands
+\*********************************************************************************************/
+
+const char kHostedCommands[] PROGMEM = "Hosted|" // Prefix
+ "Ota";
+
+void (* const HostedCommand[])(void) PROGMEM = {
+ &CmndHostedOta };
+
+void CmndHostedOta(void) {
+ /*
+ If OtaUrl = "https://ota.tasmota.com/tasmota32/tasmota32p4.bin"
+ Then use "https://ota.tasmota.com/tasmota32/coprocessor/network_adapter_" CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET ".bin"
+ As an option allow user to enter URL like:
+ HostedOta https://ota.tasmota.com/tasmota32/coprocessor/network_adapter_esp32c6.bin
+ HostedOta https://ota.tasmota.com/tasmota32/coprocessor/v2.0.14/network_adapter_esp32c6.bin
+ Or allow user to enter required version like:
+ HostedOta v2.0.17
+ */
+ Hosted.hosted_ota_url = (char*)calloc(200, sizeof(char));
+ if (!Hosted.hosted_ota_url) { return; } // Unable to allocate memory
+ if (XdrvMailbox.data_len > 15) {
+ strlcpy(Hosted.hosted_ota_url, XdrvMailbox.data, 200);
+ } else {
+ // Replace https://ota.tasmota.com/tasmota32/tasmota32p4.bin with https://ota.tasmota.com/tasmota32/coprocessor/network_adapter_esp32c6.bin
+ char ota_url[TOPSZ];
+ strlcpy(Hosted.hosted_ota_url, GetOtaUrl(ota_url, sizeof(ota_url)), 200);
+ char *bch = strrchr(Hosted.hosted_ota_url, '/'); // Only consider filename after last backslash
+ if (bch == nullptr) { bch = Hosted.hosted_ota_url; } // No path found so use filename only
+ *bch = '\0'; // full_ota_url = https://ota.tasmota.com/tasmota32
+ char version[16] = { 0 };
+ if (XdrvMailbox.data_len) {
+ snprintf_P(version, sizeof(version), PSTR("/%s"), XdrvMailbox.data);
+ }
+ snprintf_P(Hosted.hosted_ota_url, 200, PSTR("%s/coprocessor%s/network_adapter_" CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET ".bin"),
+ Hosted.hosted_ota_url, version);
+ }
+ Hosted.hosted_ota_state_flag = 1;
+ Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"),
+ XdrvMailbox.command, GetHostedMCUFwVersion().c_str(), Hosted.hosted_ota_url);
+}
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xdrv84(uint32_t function) {
+ bool result = false;
+
+ switch (function) {
+ case FUNC_EVERY_SECOND:
+ HostedMCUEverySecond();
+ break;
+ case FUNC_COMMAND:
+ result = DecodeCommand(kHostedCommands, HostedCommand);
+ break;
+ }
+ return result;
+}
+
+#endif // CONFIG_ESP_WIFI_REMOTE_ENABLED
+#endif // ESP32
\ No newline at end of file