UnitV と M5StickC で動画ストリーミングを堪能 ーエッジAI活用への道 13ー
前回はUnitVで撮影した画像をシリアルでM5StickC に転送してLCD表示いたしました。
今回はM5StickCをWebサーバにしてUnitV撮影映像をストリーミングいたしました。
目次
構成
例のごとくUnitVとM5StickCをUnitVに付属された4ピンコネクタケーブルでつなぐだけです。
UnitVで撮影した映像をjpeg圧縮してM5StickCにUART転送して、
M5StickCでWiFi配信いたします。
参考
今回はnnnさん(@nnn112358)のツイートや記事をおおいに参考にさせていただきました。
有用な情報を公開していただき本当に感謝しかありません。
- 画像データの開始データの付与などの送信方法やjpeg圧縮方法は以下を参考にしました
unitvでJpeg化してUART送信、StickCで受信して表示。
StickCのライブラリにM5.lcd.drawJpgがないので、そこを追加 pic.twitter.com/LYFHTdUDuO— nnn (@nnn112358) January 18, 2020
- WebでのストリーミングArduinoコードは以下を参考にいたしました
UnitVコード(MaixPy IDE)
UnitVで撮影jpeg画像(320×240)を10バイトの開始データと共にUART送信します。
JPEGの圧縮率は50%にしました。
転送ボーレートは1152000にしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
from machine import UART from board import board_info from fpioa_manager import fm from Maix import GPIO import sensor, lcd 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() fm.register(35, fm.fpioa.UART1_TX, force=True) fm.register(34, fm.fpioa.UART1_RX, force=True) uart = UART(UART.UART1, 1152000,8,0,0, timeout=1000, read_buf_len=4096) while(True): img = sensor.snapshot() img_buf = img.compress(quality=50) 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) uart.write(img_buf) |
M5StickCコード(Arduino IDE)
ESP32のWebサーバのサンプルコードHelloServer.ino とnnnさんのサーバコードを参考にコーディングしました。
起動後、以下にアクセスで静止画と動画をweb表示します。
http://[M5StickCのIP]/pic:静止画表示
http://[M5StickCのIP]/stream:動画表示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
#include <WiFi.h> #include <WiFiClient.h> #include <WebServer.h> #include <ESPmDNS.h> #include <M5StickC.h> const char* ssid = "WiFiのSSID"; const char* password = "WiFiのパスワード"; WebServer server(80); typedef struct { uint32_t length; uint8_t *buf; } jpeg_data_t; jpeg_data_t jpeg_data; static const int RX_BUF_SIZE = 100000; static const uint8_t packet_begin[3] = { 0xFF, 0xD8, 0xEA }; void sendPic() { int state = 1; WiFiClient client = server.client(); while(state){ if (Serial2.available()) { digitalWrite(10, LOW); uint8_t rx_buffer[10]; int rx_size = Serial2.readBytes(rx_buffer, 10); if (rx_size == 10) { 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); client.write(jpeg_data.buf, jpeg_data.length); state = 0; } } } } } void streamPic() { int state = 1; while(state){ if (Serial2.available()) { digitalWrite(10, LOW); uint8_t rx_buffer[10]; int rx_size = Serial2.readBytes(rx_buffer, 10); if (rx_size == 10) { 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); state = 0; } } } } WiFiClient client = server.client(); String response = "HTTP/1.1 200 OK\r\n"; response += "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n"; server.sendContent(response); while (1) { digitalWrite(10, HIGH); if (Serial2.available()) { digitalWrite(10, LOW); uint8_t rx_buffer[10]; int rx_size = Serial2.readBytes(rx_buffer, 10); if (rx_size == 10) { 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); } response = "--frame\r\n"; response += "Content-Type: image/jpeg\r\n\r\n"; server.sendContent(response); client.write(jpeg_data.buf, jpeg_data.length); server.sendContent("\r\n"); if (!client.connected()) break; }else{ int rx_size = Serial2.readBytes(rx_buffer, 1); break; } } if (!client.connected()) break; } } void setup() { pinMode(10, OUTPUT); M5.begin(); M5.Axp.ScreenBreath(0); Serial.begin(115200); Serial2.begin(1152000, SERIAL_8N1, 32, 33); jpeg_data.buf = (uint8_t *) malloc(sizeof(uint8_t) * RX_BUF_SIZE); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.println(""); // Wait for connection while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); if (MDNS.begin("esp32")) { Serial.println("MDNS responder started"); } server.on("/", []() { server.send(200, "text/plain", "this works as well"); }); server.on("/pic", HTTP_GET, sendPic); server.on("/stream", HTTP_GET, streamPic); server.begin(); Serial.println("HTTP server started"); } void loop() { server.handleClient(); } |
動作
念願のUnitVによる撮影映像をストリーミング配信することができました!
念願のストリーミングがでけた!#UnitV -UART-> #M5StickC <-WiFi- PCとか pic.twitter.com/pqgc3oTq1V
— HomeMadeGarbage (@H0meMadeGarbage) January 23, 2020
おわりに
さて、だいぶ回り道しましたがUnitVとの距離はかなり縮まったと思います。
次回からはやっとハイテク金魚水槽システムの実現に向けて検討を開始したいと思います。
それではまた
大変申し訳ございません。
こちらの内容に興味を持ち、参考させていただいております。
ブラウザ上で、unitVのカメラからの/stremを見ているのですが、webサーバにアクセス時にはきちんとunitVからの画像が写るのですが、(1秒~30秒)後に、画面が灰色の状態で静止します。chrome上での別のタブに切り替えwebサーバにアクセスすると再度、表示されるのですが、また数秒後には灰色の画面で静止します。原因を探ろうとしたのですが、知識不足で解決に至りませんでした。可能であればなにか教えていただけると幸いです。よろしくお願いいたします。
その現象は経験がないので検証難しいですが
何かわかりましたら報告させていただきます。
宜しくお願い致します。
返答ありがとうございます。大変感謝しております。
当たり前のことかもしれませんが、ブラウザ上の画面が灰色になるとm5stickCのLEDが点滅しなくなります。シリアル通信が原因なんですかね。
大変失礼いたしました。よろしくお願いいたします。
ありがとうございます。
点滅が止まるということはm5stickCがストリーミングを停止しているようですね。
ちょっと原因が分かりませんが
http://[M5StickCのIP]/pic:静止画表示 が複数回やってもうまくいくようであれば
UnitV-m5stickC間の通信は問題なくWiFi回りが原因かと思われます。
あとはブラウザかえてみるとかスマホからアクセスしてみるとかでしょうか。