NRIハッカソン bit.Connect 2020 に参加してきたまとめ

# はじめに
NRIハッカソン bit.Connect - Hack for NEWSTYLE　というイベントに参加してきました。
https://bitconnect.nri.co.jp/

IBMクラウドのファンクションを活用していたのを評価され、IBM賞をいただきました！

企業さんの技術サポートも手厚く、第一線の方に直接Slackで手取り足取り教えてもらえたのでめちゃくちゃ贅沢な時間を過ごせました！

# つくったもの
お地蔵さんデモ動画
https://www.youtube.com/watch?v=8mviNWsBKp8

構成図
![コンセプト_page-0016.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/2c7325d6-b59e-e492-7ca2-fec5fdf2eb15.jpeg)

ハードウェア側の動作
![ojizo_kasa_kick.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/0e82d857-c0dc-3c2b-2e99-21e7c78a4bd0.gif)

![コンセプト_page-0015.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/660f68e4-0dcb-250d-dcc1-4e56aa7573aa.jpeg)

余談ですが、最初は磁石にピップエレキバンを使ってみたんですが、意外と磁力が弱く、動作が安定しないので、100均で買った磁石に付け替えました。
![IMG_0646.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/78c85405-b07a-c708-33d1-17b4d9efaf4d.jpeg)


ストーリーなど作品の詳細はこちら
https://protopedia.net/prototype/2151

# ハードウェアの実装
今回は2人チームで参加し、私の担当がobnizを用いたハードウェアの部分だったので、実装について困ったことや役に立ったことなどをつらつらと書いていきます

IBMクラウドをはじめて触った友人（AWSは日常的に触ってる人）曰く、とても使いやすかったとのこと
・AWSみたいな迷子になりそうなUIではなく、やりたいことがどうしたらいいのかすぐわかるような素晴らしいUI
・ハッカソンで使うなら、初めてでもこっちの方が楽かもしれない
![2021-02-25_22.28.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/95514048-1511-156a-82e0-7ad23ec1d053.png)
![2021-02-25_22.28.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/b898ab30-403e-a551-ee25-2ead7dbbe520.png)


とのことなので、私も触ってみようかなと思いました。

## 構成について
今回の作品でobnizでやるべきことは以下の3点でした。
1. IBMクラウドのファンクションからAPIを叩いてハードウェアで動作をさせる
2. 1が起きてから元に戻るまでの時間を計測する
3. 2の結果をIBMクラウドのファンクションに知らせる

1~3の流れは、ユーザーがすぐ行動すれば一瞬ですが、なかなか行動しないユーザーもいると想定され、待機時間が読めません。
なので本来は、1の機能だけを請け負うobnizと2~3の機能だけを請け負うobnizの2台構成でやるべきですが、所持台数の制限と、ハッカソンという限られた時間の中で対応するために、1~3を一つのobnizで実装できるように試行錯誤を行いました。（obnizの木戸さんありがとうございました！）

## obnizクラウドでWebhookURLを吐き出す
結論から言うとこれは、今回の要件には適していなかったので他のサービスを利用しました。

