LoginSignup
7
2

More than 1 year has passed since last update.

LINE Thingsでパトランプを回してみた・改

Last updated at Posted at 2019-12-02

はじめに

この記事は私のLINE API開発のデビューとなった記事、LINE Thingsでパトランプを回してみたの続編です。当時はJavaScriptやらLINE APIやらをよくわかってなかったのですが、仕事や趣味でそれぞれを触ってきたので今ならもっと踏み込んだ内容の記事をかけるかなと思ったのでちょっとバージョンアップさせてみました。

アップデート内容

以下の感じでアップデートしました

  • プレーンのJavaScriptではなくReactTypeScriptで実装
  • UIにスライダーを追加して回転速度をコントロールさせる

実際のUIや動作は以下の動画になります。
動画

今回はソフトウェアのみでハードウェア的なアップデートはございません。

React+TypeScript

このパトランプはLIFFを介してデバイスとBLE通信します。React+TypeScriptを用いたLIFFの導入方法はこちらで解説しているので参照してください。今回のソースコードはここで公開しています。次から、LIFFでのBLE通信について解説していきます。ベースは本家のコードを参考にTypeScriptに変換しています。

まずはLIFFをinitさせます。このinitに成功すればliff.initPlugins(['bluetooth'])でBLEをinitさせます。

liff.init({ liffId: process.env.REACT_APP_LIFF_ID as string }).then(() => {
  liff.initPlugins(['bluetooth']).then(() => {
    liffCheckAvailablityAndDo(() => liffRequestDevice())
  }
)})

プラグインのinitに成功すればliffCheckAvailablityAndDo(() => liffRequestDevice())に進みます。

const liffCheckAvailablityAndDo = (callbackIfAvailable: () => void) => {
  liff.bluetooth.getAvailability().then(isAvailable => {
    if (isAvailable) {
      callbackIfAvailable()
    } else {
      setTimeout(() => liffCheckAvailablityAndDo(callbackIfAvailable), 10000)
    }
  })
}

const liffRequestDevice = () => {
  liff.bluetooth.requestDevice().then(device => {
    liffConnectToDevice(device)
  })
}

コールバックが少しだけ複雑ですが、流れとしては

  1. liffCheckAvailablityAndDoliff.bluetooth.getAvailability()が成功するかを見る
  2. 成功すればliffRequestDeviceに進むことができ、失敗すれば1秒後に1.の内容を再トライする
  3. liffRequestDeviceliff.bluetooth.requestDevice()に成功するかを見る
  4. 成功すればdeviceを取得できるのでliffConnectToDevice(device)に進む

liffConnectToDevice(device)では以下のような処理をしています。

const liffConnectToDevice = (device: BluetoothDevice) => {
  device.gatt!.connect().then(() => {
    device.gatt!.getPrimaryService(process.env.REACT_APP_USER_SERVICE_UUID as string).then(service => {
      liffGetUserService(service);
    })
  })
}

ここでUSER_SERVICE_UUIDが必要になるので、REACT_APP_USER_SERVICE_UUIDという名前で.envファイルに追加しておきます。
このliffConnectToDevicedeviceからserviceの取得を行っていきます。TypeScriptだとdevice.gattunknownを返す場合があると怒られてしまうので、今回は大胆にdevice.gatt!で進めます。serviceの取得に成功したところで、liffGetUserService(service)で以下のような処理をします。

const liffGetUserService = (service: BluetoothRemoteGATTService) => {
  const CHARACTERISTIC_UUID = 'E9062E71-9E62-4BC6-B0D3-35CDCD9B027B';
  service.getCharacteristic(CHARACTERISTIC_UUID).then(characteristic => {
    window.characteristic = characteristic
    characteristic.writeValue(Buffer.from('0'))
  })
}

CHARACTERISTIC_UUIDはこの固定値で問題ありません。ここで得られたcharacteristicwindow.characteristicに登録しておくわけですが、当然TypeScriptだとそんなobjectは存在しないと怒られてしまうので以下のような感じでwindow.characteristicを定義しておきます。

declare global {
  interface Window { characteristic: BluetoothRemoteGATTCharacteristic }
}

windowオブジェクトへの登録が完了すれば一旦characteristic.writeValue(Buffer.from('0'))で初期値を送っておきます。この送る値はstringのほうがデバイス・LIFFお互いにとって楽です。とりあえずこれでcharacteristic.writeValue()というデバイスに通信させるための関数が用意できました。

スライダーで回転数を可変させる

パトランプは10bitの範囲のPWMで回転数を制御させます。LIFF的にはデバイスに0~1023を送ればいいわけです。今回は簡単のために0が送られたときはパトランプが回転しないのでランプを停止させる、それ以外の値は回転させる、といった通信方式にしました。
今回は以下のような関数を用意して

const sendValue = (value: string) => {
  if (initSuccess) window.characteristic.writeValue(Buffer.from(value))
}

以下のようなコンポーネントを作成しました。

const App: FC = () => {
  const [lightValue, setLightValue] = useState<string>('512')
  const [lightOn, setLightOn] = useState<boolean>(false)

  useEffect(() => {
    initBle(setInitSuccess)
  }, [])

  return (
    <div className="App">
      <div className="light">
        <img src={lightOn ? imgOn : imgOff} alt="signal-light" />
      </div>
      <div className="lightValue">
        {lightValue}
      </div>
      <div className="slider">
        <Slider defaultValue={512} min={200} max={1023} className="sliderBar" onChange={(_, value) => {
          setLightValue(value.toString())
        }} />
      </div>
      <div className="switch">
        <span>Off</span>
        <Switch onClick={() => {
          setLightOn(!lightOn)
          !lightOn ? sendValue(lightValue) : sendValue('0')
        }} />
        <span>On</span>
      </div>
    </div>
  );
}

Sliderのminの値を200にしたのはそれ以下の値ではパトランプが回転しないからです。またこのSliderSwitchmaterial-uiを使いました。今回はFunctionComponentを利用してスイッチのON/OFFとスライダーの値をuseStateで持たしています。

さいごに

今回でReact+TypeScriptでLINE Thingsをさせるための土台ができました。これから色々と遊べそうです。

7
2
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
2