How to take pictures in continuous mode using ArduCAM and displaying them on Processing?

Hello, everyone. I am using an Arduino MEGA 2560 and ArduCAM OV2640 to take a JPEG picture and display it on my PC through a Processing sketch. The current Arduino and Processing sketches let me take single shots only. I am wondering how I can make it so the camera will take pictures continuously and display it on the PC, similar to a video stream. Here are the sketches, courtesy of Vernel. (https://github.com/Vernel/ArduCAM_OV562

Processing sketch:

import processing.serial.*;       // import the Processing serial library



Serial myPort;                    // The serial port



//NEW ADDITIONS

byte rawBytes[];

int sensorNum = 0;

PImage img;  

long picNum = 0;



void setup() {

  size(640, 480);



  // List all the available serial ports

  // if using Processing 2.1 or later, use Serial.printArray()

  printArray(Serial.list());





  // Change the 0 to the appropriate number of the serial port

  // that your microcontroller is attached to.

  myPort = new Serial(this, Serial.list()[0], 250000);



  // read bytes into a buffer until you get a linefeed (ASCII 10):

  myPort.bufferUntil('\n');



  // draw with smooth edges:

  smooth();



  img = createImage(320, 240, RGB);

  img.loadPixels();

  //frameRate(600);

  myPort.write(0x10);
}





void draw() {



  background(255);

  image(img, 0, 0, width, height-100);

  fill(0);

  text("Help:", 20, height-80);

  text("- Press key 1-7 to change image resolution", 30, height-60);

  text("- Press key S to capture a still photo", 30, height-40);

  text("- Press key C to enable/disable StopMotion capture", 30, height-20);
}





// serialEvent  method is run automatically by the Processing applet

// whenever the buffer reaches the  byte value set in the bufferUntil()

// method in the setup():



void serialEvent(Serial myPort) {

  while (myPort.available() > 0) {



    String incoming[];

    String myString = myPort.readStringUntil('\n');



    myString = trim(myString);

    incoming = split(myString, ',');



    if (incoming.length > 1) {

      if (incoming[0].equals("FifoLength:")) {

        //initialize raw data byte array to the size of the picture

        rawBytes = new byte[int(incoming[1])];

        println("Picture Size: "+incoming[1]+" bytes");
      } else if (incoming[0].equals("Image:")) {

        int x = 0;

        for (int i = 1; i < incoming.length; i++) {

          try {

            //add raw jpeg incoming bytes to byte array

            rawBytes[x]= (byte)int(incoming[i]);

            x++;
          }

          catch(RuntimeException e) {

            println(e.getMessage());
          }
        }

        try {

          //Save raw data to file

          String fname = "capture#"+picNum+"_"+day()+month()+year()+".jpg";

          saveBytes("data/capture/"+fname, rawBytes);



          // Open saved picture for local display

          img = loadImage("/data/capture/"+fname);

          picNum++;
        }

        catch(RuntimeException e) {

          println(e.getMessage());
        }
      } else if (incoming[0].equals("Ready:")) {

        myPort.write(0x10);

        println("Starting Capture");
      }
    } else {

      println(myString);
    }
  }
}



void keyPressed() {

  switch(key) {

  case 's':

    myPort.write(0x10);

    println("Starting Capture");

    break;



  case 'c':

    myPort.write(7);

    break;



  case '1':

    myPort.write(0);

    println("Set Image Resolution: 320x240");

    break;



  case '2':

    myPort.write(1);

    println("Set Image Resolution: 640x480");

    break;



  case '3':

    myPort.write(2);

    println("Set Image Resolution: 1024x768");

    break;




  default:

    println("Unknown Command: "+key);

    break;
  }
}

Arduino Sketch:

#include <Wire.h>

#include <ArduCAM.h>

#include <SPI.h>

#include "memorysaver.h"



//This code can only work on OV5642_MINI_5MP_PLUS platform.

#if !(defined (OV2640_MINI_2MP))



#endif

#define   FRAMES_NUM    0x00



// set pin 4 as the slave select for SPI:

const int CS1 = 7;

bool CAM1_EXIST = false;

bool stopMotion = false;



ArduCAM myCAM1(OV2640, CS1);

long int streamStartTime;



void setup() {

  // put your setup code here, to run once:

  uint8_t vid, pid;

  uint8_t temp;

  Wire.begin();

  Serial.begin(250000); //921600

  Serial.println(F("ArduCAM Start!"));

  // set the CS output:

  pinMode(CS1, OUTPUT);



  // initialize SPI:

  SPI.begin();

  //Check if the 4 ArduCAM Mini 5MP PLus Cameras' SPI bus is OK

  while (1) {

    myCAM1.write_reg(ARDUCHIP_TEST1, 0x55);

    temp = myCAM1.read_reg(ARDUCHIP_TEST1);

    if (temp != 0x55)

    {

      Serial.println(F("SPI1 interface Error!"));

    } else {

      CAM1_EXIST = true;

      Serial.println(F("SPI1 interface OK."));

    }



    if (!(CAM1_EXIST)) {

      delay(1000); continue;

    } else

      break;

  }



  while (1) {

    //Check if the camera module type is OV5642


    myCAM1.rdSensorReg8_8(OV2640_CHIPID_HIGH, &vid);
    myCAM1.rdSensorReg8_8(OV2640_CHIPID_LOW, &pid);
    if ((vid != 0x26 ) && (( pid != 0x41 ) || ( pid != 0x42 ))) {

      Serial.println(F("Can't find OV2640 module!"));

      delay(1000); continue;

    } else {

      Serial.println(F("OV2640 detected.")); break;

    }

  }



  //Change to JPEG capture mode and initialize the OV2640 module

  myCAM1.set_format(JPEG);

  myCAM1.InitCAM();

  //myCAM1.write_reg(ARDUCHIP_TIM, VSYNC_LEVEL_MASK);   //VSYNC is active HIGH

  myCAM1.clear_fifo_flag();

  //myCAM1.write_reg(ARDUCHIP_FRAMES, FRAMES_NUM);



  //myCAM1.OV5642_set_JPEG_size(OV5642_640x480); delay(1000);

  myCAM1.OV2640_set_JPEG_size(OV2640_320x240); delay(1000);

  delay(1000);

  myCAM1.clear_fifo_flag();

  Serial.println("Ready:,1");

}



void loop() {



  if (CAM1_EXIST && stopMotion) {

    streamStartTime = millis();

    myCAMSendToSerial(myCAM1);

    double fps = ((millis() - streamStartTime) / 1000);

    Serial.println("fps: " + String(1 / fps ));

  }

}





void myCAMSendToSerial(ArduCAM myCAM) {

  char str[8];

  byte buf[5];



  static int i = 0;

  static int k = 0;

  uint8_t temp = 0, temp_last = 0;

  uint32_t length = 0;

  bool is_header = false;



  myCAM.flush_fifo(); //Flush the FIFO

  myCAM.clear_fifo_flag(); //Clear the capture done flag

  myCAM.start_capture();//Start capture



  while (!myCAM.get_bit(ARDUCHIP_TRIG , CAP_DONE_MASK));



  length = myCAM.read_fifo_length();

  Serial.print(F("FifoLength:,"));

  Serial.print(length, DEC);

  Serial.println(",");



  if (length >= MAX_FIFO_SIZE) //8M

  {

    Serial.println(F("Over size."));

    return ;

  }

  if (length == 0 ) //0 kb

  {

    Serial.println(F("Size is 0."));

    return ;

  }



  myCAM.CS_LOW();

  myCAM.set_fifo_burst();



  Serial.print("Image:,");



  while ( length-- )

  {

    temp_last = temp;

    temp =  SPI.transfer(0x00);

    //Read JPEG data from FIFO

    if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while,

    {

      buf[i++] = temp;  //save the last  0XD9

      //Write the remain bytes in the buffer

      myCAM.CS_HIGH();



      for (int i = 0; i < sizeof(buf); i++) {

        Serial.print(buf[i]); Serial.print(",");

      }



      Serial.println();

      Serial.println(F("Image transfer OK."));

      is_header = false;

      i = 0;

    }

    if (is_header == true)

    {

      //Write image data to buffer if not full

      if (i < 5) {

        buf[i++] = temp;

      } else

      {

        //Stream 5 bytes of raw image data to serial

        myCAM.CS_HIGH();



        for (int i = 0; i < sizeof(buf); i++) {

          Serial.print(buf[i]); Serial.print(",");

        }



        i = 0;

        buf[i++] = temp;

        myCAM.CS_LOW();

        myCAM.set_fifo_burst();



      }

    }

    else if ((temp == 0xD8) & (temp_last == 0xFF))

    {

      is_header = true;

      buf[i++] = temp_last;

      buf[i++] = temp;

    }



  }

}





void serialEvent() {

  if (Serial.available() > 0) {

    uint8_t temp = 0xff, temp_last = 0;

    uint8_t start_capture = 0;

    temp = Serial.read();

    switch (temp)

    {

      case 0:

        temp = 0xff;



        myCAM1.OV2640_set_JPEG_size(OV2640_320x240);

        Serial.println(F("OV2640_320x240")); delay(1000);

        myCAM1.clear_fifo_flag();

        break;

      case 1:

        temp = 0xff;



        myCAM1.OV2640_set_JPEG_size(OV2640_640x480);

        Serial.println(F("OV2640_640x480")); delay(1000);

        myCAM1.clear_fifo_flag();

        break;

      case 2:

        temp = 0xff;



        myCAM1.OV2640_set_JPEG_size(OV2640_1024x768);

        Serial.println(F("OV2640_1024x768")); delay(1000);

        myCAM1.clear_fifo_flag();

        break;




      case 3:

        {

          if (stopMotion)

            stopMotion = false;

          else

            stopMotion = true;

          Serial.println("Stop Motion Enabled: " + String(stopMotion));

        }

        break;



      case 0x10:

        if (CAM1_EXIST) {

          streamStartTime = millis();

          myCAMSendToSerial(myCAM1);

          double fps = ((millis() - streamStartTime) / 1000);

          Serial.println("Total Time: " + String(fps));

        }

        break;

      default:

        break;

    }

  }

}

Thanks in advance!

Answers

  • Does your code works as it is? Are you able to load still images? If you are able to transfer data (fast) enough, then you should be able to implement the same logic to display a video stream as done by the Processing Video library: https://processing.org/reference/libraries/video/captureEvent_.html

    You just need to signal when a new image is available to show it. Now, if data transfer is not fast enough, you can still implement it using the same logic. The final video stream will have a low refreshing rate aka. video will have a feeling it is laggy.

    My question is, can you tell when a full image has been transferred with the current code?

    Kf

  • Thanks for the reply, kfrajer. The code is working fine and it tells me when the image has been transferred to the PC. Then, it displays the still image. The pictures are stored in the same folder where the Processing sketch is located. I will give the Video library a try and post my results here. Thanks again!

  • Sorry, I think I was not clear. You don't need nor you can use the video lib to do the video streaming. I was suggesting the same logic. When you detect a complete transfer of an image, you update the canvas with your new image.

    Kf

Sign In or Register to comment.