[obnizクラウド](https://obniz.com/ja/products/obnizcloud) はobnizのホスティングサービスで簡単にWebhookURLを吐き出せました（今回初利用）

obniz のコンソールにアクセスし、デバイスを選択します（デバイスをまだアカウントに紐づけてない人は紐付けをしてから）
![スクリーンショット 2021-03-04 22.22.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/20f840d4-c489-8b97-5b67-e4d2253e9938.png)
![スクリーンショット 2021-03-04 22.22.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/741aada1-ca39-97fc-4db7-3ebac79cb31f.png)

いろんなテンプレートがありますが、今回は「空のプロジェクト」を選択
![スクリーンショット 2021-03-04 22.22.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/8e325a0d-4fd4-ef33-5689-b3eaa3cdf99c.png)
![スクリーンショット 2021-03-04 22.22.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/5f981ca1-1eae-3284-6a90-07b8e6c7dd1b.png)

コードをちゃちゃっと書いて

```html
<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" />
    <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
    <script src="https://unpkg.com/obniz@3.x/obniz.js" crossorigin="anonymous" ></script>
  </head>
  <body>
    <div id="obniz-debug"></div>
    <div class="container">
      <div class="text-center">
        <p id="start_date-text">開始時間 : </p><p id="start_date"></p>
        <p id="end_date-text">終了時間 : </p><p id="end_date"></p>
      </div>
    </div>

    <script>
      //type in your obniz ID
      var obniz = new Obniz("OBNIZ_ID_HERE");

      obniz.onconnect = async function () {
        // obnizクラウド上での動作かどうかの判定
        if (Obniz.App.isCloudRunning()) {
          obniz.display.print('===== start');

          // ソレノイドを0,1に繋ぐ
          var solenoid = obniz.wired('Solenoid', {gnd:0, signal:1});
          solenoid.click();

          // マグネットスイッチを9,10,11に繋ぐ
          var ct10 = obniz.wired("CT10", {gnd:9, vcc:10, signal: 11});

          myFunc = async function(){
            const start_date = new Date();
            $("#start_date").text(start_date);
            obniz.display.print("start_date");
            obniz.display.print(start_date);

            // マグネットスイッチがONになる（笠が被される）のを待つ（ハッカソンみあふれるコード）
            await ct10.stateWait(true); 

            const end_date = new Date();
            $("#end_date").text(end_date);
            obniz.display.print("end_date");
            obniz.display.print(end_date);

            const score = end_date - start_date

            obniz.display.draw(score);
          }

          // APIをキックしてすぐだと、誤作動するので若干ディレイをかけて実行
          setTimeout(myFunc, 1000);
          
          // 実際に動かしたコードではないので雰囲気が伝われば
          $.ajax({
             type: 'POST',
             // このURLはすでに無効です
             url: 'https://b3fcdcbe.us-south.apigw.appdomain.cloud/ojizo/record',
             data: {
               score: score
             },
             dataType: 'json'
           });
        }
      }
    </script>
  </body>
</html>
```

アプリの設定からブラウザ実行にチェックを入れて設定を更新します
![スクリーンショット 2021-03-04 22.25.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/9fdac0c2-faa8-ece1-0c12-22ae705a5408.png)

デバイス一覧から「Webhook URLの確認」が選択できるようになります
![スクリーンショット 2021-03-04 22.26.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/23acc124-5991-6556-0902-1b755e46c4dd.png)

Webhook URLが発行されます
![スクリーンショット 2021-03-04 22.27.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/0385bc8a-a35d-45dd-8f53-6073294e144b.png)

### 注意点
obnizクラウドではawaitをかけて待機していても、接続が最大30秒までなので今回の要件的には適さないようでした。

## 待機ができる
以上のことをobnizの木戸さんに相談したところ、 [repl.it](https://repl.it/) というサービスで実現できるかもとの情報をいただき、試してみました。
repl.itではNode.jsのホスティングが無料ででき、APIとしても公開できます。

コードをnodejs用にすこし修正し（こちら大変たすけていただきました🙇‍♂️ありがとうございました🙇‍♂️）

```js
const express = require('express');
const Obniz = require('obniz');
const fetch = require("node-fetch")
const app = express();
const port = 3000;
let solenoid = null;
let ct10 = null;
let obniz = new Obniz("xxxxxxxx");
obniz.onconnect = function(){
  console.log("connected")
  solenoid = obniz.wired('Solenoid', {gnd:0, signal:1});
  ct10 = obniz.wired("CT10", {gnd:9, vcc:10, signal: 11});
}
// トップページに来たときに
app.get('/', async (req, res) => {
 //とりあえずレスポンスは先に返す（ブラウザ対策）
 res.json({"status": "OK"});
 if (obniz.connectionState === "connected") {
    obniz.display.print('===== start');
    solenoid.click();
    const start_date = new Date();
    // $("#start_date").text(start_date);
    obniz.display.print("start_date");
    obniz.display.print(start_date);
    await ct10.stateWait(true); 
    const end_date = new Date();
    obniz.display.print("end_date");
    obniz.display.print(end_date);
    score = end_date - start_date
    // $.ajaxはnodejsで使えないのでfetchに変換
    const data = {
        user_id: obniz.id,
        start_date: start_date,
        end_date: end_date,
    };
    await fetch('https://b3fcdcbe.us-south.apigw.appdomain.cloud/ojizo/record'
    , {
      method: "POST",
      mode: 'cors',
      headers: {
      'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
  }
})
app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
});
console.log("wakeup")
```

これで10分は待機できるようになりました！

# まとめ
・ひさびさのハッカソンはすごく楽しい
・ピップエレキバンの磁力はそんなに強くない
・ハードウェアは極力シンプルな動きにしないとツラくなる（今回で言うと、やっぱどうにかobniz二台構成にしたほうがやりやすかったろうなぁ）
・IBMクラウドのファンクションはわかりやすくて便利だぞ