MaixduinoのESP32にObniz OS(plugin込み)をいれてみた

#はじめに

MaixduinoというESP32とK210を積んだ素晴らしいボードがあったので、ESP32にObniz OSを入れてみようと思った。K210で推論した結果をObniz Cloudを介してネットにデータを吐き出せれば、できることが相当広がりそうとおもったのが動機。

#注意
まず、ここから先の内容はすべて自己責任のもとで行うこと。何かしらの理由で多重課金が発生したりボードなどの破損があっても筆者は一切の責任を負いません。

また、**obniz-cli os:flash-createコマンドを実行たびにObniz IDが発行されるため、コマンド実行ごとに150円/月の課金が発生します。** そのため、再インストール時にはobniz-cli os:createを使うこと。
(ちなみに、インストール後の翌月にOSインストール後のデバイスをネットに繋げなければ課金は発生しないはず。)

#環境
- Window10 pro(x64, BootcampにてMacbook proにインストールした)
- node.js 14.15.0 LTS (npm用にインストール)
- Arduino IDE 1.8.13
- Obnizのアカウント（クレカ登録済み）
- [Maixduino](https://www.switch-science.com/catalog/5707/)
- USB typeCケーブル

#Obniz OSをインストールする
まず、[Obniz公式Github](https://github.com/obniz/obniz-cli)の手順に沿ってObniz-cliをインストールする。

まず、MaixduinoとPCをUSBケーブルでつなぐ。obniz-cli os:portsなどでポートを確認すると、ここで２つのCOMポートが認識されるようになっているはず。このポートの一つがK210用、もう一つがESP32用。
筆者の環境では数字の大きい方がESP32用のポートだった。

まず、Obnizクラウドにサインインして、後述のコマンドを実行できるようにする。

```shell:Obnizクラウドのサインイン
obniz-cli signin
```

次に、コマンドプロンプトないしターミナルで以下のコマンドを入れてObniz OSをインストールする。新規にObniz OSをインストールする際にはここで課金が発生する。
(初めてObnizをインストールする、あるいは新しくObniz IDを発行するときのコマンド)


```shell:ObnizOSインストール
obniz-cli os:flash-create -p COMなんとか --description "Maixduino"
```

もしObniz IDを取得した後であったり、Obniz OSの再インストールをする場合はこのコマンドを実行する。

```shell:ObnizOS削除
obniz-cli os:erase
#この後ポートを指定する
```

```shell:ObnizOS再インストール
obniz-cli os:flash -p COMなんとか
```

最後にESP32の再起動を促されるが、MaixduinoのリセットボタンがESP32につながっていないようなので、USBを抜いてObniz-cliもCtrl+Cで中止して抜ける。

その後にteratermなどでObnizインストール時のCOMポートにアクセスし、Wifiなどの設定を行う。ボーレートは115200でOK。


最後に"Connected Cloud"ならびに"Online"の文字列が出力されたらブラウザからObniz Consoleにアクセスし、赤丸部分が"Active"かつ"Online"になっていればObniz OSのインストールは完了。

![Obniz Cloud](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271381/dada2846-ef12-ff22-423b-ad3d852fef0d.png)

#K210と通信・・・しかし！

[こちらの顔検出に関する記事](https://wings-sioka.info/wp/2019/10/29/maixduino%E3%81%A7%E9%A1%94%E8%AA%8D%E8%A8%BC%E3%82%92%E5%8B%95%E3%81%8B%E3%81%99/)を参考に、顔検出▶結果をUARTで送る▶Obniz Cloudに届けるいう流れを作ってみる。
推論に使ったモデルは[sipeedのファイル置き場](https://dl.sipeed.com/MAIX/MaixPy/model)にある"face_model_at_0x300000.kfpkg"を使用した。顔検出に使ったプログラムは下記。推論した結果はjson形式で表現し、文字列として送る。

```python:sample.py
import sensor
import image
import lcd
from fpioa_manager import fm
from machine import UART
from board import board_info
import KPU as kpu
import json

lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.run(1)

fm.register (6, fm.fpioa.UART1_RX)
fm.register (7, fm.fpioa.UART1_TX)
uart = UART (UART.UART1, 115200, 8, None, 1, timeout=1000, read_buf_len = 4096)


task = kpu.load(0x300000) # you need put model(face.kfpkg) in flash at address 0x300000
# task = kpu.load("/sd/face.kmodel")
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718375, 9.01025)
a = kpu.init_yolo2(task, 0.5, 0.3, 5, anchor)

while(True):
   img = sensor.snapshot()
   code = kpu.run_yolo2(task, img)
   if code:
       for i in code:
           uart.write(json.dumps(i)+'\n')
           a = img.draw_rectangle(i.rect())
   a = lcd.display(img)
a = kpu.deinit(task)
uart.deinit()
del uart
```

また、Obniz Consoleで新たなリポジトリを作って下記のプログラムを動かす。

```javascript:sample.html
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://obniz.io/js/jquery-3.4.2.min.js"></script>
  <script src="https://unpkg.com/obniz@1.16.1/obniz.js" crossorigin="anonymous"></script>
</head>
<body>

<div id="obniz-debug"></div>
<h1>obniz instant html</h1>
<button id="on">ON</button>
<button id="off">OFF</button>
<div id="print"></div>

<script>

var obniz = new Obniz("");
obniz.onconnect = async function(){
    
    var uart = obniz.getFreeUart();
  
    uart.start({tx: 1, rx: 3, baud:115200 });
    uart.onreceive = function(data, text) {
      console.log(text.charCodeAt(0));
    }

    uart.send([0,1]);
}

</script>
</body>
</html>
```

さて実行サンプルに則ってUARTにて文字列をやり取りしようとすると、GetIOに存在しないピンが〜という旨のエラーが出力されてしまう。また、推論結果の受信に関する処理はマイコンボード上で完結させてしまったほうが後々が楽というのもあり、後述のObniz pluginでUARTの受信処理を書くことにする。

#Obniz pluginでUARTを受け取る

まず、[こちらのチュートリアル](https://obniz.com/ja/doc/reference/obnizos-for-esp32/plugin/arduino-ide)に則ってArduinoでObnizを使えるようにする。

```cpp:MaixduinoObniz.ino
#include <obniz.h>
#include <string.h>

void onCommand(uint8_t* data, uint16_t length){
  Serial.printf("%s",data);
}


bool isOnline = false;

void onEvent(os_event_t event, uint8_t* data, uint16_t length) {
  switch (event) {
  case PLUGIN_EVENT_NETWORK_CLOUD_CONNECTED:
    Serial.println("cloud Connected");
    isOnline = true;
    break;
  case PLUGIN_EVENT_NETWORK_CLOUD_DISCONNECTED:
    Serial.println("cloud Disconnected");
    isOnline = false;
    break;
  case PLUGIN_EVENT_NETWORK_HARDWARE_CONNECTED:
  case PLUGIN_EVENT_NETWORK_HARDWARE_DISCONNECTED:
  case PLUGIN_EVENT_NETWORK_WIFI_SCANNING:
  case PLUGIN_EVENT_NETWORK_WIFI_NOTFOUND:
  case PLUGIN_EVENT_NETWORK_WIFI_CONNECTING:
  case PLUGIN_EVENT_NETWORK_WIFI_SOFTAP_CONNECTED:
  case PLUGIN_EVENT_NETWORK_WIFI_FAIL:
  case PLUGIN_EVENT_FAIL:
  case PLUGIN_EVENT_OTA_START:
  case PLUGIN_EVENT_OTA_END:
  case PLUGIN_EVENT_OTA_ERROR:
  case PLUGIN_EVENT_INITIALIZED:
    break;
  }
}


void setup() {
  Serial.begin(115200);

  obniz.commandReceive(onCommand);
  obniz.onEvent(onEvent);
  
  obniz.start(NULL);//NULL =  obnizOS serial not used for log

  while(!obniz.getId());
  Serial.printf("obnizID : %s\n",obniz.getId());
  Serial.printf("obnizOS : %s\n",obniz.getOsVersion());
}

uint8_t val[1024];
int i = 0;
void loop() {
  if(isOnline){

      if (Serial.available() > 0){
        val[i]= (uint8_t)Serial.read();

        if(val[i]==0 || val[i]==10){
          obniz.commandSend(val,i);
          i = 0;
        }else{
          i++;
        }
      }  
  }
}
```

このコードではSerial(UART0)から受信した値をobniz.commandSendにてプラグイン経由で送る。
このコードをArduino IDEにてコンパイル＆実行する。

環境によっては、esptool.pyが存在しない旨のエラーを吐くかもしれないが、その際にはArduinoインストール時のArduino(Arduino15)フォルダからobnizフォルダを探し、その中からesptool.pyを検索した後にエラーで催促されたフォルダにesptool.pyをコピーすればコンパイル＆実行でできるはず。

#console.logから通信確認

再びObniz Cloudでリポジトリを作成し、下記のプログラムを実行する。

```html:ObnizCheck.js
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
    />
    <link rel="stylesheet" href="/css/starter-sample.css" />
    <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
    <script
      src="https://unpkg.com/obniz@3.9.0/obniz.js"
      crossorigin="anonymous"
    ></script>
  </head>
  <body>
    
<div id="obniz-debug"></div>
<div id="print"></div>

<script>

var obniz = new Obniz("");

obniz.onconnect = async function(){
  obniz.plugin.onreceive = data => {
    console.log((new TextDecoder).decode(Uint8Array.from(data)));
  };

}

</script>
</body>
</html>
```

こんな感じにconsole画面に顔検出データが出てくればビンゴ。

![ObnizCloudへ推論結果が届いた様子](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271381/1ca36d52-52cf-f8ab-5d2b-9db14755255d.png)

#おわりに

- MaixduinoにObniz OSを入れただけでK210とのUART通信ができなかった。
- Obniz pluginを使ってK210とのUART通信ができた。
- Obniz Pluginを介してObniz Cloudまで推論結果を送ることができた。