WiFiClient.cpp:395] write(): fail on fd 60, errno: 11, "No more processes"
Posted: Fri Aug 27, 2021 1:57 am
I have an esp32 aurdino app that is serving a video stream (see code below). I'm constantly getting the error message: WiFiClient.cpp:395] write(): fail on fd 60, errno: 11, 'No more processes'. It is clogging up my logs. Strangely enough I don't see any symptoms in my app, but I really want to fix this.
I searched for "No more processes" in the entire esp32 arduino repo and got no hits. So it must be coming from the idf level. When I searched the esp32 idf repo I got
But I couldn't find any code with that string or any code referencing pdFREERTOS_ERRNO_EAGAIN.
I'm not even sure what a "process" is. Is that a task? I googled "esp32 processes" and got nothing interesting.
As you can tell I am lost. Can someone help me understand what is going on?
I searched for "No more processes" in the entire esp32 arduino repo and got no hits. So it must be coming from the idf level. When I searched the esp32 idf repo I got
- #define pdFREERTOS_ERRNO_EAGAIN 11 /* No more processes */
I'm not even sure what a "process" is. Is that a task? I googled "esp32 processes" and got nothing interesting.
As you can tell I am lost. Can someone help me understand what is going on?
- // https://RandomNerdTutorials.com/esp32-cam-video-streaming-web-server-camera-home-assistant/
- #include "Arduino.h"
- #include "main.h"
- // #include "esp_camera.h"
- // #include "esp_timer.h"
- // #include "img_converters.h"
- // #include "fb_gfx.h"
- // #include "soc/soc.h" //disable brownout problems
- // #include "soc/rtc_cntl_reg.h" //disable brownout problems
- // #include "esp_http_server.h"
- #include "OV2640.h"
- #include <WiFi.h>
- #include <WebServer.h>
- #include <WiFiClient.h>
- #include <esp_bt.h>
- #include <esp_wifi.h>
- #include <esp_sleep.h>
- #include <driver/rtc_io.h>
- #include "output.h"
- #include "http.h"
- #include "camera.h"
- #define APP_CPU 1
- #define PRO_CPU 0 = 0;
- u32_t fps = 0;
- u32_t fpsCounter = 0;
- OV2640 cam;
- WebServer multiServer(8080);
- // ===== rtos task handles =========================
- // Streaming is implemented with 3 tasks:
- TaskHandle_t tMjpeg; // handles client connections to the webserver
- TaskHandle_t tCam; // handles getting picture frames from the camera and storing them locally
- TaskHandle_t tStream; // actually streaming frames to all connected clients
- // frameSync semaphore is used to prevent streaming buffer as it is replaced with the next frame
- SemaphoreHandle_t frameSync = NULL;
- // Queue stores currently connected clients to whom we are streaming
- QueueHandle_t streamingClients;
- // We will try to achieve this frame rate
- #define FPS 14
- // We will handle web client requests every 50 ms (20 Hz)
- const int WSINTERVAL = 100;
- void camCB(void* pvParameters);
- void streamCB(void * pvParameters);
- void streamCB(void * pvParameters);
- void handleJPGSstream(void);
- void handleJPG(void);
- char* allocateMemory(char* aPtr, size_t aSize);
- // void handleNotFound();
- // ======== Server Connection Handler Task ==========================
- void mjpegCB(void* pvParameters) {
- TickType_t lastWakeTime;
- const TickType_t frequency = pdMS_TO_TICKS(WSINTERVAL);
- // Creating frame synchronization semaphore and initializing it
- frameSync = xSemaphoreCreateBinary();
- xSemaphoreGive( frameSync );
- // Creating a queue to track all connected clients
- streamingClients = xQueueCreate( 5, sizeof(WiFiClient*) );
- //=== setup section ==================
- // Creating RTOS task for grabbing frames from the camera
- xTaskCreatePinnedToCore(
- camCB, // callback
- "cam", // name
- 4096, // stacj size
- NULL, // parameters
- 2, // priority
- &tCam, // RTOS task handle
- APP_CPU); // core
- // Creating task to push the stream to all connected clients
- xTaskCreatePinnedToCore(
- streamCB,
- "strmCB",
- 4 * 1024,
- NULL, //(void*) handler,
- 2,
- &tStream,
- APP_CPU);
- // Registering webserver handling routines
- multiServer.on("/stream", HTTP_GET, handleJPGSstream);
- // multiServer.onNotFound(handleNotFound);
- // Starting webserver
- multiServer.begin();
- //=== loop() section ===================
- lastWakeTime = xTaskGetTickCount();
- for (;;) {
- multiServer.handleClient();
- // After every server client handling request, we let other tasks run and then pause
- taskYIELD();
- vTaskDelayUntil(&lastWakeTime, frequency);
- }
- }
- // Commonly used variables:
- volatile size_t camSize; // size of the current frame, byte
- volatile char* camBuf; // pointer to the current frame
- // ==== RTOS task to grab frames from the camera =========================
- void camCB(void* pvParameters) {
- TickType_t lastWakeTime;
- // A running interval associated with currently desired frame rate
- const TickType_t frequency = pdMS_TO_TICKS(1000 / FPS);
- // Mutex for the critical section of switching the active frames around
- portMUX_TYPE xSemaphore = portMUX_INITIALIZER_UNLOCKED;
- // Pointers to the 2 frames, their respective sizes and index of the current frame
- char* fbs[2] = { NULL, NULL };
- size_t fSize[2] = { 0, 0 };
- int ifb = 0;
- //=== loop() section ===================
- lastWakeTime = xTaskGetTickCount();
- while (true) {
- fpsCounter++;
- // Grab a frame from the camera and query its size
- cam.run();
- size_t s = cam.getSize();
- // If frame size is more that we have previously allocated - request 125% of the current frame space
- if (s > fSize[ifb]) {
- fSize[ifb] = s * 4 / 3;
- fbs[ifb] = allocateMemory(fbs[ifb], fSize[ifb]);
- }
- // Copy current frame into local buffer
- char* b = (char*) cam.getfb();
- memcpy(fbs[ifb], b, s);
- // Let other tasks run and wait until the end of the current frame rate interval (if any time left)
- taskYIELD();
- vTaskDelayUntil(&lastWakeTime, frequency);
- // Only switch frames around if no frame is currently being streamed to a client
- // Wait on a semaphore until client operation completes
- xSemaphoreTake( frameSync, portMAX_DELAY );
- // Do not allow interrupts while switching the current frame
- portENTER_CRITICAL(&xSemaphore);
- camBuf = fbs[ifb];
- camSize = s;
- ifb++;
- ifb &= 1; // this should produce 1, 0, 1, 0, 1 ... sequence
- portEXIT_CRITICAL(&xSemaphore);
- // Let anyone waiting for a frame know that the frame is ready
- xSemaphoreGive( frameSync );
- // Technically only needed once: let the streaming task know that we have at least one frame
- // and it could start sending frames to the clients, if any
- xTaskNotifyGive( tStream );
- // Immediately let other (streaming) tasks run
- taskYIELD();
- // If streaming task has suspended itself (no active clients to stream to)
- // there is no need to grab frames from the camera. We can save some juice
- // by suspedning the tasks
- if ( eTaskGetState( tStream ) == eSuspended ) {
- vTaskSuspend(NULL); // passing NULL means "suspend yourself"
- }
- }
- }
- // ==== Memory allocator that takes advantage of PSRAM if present =======================
- char* allocateMemory(char* aPtr, size_t aSize) {
- // Since current buffer is too smal, free it
- if (aPtr != NULL) free(aPtr);
- size_t freeHeap = ESP.getFreeHeap();
- char* ptr = NULL;
- // If memory requested is more than 2/3 of the currently free heap, try PSRAM immediately
- if ( aSize > freeHeap * 2 / 3 ) {
- if ( psramFound() && ESP.getFreePsram() > aSize ) {
- ptr = (char*) ps_malloc(aSize);
- }
- }
- else {
- // Enough free heap - let's try allocating fast RAM as a buffer
- ptr = (char*) malloc(aSize);
- // If allocation on the heap failed, let's give PSRAM one more chance:
- if ( ptr == NULL && psramFound() && ESP.getFreePsram() > aSize) {
- ptr = (char*) ps_malloc(aSize);
- }
- }
- // Finally, if the memory pointer is NULL, we were not able to allocate any memory, and that is a terminal condition.
- if (ptr == NULL) {
- ESP.restart();
- }
- return ptr;
- }
- // ==== STREAMING ======================================================
- const char HEADER[] = "HTTP/1.1 200 OK\r\n" \
- "Access-Control-Allow-Origin: *\r\n" \
- "Content-Type: multipart/x-mixed-replace; boundary=123456789000000000000987654321\r\n";
- const char BOUNDARY[] = "\r\n--123456789000000000000987654321\r\n";
- const char CTNTTYPE[] = "Content-Type: image/jpeg\r\nContent-Length: ";
- const int hdrLen = strlen(HEADER);
- const int bdrLen = strlen(BOUNDARY);
- const int cntLen = strlen(CTNTTYPE);
- // ==== Handle connection request from clients ===============================
- void handleJPGSstream(void)
- {
- // Can only acommodate 5 clients. The limit is a default for WiFi connections
- if ( !uxQueueSpacesAvailable(streamingClients) ) return;
- // Create a new WiFi Client object to keep track of this one
- WiFiClient* client = new WiFiClient();
- *client = multiServer.client();
- // Immediately send this client a header
- client->write(HEADER, hdrLen);
- client->write(BOUNDARY, bdrLen);
- // Push the client to the streaming queue
- xQueueSend(streamingClients, (void *) &client, 0);
- // Wake up streaming tasks, if they were previously suspended:
- if ( eTaskGetState( tCam ) == eSuspended ) vTaskResume( tCam );
- if ( eTaskGetState( tStream ) == eSuspended ) vTaskResume( tStream );
- }
- // ==== Actually stream content to all connected clients ========================
- void streamCB(void * pvParameters) {
- char buf[16];
- TickType_t lastWakeTime;
- TickType_t frequency;
- // Wait until the first frame is captured and there is something to send
- // to clients
- ulTaskNotifyTake( pdTRUE, /* Clear the notification value before exiting. */
- portMAX_DELAY ); /* Block indefinitely. */
- lastWakeTime = xTaskGetTickCount();
- while (true) {
- // Default assumption we are running according to the FPS
- frequency = pdMS_TO_TICKS(1000 / FPS);
- // Only bother to send anything if there is someone watching
- UBaseType_t activeClients = uxQueueMessagesWaiting(streamingClients);
- if ( activeClients ) {
- // Adjust the period to the number of connected clients
- frequency /= activeClients;
- // Since we are sending the same frame to everyone,
- // pop a client from the the front of the queue
- WiFiClient *client;
- xQueueReceive (streamingClients, (void*) &client, 0);
- // Check if this client is still connected.
- if (!client->connected()) {
- // delete this client reference if s/he has disconnected
- // and don't put it back on the queue anymore. Bye!
- delete client;
- }
- else {
- // Ok. This is an actively connected client.
- // Let's grab a semaphore to prevent frame changes while we
- // are serving this frame
- xSemaphoreTake( frameSync, portMAX_DELAY );
- client->write(CTNTTYPE, cntLen);
- sprintf(buf, "%d\r\n\r\n", camSize);
- client->write(buf, strlen(buf));
- client->write((char*) camBuf, (size_t)camSize);
- client->write(BOUNDARY, bdrLen);
- // Since this client is still connected, push it to the end
- // of the queue for further processing
- xQueueSend(streamingClients, (void *) &client, 0);
- // The frame has been served. Release the semaphore and let other tasks run.
- // If there is a frame switch ready, it will happen now in between frames
- xSemaphoreGive( frameSync );
- taskYIELD();
- }
- }
- else {
- // Since there are no connected clients, there is no reason to waste battery running
- vTaskSuspend(NULL);
- }
- // Let other tasks run after serving every client
- taskYIELD();
- vTaskDelayUntil(&lastWakeTime, frequency);
- }
- }
- // ==== Handle invalid URL requests ============================================
- // void handleNotFound()
- // {
- // String message = "Server is running!\n\n";
- // message += "URI: ";
- // message += multiServer.uri();
- // message += "\nMethod: ";
- // message += (multiServer.method() == HTTP_GET) ? "GET" : "POST";
- // message += "\nArguments: ";
- // message += multiServer.args();
- // message += "\n";
- // multiServer.send(200, "text / plain", message);
- // }
- // for thinkertoy esp32-cam
- #define PWDN_GPIO_NUM 32
- #define RESET_GPIO_NUM -1
- #define XCLK_GPIO_NUM 0
- #define SIOD_GPIO_NUM 26
- #define SIOC_GPIO_NUM 27
- #define Y9_GPIO_NUM 35
- #define Y8_GPIO_NUM 34
- #define Y7_GPIO_NUM 39
- #define Y6_GPIO_NUM 36
- #define Y5_GPIO_NUM 21
- #define Y4_GPIO_NUM 19
- #define Y3_GPIO_NUM 18
- #define Y2_GPIO_NUM 5
- #define VSYNC_GPIO_NUM 25
- #define HREF_GPIO_NUM 23
- #define PCLK_GPIO_NUM 22
- // ==== SETUP method ==================================================================
- void initCamera() {
- // Configure the camera
- camera_config_t config;
- config.ledc_channel = LEDC_CHANNEL_0;
- config.ledc_timer = LEDC_TIMER_0;
- config.pin_d0 = Y2_GPIO_NUM;
- config.pin_d1 = Y3_GPIO_NUM;
- config.pin_d2 = Y4_GPIO_NUM;
- config.pin_d3 = Y5_GPIO_NUM;
- config.pin_d4 = Y6_GPIO_NUM;
- config.pin_d5 = Y7_GPIO_NUM;
- config.pin_d6 = Y8_GPIO_NUM;
- config.pin_d7 = Y9_GPIO_NUM;
- config.pin_xclk = XCLK_GPIO_NUM;
- config.pin_pclk = PCLK_GPIO_NUM;
- config.pin_vsync = VSYNC_GPIO_NUM;
- config.pin_href = HREF_GPIO_NUM;
- config.pin_sscb_sda = SIOD_GPIO_NUM;
- config.pin_sscb_scl = SIOC_GPIO_NUM;
- config.pin_pwdn = PWDN_GPIO_NUM;
- config.pin_reset = RESET_GPIO_NUM;
- config.xclk_freq_hz = 20000000;
- config.pixel_format = PIXFORMAT_JPEG;
- // Frame parameters: pick one
- // config.frame_size = FRAMESIZE_UXGA;
- // config.frame_size = FRAMESIZE_SVGA;
- // config.frame_size = FRAMESIZE_QVGA;
- config.frame_size = FRAMESIZE_VGA; // 640x480
- config.jpeg_quality = 12;
- config.fb_count = 2;
- if (cam.init(config) != ESP_OK) {
- prtl("Error initializing the camera");
- delay(10000);
- ESP.restart();
- }
- // Start mainstreaming RTOS task
- xTaskCreatePinnedToCore(
- mjpegCB,
- "mjpeg",
- 4 * 1024,
- NULL,
- 2,
- &tMjpeg,
- APP_CPU);
- }
- u32_t lastFpsCalc = 0;
- void cameraLoop() {
- if((millis() - lastFpsCalc) > 4000) {
- fps = fpsCounter / 4;
- fpsCounter = 0;
- lastFpsCalc = millis();
- }
- }