Camera (still images) protocols/hardware advise

Hello,

Any ideas on connecting a camera (which?) to an ESP32 via long cable? How it is usually done? What cameras are popular for this? What protocols used to transmit pictures from the camera?

What I need is to make a picture once a hour, low resolution, and transmit it over the 22m cable to my ESP32 board.

Any opinions/ideas/links are welcome :). This is for a seawater monitoring (particles, silt, mud). There is a static flat underwater object (a white and black board) which is located 2m away from the camera. What I need - is to take a picture of this b/w board and send it to the mainland via cable link.

Thanks

Not sure why you chose an ESP32 for. 22m is quite a way for a camera cable. Maybe consider a USB camera, and appropriate hardware to work with it.

For that low data rate, you can use an ESP32-CAM to take the picture and then sent it elsewhere via UART serial over the 22 m cable.

There are also camera modules with a serial connection.

1 Like

So, you are looking for a ESP32 camera connector that is 22m long?

Camera, connector, protocol to transmit a JPEG over 22m link. Now looking into ESP32Cam. It has an UART which I can run over rs485 so will probably fit my needs.

I have used this code to take gray scale snapshots and send the raw image data over UART serial to a PC. It may require minor modifications as the default ESP32-CAM libraries were recently updated. Or use older versions of the libraries.

For long serial cables, test with much lower serial data rates and work up.

// from https://github.com/alanesq/esp32cam-demo/blob/master/Misc/esp32camdemo-greyscale.ino
#if !defined ESP32
#error This sketch is only for an ESP32Cam module
#endif

#include "esp_camera.h"       // https://github.com/espressif/esp32-camera
// #include "camera_pins.h"

// ---------------------------------------------------------------
//                           -SETTINGS
// ---------------------------------------------------------------

const char* stitle = "ESP32Cam-demo-gs";               // title of this sketch
const char* sversion = "10Jul21";                      // Sketch version

// Camera related
const bool flashRequired = 1;                        // If flash to be used when capturing image (1 = yes)
const framesize_t FRAME_SIZE_IMAGE = FRAMESIZE_UXGA;
// Image resolution:
//               default = "const framesize_t FRAME_SIZE_IMAGE = FRAMESIZE_VGA"
//               160x120 (QQVGA), 128x160 (QQVGA2), 176x144 (QCIF), 240x176 (HQVGA),
//               320x240 (QVGA), 400x296 (CIF), 640x480 (VGA, default), 800x600 (SVGA),
//               1024x768 (XGA), 1280x1024 (SXGA), 1600x1200 (UXGA)
#define PIXFORMAT PIXFORMAT_GRAYSCALE;               // image format, Options =  YUV422, GRAYSCALE, RGB565, JPEG, RGB888                                                         
#define WIDTH 1600                                    // image size
#define HEIGHT 1200

int cameraImageExposure = 0;                         // Camera exposure (0 - 1200)   If gain and exposure both set to zero then auto adjust is enabled
int cameraImageGain = 0;                             // Image gain (0 - 30)

const int TimeBetweenStatus = 600;                     // speed of flashing system running ok status light (milliseconds)

const int indicatorLED = 33;                           // onboard small LED pin (33)

const int brightLED = 4;                               // onboard Illumination/flash LED pin (4)

const int iopinA = 13;                                 // general io pin 13
const int iopinB = 12;                                 // general io pin 12 (must not be high at boot)
const int iopinC = 16;                                 // input only pin 16 (used by PSRam but you may get away with using it for a button)

const int serialSpeed = 115200;                        // Serial data speed to use


// camera settings (for the standard - OV2640 - CAMERA_MODEL_AI_THINKER)
// see: https://randomnerdtutorials.com/esp32-cam-camera-pin-gpios/
// set camera resolution etc. in 'initialiseCamera()' and 'cameraImageSettings()'
#define CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM     32      // power to camera (on/off)
#define RESET_GPIO_NUM    -1      // -1 = not used
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26      // i2c sda
#define SIOC_GPIO_NUM     27      // i2c scl
#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      // vsync_pin
#define HREF_GPIO_NUM     23      // href_pin
#define PCLK_GPIO_NUM     22      // pixel_clock_pin



// ******************************************************************************************************************

#include "driver/ledc.h"      // used to configure pwm on illumination led

// Used to disable brownout detection
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"

// sd-card
#include "SD_MMC.h"                         // sd card - see https://randomnerdtutorials.com/esp32-cam-take-photo-save-microsd-card/
#include <SPI.h>
#include <FS.h>                             // gives file access 
#define SD_CS 5                             // sd chip select pin = 5

