M5STICK-Cとシリアルカメラを繋いでみた

UART接続をさらに追求するために、また、手元にあったGrove – Serial Camera Kitを、M5STICK-CのGROVE端子に接続して、写真を取得してみようと思った。

接続イメージ図

一般の方は、撮影した写真をM5STICK-Cの液晶に表示するだろうけど、M5STICK-Cのライブラリ ver0.05は、M5STACKのように、JPGデータを表示するAPIがまだ実装されていない(コメントアウトされている)。
M5STACKみたいに、TFカードスロットが搭載されていないので、メディアにも書き出しできない。
じゃあ、汎用I/Oを使ってSPIインターフェースを使ってTFカードスロットを搭載するという手段も考えられるけど、3つしかGPIOがないので、実装できない。

ここで、諦める訳にいかない私は、文字列で画像を表示する手段に打って出ることにした。
Chromeブラウザを使っている人は、下記の文字列(長いけど)をURLにコピペしてみましょう!

data:image/jpeg;base64,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

すると、どうでしょう!画像が表示されませんでしたか?
HTMLファイルの中に画像を埋め込んで表示させる方法がありまして、これを利用しています。詳細は、リンクの記事読んでね。

ソースコード

// M5STICK-CとGrove - Serial Camera Kiを繋いで、文字列(Base64)で、写真を取得するプログラム
// Programed by Kazuyuki Eguchi

#include <M5StickC.h>

#include <base64.h>

#define PIC_MAX_SIZE 10000

#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 PIC_FMT        PIC_FMT_CIF

HardwareSerial CAM_SERIAL(1);

const byte cameraAddr = (CAM_ADDR << 5);  // addr
unsigned long picTotalLen = 0;            // picture length

unsigned char image_data[PIC_MAX_SIZE];
unsigned long image_length = 0;

void setup() {
  M5.begin();
  
  CAM_SERIAL.begin(9600, SERIAL_8N1, 33, 32);        // GROVE端子の場合

  initialize();
  preCapture();
}

void loop() {
  Capture();
  GetData();
}

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]; 
  
  while (1)
  {
    clearRxBuf();
    sendCmd(cmd, 6);
    if (readBytes((char *)resp, 6, 100) != 6) continue; 
    if (resp[0] == 0xaa && resp[1] == (0x0e | cameraAddr) && resp[2] == 0x01 && resp[4] == 0 && resp[5] == 0) break; 
  }
}

void Capture()
{
  char cmd[] = { 0xaa, 0x06 | cameraAddr, 0x08, PIC_PKT_LEN & 0xff, (PIC_PKT_LEN>>8) & 0xff ,0}; 
  unsigned char resp[6];

  while (1)
  {
    clearRxBuf();
    sendCmd(cmd, 6);
    if (readBytes((char *)resp, 6, 100) != 6) continue;
    if (resp[0] == 0xaa && resp[1] == (0x0e | cameraAddr) && resp[2] == 0x06 && resp[4] == 0 && resp[5] == 0) break; 
  }
  cmd[1] = 0x05 | cameraAddr;
  cmd[2] = 0;
  cmd[3] = 0;
  cmd[4] = 0;
  cmd[5] = 0; 
  while (1)
  {
    clearRxBuf();
    sendCmd(cmd, 6);
    if (readBytes((char *)resp, 6, 100) != 6) continue;
    if (resp[0] == 0xaa && resp[1] == (0x0e | cameraAddr) && resp[2] == 0x05 && resp[4] == 0 && resp[5] == 0) break;
  }
  cmd[1] = 0x04 | cameraAddr;
  cmd[2] = 0x1;
  while (1) 
  {
    clearRxBuf();
    sendCmd(cmd, 6);
    if (readBytes((char *)resp, 6, 100) != 6) continue;
    if (resp[0] == 0xaa && resp[1] == (0x0e | cameraAddr) && resp[2] == 0x04 && resp[4] == 0 && resp[5] == 0)
    {
      if (readBytes((char *)resp, 6, 1000) != 6)
      {
        continue;
      }
      if (resp[0] == 0xaa && resp[1] == (0x0a | cameraAddr) && resp[2] == 0x01)
      {
        picTotalLen = (resp[3]) | (resp[4] << 8) | (resp[5] << 16); 
        Serial.print("picTotalLen:");
        Serial.println(picTotalLen);
        break;
      }
    }
  }  
}

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_length = 0;
  
  for (unsigned int i = 0; i < pktCnt; i++)
  {
    cmd[4] = i & 0xff;
    cmd[5] = (i >> 8) & 0xff;
    
    int retry_cnt = 0;
  retry:
    delay(10);
    clearRxBuf(); 
    sendCmd(cmd, 6); 
    uint16_t cnt = readBytes((char *)pkt, PIC_PKT_LEN, 200);
    
    unsigned char sum = 0; 
    for (int y = 0; y < cnt - 2; y++)
    {
      sum += pkt[y];
    }
    if (sum != pkt[cnt-2])
    {
      if (++retry_cnt < 100) goto retry;
      else break;
    }

    if(picTotalLen < PIC_MAX_SIZE)
    {
      int co;
      for(co = 0; co < (cnt-6) ; co++)
      {
        image_data[image_length] = pkt[co + 4];
        image_length++;
      }
    }
  }

  if(picTotalLen < PIC_MAX_SIZE)
  {
    Serial.print("data:image/jpeg;base64,");
    String encoded = base64::encode(image_data, image_length);
    Serial.println(encoded);
  }
  
  cmd[4] = 0xf0;
  cmd[5] = 0xf0; 
  sendCmd(cmd, 6); 
}

動作結果(例)デバッグログ上

以上、ご参考までに