M5stackCore2+Unit Camで画像を取得する

# はじめに
サンプルコードはあるのですが動かしている記事があまり見つからず。備忘録的に書き出します。

# デバイス
M5stack core2

https://www.switch-science.com/collections/m5stack/products/9349

OV2640搭載 Unit Cam Wi-Fi Camera

https://www.switch-science.com/products/7231

groveケーブルもあると便利

https://www.switch-science.com/products/5216


# 接続

今回はPORT Aに接続します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/6a681521-bc55-76f5-d64c-01b83744d8ca.png)

# コード
環境はplatformIoです
```
main.cpp
uart_frame.cpp
uart_frame.h
```

```main.cpp
#include <M5Core2.h>
#include "uart_frame.h"

typedef struct _JpegFrame_t
{
  uint8_t *buf;
  uint32_t size;
} JpegFrame_t;

static QueueHandle_t jpeg_fream_queue = NULL;

// UART
const int UART_RX_PIN = 33; 
const int UART_TX_PIN = 32;
const int UART_BAUD_RATE = 1500000;

void frame_recv_callback(int cmd, const uint8_t *buf, int len)
{
  JpegFrame_t frame;
  frame.buf = (uint8_t *)malloc(sizeof(uint8_t) * len);
  memcpy(frame.buf, buf, len);
  frame.size = len;

  if (xQueueSend(jpeg_fream_queue, &frame, 0) != pdTRUE)
  {
    free(frame.buf);
  }
}

void setup()
{
  M5.begin();
  Serial.begin(1500000);
   uart_frame_init(UART_RX_PIN, UART_TX_PIN, UART_BAUD_RATE);
}


void loop()
{
  JpegFrame_t frame;
  int time_last = 0;
  time_last = millis();
  xQueueReceive(jpeg_fream_queue, &frame, portMAX_DELAY);
  M5.Lcd.drawJpg(frame.buf, frame.size, 0, 0);
  Serial.printf("--%ld--size--%d--\r\n", millis() - time_last, frame.size);
  free(frame.buf);
}


```

:::note info
core2のportAは33,32ピンに該当する。
```
// UART
const int UART_RX_PIN = 33; 
const int UART_TX_PIN = 32;
const int UART_BAUD_RATE = 1500000;
```
:::


# 動作確認

サンプルコードそのままだとヌルヌル感はない。チューニングが必要だろう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342722/8e8b778c-6f54-5507-edb0-19a2179b2aec.png)



# (おまけ)ライブラリ

サンプルコードリポジトリにもありますが、こちらもに転記します。

``` uart_frame.h
#ifndef _UART_FRAME_H
#define _UART_FRAME_H

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"

typedef enum {
    IDLE = 0x00,
    WAIT_FINISH ,
    FINISH,
} frame_state_n;

extern volatile frame_state_n frame_state;

void uart_frame_init(int32_t rx, int32_t tx, int baud);
void uart_frame_send(uint8_t cmd, const uint8_t* frame, uint32_t len, bool wait_finish);
void uart_update_pin(int32_t rx, int32_t tx);

#endif
```