// Define some global variables:
uint32_t lastStatus = millis();           // last time status light changed status (to flash all ok led)
uint32_t lastCamera = millis();           // timer for periodic image capture
bool sdcardPresent;                       // flag if an sd card is detected
int imageCounter;                         // image file name on sd card counter
uint32_t illuminationLEDstatus;           // current brightness setting of the illumination led


void setup() {

  Serial.begin(serialSpeed);                     // Start serial communication

  Serial.println("\n");                      // line feeds
  Serial.printf("Starting - %s - %s \n", stitle, sversion);
  // Serial.print("Reset reason: " + ESP.getResetReason());


  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);     // Turn-off the 'brownout detector'

  // small indicator led on rear of esp32cam board
  pinMode(indicatorLED, OUTPUT);

  digitalWrite(indicatorLED, LOW);              // small indicator led on
  delay(1000);
  digitalWrite(indicatorLED, HIGH);             // small indicator led off

  // set up camera
  Serial.print(("\nInitialising camera: "));
  if (initialiseCamera()) {
    Serial.println("OK");
  }
  else {
    Serial.println("Error!");
  }

  // define i/o pins
  pinMode(indicatorLED, OUTPUT);            // defined again as sd card config can reset it
  digitalWrite(indicatorLED, HIGH);         // led off = High
  pinMode(iopinA, OUTPUT);                  // pin 13 - free io pin, can be used for input or output
  pinMode(iopinB, OUTPUT);                  // pin 12 - free io pin, can be used for input or output (must not be high at boot)
  pinMode(iopinC, INPUT);                   // pin 16 - free input only pin

  // startup complete
  Serial.println("\nSetup complete...");

}  // setup

void loop() {

  //  //  demo to Capture an image 
  if ( (unsigned long)(millis() - lastCamera) >= 10000UL) { // ever 10 sec
    lastCamera = millis();     // reset timer
    capture_still();              // collect and send image out serial port
  }
  // flash status LED to show sketch is running ok
  if ((unsigned long)(millis() - lastStatus) >= TimeBetweenStatus) {
    lastStatus = millis();                                               // reset timer
    digitalWrite(indicatorLED, !digitalRead(indicatorLED));              // flip indicator led status
  }

}  // loop

// ----------------------------------------------------------------
//                        Initialise the camera
// ----------------------------------------------------------------
// returns TRUE if successful

bool initialiseCamera() {

  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;               // XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
  config.pixel_format = PIXFORMAT;              // Options =  YUV422, GRAYSCALE, RGB565, JPEG, RGB888
  config.frame_size = FRAME_SIZE_IMAGE;         // Image sizes: 160x120 (QQVGA), 128x160 (QQVGA2), 176x144 (QCIF), 240x176 (HQVGA), 320x240 (QVGA),
  //              400x296 (CIF), 640x480 (VGA, default), 800x600 (SVGA), 1024x768 (XGA), 1280x1024 (SXGA),
  //              1600x1200 (UXGA)
  config.jpeg_quality = 10;                     // 0-63 lower number means higher quality
  config.fb_count = 1;                          // if more than one, i2s runs in continuous mode. Use only with JPEG

  // check the esp32cam board has a psram chip installed (extra memory used for storing captured images)
  //    Note: if not using "AI thinker esp32 cam" in the Arduino IDE, PSRAM must be enabled
  if (!psramFound()) {
    Serial.println("Warning: No PSRam found so defaulting to image size 'CIF'");
    config.frame_size = FRAMESIZE_CIF;
  }

  //#if defined(CAMERA_MODEL_ESP_EYE)
  //  pinMode(13, INPUT_PULLUP);
  //  pinMode(14, INPUT_PULLUP);
  //#endif

  esp_err_t camerr = esp_camera_init(&config);  // initialise the camera
  if (camerr != ESP_OK) {
    Serial.printf("ERROR: Camera init failed with error 0x%x", camerr);
  }

  cameraImageSettings();                        // apply custom camera settings

  return (camerr == ESP_OK);                    // return boolean result of camera initialisation
}

// ----------------------------------------------------------------
//                   -Change camera image settings
// ----------------------------------------------------------------
// Adjust image properties (brightness etc.)
// Defaults to auto adjustments if exposure and gain are both set to zero
// - Returns TRUE if successful
// BTW - some interesting info on exposure times here: https://github.com/raduprv/esp32-cam_ov2640-timelapse

