Files
desklock/firmware/components/esp_hosted/slave/main/example_http_client.c
T
jpmschweitzerandClaude Fable 5 cb5826e02b
Test, Build and Push / test-gateway (push) Successful in 12s
Test, Build and Push / release (push) Skipped
Test, Build and Push / build-gateway (push) Skipped
Fork fix: the SDIO wedge is FIXED (esp-hosted-mcu #167)
Root cause (verified against our exact IDF tree, not the community guess):
the "258" in "sdio_write_task: Failed to send data: 258" is NOT a timeout
(that is 263). 258 = 0x102 = ESP_ERR_INVALID_ARG. On the ESP32-P4, block-
mode CMD53 writes require the SOURCE buffer to be 64-byte (cache-line)
aligned; the IDF sdmmc driver rejects a misaligned source with INVALID_ARG
BEFORE any bus activity. esp_hosts write loop then declares "Unrecoverable
host sdio state" and reboots the whole P4. The audio TX payload is not
64-aligned, so streaming mic audio wedged on the very FIRST frame (which is
exactly what we saw: listening -> instant Failed to send -> reboot).

This also explains why buffer/queue/clock/retry tuning all did nothing: the
write never reached the bus. And why our symptom was instant, not after
~100 writes (the community block-mode-desync theory) — it is the first
misaligned buffer, every time.

Fix: vendored esp_hosted 2.12.11 as an editable local component (overrides
the registry copy) and bounce a misaligned TX payload through one aligned
DMA scratch buffer in hosted_sdio_write_block (port_esp_hosted_host_sdio.c).
TX is serialized by the bus lock so a single static bounce buffer is safe;
freed in hosted_sdio_deinit. Host-only change — no C6 reflash.

VERIFIED ON HARDWARE (autonomous self-test): 40s of continuous mic-audio
upstream streaming — the traffic that previously wedged on the first frame
— ran clean, zero timeouts, zero reboots. A guarded SDIO_TX_SELFTEST harness
is kept (compiled out) for future SDIO stress testing.

Credit: root cause + patch designed via multi-agent investigation; the
precise 258=INVALID_ARG decode (correcting the upstream community timeout
assumption) came from checking our actual esp_err.h.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 09:50:27 +02:00

266 lines
6.4 KiB
C

/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/* HTTP GET Example using plain POSIX sockets
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "sdkconfig.h"
#ifdef CONFIG_ESP_HOSTED_COPROCESSOR_EXAMPLE_HTTP_CLIENT
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#define WEB_SERVER CONFIG_HTTP_WEBSERVER
#define WEB_PORT CONFIG_HTTP_WEBSERVER_PORT
#define WEB_PATH CONFIG_HTTP_WEBSERVER_PATH
static const char *TAG = "http_req";
static const char *REQUEST = "GET " WEB_PATH " HTTP/1.0\r\n"
"Host: "WEB_SERVER":"WEB_PORT"\r\n"
"User-Agent: esp-idf/1.0 esp32\r\n"
"\r\n";
// State management
static TaskHandle_t http_task_handle = NULL;
static volatile bool is_paused = false;
static volatile bool should_stop = false;
static void http_get_task(void *pvParameters)
{
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *res;
struct in_addr *addr;
int s, r;
char recv_buf[64];
while (!should_stop) {
// Wait if paused
while (is_paused && !should_stop) {
vTaskDelay(pdMS_TO_TICKS(100));
}
if (should_stop) break;
int err = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &res);
if (err != 0 || res == NULL) {
ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res);
vTaskDelay(pdMS_TO_TICKS(1000));
continue;
}
/* Code to print the resolved IP.
Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */
addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*addr));
s = socket(res->ai_family, res->ai_socktype, 0);
if (s < 0) {
ESP_LOGE(TAG, "... Failed to allocate socket.");
freeaddrinfo(res);
vTaskDelay(pdMS_TO_TICKS(1000));
continue;
}
ESP_LOGV(TAG, "... allocated socket");
if (connect(s, res->ai_addr, res->ai_addrlen) != 0) {
ESP_LOGE(TAG, "... socket connect failed errno=%d", errno);
close(s);
freeaddrinfo(res);
vTaskDelay(pdMS_TO_TICKS(4000));
continue;
}
ESP_LOGV(TAG, "... connected");
freeaddrinfo(res);
if (write(s, REQUEST, strlen(REQUEST)) < 0) {
ESP_LOGE(TAG, "... socket send failed");
close(s);
vTaskDelay(pdMS_TO_TICKS(4000));
continue;
}
ESP_LOGV(TAG, "... socket send success");
struct timeval receiving_timeout;
receiving_timeout.tv_sec = 5;
receiving_timeout.tv_usec = 0;
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &receiving_timeout,
sizeof(receiving_timeout)) < 0) {
ESP_LOGE(TAG, "... failed to set socket receiving timeout");
close(s);
vTaskDelay(pdMS_TO_TICKS(4000));
continue;
}
ESP_LOGV(TAG, "... set socket receiving timeout success");
/* Read HTTP response */
do {
bzero(recv_buf, sizeof(recv_buf));
r = read(s, recv_buf, sizeof(recv_buf)-1);
#if 0
for(int i = 0; i < r; i++) {
putchar(recv_buf[i]);
}
#endif
} while(r > 0);
if (r)
ESP_LOGE(TAG, "HTTP req: return=%d errno=%d.", r, errno);
else
ESP_LOGI(TAG, "HTTP req fetched successful");
close(s);
// Wait before next request (only if not paused/stopped)
for (int i = 0; i < 100 && !is_paused && !should_stop; i++) {
vTaskDelay(pdMS_TO_TICKS(100)); // 10 second delay split into 100ms chunks
}
}
ESP_LOGI(TAG, "HTTP client task stopped");
http_task_handle = NULL;
vTaskDelete(NULL);
}
/**
* Initialize and start the HTTP client task
* Call this once during application startup
*
* @return ESP_OK on success, ESP_FAIL if task already running
*/
esp_err_t example_http_client_init(void)
{
if (http_task_handle != NULL) {
ESP_LOGW(TAG, "HTTP client already initialized");
return ESP_FAIL;
}
should_stop = false;
is_paused = false;
BaseType_t ret = xTaskCreate(&http_get_task,
"http_get_task",
4096,
NULL,
5,
&http_task_handle);
if (ret != pdPASS) {
ESP_LOGE(TAG, "Failed to create HTTP client task");
return ESP_FAIL;
}
ESP_LOGI(TAG, "HTTP client initialized and started");
return ESP_OK;
}
/**
* Pause HTTP client requests
* Call this on IP_EVENT_STA_LOST_IP or when network is unavailable
* The task will stop making requests but remain alive
*
* @return ESP_OK on success, ESP_FAIL if client not initialized
*/
esp_err_t example_http_client_pause(void)
{
if (http_task_handle == NULL) {
ESP_LOGW(TAG, "HTTP client not initialized");
return ESP_FAIL;
}
if (is_paused) {
ESP_LOGW(TAG, "HTTP client already paused");
return ESP_OK;
}
ESP_LOGI(TAG, "Pausing HTTP client");
is_paused = true;
return ESP_OK;
}
/**
* Resume HTTP client requests
* Call this on IP_EVENT_STA_GOT_IP or when network is available again
* Automatically initializes the client if it doesn't exist
*
* @return ESP_OK on success, ESP_FAIL on error
*/
esp_err_t example_http_client_resume(void)
{
// Auto-initialize if not created
if (http_task_handle == NULL) {
ESP_LOGI(TAG, "HTTP client not initialized, creating...");
return example_http_client_init();
}
if (!is_paused) {
ESP_LOGW(TAG, "HTTP client already running");
return ESP_OK;
}
ESP_LOGI(TAG, "Resuming HTTP client");
is_paused = false;
return ESP_OK;
}
/**
* Stop and cleanup HTTP client (optional)
* Call this to completely stop the HTTP client task
*
* @return ESP_OK on success
*/
esp_err_t example_http_client_stop(void)
{
if (http_task_handle == NULL) {
ESP_LOGW(TAG, "HTTP client not running");
return ESP_OK;
}
ESP_LOGI(TAG, "Stopping HTTP client");
should_stop = true;
// Wait for task to finish (with timeout)
int timeout = 100; // 10 seconds
while (http_task_handle != NULL && timeout-- > 0) {
vTaskDelay(pdMS_TO_TICKS(100));
}
if (http_task_handle != NULL) {
ESP_LOGW(TAG, "Task did not stop gracefully, deleting forcefully");
vTaskDelete(http_task_handle);
http_task_handle = NULL;
}
return ESP_OK;
}
//example_http_client_pause(); // Pause on disconnect
//example_http_client_resume(); // Resume on got IP (auto-creates if needed)
#endif /* CONFIG_ESP_HOSTED_COPROCESSOR_EXAMPLE_HTTP_CLIENT */