ESP32など

電子工作してます

Grove接続でTimer Cameraで撮影した画像をM5StackのLCDに表示する(少しだけ高速化)

概要

  • 前回、Grove接続(シリアル)で受け取ってLCDに表示できるようにしたが、受信側のM5Stackの処理について単純に(1)データ受け取る→(2)LCD表示→(1)データ受け取る→・・・と順番に繰り返していたので、(1)と(2)で待ち時間が両方独立して必要だった。
  • 今回は、バッファを持たせることで(1)と(2)の待ち時間を重複させてちょっとだけ早くする。

M5Stack表示

コード

送信側:Timer Camera / Unit Cam

前回と同じ

#include "esp_camera.h"

void setup() {

  // USBシリアル通信(確認用)
  Serial.begin(115200);

  // GROVEのシリアル通信
  Serial1.begin(115200, SERIAL_8N1, 13, 4); // Timer Camera / Unit Cam

  // カメラ用パラメータ(Timer Camera / Unit Cam)
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer   = LEDC_TIMER_0;
  config.pin_d0       = 32;
  config.pin_d1       = 35;
  config.pin_d2       = 34;
  config.pin_d3       = 5;
  config.pin_d4       = 39;
  config.pin_d5       = 18;
  config.pin_d6       = 36;
  config.pin_d7       = 19;
  config.pin_xclk     = 27;
  config.pin_pclk     = 21;
  config.pin_vsync    = 22;
  config.pin_href     = 26;
  config.pin_sccb_sda = 25;
  config.pin_sccb_scl = 23;
  config.pin_pwdn     = -1;
  config.pin_reset    = 15;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG; // jpeg出力
  config.frame_size   = FRAMESIZE_QVGA; // 320x240
  config.jpeg_quality = 30; // jpeg品質 0(高品質)~63(低品質)
  config.fb_count     = 2; // CAMERA_GRAB_LATEST設定時は2以上必要
  config.grab_mode    = CAMERA_GRAB_LATEST; // 最終フレームバッファだけを取得
  config.fb_location  = CAMERA_FB_IN_PSRAM; // PSRAM使用する(Timer Camera用)
//  config.fb_location  = CAMERA_FB_IN_DRAM; // PSRAM使用しない(Unit Cam用)

  // カメラ初期化
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
      Serial.printf("Camera init failed with error 0x%x", err);
      return;
  }

  // 画像反転
  sensor_t *s = esp_camera_sensor_get();
  s->set_vflip(s, 1);
}

void loop() {

  // 画像取得&送信
  camera_fb_t *fb = esp_camera_fb_get();
  if (fb) {

    // データ区切り用文字列(DATA)
    Serial1.print("DATA");

    // サイズ送信
    Serial1.write((byte *)&(fb->len), 4); // size_t(int) -> byte[4]で送信

    // データ送信
    Serial1.write(fb->buf, fb->len);

    // フレームバッファ開放
    esp_camera_fb_return(fb);
  }
}
受信側:M5Stack

バッファを2つ用意して、空いてる方に入れる&入ってる方を表示するだけ。

#include <M5Stack.h>

uint8_t buf[4];
uint8_t buf1[10000];
volatile size_t data_size1 = 0;
uint8_t buf2[10000];
volatile size_t data_size2 = 0;

void setup() {

  M5.begin();

  // USBシリアル通信(確認用)
  Serial.begin(115200);

  // GROVEのシリアル通信
  Serial1.begin(115200, SERIAL_8N1, 21, 22); // PORT-A(Red)(GPIO21, GPIO22)

  // マルチタスク
  xTaskCreatePinnedToCore(subProcess, "subProcess", 4096, NULL, 1, NULL, 1);
}

void loop() {

  if (Serial1.available() > 4){
    
    // データ区切り用文字列(DATA)
    if (Serial1.read() == 'D') {
      if (Serial1.read() == 'A') {
        if (Serial1.read() == 'T') {
          if (Serial1.read() == 'A') {
 
            // サイズ取得
            Serial1.readBytes(buf, 4);
            size_t temp = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24 ; // byte[4] -> size_t(int)

            // データ取得
            while (1) {
              if (data_size1 == 0) {
                Serial1.readBytes(buf1, temp);
                data_size1 = temp;
                break;
              }
              else if (data_size2 == 0) {
                Serial1.readBytes(buf2, temp);
                data_size2 = temp;
                break;
              }
            }
            
          }
        }
      }
    }
  }
}

// 別タスク
void subProcess(void * pvParameters) {
  // 画面表示
  while (1) {
    if (data_size1 != 0) {
      M5.Lcd.drawJpg(buf1, data_size1, 0, 0, 320, 240);
      data_size1 = 0;
    }
    else if (data_size2 != 0) {
      M5.Lcd.drawJpg(buf2, data_size2, 0, 0, 320, 240);
      data_size2 = 0;
    }
  }
}

結果

  • ちょっとだけ早くなった感じはする。