We are about to switch to a new forum software. Until then we have removed the registration on this forum.
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