C++ library for the C329 SPI camera module [w/ code]

Just putting it out there for whoever is trying to use the C329 camera module of the SPI variety (the URAT module has code posted online)

The UART code is at: https://github.com/svoisen/c329
But electronics123.com have a SPI module as well, without code supplied.
I took the UART code and changed it to work with Arduino's SPI library. The changes are minuscule, but someone might find them useful instead of writing them again.

I used C329 with Teensy 2.0, and connected the MISO MOSI SCLK pins to the defaults, the only thing left is to connect the SPI CS pin on the camera module (check out Page 3 in the manual) to some GPIO, I chose the Teensy's B0 pin.

So the operation of the library is very similar to the example online:

#include "CameraC329.h"
#include <SPI.h>

CameraC329 camera(PIN_B0); //set B0 as the CS pin

void getPicture_callback(uint32_t pictureSize, uint16_t packetSize, uint32_t packetStartPosition, byte* packet)
{
//do something with picture buffer
}

void setup() {
          pinMode(PIN_B0, OUTPUT);
          digitalWrite(PIN_B0,HIGH); //unselect

          SPI.setDataMode(SPI_MODE0);
          SPI.begin();

          delay(2000); //time for the module to load stuff from EEPROM

          if (!camera.sync())
          {
            Serial.println("Sync failed");
            camera.powerOff();
            SPI.end();
            return;
          } else {
            Serial.println("Camera sync");
          }
        
          if (!camera.initialize(CameraC329::BAUD14400, CameraC329::CT_JPEG, CameraC329::PR_160x120, CameraC329::JR_320x240))
          {
            Serial.println("Initialize failed");
            return;
          } else {
            Serial.println("Camera init");
          }
          
          if (!camera.setQuality(CameraC329::QL_BEST))
          {
            Serial.println("Set quality failed");
            return;
          } else {
            Serial.println("Camera quality set");
          }

          if (!camera.getPicture(CameraC329::PT_JPEG_PREVIEW, &getPicture_callback))
          {
            Serial.println("Get Picture Failed");
            return;
          }
}

The major changes to the UART library are in the sendCommand and waitForResponse functions

void CameraC329::sendCommand()
{
  uint8_t i;
//Serial.print("send command ");
  // Big endian
  for (i = 0; i < CMD_SIZE; i++)
  {
//Serial.print(outputCommand[i]); Serial.print(" ");
    digitalWrite(spi_cs_pin,LOW);
    SPI.transfer(outputCommand[i]);
    digitalWrite(spi_cs_pin,HIGH);
  }
//Serial.println();
}

bool CameraC329::waitForResponse(uint32_t timeout, byte buffer[], uint16_t bufferLength)
{
  uint8_t byteCount = 0;
  unsigned long time = millis();

//Serial.print("response: ");
  while (millis() - time <= timeout)
  {
//    while (cameraPort.available() > 0) //hmmm...
    {
      digitalWrite(spi_cs_pin,LOW);
      buffer[byteCount] = SPI.transfer(0x00); //cameraPort.read();
      digitalWrite(spi_cs_pin,HIGH);
      
//Serial.print(buffer[byteCount],DEC); Serial.print(" ");
      byteCount++;

      if (byteCount == bufferLength) {
//Serial.println();
        return true;
      }
    }
  }
//Serial.println();
  if (byteCount > 0)
    return true;

  return false;
}

But I also found the GET PIC command to be very very picky. It really only responds very sporadically, so I added a bunch of retries:

bool CameraC329::getPicture(PictureType pictureType, void (*callback)(uint32_t pictureSize, uint16_t packetSize, uint32_t packetStartPosition, byte* packet))
{
  uint32_t pictureSize = 0;

  reset(RT_STATE);

  //Although ACK is part of the spec, we're really waiting for a DATA response, so let's skip waiting for ACK and just wait for DATA
//  while (!waitForACK(RESPONSE_DELAY, CMD_GETPICTURE) && ack_counter < 100) {
//    delay(10);
//  }

  //try to send the GET PIC command for 10 times before giving up
  uint32_t get_pic_tries = 0;
  while(get_pic_tries++ < 10) {
    setOutputCommand(CMD_GETPICTURE, pictureType, 0, 0, 0);
    sendCommand();

    uint32_t max_tries = 0, total_tries = 10;
    while (!(waitForResponse(RESPONSE_DELAY) && inputCommand[3] == CMD_DATA) && max_tries++ < total_tries) {
      //this is not a DATA response (start of the image data), let's wait try again shortly
      Serial.print("not DATA ("); Serial.print(inputCommand[3]); Serial.println(")"); 
      delay(10);
    }
    
    if(max_tries < total_tries) break;
  }
  if(get_pic_tries >= 10) return false;
  
  pictureSize = inputCommand[7] << 8;
  pictureSize |= inputCommand[6] << 8;
  pictureSize |= inputCommand[5];

  uint32_t bytePosition = 0;
  uint8_t package[DEFAULT_PACKAGE_SIZE];

  while (bytePosition < pictureSize)
  {
    if (!waitForResponse(RESPONSE_DELAY, package, DEFAULT_PACKAGE_SIZE))
      return false;

    callback(pictureSize, min(DEFAULT_PACKAGE_SIZE, pictureSize - bytePosition), bytePosition, package);
    bytePosition += DEFAULT_PACKAGE_SIZE;
  }

  return true;
}

Even with that, it's not 100% stable!
I think there's still problem with the SPI timing...

Code can be grabbed at: http://web.media.mit.edu/~roys/src/CameraC329SPI.zip

Enjoy,
Roy.

Share