Groveのシリアルカメラ「OV528」を調べてみる

Arduinoにシリアルカメラ「OV528」を[Groveコネクタ](http://wiki.seeedstudio.com/Grove_System/)経由で接続しようとしたところ、そう簡単にはつながらないことがわかったので、とりあえず調べた内容をメモとして残しておきます。

**【後日談】無事繋がりましたので、[こちら](https://qiita.com/mine820/items/d8e3069eedd7eed99f51)をご参照ください**

ちなみに、使用するArduinoは[Duemilanove](https://www.arduino.cc/en/Main/ArduinoBoardDuemilanove)互換のボードで、センサー等とのコネクタはGroveのみです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/130771/5bf3fc8c-ec83-941c-1016-1e309aaa8bf3.png)

# よくあるサンプルコード
調べるとまず見つかるのはこの[githubページ](https://github.com/Seeed-Studio/Grove_Serial_Camera_Kit)。
ここには2種類のソースコードが載っており、1つはハードウェアシリアル用、もう一つがソフトウェアシリアル用。
ただどちらも、ArduinoからSDカードシールドを経由して、カメラとプッシュボタンをつなぐという仕組みは一緒。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/130771/e615fdae-048a-ce66-8295-85e764472268.png)

いや、私がやりたいことではSDカードやプッシュボタンは必要ない！　なんてことができない。
ここから調べまくりです。

※[こちら](https://github.com/dotstudio/nefrybt_camera_server/blob/master/nefry/nefrybt_grove_camera/nefrybt_grove_camera.ino)にハードウェアシリアルでのサンプルがあるのですが、私の環境（ESP32ではない）ではうまく動かず．．．

イメージとしては、ソフトウェアシリアルを使って、カメラから画像を取得します

```C
#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];

  // ...
}
```

しかし、初期化ができない．．．
![image.png](https://qiita-image-store.s3.amazonaws.com/0/130771/562dd586-1811-d43b-71d0-e1938f20f496.png)

# 同じことで悩んでいる人、および、解決方法を探す
が、見つからない。

[Arduino Duemilanove](https://www.arduino.cc/en/Main/ArduinoBoardDuemilanove)のページをよくよく読み直してみると、Serialポートは1つ（0, 1 pin）しかなさそう。
ということで、他のデジタルポートで繋げないか、調査を続行。

更に調べていくと、ソフトウェアシリアルで頑張ろうとしている人を[発見](https://forum.arduino.cc/index.php?topic=293520.0)。
そこで「RS-232CではなくでTTLでつなげ」的な文章を見つけました。
「MAX232」？？？
コレが使えるぐらいなら、Groveを選ばないし．．．ということで、ここでくじける．．．＿|￣|○

# 別の方法を探すが．．．
初心に返り、カメラの仕様を探します。
カメラの型番は「OV528」なので、これが仕様の[ドキュメント](https://os.mbed.com/media/uploads/jplunkett/cj-ov528_protocol.pdf)となります。

<font color="FF0000">※この資料はプロトコルの資料なので、ここより本来の目的から脱線していきます</font>

## コマンド
OV528には11のコマンドが用意されています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/130771/89642e2d-2dea-1c22-4ed9-f0770c7135c7.png)
詳細はドキュメントを参照してください。

ちなみに、エラーだった場合のコードは以下のようになっているようです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/130771/88e069d4-ba90-02c0-f8ac-8899f1dc3725.png)

## ハンドシェイク
ホスト側から「SYNC」を送ると、カメラ側から「ACK」が返ってきて、さらにカメラ側から「SYNC」が送られてくるので、ホスト側から「ACK」を返すと接続OKです。
（ホストから25回ぐらい「SYNC」を送らないと「ACK」が返ってこないらしい）

それ以外のコマンドは、ホスト側から送ればちゃんと返事が返ってくるようです。

## 画像の取得（RAWデータ）
画像取得の手順は以下のとおりです。
1. Initial（Preview, VGA）
2. Snapshot（uncompressed picture）
3. Get picture（Snapshot picture）<br/>※ここでデータが送られてくる
4. ACK

なお、画像には「Snapshot」と「Preview」があり、上記の流れはSnapshotの場合で、Previewの場合は「2. Snapshot」を省略し、「4. ACK」のあとに「3. Get Picture」を繰り返せばOK。

# で、何だったっけ？
調べたかったのはコマンドではなくてつなぎ方だったのですが、単純な話ではなさそうなので、もうちょっと頑張ってみることにします。
いい手をご存知の方、アドバイスを頂けると助かります！
<font color="FF0000">【未完】</font>

# 参考サイト
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/