bool cameraImageSettings() {

  sensor_t *s = esp_camera_sensor_get();
  // something to try?:     if (s->id.PID == OV3660_PID)
  if (s == NULL) {
    Serial.println("Error: problem reading camera sensor settings");
    return 0;
  }

  // if both set to zero enable auto adjust
  if (cameraImageExposure == 0 && cameraImageGain == 0) {
    // enable auto adjust
    s->set_gain_ctrl(s, 1);                       // auto gain on
    s->set_exposure_ctrl(s, 1);                   // auto exposure on
    s->set_awb_gain(s, 1);                        // Auto White Balance enable (0 or 1)
  } else {
    // Apply manual settings
    s->set_gain_ctrl(s, 0);                       // auto gain off
    s->set_awb_gain(s, 1);                        // Auto White Balance enable (0 or 1)
    s->set_exposure_ctrl(s, 0);                   // auto exposure off
    s->set_agc_gain(s, cameraImageGain);          // set gain manually (0 - 30)
    s->set_aec_value(s, cameraImageExposure);     // set exposure manually  (0-1200)
  }

  return 1;
}  // cameraImageSettings


//    // More camera settings available:
//    // If you enable gain_ctrl or exposure_ctrl it will prevent a lot of the other settings having any effect
//    // more info on settings here: https://randomnerdtutorials.com/esp32-cam-ov2640-camera-settings/
//    s->set_gain_ctrl(s, 0);                       // auto gain off (1 or 0)
//    s->set_exposure_ctrl(s, 0);                   // auto exposure off (1 or 0)
//    s->set_agc_gain(s, cameraImageGain);          // set gain manually (0 - 30)
//    s->set_aec_value(s, cameraImageExposure);     // set exposure manually  (0-1200)
//    s->set_vflip(s, cameraImageInvert);           // Invert image (0 or 1)
//    s->set_quality(s, 10);                        // (0 - 63)
//    s->set_gainceiling(s, GAINCEILING_32X);       // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128)
//    s->set_brightness(s, cameraImageBrightness);  // (-2 to 2) - set brightness
//    s->set_lenc(s, 1);                            // lens correction? (1 or 0)
//    s->set_saturation(s, 0);                      // (-2 to 2)
//    s->set_contrast(s, cameraImageContrast);      // (-2 to 2)
//    s->set_sharpness(s, 0);                       // (-2 to 2)
//    s->set_hmirror(s, 0);                         // (0 or 1) flip horizontally
//    s->set_colorbar(s, 0);                        // (0 or 1) - show a testcard
//    s->set_special_effect(s, 0);                  // (0 to 6?) apply special effect
//    s->set_whitebal(s, 0);                        // white balance enable (0 or 1)
//    s->set_awb_gain(s, 1);                        // Auto White Balance enable (0 or 1)
//    s->set_wb_mode(s, 0);                         // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
//    s->set_dcw(s, 0);                             // downsize enable? (1 or 0)?
//    s->set_raw_gma(s, 1);                         // (1 or 0)
//    s->set_aec2(s, 0);                            // automatic exposure sensor?  (0 or 1)
//    s->set_ae_level(s, 0);                        // auto exposure levels (-2 to 2)
//    s->set_bpc(s, 0);                             // black pixel correction
//    s->set_wpc(s, 0);                             // white pixel correction



// ----------------------------------------------------------------
//      -access image as greyscale data - i.e. http://x.x.x.x/
// ----------------------------------------------------------------

bool capture_still() {
  uint16_t x, y;

  Serial.print("***** greyscale image dump\n");
  camera_fb_t *frame = esp_camera_fb_get();

  if (!frame)
    return false;

  int npix=0;
  // for each pixel in image
    
  for (size_t i = 0; i < frame->len; i++) {

    x = i % WIDTH;                   // x position in image
    y = floor(i / WIDTH);            // y position in image
    byte pixel = frame->buf[i];                     // pixel value

    // show data
   Serial.print((unsigned int)pixel);
   Serial.print("\n");
    npix++;
  }
  Serial.print("***** ");  //separator
  Serial.print(npix);
  Serial.print(" data lines\n");
  
  esp_camera_fb_return(frame);                      // return storage space
  return true;
}

1 Like

Thank you for sharing!

Did you use some sort of flow control here? I am worring about buffer overruns on the receiving side. Or it is not an issue?

ESP32Cam UART (null modem) --> RS485 Converter -->.........shielded twisted pair.....->RS485 converter-->ESP32 UART

Not an issue on the PC! Let us know if it works on ESP32.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.