obniz と kintone で IoT 連携してみよう!

著者名:大竹 遼(サイボウズ株式会社)

目次

はじめに

ハードウェア(IoT 機器)と kintone の連携 Tips はすでにいくつかありますが、そのほとんどがハードウェア側の知識も必要なことが多いです。
ハード側で利用する言語も C や Java など Web とは異なる言語を利用することが多いため、その点でも Web エンジニアからは敷居が高かったりします。

obniz(オブナイズ) (External link) は JavaScript でプログラムを書くことができ、Web の知識でハードウェアの操作が可能なデバイス(IoT プラットフォーム)です。

JavaScript でカスタマイズできる kintone とはいろいろと相性が良く、今回はその obniz と kintone を連携させて 物理ボタンクイズ を実現してみます。

obniz とは

現実とコンピューター世界をつなぐデバイスやクラウドを提供しているオールインワンの IoT プラットフォームです。
obniz board と呼ばれるデバイスの他に、obniz OS という市販の開発デバイスにインストールすることで obniz として扱うことが可能な OS も提供しています。

特徴はなんといっても クラウドで動かすことができる ところです。
従来の「デバイスにプログラムを書き込む」タイプではなく、obniz ではプログラムをクラウドに配置します。
配置したプログラムは Wi-Fi 経由で obniz に読み取られ、動作するというしくみになっています。

そのため Web エンジニアでも簡単にハードウェアをいじることができるのでとてもおすすめです!

構成

obniz に接続した物理ボタンを押すと、ブラウザーが切り替わるしくみでクイズを作っています。
ポイントは obniz のプログラムを kintone 上にアップしている 点です。
obniz のプログラムはクラウド上に配置すればよく、クラウド製品の kintone 上に配置しても動かすことができます。

さらに、kintone 上に配置することで kintone が用意している kintone JavaScript API も一緒に利用できるため、かなり連携がしやすくなります!

実際の動き(デモ)

まるでマウス操作をしているかのように、物理ボタンで kintone が操作できます!
また、同アプリに正誤率を貯めるようにもしているので、あとで「どの問題が難しいか」なども把握できます。

kintone の設定

クイズ出題アプリ、クイズマスターアプリの 2 つのアプリを用意しています 。

クイズ出題アプリ

正誤の情報を登録するアプリです。
アプリの構成は以下のようになっています。

フィールド名 フィールドタイプ フィールドコード
クイズレコードURL 文字列(1行) url
問題 文字列(1行) question
正誤 ドロップダウン result
作成日時 作成日時 CreateTime

さらにこちらのアプリには、カスタマイズビューとして「出題ビュー」「正解ビュー」「不正解ビュー」を作成しています。
カスタマイズビューの HTML については プログラム で説明します。

クイズマスターアプリ

アプリの構成は以下のようになっています。

フィールド名 フィールドタイプ フィールドコード
問題 文字列(1行) question
選択肢1 文字列(1行) choice1
選択肢2 文字列(1行) choice2
選択肢3 文字列(1行) choice3
選択肢4 文字列(1行) choice4
正解 文字列(1行) answer

本当は正解フィールドはラジオボタン等の選択式にしたかったですが、今回はコードのわかりやすさ重視で文字列 1 行フィールドとしています。

プログラム

今回作成した JavaScript、CSS、カスタマイズビュー用 HTML は以下となります。

script.js

  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
/*
 * kintone-obniz sample program
 * Copyright (c) 2019 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
 */