```uart_frame.cpp
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "string.h"
#include "esp_heap_caps.h"
#include "uart_frame.h"

#define UART_NUM UART_NUM_1
#define UART_QUEUE_LENGTH 10
#define TX_BUF_SIZE 0
#define FRAME_MAX_SIZE (16 * 1024)
#define RX_BUF_SIZE 2 * 1024

#define PACK_FIRST_BYTE 0xAA
#define PACK_SECOND_BYTE 0x55

volatile frame_state_n frame_state;
static QueueHandle_t uart_queue = NULL;
static QueueHandle_t uart_buffer_queue = NULL;

static void uart_frame_task(void *arg);
static void uart_frame_send_task(void *arg);

typedef struct _UartFrame_t {
  bool free_buffer;
  uint8_t* buffer;
  uint32_t len;
} UartFrame_t;

int32_t baud_rate = 1500000;
uint16_t uart_tx_pin = 26;
uint16_t uart_rx_pin = 36;

void __attribute__((weak)) frame_post_callback(uint8_t cmd) {

}

void __attribute__((weak)) frame_recv_callback(int cmd, const uint8_t*buf, int len) {

}

static void uart_base_init() {
  uart_driver_delete(UART_NUM);
  const uart_config_t uart_config = {
    .baud_rate = baud_rate,
    .data_bits = UART_DATA_8_BITS,
    .parity = UART_PARITY_DISABLE,
    .stop_bits = UART_STOP_BITS_1,
    .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
  };
  uart_param_config(UART_NUM, &uart_config);
  uart_set_pin(UART_NUM, uart_tx_pin, uart_rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
  uart_driver_install(UART_NUM, RX_BUF_SIZE, TX_BUF_SIZE, UART_QUEUE_LENGTH, &uart_queue, ESP_INTR_FLAG_LOWMED);
  uart_set_rx_timeout(UART_NUM, 50);
} 

void uart_frame_init(int32_t rx, int32_t tx, int baud) {
  baud_rate = baud;
  uart_rx_pin = rx;
  uart_tx_pin = tx;
  uart_buffer_queue = xQueueCreate(1, sizeof(UartFrame_t));
  uart_base_init();
  xTaskCreatePinnedToCore(uart_frame_task, "uart_queue_task", 4 * 1024, NULL, 3, NULL, 0);
  xTaskCreatePinnedToCore(uart_frame_send_task, "uart_frame_send_task", 2 * 1024, NULL, 1, NULL, 0);
}

void uart_update_pin(int32_t rx, int32_t tx) {
  uart_set_pin(UART_NUM, tx, rx, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}

typedef enum {
  kWaitStart = 0x00, 
  kRecvLenght,
  kLenghtCheck,
  kRecvCMD,
  kRecvData,
  kCrcCheck,
  kSuccess,
} frame_state_t;

static void uart_frame_send_task(void *arg) {
  UartFrame_t frame;
  
  for (;;) {
    xQueueReceive(uart_buffer_queue, &frame, portMAX_DELAY);
    uart_wait_tx_done(UART_NUM, portMAX_DELAY);
    uart_write_bytes(UART_NUM, (const char *)frame.buffer, frame.len);
    uart_wait_tx_done(UART_NUM, portMAX_DELAY);
    frame_post_callback(frame.buffer[7]);
    if (frame.free_buffer) {
      free(frame.buffer);
    }
  }
  vTaskDelete(NULL);
}

static void uart_frame_task(void *arg) {
  uart_event_t xEvent;
  // uint32_t buf_pos = 0;

  uint8_t *buf = (uint8_t *)heap_caps_calloc(FRAME_MAX_SIZE, sizeof(uint8_t),  MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
  uint8_t *recv_buf = (uint8_t *)malloc(1024 * sizeof(uint8_t));
  uint8_t* ptr = recv_buf;
  uint32_t packet_pos = 0;
  uint8_t packet_ahead[] = {PACK_FIRST_BYTE, PACK_SECOND_BYTE};
  uint32_t frame_length = 0;
  uint8_t crc_xor = 0x00;
  uint16_t size;
  buf[0] = PACK_FIRST_BYTE;
  buf[1] = PACK_SECOND_BYTE;
  frame_state_t frame_state = kWaitStart;

  for(;;) {
    if (xQueueReceive(uart_queue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) {
      switch(xEvent.type) {
        case UART_DATA: {
          uart_read_bytes(UART_NUM, recv_buf, xEvent.size, portMAX_DELAY);
          ptr = recv_buf;
          size = xEvent.size;
          while (size) {
            switch (frame_state) {
              case kWaitStart:
                buf[packet_pos] = *ptr;
                ptr += 1;
                size -= 1;

                if (buf[packet_pos] == packet_ahead[packet_pos]) {
                  packet_pos += 1;
                  if (packet_pos == 2) {
                    frame_length = 0;
                    frame_state = kRecvLenght;

                  }
                } else {
                  packet_pos = 0;
                }
                break;

              case kRecvLenght:
                buf[packet_pos] = *ptr;
                ptr += 1;
                size -= 1;
                packet_pos += 1;
                if (packet_pos == (2 + 4)) {
                  crc_xor = 0x000;
                  frame_state = kLenghtCheck;
                }
                break;

              case kLenghtCheck:
                buf[packet_pos] = *ptr;
                ptr += 1;
                size -= 1;
                crc_xor = 0x00 ^ buf[2] ^ buf[3] ^ buf[4] ^ buf[5];
                if (crc_xor == buf[packet_pos]) {
                  packet_pos += 1;
                  frame_length = (buf[2] << 24) | (buf[3] << 16) | (buf[4] << 8) | (buf[5] << 0);
                  if (frame_length > FRAME_MAX_SIZE - 9) {
                    packet_pos = 0;
                    frame_state = kWaitStart;
                  } else {
                    frame_state = kRecvCMD;
                  }
                } else {
                  packet_pos = 0;
                  frame_state = kWaitStart;
                }
                break;

              case kRecvCMD: {
                buf[packet_pos] = *ptr;
                ptr += 1;
                size -= 1;
                packet_pos += 1;
                frame_state = kRecvData;
                break;
              }
              
              case kRecvData:
                while(size > 0) {
                  buf[packet_pos] = *ptr;
                  ptr += 1;
                  size -= 1;
                  packet_pos += 1;
                  if (packet_pos == (frame_length + 6)) {
                    frame_state = kCrcCheck;
                    break;
                  }
                }
                break;

              case kCrcCheck:
                buf[packet_pos] = *ptr;
                ptr += 1;
                size -= 1;
                crc_xor = 0x00;

                for (uint16_t i = 0; i < packet_pos; i++) {
                  crc_xor = buf[i] ^ crc_xor;
                }

                if (crc_xor != buf[packet_pos]) {
                  packet_pos = 0;
                  frame_state = kWaitStart;
                  break;
                }

              case kSuccess:
                packet_pos = 0;
                frame_state = kWaitStart;
                frame_recv_callback(buf[7], &buf[8], frame_length - 2);
                break;

              default:
                break;
            }
          }
          break;
        }
        
        case UART_FIFO_OVF: {
          xQueueReset(uart_queue);
          break;
        }

        case UART_BUFFER_FULL: {
          uart_flush_input(UART_NUM);
          xQueueReset(uart_queue);
          break;
        }

        case UART_BREAK: {
          break;
        }

        case UART_PARITY_ERR: {
          break;
        }

        case UART_FRAME_ERR: {
          break;
        }

        default: {
          break;
        }
      }
    }
  }
  vTaskDelete(NULL);
}

void uart_frame_send(uint8_t cmd, const uint8_t* frame, uint32_t len, bool wait_finish) {
  uint32_t out_len = 9 + len;
  uint8_t* out_buf = (uint8_t *)malloc(sizeof(uint8_t) * out_len);

  out_buf[0] = PACK_FIRST_BYTE;
  out_buf[1] = PACK_SECOND_BYTE;
  out_buf[2] = (out_len - 7) >> 24;
  out_buf[3] = (out_len - 7) >> 16;
  out_buf[4] = (out_len - 7) >> 8;
  out_buf[5] = (out_len - 7);
  out_buf[6] = 0x00 ^ out_buf[2] ^ out_buf[3] ^ out_buf[4] ^ out_buf[5];
  out_buf[7] = cmd;
  memcpy(&out_buf[8], frame, len);
  int xor_resilt = 0x00;
  for (uint32_t i = 0; i < out_len - 1; i++) {
    xor_resilt = out_buf[i] ^ xor_resilt;
  }
  out_buf[out_len - 1] = xor_resilt;

  if (wait_finish) {
    while (uxQueueMessagesWaiting(uart_buffer_queue)) {
      vTaskDelay(pdMS_TO_TICKS(10));
    }
  }

  UartFrame_t uart_frame;
  uart_frame.buffer = out_buf;
  uart_frame.len = out_len;
  uart_frame.free_buffer = true;

  xQueueSend(uart_buffer_queue, &uart_frame, portMAX_DELAY);
}
```