Arduinoにシリアルカメラ「OV528」をGroveコネクタ経由で接続しようとしたところ、そう簡単にはつながらないことがわかったので、とりあえず調べた内容をメモとして残しておきます。
【後日談】無事繋がりましたので、こちらをご参照ください
ちなみに、使用するArduinoはDuemilanove互換のボードで、センサー等とのコネクタはGroveのみです。
よくあるサンプルコード
調べるとまず見つかるのはこのgithubページ。
ここには2種類のソースコードが載っており、1つはハードウェアシリアル用、もう一つがソフトウェアシリアル用。
ただどちらも、ArduinoからSDカードシールドを経由して、カメラとプッシュボタンをつなぐという仕組みは一緒。
いや、私がやりたいことではSDカードやプッシュボタンは必要ない! なんてことができない。
ここから調べまくりです。
※こちらにハードウェアシリアルでのサンプルがあるのですが、私の環境(ESP32ではない)ではうまく動かず...
イメージとしては、ソフトウェアシリアルを使って、カメラから画像を取得します
#include <SoftwareSerial.h>
#define PIC_PKT_LEN 128 //data length of each read, dont set this too big because ram is limited
#define PIC_FMT_VGA 7
#define PIC_FMT_CIF 5
#define PIC_FMT_OCIF 3
#define CAM_ADDR 0
#define CAM_SERIAL softSerial
#define PIC_FMT PIC_FMT_VGA
SoftwareSerial softSerial(9, 10); //rx,tx ※この番号で合っているのか怪しい
const byte cameraAddr = (CAM_ADDR << 5); // addr
unsigned long picTotalLen = 0; // picture length
/*********************************************************************/
void setup() {
Serial.begin(115200);
CAM_SERIAL.begin(9600); //cant be faster than 9600, maybe difference with diff board.
initialize();
}
/*********************************************************************/
void loop() {
int n = 0;
while(1){
{
delay(200);
if (n == 0) preCapture();
Capture();
GetData();
}
n++ ;
}
}
/*********************************************************************/
void clearRxBuf()
{
while (CAM_SERIAL.available())
{
CAM_SERIAL.read();
}
}
/*********************************************************************/
void sendCmd(char cmd[], int cmd_len)
{
for (char i = 0; i < cmd_len; i++) CAM_SERIAL.write(cmd[i]);
}
/*********************************************************************/
int readBytes(char *dest, int len, unsigned int timeout)
{
int read_len = 0;
unsigned long t = millis();
while (read_len < len)
{
while (CAM_SERIAL.available()<1)
{
if ((millis() - t) > timeout)
{
return read_len;
}
}
*(dest+read_len) = CAM_SERIAL.read();
Serial.write(*(dest+read_len));
read_len++;
}
return read_len;
}
/*********************************************************************/
void initialize()
{
char cmd[] = {0xaa,0x0d|cameraAddr,0x00,0x00,0x00,0x00} ;
unsigned char resp[6];
Serial.print("initializing camera...");
while (1)
{
sendCmd(cmd,6);
if (readBytes((char *)resp, 6,1000) != 6)
{
Serial.print("."); // ※ここから進まない
continue;
}
if (resp[0] == 0xaa && resp[1] == (0x0e | cameraAddr) && resp[2] == 0x0d && resp[4] == 0 && resp[5] == 0)
{
if (readBytes((char *)resp, 6, 500) != 6) continue;
if (resp[0] == 0xaa && resp[1] == (0x0d | cameraAddr) && resp[2] == 0 && resp[3] == 0 && resp[4] == 0 && resp[5] == 0) break;
}
}
cmd[1] = 0x0e | cameraAddr;
cmd[2] = 0x0d;
sendCmd(cmd, 6);
//Serial.println("\nCamera initialization done.");
}
/*********************************************************************/
void preCapture()
{
char cmd[] = { 0xaa, 0x01 | cameraAddr, 0x00, 0x07, 0x00, PIC_FMT };
unsigned char resp[6];
// ※省略
}
void Capture()
{
char cmd[] = { 0xaa, 0x06 | cameraAddr, 0x08, PIC_PKT_LEN & 0xff, (PIC_PKT_LEN>>8) & 0xff ,0};
unsigned char resp[6];
// ※省略
}
/*********************************************************************/
void GetData()
{
unsigned int pktCnt = (picTotalLen) / (PIC_PKT_LEN - 6);
if ((picTotalLen % (PIC_PKT_LEN-6)) != 0) pktCnt += 1;
char cmd[] = { 0xaa, 0x0e | cameraAddr, 0x00, 0x00, 0x00, 0x00 };
unsigned char pkt[PIC_PKT_LEN];
// ...
}
同じことで悩んでいる人、および、解決方法を探す
が、見つからない。
Arduino Duemilanoveのページをよくよく読み直してみると、Serialポートは1つ(0, 1 pin)しかなさそう。
ということで、他のデジタルポートで繋げないか、調査を続行。
更に調べていくと、ソフトウェアシリアルで頑張ろうとしている人を発見。
そこで「RS-232CではなくでTTLでつなげ」的な文章を見つけました。
「MAX232」???
コレが使えるぐらいなら、Groveを選ばないし...ということで、ここでくじける..._| ̄|○
別の方法を探すが...
初心に返り、カメラの仕様を探します。
カメラの型番は「OV528」なので、これが仕様のドキュメントとなります。
※この資料はプロトコルの資料なので、ここより本来の目的から脱線していきます
コマンド
OV528には11のコマンドが用意されています。
詳細はドキュメントを参照してください。
ちなみに、エラーだった場合のコードは以下のようになっているようです。
ハンドシェイク
ホスト側から「SYNC」を送ると、カメラ側から「ACK」が返ってきて、さらにカメラ側から「SYNC」が送られてくるので、ホスト側から「ACK」を返すと接続OKです。
(ホストから25回ぐらい「SYNC」を送らないと「ACK」が返ってこないらしい)
それ以外のコマンドは、ホスト側から送ればちゃんと返事が返ってくるようです。
画像の取得(RAWデータ)
画像取得の手順は以下のとおりです。
- Initial(Preview, VGA)
- Snapshot(uncompressed picture)
- Get picture(Snapshot picture)
※ここでデータが送られてくる - ACK
なお、画像には「Snapshot」と「Preview」があり、上記の流れはSnapshotの場合で、Previewの場合は「2. Snapshot」を省略し、「4. ACK」のあとに「3. Get Picture」を繰り返せばOK。
で、何だったっけ?
調べたかったのはコマンドではなくてつなぎ方だったのですが、単純な話ではなさそうなので、もうちょっと頑張ってみることにします。
いい手をご存知の方、アドバイスを頂けると助かります!
【未完】
参考サイト
https://os.mbed.com/media/uploads/jplunkett/cj-ov528_protocol.pdf
https://forum.arduino.cc/index.php?topic=293520.0
https://dotstud.io/blog/nefrybt-pepper-monitoring-system/
https://github.com/Seeed-Studio/Grove_Serial_Camera_Kit
http://wiki.seeedstudio.com/Grove-Serial_Camera_Kit/