(() => {
  'use strict';
  // クイズマスタアプリのアプリID
  const quizAppId = '{YOUR_QUIZ_MASTER_APP_ID}';

  // クイズ出題アプリ(自身)のアプリID
  const thisAppId = kintone.app.getId();

  // 出題・正解・不正解のカスタマイズビューID
  const view = {
    index: '{YOUR_INDEX_VIEW_ID}',
    success: '{YOUR_SUCCESS_VIEWP_ID}',
    fail: '{YOUR_FAIL_VIEW_ID}',
  };

  // obnizのID
  const obnizId = '{YOUR_OBNIZ_ID}';

  // クイズの正誤をレコード登録する関数
  const postRecord = (RECORD, TEXT) => {
    const body = {
      app: thisAppId,
      record: {
        url: {
          value: `https://${document.domain}/k/${quizAppId}/show#record=${RECORD.$id.value}`,
        },
        question: {
          value: RECORD.question.value,
        },
        result: {
          value: TEXT,
        },
      }
    };
    return kintone.api(kintone.api.url('/k/v1/record', true), 'POST', body)
      .then(resp => console.log('レコードの登録成功')).catch(err => window.alert('レコードの登録失敗'));
  };

  // クイズマスタアプリからクイズ内容を取得する関数
  const getQuizRecords = () => {
    return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', {app: quizAppId})
      .then(resp => console.log('レコード取得成功')).catch(err => window.alert('レコードの取得失敗'));
  };

  // クイズ出題ビューを開いたとき
  kintone.events.on('app.record.index.show', e => {
    if (e.viewId !== view.index) return;

    const obniz = new Obniz(obnizId);
    obniz.onconnect = async () => {
      obniz.display.clear();

      // クイズ内容のレコードを取得
      const QuizData = await getQuizRecords();

      // クイズ内容の中からランダムで1つ表示
      const random = Math.floor(Math.random() * QuizData.length);
      const answer = QuizData[random].answer.value;

      // 問題文を表示するDOM
      const $questionTextArea = $('.question-text');
      // 解答の選択肢を表示するDOM
      const $answerTextArea = $('.answer-text');

      // それぞれのDOMにテキスト表示
      $questionTextArea.text(QuizData[random].question.value);
      $answerTextArea.each((index, val) => {
        $(val).text(QuizData[random]['choice' + (index + 1)].value);
      });

      // obnizのボタンの設定
      const buttons = [
        obniz.wired('Button', {signal: 0, gnd: 1}),
        obniz.wired('Button', {signal: 2, gnd: 3}),
        obniz.wired('Button', {signal: 4, gnd: 5}),
        obniz.wired('Button', {signal: 6, gnd: 7}),
      ];

      // それぞれのボタンにイベントをつける
      buttons.forEach((btn, index) => {
        btn.onchange = async pressed => {
          if (!pressed) return;

          // 枠の色を変える
          $($answerTextArea[index]).css({
            borderColor: '#ffff00'
          });

          // 正解なら「正解」とレコード登録して、正解ビューへ遷移
          // 不正解なら「不正解」とレコード登録して、不正解ビューへ遷移
          if (answer === QuizData[random]['choice' + (index + 1)].value) {
            await postRecord(QuizData[random], '正解');
            location.href = `/k/${thisAppId}/?view=${view.success}`;
          } else {
            await postRecord(QuizData[random], '不正解');
            location.href = `/k/${thisAppId}/?view=${view.fail}`;
          }
        };
      });
    };
  });

  // 正解・不正解ビューを開いたとき
  kintone.events.on('app.record.index.show', e => {
    if (e.viewId !== view.success && e.viewId !== view.fail) return;

    // 3秒後にクイズ出題ビューへ遷移
    setTimeout(() => {
      location.href = `/k/${thisAppId}/?view=${view.index}`;
    }, 3000);
  });
})();

style.css

 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
/*
 * kintone-obniz sample program
 * Copyright (c) 2019 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
 */

/* index html */
#myDiv {
  text-align: center;
  font-weight: bold;
  font-size: 20px;
}
.question-frame,
.answer-frame {
  margin: 2%;
}
.question-top {
  font-size: 50px;
}
.question-text {
  font-size: 40px;
}
.answer {
  margin: 0 auto;
}
.answer tr {
  height: 200px;
}
.answer tr td div {
  display: inline-block;
  border: solid 8px #ccc;
  word-break: break-all;
  height: 100px;
  vertical-align: middle;
  display: table-cell;
  padding: 0 10% 0 20%;
  width: 250px;
}
.td-space {
  width: 80px;
}

/* success/fail html */
#resultDiv {
  text-align: center;
}
#resultDiv img {
  width: 50%;
}

出題ビュー

 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
<!--
* kintone-obniz sample program
* Copyright (c) 2019 Cybozu
* 
* Licensed under the MIT License
* https://opensource.org/license/mit/
-->

<div id="myDiv">
  <div class="question-frame">
    <span class="question-top">Q. </span>
    <span class="question-text"></span>
  </div>
  <div class="answer-frame">
    <table class="answer">
      <tr>
        <td><img class="answer-img" src="{ボタンAの画像のURLなど}"></td>
        <td><div class="answer-text"></div></td>
        <td class="td-space"></td>
        <td><img class="answer-img" src="{ボタンBの画像のURLなど}"></td>
        <td><div class="answer-text"></div></td>
      </tr>
      <tr>
        <td><img class="answer-img" src="{ボタンCの画像のURLなど}"></td>
        <td><div class="answer-text"></div></td>
        <td class="td-space"></td>
        <td><img class="answer-img" src="{ボタンDの画像のURLなど}"></td>
        <td><div class="answer-text"></div></td>
      </tr>
    </table>
  </div>
</div>

正解ビュー

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!--
* kintone-obniz sample program
* Copyright (c) 2019 Cybozu
* 
* Licensed under the MIT License
* https://opensource.org/license/mit/
-->

<div id="resultDiv">
  <img src="{正解画面に挿入する画像のURLなど}">
</div>

不正解ビュー

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!--
* kintone-obniz sample program
* Copyright (c) 2019 Cybozu
* 
* Licensed under the MIT License
* https://opensource.org/license/mit/
-->

<div id="resultDiv">
  <img src="{不正解画面に挿入する画像のURLなど}">
</div>

プログラムの配置

jQuery と obniz のライブラリ、上記 script.js を以下の順番で配置します。

順番 詳細 URL / JSファイル
1 jQuery https://js.cybozu.com/jquery/3.4.1/jquery.min.js
2 obniz のライブラリ https://unpkg.com/obniz@2.0.3/obniz.js
3 上記JSファイル script.js

おわりに

まるで JavaScript のライブラリのように obniz(ハードウェア)が操作できます!
obniz 側のプログラム次第では LED を光らせたり、 サーボモータを動かしたりできるので、kintone との連携の夢が広がりますね!

information

この Tips は、2019 年 7 月版 kintone で動作を確認しています。