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で動作を確認しています。