Project with ArduCAM : bugs and issues

I did a project on Arduino. I used the ArduCAM shield to take photos when triggered by a IR movement sensor.

This project is not done for the moment : it is triggered correctly, but JPEG format does not work properly, and the photos in BMP format it takes are quite ugly ! If you have some tricks to make it better, it would be just awesome ! :smiley:

There are already some projects using this shield to take photos, which helped me a lot :

But those projects reports mainly focus on the material part, and since there is no real documentation for ArduCAM out of the small examples of code given on the website, it is quite hard to use this library. I will here post my final code, and all the issues I had during its development, to (hopefully) help those who want to use ArduCAM but have problems with the library.

(I will write a lot of trivial things, but since I had problems with them because I was new to Arduino and ArduCAM, I suppose someone else can have the same problems again)

Here is my final code :

CAM_custom_v2.ino file

Bugs and issues :

  • Using Serial.print() : this instruction is amazingly useful to debug a code. I works just like a normal print is other languages, but you have to put a special instruction in your code to enable the communication between arduino and the computer :
void setup(){
 // ...
 Serial.begin(9600);
 //...
}
void loop(){
 Serial.print("Hello world !");
}
  • ArduCAM.init() freezing : When I launched the program, it freezed when it reaches the MyCAM.init() instruction. This is because I putted a myCAM.set_format(JPEG) instruction before it. The simplest way to fix the bug is to put a myCAM.set_format(BMP) instruction before the initialisation, and to set the format to JPEG afterwards :
myCAM.set_format(BMP);
MyCAM.init()
myCAM.set_format(JPEG);
  • Waiting for the capture to be done : You have to wait until the capture is done, and even if it was a bit silly of me, I had problems choosing the right condition to know whether the capture was already done or not. The good piece of instruction allowing you to wait for the capture to be done is the following :
// Useful debugging lines
Serial.print("Waiting for capture...");
while (!(myCAM.read_reg(ARDUCHIP_TRIG) & CAP_DONE_MASK)) { // the right condition used
  delay(10); 
}
// Debug again !
Serial.println("OK");

I just put my final code here, my last message was too long :

// inspired by http://sauerwine.blogspot.fr/2013/07/an-arduino-time-lapse-camera-using.html
// and http://www.arducam.com/how-arducam-use-a-external-trigger-from-a-sensor/


#include <UTFT_SPI.h>
#include <SD.h>
#include <Wire.h>
#include <ArduCAM.h>
#include <SPI.h>
#include "memorysaver.h"

#define SD_CS 9 
const int SPI_CS = 10;

// Constants that define the format of the picture taken
const int jpg_mode = 1;
const int bmp_mode = 0;
const int mode = bmp_mode;

