s60sc / ESP32-CAM_MJPEG2SD

ESP32 Camera motion capture application to record JPEGs to SD card as AVI files and stream to browser as MJPEG. If a microphone is installed then a WAV file is also created. Files can be uploaded via FTP or downloaded to browser.
GNU Affero General Public License v3.0
855 stars 209 forks source link

Adding support for the SPI screen #386

Closed ChangeClock closed 5 months ago

ChangeClock commented 5 months ago

I'm working on adding an SPI screen feature to this project, which could stream the camera content to a TFT screen attached. This could make this ESP cam working as a peephole camera. I already have a working demo on the display streaming part. But when I added this feature as a task to this project, the display didn't respond to the write action in the task loop ( successfully initiated ).

I'm attaching the modified part below so if anyone could help on this part, it will be a great help.

Currenly my setup is XIAO ESP32S3 Sence with XIAO Round Display

ESP32-CAM_MJPEG2SD.cpp

void setup() {
  #if INCLUDE_TFT
    prepDisplay();
  #endif

  logSetup();
  // prep SD card storage
  startStorage(); 
  // Load saved user configuration
  loadConfig();
  // initialise camera
  if (psramFound()) {
    LOG_INF("PSRAM size: %s", fmtSize(esp_spiram_get_size()));
    if (esp_spiram_get_size() > 3 * ONEMEG) prepCam();
    else snprintf(startupFailure, SF_LEN, "Startup Failure: Insufficient PSRAM for app");
  }
  else snprintf(startupFailure, SF_LEN, "Startup Failure: Need PSRAM to be enabled");

#ifdef DEV_ONLY
  devSetup();
#endif

#if INCLUDE_TFT
    startDisplay();
#endif

.....
}

display.cpp

#include "appGlobals.h"
#include <SPI.h>
#include "TFT_eSPI.h"

// Initialize the TFT screen

static const uint16_t screenWidth  = 240;
static const uint16_t screenHeight = 240;

// TFT driver code

static TFT_eSPI tftDisplay = TFT_eSPI(screenWidth, screenHeight);
static SemaphoreHandle_t displayMutex = NULL;

void displayInit() {
  displayMutex = xSemaphoreCreateMutex();
  if (displayMutex == NULL) {
    Serial.println("Failed to create tftDisplay mutex");
  }

  FileMutSpi::sdTakeSem();
  xSemaphoreTake(displayMutex, portMAX_DELAY);
  tftDisplay.init();
  tftDisplay.setRotation(1);
  FileMutSpi::sdGiveSem();
  xSemaphoreGive(displayMutex);
}

void displayShowJpeg(uint8_t* buf, uint32_t len) {
  FileMutSpi::sdTakeSem();
  xSemaphoreTake(displayMutex, portMAX_DELAY);
  tftDisplay.startWrite();
  tftDisplay.setAddrWindow(0, 0, screenWidth, screenHeight);
  tftDisplay.pushColors(buf, len);
  tftDisplay.endWrite();
  FileMutSpi::sdGiveSem();
  xSemaphoreGive(displayMutex);
}

void displayFillScreen(uint16_t color) {
  FileMutSpi::sdTakeSem();
  xSemaphoreTake(displayMutex, portMAX_DELAY);
  tftDisplay.fillScreen(color);
  FileMutSpi::sdGiveSem();
  xSemaphoreGive(displayMutex);
}

// TFT driver code ends

// Your camera configuration here
camera_config_t config;

static void displayImageTask(void * pvParameters) {
  uint8_t c = 0;
  while (true) {

    camera_fb_t *fb = esp_camera_fb_get();
    if (!fb) {
      Serial.println("Camera capture failed");
      vTaskDelay(1000 / portTICK_PERIOD_MS); // Delay for 100ms
      continue;
    }

    // Decode JPEG images
    uint8_t* buf = fb->buf;
    uint32_t len = fb->len;
    Serial.println("Image size: " + String(len) + " bytes");

    switch (c%3)
    {
    case 0:
      displayFillScreen(TFT_ORANGE);
      break;
    case 1:
      displayFillScreen(TFT_BLACK);
      break;
    case 2:
      displayFillScreen(TFT_BLUE);
      break;
    default:
      break;
    }
    Serial.println("Displaying image");

    esp_camera_fb_return(fb);    
    Serial.println("Release frame buffer");

    // Delay for a bit before capturing the next frame
    vTaskDelay(2000 / portTICK_PERIOD_MS); // Delay for 100ms
    c++;
  }
  vTaskDelete(NULL);
}

void prepDisplay() {
    // Initialize the TFT screen
    displayInit();
    displayFillScreen(TFT_ORANGE);
    delay(1000);
    displayFillScreen(TFT_BLACK);
    delay(1000);
    displayFillScreen(TFT_BLUE);
}

void startDisplay() {
// Create the display image task
    prepDisplay();

  xTaskCreate(
    &displayImageTask,   // Task function
    "DisplayImageTask", // Task name
    DISPLAY_STACK_SIZE,              // Stack size (this might need to be adjusted)
    NULL,               // Task input parameter
    DISPLAY_PRI,                  // Task priority
    NULL                // Task handle
  );
}

Also I attached the modified project link here

f90a78bd-c909-47a9-8002-681cd1fb3905

s60sc commented 5 months ago

prepDisplay() is being called twice.

To improve efficiency and possible conflict involving write, instead of calling esp_camera_fb_get(), declare another streamBuffer in processFrame() in mjpeg2sd.cpp.

To avoid conflict with log task, replace Serial.println() calls with LOG_* macros.

Cant help with TFT_eSPI.