Arduinoで遊ぼう - JPEGカメラモジュールで写真を撮る
Posted by arms22 on 2009年10月13日

SparkFunで取り扱っているJPEGカメラモジュール(C328-7640)は解像度640x480のカラー静止画をJPEG圧縮して出力するモジュールだ。マイコンとの接続はシリアルでたった4本のワイヤでArduinoと接続、制御することができる。スナップショットコマンドを送ると撮影した画像をJPEGで圧縮してシリアルで送信する。日本だとスイッチサイエンスさん、ストロベリーリナックスさんで買える。今回はこのJPEGカメラモジュールを使って静止画を撮影し、Processingの画面に撮影した画像を表示してみよう。
※夜に撮影したのでちょっと画像が荒い感じ。昼に撮ればもう少し綺麗になると思う。
SparkFun Electronics - JPEG Color Camera - UART Interface
http://www.sparkfun.com/commerce/product_info.php?products_id=9334
スイッチサイエンス - シリアル接続JPEGカラーカメラ
http://www.switch-science.com/products/detail.php?product_id=219
Strawberry Linux - シリアル出力JPEGカラーカメラ(640x480)
http://strawberry-linux.com/catalog/items?code=18104
このモジュールの仕様は次の通り。
- サイズ:20x28mm
- 解像度:640x480, 320x240, 160x128, 80x60
- 電源:3.3V, 60mA
- 通信速度:115.2Kbps(自動速度検出付)
Arduinoと接続する

このモジュールの電源は3.3Vなので5VのArduinoと直結してしまうとポートが壊れてしまう。その為、ArduinoのTxD信号を5Vから3Vに抵抗で分圧(100K/200K)してモジュールに入力する。またArduinoのハードウェアシリアル(0番と1番)はPCとの通信に利用するのでモジュールとの通信にはソフトウェアシリアル(2番と3番)を使う。
ライブラリをダウンロードする
次のサイトでJPEGカメラモジュール(C328-7640)用のライブラリが公開されている。
gizmologi.st - Taking Pictures with Arduino
http://gizmologi.st/2009/04/taking-pictures-with-arduino/
しかし、上記ライブラリはモジュールとの通信にハードウェアシリアルを使っている。ハードウェアシリアルはPCとの通信に利用したいので、ソフトウェアシリアルを使うようにライブラリを変更した。
ソフトウェアシリアルを使うように改造したライブラリ(CameraC328R)
http://arms22.googlecode.com/files/CameraC328R.zip
ソフトウェアシリアルはNewSoftSerialというライブラリを使う。Arduino IDEにはソフトウェアシリアルライブラリ(SoftwareSerial)が標準で付属しているがavailable関数が使えない。NewSoftSerialは次のサイトからダウンロードできる。
Arduiniana - NewSoftSerial
http://arduiniana.org/libraries/newsoftserial/
ダウンロードしたライブラリはSKETCHBOOK/librariesにコピーする。
スケッチ
最初のスケッチはArduino。ハードウェアシリアル、ソフトウェアシリアルの初期化を行って(setup)、PCから1byte、データが送られてくるのを待つ(loop)。データを受信(撮影のトリガー)したらJPEGカメラモジュールとの通信を開始し、スナップショットコマンド(snapshot)を送る。そして撮影した画像をJPEGカメラモジュールから受け取りPCに送信する(getJPEGPicture_callback)。
#include <CameraC328R.h>
#include <NewSoftSerial.h>
#define LED_PIN 13
#define PAGE_SIZE 64
#define USB_BAUD 115200
#define CAMERA_BAUD 14400
NewSoftSerial mySerial(2, 3);
CameraC328R camera(&mySerial);
uint16_t pictureSizeCount = 0;
/**
* This callback is called EVERY time a JPEG data packet is received.
*/
void getJPEGPicture_callback( uint16_t pictureSize, uint16_t packageSize, uint16_t packageCount, byte* package )
{
// packageSize is the size of the picture part of the package
pictureSizeCount += packageSize;
Serial.write(package,packageSize);
if( pictureSizeCount >= pictureSize )
{
digitalWrite( LED_PIN, LOW );
Serial.flush();
}
}
void setup()
{
Serial.begin( USB_BAUD );
mySerial.begin(CAMERA_BAUD);
pinMode( LED_PIN, OUTPUT );
digitalWrite( LED_PIN, LOW );
}
void loop()
{
if( Serial.available() ){
while(Serial.read() != -1);
digitalWrite( LED_PIN, HIGH );
if( !camera.sync() )
{
Serial.println( "Sync failed." );
return;
}
if( !camera.initial( CameraC328R::CT_JPEG, CameraC328R::PR_160x120, CameraC328R::JR_640x480 ) )
{
Serial.println( "Initial failed." );
return;
}
if( !camera.setPackageSize( 64 ) )
{
Serial.println( "Package size failed." );
return;
}
if( !camera.setLightFrequency( CameraC328R::FT_50Hz ) )
{
Serial.println( "Light frequency failed." );
return;
}
if( !camera.snapshot( CameraC328R::ST_COMPRESSED, 0 ) )
{
Serial.println( "Snapshot failed." );
return;
}
pictureSizeCount = 0;
if( !camera.getJPEGPicture( CameraC328R::PT_JPEG, PROCESS_DELAY, &getJPEGPicture_callback ) )
{
Serial.println( "Get JPEG failed." );
return;
}
}
}
次のスケッチはProcessing。Arduino IDEのシリアルモニタを閉じ、アプリを起動する。画面が開いたら適当なキーを押すと撮影が開始され、Arduinoから撮影された画像が送られてくる。データの転送中、ArduinoのLEDが点灯している。このLEDが消えたら再度、適当なキーを押すと画面に撮影した画像が表示される。撮影された画像はスケッチと同じフォルダに保存される。
import processing.serial.*;
Serial myPort;
String filename = "photo.jpg";
byte[] photo = {
};
Boolean readData = false;
PImage captureImage;
void setup()
{
size(640,480);
println( Serial.list() );
myPort = new Serial( this, Serial.list()[3], 115200 ); //Serial.list()[3]は環境に合わせて変更すること。
}
void draw()
{
byte[] buffer = new byte[64];
if( readData )
{
while( myPort.available() > 0 )
{
int readBytes = myPort.readBytes( buffer );
print( "Read " );
print( readBytes );
println( " bytes ..." );
for( int i = 0; i < readBytes; i++ )
{
photo = append( photo, buffer[i] );
}
}
}
else
{
while( myPort.available() > 0 )
{
print( "COM Data: " );
println( myPort.readString() );
}
}
}
void keyPressed()
{
if( photo.length > 0 ) {
readData = false;
print( "Writing to disk " );
print( photo.length );
println( " bytes ..." );
saveBytes( filename, photo );
println( "DONE!" );
photo = new byte[0];
captureImage = loadImage(filename);
image(captureImage, 0, 0);
}
else {
readData = true;
myPort.write(0);
println( "Waiting for data ..." );
}
}
該当の記事は見つかりませんでした。