LoginSignup
7
7

More than 3 years have passed since last update.

M5UnitVのカメラ映像をシリアル通信でM5Stackの液晶に表示する

Last updated at Posted at 2020-12-31

はじめに

  • M5UnitVのカメラからQVGA(320 * 240)サイズの画像を撮影し、M5Stackの液晶画面に表示します。
    • UnitVは本来AIカメラですが、今回は単に外付けのカメラとして使用します。
    • M5UnitVとM5Stack(Gray)はGroveケーブルで接続し、JPEG形式で圧縮してシリアル転送します。

IMG_3310D.jpeg

参考にしたページ

本体を接続

  • 接続には、M5UnitVに付いてきたGroveケーブルを使います。
    • UnitVにはバッテリが搭載されていませんが、M5Stackから電源供給できるのでこんな感じでシンプルな構成になります。
    • 自作でここまで最適化された構成を作るのはかなり難しいので、こう言う使い方が出来るところがM5Stackの良いところですね。

IMG_3312D.jpeg

M5UnitV側のコード

  • カメラの撮影画像サイズはQVGA(320 * 240)、JPEG圧縮率はとりあえず50%。
  • Groveポートに割り当てられているGPIO34, 35を使ってシリアル通信します。
    • ポートレートは115200
    • JPEG画像データを送る前に、uart.write(data_packet)でPacketヘッダ10byteを先に送ります。
    • 先頭ビットは固定で0xxFF, 0xD8, 0xEA, 0x01、その後に画像サイズが続きます。
    • JPEG圧縮後の画像データはuart.write(img_buf)で送ります。
  • UnitVは液晶画面が無く、ちゃんと動いてるか見た目で分からないためRGB LEDでステータスを表示します。
    • class_ws2812 = ws2812(8,100)GPIO8と、(多分)明度の刻み100を指定します。
    • ブートしたら、3回青色に点滅後、点灯させます。
    • これでちゃんと動いてるかが分かるようになります。
  • このプログラムは、SDカード直下にboot.pyと言うファイル名で配置します。
    • こうしておくと、UnitVの電源がONになったタイミングでこのファイルを読み込んで実行されます。
    • SDカードの抜かずにMaixPyから直接配置できる機能があるので、こちらのページを参考にしてください。

スクリーンショット 2020-12-31 17.08.18.png

boot.py
import sensor
import time
from machine import UART
from fpioa_manager import fm
from Maix import GPIO
from modules import ws2812

# RGB LED設定
class_ws2812 = ws2812(8,100)
BRIGHTNESS = 0x10

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1)
sensor.set_hmirror(1)
sensor.run(1)
sensor.skip_frames()

# UART
fm.register(35, fm.fpioa.UART1_TX, force=True)
fm.register(34, fm.fpioa.UART1_RX, force=True)

# RGB LEDを光らせる
def RGB_LED(r,g,b):
    a = class_ws2812.set_led(0,(r,g,b))
    a = class_ws2812.display()
    time.sleep(0.3)
    a = class_ws2812.set_led(0,(0,0,0))
    a = class_ws2812.display()
    time.sleep(0.3)
    a = class_ws2812.set_led(0,(r,g,b))
    a = class_ws2812.display()

# 起動インジケータとしてblueで点滅、最後に点灯
RGB_LED(0,0,BRIGHTNESS)
RGB_LED(0,0,BRIGHTNESS)
RGB_LED(0,0,BRIGHTNESS)

uart = UART(UART.UART1, 115200,8,0,0, timeout=1000, read_buf_len=4096)

while(True):
    img = sensor.snapshot()
    # Jpeg圧縮率を上げると表示フレームレートは上がるが、綺麗さとトレードオフ
    img_buf = img.compress(quality=50)
    # Packetヘッダ
    img_size1 = (img.size()& 0xFF0000)>>16
    img_size2 = (img.size()& 0x00FF00)>>8
    img_size3 = (img.size()& 0x0000FF)>>0
    data_packet = bytearray([0xFF,0xD8,0xEA,0x01,img_size1,img_size2,img_size3,0x00,0x00,0x00])
    uart.write(data_packet)

    # imgデータ転送
    uart.write(img_buf)

