Page 1 of 1

WiFiClient.cpp:395] write(): fail on fd 60, errno: 11, "No more processes"

Posted: Fri Aug 27, 2021 1:57 am
by mchahn
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

  1. #define pdFREERTOS_ERRNO_EAGAIN           11  /* No more processes */
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?

  1. // https://RandomNerdTutorials.com/esp32-cam-video-streaming-web-server-camera-home-assistant/
  2.  
  3. #include "Arduino.h"
  4. #include "main.h"
  5.  
  6. // #include "esp_camera.h"
  7. // #include "esp_timer.h"
  8. // #include "img_converters.h"
  9. // #include "fb_gfx.h"
  10. // #include "soc/soc.h"           //disable brownout problems
  11. // #include "soc/rtc_cntl_reg.h"  //disable brownout problems
  12. // #include "esp_http_server.h"
  13.  
  14. #include "OV2640.h"
  15. #include <WiFi.h>
  16. #include <WebServer.h>
  17. #include <WiFiClient.h>
  18. #include <esp_bt.h>
  19. #include <esp_wifi.h>
  20. #include <esp_sleep.h>
  21. #include <driver/rtc_io.h>
  22.  
  23. #include "output.h"
  24. #include "http.h"
  25. #include "camera.h"
  26.  
  27. #define APP_CPU 1
  28. #define PRO_CPU 0 = 0;
  29.  
  30. u32_t fps        = 0;
  31. u32_t fpsCounter = 0;
  32.  
  33. OV2640 cam;
  34. WebServer multiServer(8080);
  35.  
  36. // ===== rtos task handles =========================
  37. // Streaming is implemented with 3 tasks:
  38. TaskHandle_t tMjpeg;   // handles client connections to the webserver
  39. TaskHandle_t tCam;     // handles getting picture frames from the camera and storing them locally
  40. TaskHandle_t tStream;  // actually streaming frames to all connected clients
  41.  
  42. // frameSync semaphore is used to prevent streaming buffer as it is replaced with the next frame
  43. SemaphoreHandle_t frameSync = NULL;
  44.  
  45. // Queue stores currently connected clients to whom we are streaming
  46. QueueHandle_t streamingClients;
  47.  
  48. // We will try to achieve this frame rate
  49. #define FPS 14
  50.  
  51. // We will handle web client requests every 50 ms (20 Hz)
  52. const int WSINTERVAL = 100;
  53.  
  54. void camCB(void* pvParameters);
  55. void streamCB(void * pvParameters);
  56. void streamCB(void * pvParameters);
  57. void handleJPGSstream(void);
  58. void handleJPG(void);
  59. char* allocateMemory(char* aPtr, size_t aSize);
  60. // void handleNotFound();
  61.  
  62. // ======== Server Connection Handler Task ==========================
  63. void mjpegCB(void* pvParameters) {
  64.   TickType_t lastWakeTime;
  65.   const TickType_t frequency = pdMS_TO_TICKS(WSINTERVAL);
  66.  
  67.   // Creating frame synchronization semaphore and initializing it
  68.   frameSync = xSemaphoreCreateBinary();
  69.   xSemaphoreGive( frameSync );
  70.  
  71.   // Creating a queue to track all connected clients
  72.   streamingClients = xQueueCreate( 5, sizeof(WiFiClient*) );
  73.  
  74.   //=== setup section  ==================
  75.  
  76.   //  Creating RTOS task for grabbing frames from the camera
  77.   xTaskCreatePinnedToCore(
  78.     camCB,        // callback
  79.     "cam",        // name
  80.     4096,         // stacj size
  81.     NULL,         // parameters
  82.     2,            // priority
  83.     &tCam,        // RTOS task handle
  84.     APP_CPU);     // core
  85.  
  86.   //  Creating task to push the stream to all connected clients
  87.   xTaskCreatePinnedToCore(
  88.     streamCB,
  89.     "strmCB",
  90.     4 * 1024,
  91.     NULL, //(void*) handler,
  92.     2,
  93.     &tStream,
  94.     APP_CPU);
  95.  
  96.   //  Registering webserver handling routines
  97.   multiServer.on("/stream", HTTP_GET, handleJPGSstream);
  98.   // multiServer.onNotFound(handleNotFound);
  99.  
  100.   //  Starting webserver
  101.   multiServer.begin();
  102.  
  103.   //=== loop() section  ===================
  104.   lastWakeTime = xTaskGetTickCount();
  105.   for (;;) {
  106.     multiServer.handleClient();
  107.  
  108.     //  After every server client handling request, we let other tasks run and then pause
  109.     taskYIELD();
  110.     vTaskDelayUntil(&lastWakeTime, frequency);
  111.   }
  112. }
  113.  
  114. // Commonly used variables:
  115. volatile size_t camSize;    // size of the current frame, byte
  116. volatile char* camBuf;      // pointer to the current frame
  117.  
  118.  
  119. // ==== RTOS task to grab frames from the camera =========================
  120. void camCB(void* pvParameters) {
  121.  
  122.   TickType_t lastWakeTime;
  123.  
  124.   //  A running interval associated with currently desired frame rate
  125.   const TickType_t frequency = pdMS_TO_TICKS(1000 / FPS);
  126.  
  127.   // Mutex for the critical section of switching the active frames around
  128.   portMUX_TYPE xSemaphore = portMUX_INITIALIZER_UNLOCKED;
  129.  
  130.   //  Pointers to the 2 frames, their respective sizes and index of the current frame
  131.   char* fbs[2] = { NULL, NULL };
  132.   size_t fSize[2] = { 0, 0 };
  133.   int ifb = 0;
  134.  
  135.   //=== loop() section  ===================
  136.   lastWakeTime = xTaskGetTickCount();
  137.  
  138.   while (true)  {
  139.     fpsCounter++;
  140.  
  141.     //  Grab a frame from the camera and query its size
  142.     cam.run();
  143.     size_t s = cam.getSize();
  144.  
  145.     //  If frame size is more that we have previously allocated - request  125% of the current frame space
  146.     if (s > fSize[ifb]) {
  147.       fSize[ifb] = s * 4 / 3;
  148.       fbs[ifb] = allocateMemory(fbs[ifb], fSize[ifb]);
  149.     }
  150.  
  151.     //  Copy current frame into local buffer
  152.     char* b = (char*) cam.getfb();
  153.     memcpy(fbs[ifb], b, s);
  154.  
  155.     //  Let other tasks run and wait until the end of the current frame rate interval (if any time left)
  156.     taskYIELD();
  157.     vTaskDelayUntil(&lastWakeTime, frequency);
  158.  
  159.     //  Only switch frames around if no frame is currently being streamed to a client
  160.     //  Wait on a semaphore until client operation completes
  161.     xSemaphoreTake( frameSync, portMAX_DELAY );
  162.  
  163.     //  Do not allow interrupts while switching the current frame
  164.     portENTER_CRITICAL(&xSemaphore);
  165.     camBuf = fbs[ifb];
  166.     camSize = s;
  167.     ifb++;
  168.     ifb &= 1;  // this should produce 1, 0, 1, 0, 1 ... sequence
  169.     portEXIT_CRITICAL(&xSemaphore);
  170.  
  171.     //  Let anyone waiting for a frame know that the frame is ready
  172.     xSemaphoreGive( frameSync );
  173.  
  174.     //  Technically only needed once: let the streaming task know that we have at least one frame
  175.     //  and it could start sending frames to the clients, if any
  176.     xTaskNotifyGive( tStream );
  177.  
  178.     //  Immediately let other (streaming) tasks run
  179.     taskYIELD();
  180.  
  181.     //  If streaming task has suspended itself (no active clients to stream to)
  182.     //  there is no need to grab frames from the camera. We can save some juice
  183.     //  by suspedning the tasks
  184.     if ( eTaskGetState( tStream ) == eSuspended ) {
  185.       vTaskSuspend(NULL);  // passing NULL means "suspend yourself"
  186.     }
  187.   }
  188. }
  189.  
  190.  
  191. // ==== Memory allocator that takes advantage of PSRAM if present =======================
  192. char* allocateMemory(char* aPtr, size_t aSize) {
  193.  
  194.   //  Since current buffer is too smal, free it
  195.   if (aPtr != NULL) free(aPtr);
  196.  
  197.  
  198.   size_t freeHeap = ESP.getFreeHeap();
  199.   char* ptr = NULL;
  200.  
  201.   // If memory requested is more than 2/3 of the currently free heap, try PSRAM immediately
  202.   if ( aSize > freeHeap * 2 / 3 ) {
  203.     if ( psramFound() && ESP.getFreePsram() > aSize ) {
  204.       ptr = (char*) ps_malloc(aSize);
  205.     }
  206.   }
  207.   else {
  208.     //  Enough free heap - let's try allocating fast RAM as a buffer
  209.     ptr = (char*) malloc(aSize);
  210.  
  211.     //  If allocation on the heap failed, let's give PSRAM one more chance:
  212.     if ( ptr == NULL && psramFound() && ESP.getFreePsram() > aSize) {
  213.       ptr = (char*) ps_malloc(aSize);
  214.     }
  215.   }
  216.  
  217.   // Finally, if the memory pointer is NULL, we were not able to allocate any memory, and that is a terminal condition.
  218.   if (ptr == NULL) {
  219.     ESP.restart();
  220.   }
  221.   return ptr;
  222. }
  223.  
  224.  
  225. // ==== STREAMING ======================================================
  226. const char HEADER[] = "HTTP/1.1 200 OK\r\n" \
  227.                       "Access-Control-Allow-Origin: *\r\n" \
  228.                       "Content-Type: multipart/x-mixed-replace; boundary=123456789000000000000987654321\r\n";
  229. const char BOUNDARY[] = "\r\n--123456789000000000000987654321\r\n";
  230. const char CTNTTYPE[] = "Content-Type: image/jpeg\r\nContent-Length: ";
  231. const int hdrLen = strlen(HEADER);
  232. const int bdrLen = strlen(BOUNDARY);
  233. const int cntLen = strlen(CTNTTYPE);
  234.  
  235.  
  236. // ==== Handle connection request from clients ===============================
  237. void handleJPGSstream(void)
  238. {
  239.   //  Can only acommodate 5 clients. The limit is a default for WiFi connections
  240.   if ( !uxQueueSpacesAvailable(streamingClients) ) return;
  241.  
  242.   //  Create a new WiFi Client object to keep track of this one
  243.   WiFiClient* client = new WiFiClient();
  244.   *client = multiServer.client();
  245.  
  246.   //  Immediately send this client a header
  247.   client->write(HEADER, hdrLen);
  248.   client->write(BOUNDARY, bdrLen);
  249.  
  250.   // Push the client to the streaming queue
  251.   xQueueSend(streamingClients, (void *) &client, 0);
  252.  
  253.   // Wake up streaming tasks, if they were previously suspended:
  254.   if ( eTaskGetState( tCam ) == eSuspended ) vTaskResume( tCam );
  255.   if ( eTaskGetState( tStream ) == eSuspended ) vTaskResume( tStream );
  256. }
  257.  
  258.  
  259. // ==== Actually stream content to all connected clients ========================
  260. void streamCB(void * pvParameters) {
  261.   char buf[16];
  262.   TickType_t lastWakeTime;
  263.   TickType_t frequency;
  264.  
  265.   //  Wait until the first frame is captured and there is something to send
  266.   //  to clients
  267.   ulTaskNotifyTake( pdTRUE,          /* Clear the notification value before exiting. */
  268.                     portMAX_DELAY ); /* Block indefinitely. */
  269.  
  270.   lastWakeTime = xTaskGetTickCount();
  271.   while (true) {
  272.     // Default assumption we are running according to the FPS
  273.     frequency = pdMS_TO_TICKS(1000 / FPS);
  274.  
  275.     //  Only bother to send anything if there is someone watching
  276.     UBaseType_t activeClients = uxQueueMessagesWaiting(streamingClients);
  277.     if ( activeClients ) {
  278.       // Adjust the period to the number of connected clients
  279.       frequency /= activeClients;
  280.  
  281.       //  Since we are sending the same frame to everyone,
  282.       //  pop a client from the the front of the queue
  283.       WiFiClient *client;
  284.       xQueueReceive (streamingClients, (void*) &client, 0);
  285.  
  286.       //  Check if this client is still connected.
  287.  
  288.       if (!client->connected()) {
  289.         //  delete this client reference if s/he has disconnected
  290.         //  and don't put it back on the queue anymore. Bye!
  291.         delete client;
  292.       }
  293.       else {
  294.  
  295.         //  Ok. This is an actively connected client.
  296.         //  Let's grab a semaphore to prevent frame changes while we
  297.         //  are serving this frame
  298.         xSemaphoreTake( frameSync, portMAX_DELAY );
  299.  
  300.         client->write(CTNTTYPE, cntLen);
  301.         sprintf(buf, "%d\r\n\r\n", camSize);
  302.         client->write(buf, strlen(buf));
  303.         client->write((char*) camBuf, (size_t)camSize);
  304.         client->write(BOUNDARY, bdrLen);
  305.  
  306.         // Since this client is still connected, push it to the end
  307.         // of the queue for further processing
  308.         xQueueSend(streamingClients, (void *) &client, 0);
  309.  
  310.         //  The frame has been served. Release the semaphore and let other tasks run.
  311.         //  If there is a frame switch ready, it will happen now in between frames
  312.         xSemaphoreGive( frameSync );
  313.         taskYIELD();
  314.       }
  315.     }
  316.     else {
  317.       //  Since there are no connected clients, there is no reason to waste battery running
  318.       vTaskSuspend(NULL);
  319.     }
  320.     //  Let other tasks run after serving every client
  321.     taskYIELD();
  322.     vTaskDelayUntil(&lastWakeTime, frequency);
  323.   }
  324. }
  325.  
  326. // ==== Handle invalid URL requests ============================================
  327. // void handleNotFound()
  328. // {
  329. //   String message = "Server is running!\n\n";
  330. //   message += "URI: ";
  331. //   message += multiServer.uri();
  332. //   message += "\nMethod: ";
  333. //   message += (multiServer.method() == HTTP_GET) ? "GET" : "POST";
  334. //   message += "\nArguments: ";
  335. //   message += multiServer.args();
  336. //   message += "\n";
  337. //   multiServer.send(200, "text / plain", message);
  338. // }
  339.  
  340. // for thinkertoy esp32-cam
  341. #define PWDN_GPIO_NUM     32
  342. #define RESET_GPIO_NUM    -1
  343. #define XCLK_GPIO_NUM      0
  344. #define SIOD_GPIO_NUM     26
  345. #define SIOC_GPIO_NUM     27
  346.  
  347. #define Y9_GPIO_NUM       35
  348. #define Y8_GPIO_NUM       34
  349. #define Y7_GPIO_NUM       39
  350. #define Y6_GPIO_NUM       36
  351. #define Y5_GPIO_NUM       21
  352. #define Y4_GPIO_NUM       19
  353. #define Y3_GPIO_NUM       18
  354. #define Y2_GPIO_NUM        5
  355. #define VSYNC_GPIO_NUM    25
  356. #define HREF_GPIO_NUM     23
  357. #define PCLK_GPIO_NUM     22
  358.  
  359. // ==== SETUP method ==================================================================
  360. void initCamera() {
  361.   // Configure the camera
  362.   camera_config_t config;
  363.   config.ledc_channel = LEDC_CHANNEL_0;
  364.   config.ledc_timer = LEDC_TIMER_0;
  365.   config.pin_d0 = Y2_GPIO_NUM;
  366.   config.pin_d1 = Y3_GPIO_NUM;
  367.   config.pin_d2 = Y4_GPIO_NUM;
  368.   config.pin_d3 = Y5_GPIO_NUM;
  369.   config.pin_d4 = Y6_GPIO_NUM;
  370.   config.pin_d5 = Y7_GPIO_NUM;
  371.   config.pin_d6 = Y8_GPIO_NUM;
  372.   config.pin_d7 = Y9_GPIO_NUM;
  373.   config.pin_xclk = XCLK_GPIO_NUM;
  374.   config.pin_pclk = PCLK_GPIO_NUM;
  375.   config.pin_vsync = VSYNC_GPIO_NUM;
  376.   config.pin_href = HREF_GPIO_NUM;
  377.   config.pin_sscb_sda = SIOD_GPIO_NUM;
  378.   config.pin_sscb_scl = SIOC_GPIO_NUM;
  379.   config.pin_pwdn = PWDN_GPIO_NUM;
  380.   config.pin_reset = RESET_GPIO_NUM;
  381.   config.xclk_freq_hz = 20000000;
  382.   config.pixel_format = PIXFORMAT_JPEG;
  383.  
  384.   // Frame parameters: pick one
  385.   //  config.frame_size = FRAMESIZE_UXGA;
  386.   //  config.frame_size = FRAMESIZE_SVGA;
  387.   //  config.frame_size = FRAMESIZE_QVGA;
  388.   config.frame_size = FRAMESIZE_VGA;  // 640x480
  389.   config.jpeg_quality = 12;
  390.   config.fb_count = 2;
  391.  
  392.   if (cam.init(config) != ESP_OK) {
  393.     prtl("Error initializing the camera");
  394.     delay(10000);
  395.     ESP.restart();
  396.   }
  397.  
  398.   // Start mainstreaming RTOS task
  399.   xTaskCreatePinnedToCore(
  400.     mjpegCB,
  401.     "mjpeg",
  402.     4 * 1024,
  403.     NULL,
  404.     2,
  405.     &tMjpeg,
  406.     APP_CPU);
  407. }
  408.  
  409. u32_t lastFpsCalc = 0;
  410.  
  411. void cameraLoop() {
  412.   if((millis() - lastFpsCalc) > 4000) {
  413.     fps = fpsCounter / 4;
  414.     fpsCounter = 0;
  415.     lastFpsCalc = millis();
  416.   }
  417. }