Hello LINE Things Next

前回
[Hello LINE Things](https://qiita.com/ikegam1/items/8510961a4fb3486f7ee6)

前回はTTGOの高機能なESP32デバイス使ってみたのですが、GPIOのピンとかがなくてアウトプットのコントロールが難しそうでした。
そんなわけでまずはESP32-CAM([参考](https://qiita.com/ikegam1/items/d449ae25ef03e49888f7))に乗り換えます。

そしてひとまずLチカはやってみた。

![hnqm2-wg8z4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308709/68b6df0e-ed39-b336-1c6b-b55d3ad4fae9.gif)

## 今回やりたいこと

- LIFFアプリをdeployする
- サーボ動かす
- Botになんか通知する（できれば画像）

なんとなく作ろうと思うものはイメージできたんだけど、チュートリアル的にこなしておきたい3点です。

## その前に
### トライアルプロダクト情報を作成する
https://developers.line.biz/ja/reference/line-things/#post-trial-products

curlでやっておきます。

```bash
curl -X POST https://api.line.me/things/v1/trial/products \
-H "Authorization: Bearer {channel access token}" \
-H "Content-Type: application/json" \
-d '{
  "name": "{trial product name}",
  "liffId": "{LIFF APP ID}"
}'
```

そうすっと```serviceUuid```と```psdiServiceUuid```と```psdiCharacteristicUuid```が帰ってきました。LIFFアプリやデバイスに埋めとけばいいのかな。ちなみに後ろの二つはトライアルでは共通との事。

あとこれもやらないとLINEからうまく接続できなかった。

```
curl -X PUT https://api.line.me/things/v1/products/00000000/scenario-set \
-H "Authorization: Bearer ｘｘｘｘｘｘｘｘ" \
-H "Content-Type: application/json" \
-d '{
  "autoClose": false,
  "suppressionInterval": 0,
  "scenarios": [
    {
      "trigger": {
        "type": "BLE_NOTIFICATION",
        "serviceUuid": "xxxxxxxx",
        "characteristicUuid": "xxxxxxxx"
      },
      "actions": [
        {
          "type": "SLEEP",
          "sleepMillis": 1000
        },
        {
          "type": "GATT_READ",
          "serviceUuid": "xxxxxxxx",
          "characteristicUuid": "xxxxxxxx"
        },
        {
          "type": "GATT_WRITE",
          "serviceUuid": "xxxxxxxx",
          "characteristicUuid": "xxxxxxxx",
          "data": "xxxxxxxx"
        }
      ]
    }
  ]
}'
```

なお、作ったLINE Botとお友達になっておかないと、俺俺LIFFとはBLEでつなげません。

## LIFFアプリをdeployする

[line-things-starter](https://github.com/line/line-things-starter)をforkしてあるので、これを拡張する感じです。

- サーボ用にスライダーをつける
- LEDボタンはカメラボタンにする

### line-things-starterのliff.jsをみてみる

これは```Switch LED ON```が押された時に走る。
そして```liffToggleDeviceLedState```でデバイスにLEDのstatusが送られる

```js:liff.js
// ----------------- //
// Handler functions //
// ----------------- //

function handlerToggleLed() {
    ledState = !ledState;

    uiToggleLedButton(ledState);
    liffToggleDeviceLedState(ledState);
}
```

ここも大した事やってないな。```ledCharacteristic``` ってやつをみないと

```js:liff.js
function liffToggleDeviceLedState(state) {
    // on: 0x01
    // off: 0x00
    window.ledCharacteristic.writeValue(
        state ? new Uint8Array([0x01]) : new Uint8Array([0x00])
    ).catch(error => {
        uiStatusError(makeErrorMsg(error), false);
    });
}
```

```liffGetUserService``` というのがdeviceが繋がった時に呼ばれてて、
```device.gatt.getPrimaryService```ってやってデバイス側のサービスを取得するんかな？
そして```service``` っていうのが取得されて、そのserviceはcharacterをwriteするメソッドが生えてるみたいな。
さらに詳しくはhttps://d.line-scdn.net/liff/1.0/sdk.js を見ないといけない。

```js:liff.js
function liffGetUserService(service) {
    // Button pressed state
    service.getCharacteristic(BTN_CHARACTERISTIC_UUID).then(characteristic => {
        liffGetButtonStateCharacteristic(characteristic);
    }).catch(error => {
        uiStatusError(makeErrorMsg(error), false);
    });

    // Toggle LED
    service.getCharacteristic(LED_CHARACTERISTIC_UUID).then(characteristic => {
        window.ledCharacteristic = characteristic;

        // Switch off by default
        liffToggleDeviceLedState(false);
    }).catch(error => {
        uiStatusError(makeErrorMsg(error), false);
    });
}
```

なわけで、characterをwriteする方法はわかった。そしてキャラクターはUint8_Arrayっぽいな。簡単にやるなら0〜255の範囲で表現するのがいいのか。
で、Arrayなので0はボタン、1はサーボ1にみたいに決め打ちしよう。
なので```handlerServoOne(angle)```を追加して、最終的に```ledCharacteristic.writeValue```みたいなのでdeviceに送信できれば良しとします。


## そして結局。。

![spo0m-fhntf.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308709/f64302b8-7685-6efa-db57-998f619e2584.gif)
ごちゃごちゃしててすみません。

1. ESP32-CAMからサーボを動かす事ができなかった
2. LINEに通知は時間切れ(notify-apiとやらを使うつもりではあった)
3. 苦肉の策で別のarduinoをシリアル接続してサーボ動かした

1はなんでかわからない。GPIOの12とか13とか2とかでLED光らせたり、ボタンを拾ったりはできてるんだが、サーボだけ動かない。サーボは別電源にしてるにもかかわらず。わからずギブアップ。
あとLIFFでスライダーはうまく動かなかった。LINEから起動するブラウザってwebviewかなんかだっけか。スライダー(input type=range)はちょっと早いのかもしれない。
そして雑で中途半端だけどとりあえずの成果は(結果的に)githubに上がってます。
https://github.com/ikegam1/line-things-starter/tree/master/liff-app
(そういやUSER_SERVICE_UUIDとかめっちゃ埋め込まれてるけど問題ないやろか。知ってるからなんかできるってようなもんではないと思うが。今回はデモアプリだし良しとする。）

あとESP32の方はというと、こういう感じ。
結果、サーボについてはSerial.writeで数値を送ってるだけです。

```cpp
class writeCallback: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *bleWriteCharacteristic) {
    std::string value = bleWriteCharacteristic->getValue();
    char* c_str = const_cast<char*>(value.c_str());
    int c1 = (unsigned char)value[0] - '0';
    int c2 = (unsigned char)value[1] - '0';
    //camera action
    if (c1 > 0) {
      //Serial.println("Camera action..");
      digitalWrite(LED1, HIGH);
      delay(50);
      digitalWrite(LED1, LOW);
      delay(50);
    }
    //servo action
    if (c2 < 10) {
      int angle = map( c2, 0, 9, 0, 180 ); 
      // set the servo position
      Serial.write(angle);
      // wait for the servo to get there
      delay(15);
    }
  }
};
```

そして受け側のarduinoは

```cpp
// include the Servo library
#include <Servo.h>

Servo myServo;  // create a servo object

int angle;   // variable to hold the angle for the servo motor

void setup() {
  myServo.attach(13); // attaches the servo on pin 9 to the servo object
  Serial.begin(9600); // open a serial connection to your computer

  angle = map(0, 0, 9, 0, 179);
  myServo.write(angle);
  delay(10);
}

void loop() {
  while(Serial.available()){
    byte inChar = (byte)Serial.read();
    if(inChar >= 0){
      angle = map(inChar, 0, 9, 0, 179);
      Serial.print(", angle: ");
      Serial.println(angle);
      myServo.write(angle);
    }
    delay(10);
  }
}
```

めちゃくちゃプレーンなやつ。ほぼサンプルスケッチそのままです。
こいつらがBLEで繋がったりするとちょっとかっこいいかもな。


というわけでめっちゃ中途半端なのですが、時間切れ。サーボ動かすのでめっちゃ詰まった。ESP32-CAMでなんでサーボが動かせないか、知ってる人いたら教えてください。
そして、あまり一つの記事に時間をかける主義ではないので公開して一回締めます。
LINE Things関連はきっとまた書く。