const char bmp_header[54] PROGMEM =
{
      0x42, 0x4D, 0x36, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
      0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// Instanciation of the class ArduCAM
ArduCAM myCAM(OV2640, SPI_CS);
static int k = 0;

// Used in takePicture() to transfer the buffer of the ArduCAM into a BMP file
// inspired by http://sauerwine.blogspot.fr/2013/07/an-arduino-time-lapse-camera-using.html
void writeBMP(File outFile){
  
  char VH, VL;
  uint8_t temp,temp_last;
  int i, j, posn, nextNum;
  
    //Write the BMP header
  for( i = 0; i < 54; i++)
  {
    char ch = pgm_read_byte(&bmp_header[i]);
    outFile.write((uint8_t*)&ch,1);
  }

  //Read the first dummy byte from FIFO
  temp = myCAM.read_fifo();
  //Read 320x240x2 byte from FIFO
  for(i = 0; i < 240; i++)
    for(j = 0; j < 320; j++)
    {
      VH = myCAM.read_fifo();
      VL = myCAM.read_fifo();
      //RGB565 to RGB555 Conversion
      if (false) {
        VL = (VH << 7) | ((VL & 0xC0) >> 1) | (VL & 0x1f);
        VH = VH >> 1;
      }
      //Write image data to file
      outFile.write(VL);
      outFile.write(VH);
  }
}


// Transfer the buffer of the ArduCAM into a JPEG file
// NOT STABLE
void writeJPEG(File outFile){ 
  uint8_t temp,temp_last;
  static int i = 0;
  byte buf[256];
  i=0;
  
  //read a byte of the ArduCAM buffer
  temp = myCAM.read_fifo();
  //put it into the buffer array
  buf[i++] = temp;
  long j = 0;
  while( (temp != 0xD9) | (temp_last != 0xFF) ){
    temp_last = temp;
    j=j+1;
    temp = myCAM.read_fifo();
    // write the byte from the ArduCAM into the buffer...
     if(i < 256){
        buf[i++] = temp;
     }
    // until it is full : write the buffer content onto the SD card
    else{
      outFile.write(buf,256);
      i = 0;
      buf[i++] = temp;
      //DEBUG
      if (j%10000){
        Serial.print("J : ");
        Serial.println(j);
      }
    }
  }
  Serial.println("Buffering done.");
  // write remaining bytes in the buffer onto the SD card
  if(i > 0)
    outFile.write(buf,i);
}

// IMAGE FILE GENERATION
// generates a name and returns the opened file
File imageFile(){
 // generates the filename
 char str[8];
 File outFile;

 k=k+1;
 itoa(k, str, 10);
 if (mode == jpg_mode){
 strcat(str,".jpg");
 } else if (mode == bmp_mode) {
 strcat(str,".bmp");
 }
 //  Write the name of the file in the Serial
 Serial.println(str);
 // opens the file
 outFile = SD.open(str,O_WRITE | O_CREAT | O_TRUNC);
 if (! outFile){ 
 Serial.println("open file failed");
 } else {
 Serial.println("File opened sucessfully");
 }

 return outFile;
}

// ========= TAKE PICTURE ========
// simple to use function, which takes a picture
// either in JPEG (bugged) or BMP format in function 
// of the 'mode' constant

void takePicture(){
  Serial.println("Taking picture...");
  File outFile;

  uint8_t start_capture = 0;
  
  myCAM.write_reg(ARDUCHIP_MODE, 0x00);
  
  // Initialisation
  // Warning : if you use set_format(JPEG) before Init, it will freeze
  myCAM.set_format(BMP);
  Serial.print("Init ? ");
  myCAM.InitCAM();
  
  // If it is in JPEG mode, switch to JPEG format
  if (mode == jpg_mode){
    myCAM.set_format(JPEG);
    myCAM.OV2640_set_JPEG_size(OV2640_320x240);
  }
  Serial.println("OK"); //debug
  
  
  myCAM.flush_fifo(); // clean ArduCAM buffer
  myCAM.clear_fifo_flag(); // start capture
  myCAM.start_capture();
  Serial.print("Waiting for capture..."); //waiting until capture is done
  while (!(myCAM.read_reg(ARDUCHIP_TRIG) & CAP_DONE_MASK)) {
   delay(10); 
  }
  Serial.println("OK");
  
  // open a file onto the SD card
  outFile = imageFile();
    
    // writes the content of the ArduCAM buffer into the file, with the right
    // function, defined by the 'mode' value
    Serial.print("Buffering...");
    if (mode == bmp_mode){
      writeBMP(outFile);
    } else if (mode == jpg_mode) {
      writeJPEG(outFile);
    }
    Serial.println("OK");
    
    // close the file and clean the flags and the buffers
    outFile.close(); 
    Serial.println("Capture terminée");
    myCAM.clear_fifo_flag();
    start_capture = 0;
    myCAM.InitCAM();
}

// Function that waits for n seconds, writing a countdown on the Serial console
void countdown(int n){
  while (n>0){
    Serial.print(n);
    Serial.print("... ");
    n=n-1;
    delay(1000);
  }
}

// ======== SETUP ========

void setup(){
  uint8_t vid,pid;
  uint8_t temp; 
  #if defined (__AVR__)
    Wire.begin(); 
  #endif
  #if defined(__arm__)
    Wire1.begin(); 
  #endif
  // begins the Serial communication
  Serial.begin(9600);
  Serial.println("ArduCAM Start!");
  pinMode(SPI_CS, OUTPUT);
  SPI.begin();
 
  myCAM.write_reg(ARDUCHIP_TEST1, 0x55);
  temp = myCAM.read_reg(ARDUCHIP_TEST1);
  if(temp != 0x55)
  {
   Serial.println("SPI interface Error!");
   while(1);
  } 
  
  // change MCU mode (?)
  myCAM.write_reg(ARDUCHIP_MODE, 0x00);
  
  // Verfication of camera type (OV2640 expected)
    myCAM.rdSensorReg8_8(OV2640_CHIPID_HIGH, &vid);
  myCAM.rdSensorReg8_8(OV2640_CHIPID_LOW, &pid);
  if((vid != 0x26) || (pid != 0x42))
   Serial.println("Can't find OV2640 module!");
  else
   Serial.println("OV2640 detected");
  
  myCAM.InitCAM();
  
  // SD card initialisation
  if (!SD.begin(SD_CS)) 
  {
    while (1); //If failed, stop here
    Serial.println("SD Card Error");
  }
  else
    Serial.println("SD Card detected!");
}



void loop(){
 // if it receives a trigger from the IR sensor, 
 // it takes a picture, then waits for 10 seconds
 if(!(myCAM.read_reg(ARDUCHIP_TRIG) & SHUTTER_MASK)){
 Serial.println("Triggered !!");
 takePicture();
 delay(10000);
 }
}

Any help or comment is more than welcome !