
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=219Strawberry 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 ..." );
}
}
スイッチサイエンス
売り上げランキング: 23827
スイッチサイエンス
売り上げランキング: 1910
このコメントは管理人のみ閲覧できます
C328Rのソースコードのライセンスは
Apache License, Version 2.0 なので
特に問題はないと思います。
私が改変した部分もオリジナルのライセンスに従います。
NewSoftSerialはLGPLになります。
ソースコードで添付するなら特に問題にならないと思います。
このコメントは管理人のみ閲覧できます
こちらの環境はMacOSX 10.6でProcessingのバージョンは
1.0.9 です。コンパイルが通ることを確認できております。
一度、Processingのバージョンを確認してみてください。
※あとinportではなくimportですね。
これが原因ではないですよね?
このコメントは管理人のみ閲覧できます
arduino-0018はArduinoというマイコン上で動作させるプログラムを書く為のソフトです。
Processingはパソコン上で動作するソフトです。
つまり今回のサンプルを動作させるにはarduino-0018+ArduinoとProcessingの1つのハードウェアと2つのソフトウェアが必要になります。
1つ目のサンプルコードはarduino-0018を使ってコンパイル&Arduinoにアップロードします。
2つ目のサンプルコードはProcessingで使います。
このコメントは管理人のみ閲覧できます
このコメントは管理人のみ閲覧できます
従来のカメラモジュールとの差異はこちらにまとめられている通りで、
初期化時のシリアル通信の速度が固定になっているようですね。
http://www.silentsystem.jp/c1098soft.htm
CameraC328R::sync()
CameraC328R::initial()
このへんをうまく変更すればいけると思います。
とりあえず動かせればいいというのであれば、カメラモジュールとの通信速度を14400bpsに固定して、
CameraC328R::initial()のcreateCommandの0のところを14400に相当する値を設定すれば14400bpsで通信できると思います。
がんばってください。
アドバイス助かります。
自分の変更点として
.hの125行目を
bool initial( BaudRate, ColorType, PreviewResolution, JPEGResolution );
とし
.cppの118行目を
bool CameraC328R::initial( BaudRate baudRate, ColorType colorType, PreviewResolution previewResolution, JPEGResolution jpegResolution )
{
createCommand( CMD_INITIAL, 0x07, colorType, previewResolution, jpegResolution );
としました。
.ccpの70行目の
createCommand( CMD_SYNC, 0, 0, 0, 0 );
も色々と変更してみましたが上手くいきませんでした。
syncコマンドに対して、ACKは返ってきてますか?
ACKが返ってきているのであれば、その後のINITIALでうまくいっていなさそうですね。
INITIALに対してはACKは返ってきてますか?
いつも参考にさせていただいております。
以前、C328をつかった方式でここを参考にさせていただきました。感謝です。
つい最近、C1098へ切り替わり328が入手困難となりやもう得ず、C1098を購入いたしました。
すでに、質問もあることなのですが、伺わせていただきたい内容が質問と上がっていたので、重ねての質問になることをお許しください。
Rioさんと同じく変更を加え、
ここのサイトを参考にさせていただいた形で
if( !camera.initial( CameraC328R::BAUD14400,CameraC328R::CT_JPEG, CameraC328R::PR_160x120, CameraC328R::JR_640x480 ) ) {
Serial.println( "Initial failed." );
return; }
と記述しコンパイルは成功しました。
実行結果としては
if( !camera.sync() ) {
Serial.println( "Sync failed." );
return;
}
の部分でのエラーで止まっている状態です。
何分、電子工作一年生なのでオシロなどの設備が乏しいため、通常起動状態での信号を見たりできないのですが、同期信号を得ることができていないようです。
カメラとの通信速度は14400bpsにして、
CameraC328R.cppの次の行を
createCommand( CMD_INITIAL, 0, colorType, previewResolution, jpegResolution );
次のように書き換えてためしてみてください。
createCommand( CMD_INITIAL, 0x07, 0x07, 0x00, jpegResolution ); // jpegResolutionは 0x05 か 0x07
新設計の画像処理チップって書いてあるし、C328Rのコマンドあんまり対応してないのかな。。
早速の対応ありがとうございます。
>>createCommand( CMD_INITIAL, 0x07, 0x07, 0x00, jpegResolution ); // jpegResolutionは 0x05 か 0x07
を試しましたが。
Sync failed.
と表示が相変わらずの状態です。
販売元のHPではコマンドは同じと記述されていますけど、違うと考えるほうが良いのかもしれませんね。
.ccpの464~477行の
sendACK( cmd, 0 );
createCommand( CMD_ACK, cmd,0, (byte)(packageId & 0xFF), (byte)(packageId >> 8) );
などの’0’も気になったので、いじってみましたが、やはりダメでした。
カメラのとの通信速度を14400bpsにして、
最初のsyncが通らないのはたぶんハードの問題のような気がします。
まずは結線を確認して、RX/TXが逆になっていないか確認してください。
このコメントは管理人のみ閲覧できます
短時間使わせていただいて、328と1098を同じ接続で状態を見ましたが、やはり1098は返答がありませんでした。
スケッチは、同じものを使用しました。
syncにたいしてACKが返ってこないのですね。。
うーん、、ボーレートが間違ってるか、RX/TXが間違ってるか、電源電圧を間違ってるか、カメラが壊れているか。。その辺ですね。。
Mini USB Adapter で、C328とC1098を接続してテストしてみましたが、1098では全く無反応のようです。
これはもう、ハード的に故障だという結論に達したようです。
何かわかりましたら、また書き込みさせていただきます。
これからも、参考のHPとして活用させていただきますので、よろしくお願いします。
販売元から、ソフトをダウンロードして行いました。
禁止ワードにひっかかて、名前が書けません
C1098はC328より多く電流を消費するようですが、
USBアダプタの3.3Vで動作させている場合、
C1098が動作しない可能性がありますね。
AdruinioのVinに接続し、エラー毎でLEDを表示するようにして、タックスイッチを押すことで開始するように回路を組んで、スケッチして再度オシロで反応を見ましたが、やはり、反応なしでした。
カメラにつなぐ3.3Vの電源に余裕は十分ありますか?
レギュレータは別途繋いでいる?
ご指摘通り、ただ箱電池を9VをArduinoに繋げているだけでした。
USBからとっても、Vinにつないでもこれだと、FTDI232を経由した、電源供給になるので変わらないんですね。
TOSHIBAのTA48033Sという3端子レギュレーターを貰えたので、入力ーGNDにセラミックコンデンサ0.47μF、GND-出力に電解コンデンサ100μFを接続し、入力からARDUINO Vinに、出力(3.3V)はカメラ側へ接続しています。
結果として、それでも反応なしでした。
電流消費が大きい件、LinkSpriteのカメラと勘違いしてました。。すみません。
レギュレータ別にしても動かないとなるとやっぱり故障なのかな。うーむ。
まだ、C1098には苦戦していますが、とりあえず動く方のC328を使って、作ってしまって。
C1098をあと乗せしようと思います。
そこで質問なのですが、MegaでSerial1~3へNewserialを使ったようにカメラを接続するのはどうすればよいでしょうか?
class NewSoftSerial;
CameraC328R(NewSoftSerial *softSerial = NULL);
NewSoftSerial *_serialPort;
の3行を変更すればいいのかな?程度はわかるのですが・・・。
んー。結構改造しないとだめかも。
とりあえず、オリジナルのCameraC328Rのコード使ったほうが早いかな。
ぼくならStreamクラスに対応して、NewSoftSerialでもHardwareSerialどちらにも対応すると思う。
久々に作業してます。
いろいろ手を加える必要が在るという事ですね。
オリジナルの方を試していますが、サンプルスケッチでSPI_PINの設定がなかったりで、何が何やらわからない状態で嵌ってます。
こうしてみると、ずいぶん管理人さんのライブラリーで楽をさせていただいていたのだと痛感しています。
ソフトとハードの両立(一度に覚えよう)するのは至難ですね・・・。
> ソフトとハードの両立(一度に覚えよう)するのは至難ですね・・・。
少しずつやっていきましょうー
そうそうスイッチサイエンスで新しいJPEGカメラの取り扱いがはじまてます。
今、動作確認中ですがうまくいったらまた記事にするので、参考にしてみてください。
http://www.switch-science.com/products/detail.php?product_id=453
記事公開されたら参考にさせていただきます。
ハード的にC1098の動かない理由が何となくわかったような気がします。
同じ回路でC328とC1098の受け取り電圧に差があるのでC1098では信号が来ているのに信号が来ていないような処理をしている感じがします。
MEGAでいろいろ接続計画頑張ってますが、C328をTX/RX接続いまだに出来てないです。
先は長いなぁ…他は、TX1~で接続しました。
とりあえず、C328がうまく繋がらないと、C1098へマラソン移行できないですから、う~ん、ライブラリ改造の底なし沼へ足を一歩二歩・・・。
オリジナルのC328ライブラリはシリアルポート0(TX0/RX0)を使っていますね。
シリアルの関数呼び出しを、
Serial.flush();
Serial.print( _command[i], BYTE );
Serial.available()
Serial.read()
次のように使いたいシリアルポート(1~3)に変更すればそのポートで動くと思いますよ。
Serial1.flush();
Serial1.print( _command[i], BYTE );
Serial1.available()
Serial1.read()
コンパイルもちゃんと通りました。
このページのサンプルを一部変更した形で、processingからの読み込みができるかを確認するために、IDEのシリアルモニターで1キャラ送信し返答を見ましたが、どうも、if( Serial.available() ){での、シリアルバッファのチェックでPC側からの通信が得られていないような状態になっています。謎です。
結果として、タックスイッチで撮影開始トリガーにしてやってみました。 成功です。
あとは、C1098を組み込んだ形にするのと、Serialの問題を解決です。
ありがとうございました。
MEGAに取り付けたC1098で撮影できました。
if( !camera.setLightFrequency( CameraC328R::FT_50Hz ) ) の部分で、エラーを起こしましたが。この部分をすっ飛ばした形でうまくいけました。
山を一つ越えた感じです。
ありがとう、ございました。
初めまして。
このカメラはPCと切り離して
(つまりArduino単体で)
使用すること可能なのでしょうか?
データはイーサネットシールドをつけたmicroSDに格納したいと思っています。
よろしくお願いします。
はじめまして、私はArduino初心者で、
電子回路の初歩しかわからないド素人ですが、
C1098でSyncが取れない原因に2日ほど悩んでいたところ、
arms22さんの分圧回路そのまま使ってて、
もしかしたら必要電流量が足りてないのかと思ったので、
抵抗を100k/200kから10k/20kにしたらSyncがとれました。
素人考えですが、このまま使用を続けても良いのでしょうか?
こんにちは。
100k/200kだと波形がなまってsyncがとれなかったのかもしれません。
10k/20kで問題ないと思います。
はじめまして、このプログラムを参考にさせてもらっているものです。
画像取得時に、processing画面で適当なキーを押す作業をせず、ダイレクトに画像を取得(ようはカメラからの片方向通信)したいのですが、その場合プログラムのどこをどういじればいいのでしょうか?
Arduinoのスケッチを画像を繰り返し撮影して、送信するように改造。
Processing側はJPEGのSOI(FFD8)を受信したらデータの書き込みを開始、
EOI(FFD9)を受信したら書き込み終了ってすればいいんじゃないかな?
http://siisise.net/jpeg.html
このコメントは管理人のみ閲覧できます
ごめんなさい。
リンク先のファイルは持っていません。
このコメントは管理人のみ閲覧できます
WProgram.hとWConstants.hのインクルードを削除して、
Arduino.hをインクルードしてください。
NewSoftSerialのかわりにSoftwareSerial使ってみてください。