M5Stack側のコード

  • M5Stack GrayのGroveポートに割り当てられているGPIOは21, 22なので、これを使ってシリアル通信します。
    • Serial2.begin(115200, SERIAL_8N1, 21, 22);でポートレートは送信側と同じ115200に合わせます。
  • loop()でデータ受信と画面表示を行います。
    • Packetデータ10byteを読み込みます。
    • 先頭バイト列がフォーマットに合ってた場合、データサイズを読み込みます。
    • Serial2.readBytes(jpeg_data.buf, jpeg_data.length);でそのサイズ分のJPEGデータを読み込みます。
    • 最後にdrawJpgでM5Stackの液晶画面に表示します。
  • drawJpgについて
    • 最初、M5.Lcd.drawJpgを使って描画したところ、30フレーム目くらいで更新がストップしてしまいました。
    • 高速表示できる事で有名なLovyanGFXに差し替える事で、ストップせずに表示し続けられるようになりました。
    • 素晴らしいライブラリを提供していただき、ありがとうございます。

スクリーンショット 2020-12-31 17.08.36.png

#include <M5Stack.h>

#define LGFX_M5STACK
#include <LovyanGFX.hpp>

static LGFX lcd;

typedef struct {
    uint32_t  length;  //Jpegデータサイズ
    uint8_t   *buf;    //Jpegデータバッファ
} jpeg_data_t;

jpeg_data_t jpeg_data;
static const int RX_BUF_SIZE = 100000;

// Packetヘッダ判別用固定バイト列
static const uint8_t packet_begin[3] = { 0xFF, 0xD8, 0xEA };

void setup() {
    M5.begin();

    Serial2.begin(115200, SERIAL_8N1, 21, 22);
    jpeg_data.buf = (uint8_t *) malloc(sizeof(uint8_t) * RX_BUF_SIZE);

    lcd.init();
    lcd.setRotation(1);
    lcd.setBrightness(255);
    lcd.setColorDepth(16);
    lcd.clear();
}

void loop() {
    if (Serial2.available()) {
        uint8_t rx_buffer[10];
        // 10byteずつ読み込む
        int rx_size = Serial2.readBytes(rx_buffer, 10);
        if (rx_size == 10) {
            // Packetヘッダのbyte列を特定
            if ((rx_buffer[0] == packet_begin[0]) && (rx_buffer[1] == packet_begin[1]) && (rx_buffer[2] == packet_begin[2])) {
                // データサイズを取得
                jpeg_data.length = (uint32_t)(rx_buffer[4] << 16) | (rx_buffer[5] << 8) | rx_buffer[6];
                Serial.println(jpeg_data.length);
                // データサイズ分バッファに読み込み
                int rx_size = Serial2.readBytes(jpeg_data.buf, jpeg_data.length);
            }

            // Jpegデータを画面に描画
            //M5.Lcd.drawJpg(jpeg_data.buf, jpeg_data.length,0, 0, 320, 240, 0, 0, JPEG_DIV_NONE);

            // LovyanGFXの方が高速
            lcd.drawJpg(jpeg_data.buf, jpeg_data.length,0, 0, 320, 240, 0, 0, ::JPEG_DIV_NONE);
        }
    }
}

実行してみる

  • ポートレート115200でQVGAサイズのJPEG50%圧縮した画像で、フレームレート5fpsくらいで結構綺麗に画像が表示されます。
    • これで液晶付きのM5StickVを買わなくても、M5UnitVをM5Stackと組み合わせる事でイケてる表示環境が手に入りますね。
    • フレームレートを上げたい場合は、JPEG圧縮率を上げると良いです。(画質はかなり落ちますが・・・)
  • このガジェット感、良いな。

IMG_3311D.jpeg

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7