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>
266 lines
6.4 KiB
C
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 */
|