This is an Arduino-based camera that sends images wirelessly to your computer. In addition to the Grove base shield - used to connect the camera module, a button and an LED light to the Arduino - we need an SD card shield to save the photos we take, and a Bluetooth shield to send the images off.
A Processing sketch on the computer receives the sent picture, saves it to the hard drive and displays it in the program window. In addition to using the hardware button connected to the Arduino, photos can be taken directly from the computer using the on-screen button or the space bar.
This camera will be part of a garden explorer robot that I'm building, inspired by the Mars Curiosity rover. I'll be adding a time-lapse feature and motion trigger.
Parts: -Ardiuno Uno R3 -Seeed SD Card Shield -Seeed Bluetooth Shield -Grove Base Shield -Grove Serial JPEG Camera -Grove Button -Grove LED -Grove 4-Pin Cables -Battery Pack |
Suppliers: Trossen Robotics Epic Tinker Seeed Studios |
Make sure your portName in the Processing sketch matches the blueToothName in Arduino. And, please make sure this name sticks out - don't just name them "Bluetooth" - or the automatic selector might open the wrong port.
The Arduino sends out a series of 3-letter commands to let Processing know how to update the on-screen status and when to start and stop saving the received jpeg ("CAM" when the camera has finished taking its picture, "PIC" when it starts sending jpeg data and "EOF" at the end of the file). I plan on having error messages sent this way, too.
Photos are saved sequentially in a folder called "rcamera" on the SD card. The Processing sketch creates a new time-stamped folder for each session. These are also kept in "rcamera" within the remotePhoto sketch folder.
Arduino:
#include <SD.h>
#include <SoftwareSerial.h>
File photoFile, textFile;
const int blueToothRx = 6;
const int blueToothTx = 7;
SoftwareSerial blueToothSerial(blueToothRx,blueToothTx);
String blueToothName = "DreaduinoBluetooth";
const int photoButtonPin = 5;
const int ledPin = 4;
const int sdCardPin = 10; // cs pin of sd card shield
const int photoBufferSize = 96;
unsigned int photoSize = 0;
int readData = 0;
boolean firstPhoto = true;
int photoNumber = 0;
char photoName[] = "rcamera/pic00000.jpg";
char cameraReset[] = {
0x56,0x00,0x26,0x00}; // reset camera
char cameraCapture[] = {
0x56,0x00,0x36,0x01,0x00}; // take picture
char cameraContinue[] = {
0x56,0x00,0x36,0x01,0x02}; // take next picture
char cameraGetSize[] = {
0x56,0x00,0x34,0x01,0x00}; // read jpg file size
char cameraGetData[] = { // read jpg data
0x56,0x00,0x32,0x0c,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a};
void setup()
{
Serial.begin(115200);
pinMode(blueToothRx, INPUT);
pinMode(blueToothTx, OUTPUT);
pinMode(photoButtonPin,INPUT);
pinMode(ledPin,OUTPUT);
pinMode(sdCardPin,OUTPUT);
blueToothSetup();
sdCardSetup();
checkLastFile();
readLastFile();
cameraSetup();
ledFlash();
}
void loop()
{
readPhotoPin();
readBlueTooth();
}
void readPhotoPin()
{
if(digitalRead(photoButtonPin) == HIGH){
delay(50); //debounce
if(digitalRead(photoButtonPin) == HIGH){
takePhoto();
}
}
}
void readBlueTooth()
{
if (blueToothSerial.available()) {
readData = blueToothSerial.read();
if (readData == 80)takePhoto();
}
}
void takePhoto()
{
if(firstPhoto) {
cameraCaptureImage();
firstPhoto = false;
}
else cameraContinueCapture();
ledFlash();
cameraReadData();
ledFlash();
sendPhoto();
ledFlash();
}
void savePhoto(File &photoFile,int toBeReadSize)
{
char readSize = 0;
for(char i = 0;i < 5;i ++){ //read "ready to send" signal
Serial.read();
}
while(readSize < toBeReadSize){ //read and store the jpeg data
photoFile.write(Serial.read());
readSize++;
}
for(char i = 0;i < 5;i ++){ //read "sussessfully sent" signal
Serial.read();
}
}
void sendPhoto()
{
Serial.println("sending photo");
blueToothSerial.print("PIC");
File photoFile = SD.open(photoName);
if (photoFile) {
ledOn();
while (photoFile.position() < photoFile.size()) {
blueToothSerial.write(photoFile.read());
}
photoFile.close();
Serial.println("photo sent");
blueToothSerial.print("EOF");
saveLastFile();
ledOff();
}
else {
Serial.println("error sending photo");
}
}
////////////////////////////////////////////////////////////////////////////
// bluetooth
void blueToothSetup()
{
blueToothSerial.begin(115200);
blueToothSerial.print("\r\n+STBD=115200\r\n ");
blueToothSerial.print("\r\n+STWMOD=0\r\n"); // set bluetooth to work in slave mode
blueToothSerial.print("\r\n+STNA="+blueToothName+"\r\n"); // set bluetooth name
blueToothSerial.print("\r\n+STOAUT=1\r\n"); // permit paired device to connect
blueToothSerial.print("\r\n+STAUTO=0\r\n"); // auto-connection should be forbidden here
delay(2000); // required delay
blueToothSerial.print("\r\n+INQ=1\r\n"); // make bluetooth inquirable
Serial.println("dreaduino bluetooth is inquirable");
delay(2000); // required delay
blueToothSerial.flush();
}
////////////////////////////////////////////////////////////////////////////
// sd card
void sdCardSetup()
{
Serial.println("initializing sd card");
if (!SD.begin(sdCardPin)) {
Serial.print("initialzation failed");
return;
}
Serial.println("sd initialization done");
}
void checkLastFile()
{
if (!SD.exists("rcamera/lastfile.txt")) {
SD.mkdir("/rcamera");
textFile = SD.open("rcamera/lastfile.txt", FILE_WRITE);
textFile.seek(0);
textFile.write(photoName);
textFile.close();
}
}
void readLastFile()
{
textFile = SD.open("rcamera/lastfile.txt", FILE_READ);
for (int i=0; i < 16; i++){
photoName[i] = textFile.read();
}
textFile.close();
photoNumber = ((photoName[11]-48)*10000);
photoNumber = photoNumber + ((photoName[12]-48)*1000);
photoNumber = photoNumber + ((photoName[13]-48)*100);
photoNumber = photoNumber + ((photoName[14]-48)*10);
photoNumber = photoNumber + ((photoName[15]-48));
photoNumber ++;
}
void saveLastFile()
{
textFile = SD.open("rcamera/lastfile.txt", FILE_WRITE);
if(!textFile){
Serial.println("failed to open text file");
}
else{
textFile.seek(0);
textFile.write(photoName);
}
textFile.close();
}
////////////////////////////////////////////////////////////////////////////
// serial camera
void cameraSetup()
{
sendCameraCmd(cameraReset,4);
delay(1000);
while(Serial.available() > 0)Serial.write(Serial.read());
Serial.println("camera initialization done");
}
void cameraCaptureImage()
{
sendCameraCmd(cameraCapture,5);
delay(50);
while(Serial.available()){
Serial.read();
}
}
void cameraContinueCapture()
{
sendCameraCmd(cameraContinue,5);
delay(50);
while(Serial.available() > 0){
Serial.write(Serial.read());
}
}
void cameraReadData()
{
unsigned int photoSize;
sendCameraCmd(cameraGetSize,5);
delay(50);
for(char i = 0; i < 7; i++){
Serial.read();
}
photoSize = 0;
int high = Serial.read();
photoSize |= high ;
int low = Serial.read();
photoSize = photoSize << 8 ;
photoSize |= low ;
unsigned int addr = 0;
cameraGetData[8] = 0;
cameraGetData[9] = 0;
unsigned int count = photoSize / photoBufferSize;
char tail = photoSize % photoBufferSize;
cameraGetData[13] = photoBufferSize;
photoName[11] = photoNumber/10000 + '0';
photoName[12] = photoNumber/1000 + '0';
photoName[13] = photoNumber/100 + '0';
photoName[14] = photoNumber/10 + '0';
photoName[15] = photoNumber%10 + '0';
Serial.println("saving photo");
blueToothSerial.print("CAM");
photoFile = SD.open(photoName, FILE_WRITE);
ledOn();
if(!photoFile){
Serial.println("failed to open photo file");
}
else{
for(char i = 0; i < count; i++){ //get and save count*photoBufferSize data
sendCameraCmd(cameraGetData,16);
delay(10);
savePhoto(photoFile, photoBufferSize);
addr += photoBufferSize;
cameraGetData[8] = addr >> 8;
cameraGetData[9] = addr & 0x00FF;
}
cameraGetData[13] = tail; //get rest of the image data
sendCameraCmd(cameraGetData,16);
delay(10);
savePhoto(photoFile, tail);
}
photoFile.close();
ledOff();
photoNumber ++;
}
void sendCameraCmd(char cmd[] ,int cmd_size)
{
for(char i = 0; i < cmd_size; i++)Serial.print(cmd[i]);
}
////////////////////////////////////////////////////////////////////////////
// led light
void ledOn()
{
digitalWrite(ledPin,HIGH);
}
void ledOff()
{
digitalWrite(ledPin,LOW);
}
void ledFlash()
{
digitalWrite(ledPin,HIGH);
delay(60);
digitalWrite(ledPin,LOW);
delay(60);
digitalWrite(ledPin,HIGH);
delay(60);
digitalWrite(ledPin,LOW);
delay(60);
}
Processing:
import processing.serial.*;
import controlP5.*;
Serial dreadPort;
OutputStream output;
PImage img;
ControlP5 controlP5;
Button photoButton;
PFont font = createFont("Arial", 14);
String photoName;
String photoNameNumber;
int photoNumber = 1;
boolean cameraSave = false;
boolean photoOpen = false;
boolean photoSwitch = false;
String filePath = "remoteCamera";
String fileStatus;
String folderName = timeStamp();
int frameLocationX = 50;
int frameLocationY = 50;
int frameLocationSet = 0;
String[] serialString;
String serialCheck;
String portName = "DreaduinoBluetooth";
int portNumber;
int serialIndex;
int dataIn = 0;
int charC = 67;
int charA = 65;
int charM = 77;
int charP = 80;
int charI = 73;
int charE = 69;
int charO = 79;
int charF = 70;
int[] checkData = new int[3];
int[] checkEOF = new int[3];
void setup()
{
size(640, 580);
background(40);
frame.setResizable(true);
findSerialPort();
dreadPort = new Serial(this, Serial.list()[portNumber], 115200);
dreadPort.clear();
setupControlP5();
statusUpdate();
println("remoteCamera");
println("ready");
}
void draw()
{
if (frameLocationSet < 2) {
frame.setLocation(frameLocationX, frameLocationY);
frameLocationSet ++;
}
if (photoSwitch) remotePhoto();
}
void serialEvent(Serial myPort)
{
dataIn = myPort.read();
checkData[2] = checkData[1];
checkData[1] = checkData[0];
checkData[0] = dataIn;
if (checkData[2] == charC && checkData[1] == charA && checkData[0] == charM) {
photoNameNumber = nf(photoNumber, 5);
photoName = ("img" + photoNameNumber + ".jpg");
filePath = (folderName + "/" + photoName);
println("saving");
cameraSave = true;
statusUpdate();
}
if (checkData[2] == charP && checkData[1] == charI && checkData[0] == charC) {
println("sending");
openPhoto();
return;
}
if (photoOpen) writePhoto();
}
void findSerialPort()
{
serialString = Serial.list();
println(serialString);
for (int i = serialString.length - 1; i > 0; i--) {
serialCheck = serialString[i];
serialIndex = serialCheck.indexOf(portName);
if (serialIndex > -1) portNumber = i;
}
}
void statusUpdate()
{
noStroke();
fill(40);
rect(0, 481, 640, 100);
fill(255);
text(filePath, 300, 512);
if (cameraSave) fileStatus = "saving";
if (photoOpen) fileStatus = "sending";
if (!cameraSave && !photoOpen) fileStatus = "ready";
fill(255);
text(fileStatus, 300, 547);
}
void openPhoto()
{
if (!photoOpen) {
cameraSave = false;
photoOpen = true;
output = createOutput("rcamera/" + filePath);
statusUpdate();
}
}
void writePhoto()
{
try {
output.write(dataIn);
checkEOF[2] = checkEOF[1];
checkEOF[1] = checkEOF[0];
checkEOF[0] = dataIn;
if (checkEOF[2] == charE && checkEOF[1] == charO && checkEOF[0] == charF) {
closePhoto();
println(photoName + " received");
println("ready");
photoOpen = false;
statusUpdate();
}
}
catch (IOException e) {
e.printStackTrace();
}
}
void closePhoto()
{
try {
output.flush();
output.close();
photoOpen = false;
statusUpdate();
photoNumber ++;
displayPhoto();
}
catch (IOException e) {
e.printStackTrace();
}
}
void displayPhoto()
{
noStroke();
fill(40);
rect(0, 0, 640, 480);
img = loadImage("rcamera/" + filePath);
image(img, 0, 0);
}
void setupControlP5()
{
controlP5 = new ControlP5(this);
controlP5.setControlFont(font);
photoButton = controlP5.addButton("photo", 0, 120, 500, 58, 58);
photoButton.setSwitch(photoSwitch);
photoButton.setColorBackground(color(150, 0, 0, 255));
}
void controlEvent(ControlEvent theEvent)
{
if (theEvent.controller().name()=="photo") remotePhoto();
}
void remotePhoto() {
dreadPort.write('P');
photoSwitch = false;
}
void keyPressed() {
if (key == ' ' && !photoSwitch) {
photoSwitch = true;
remotePhoto();
}
}
String timeStamp() {
String timestamp = year()+"_"+month()+"_"+day()+"_"+hour()+"_"+minute();
return timestamp;
}
Have you tried using eye-fi wireless sd cards?
ReplyDeleteThis is the first I'm hearing of it. Looks interesting.
Deleteit's possible to save pic directly to computer?
ReplyDeletehow to do it?
tq
I haven't tried, yet. The Arduino would need to send over Bluetooth any data that it receives from the camera. I imagine that the cameraReadData and savePhoto functions could be moved over to the Processing sketch without too much trouble. Use createOutput in Processing to save the data that was being saved to photoFile on the Arduino.
Deletehey we are trying something similar... we use aubtm20 bluetooth breakout.. is it possible??
ReplyDeleten how do we save the image?