twilio
obniz

Twilioとobnizでポケベルを作る


はじめに

これは、2019年3月25日に開催されたSIer IoTLTで発表したネタ記事です。

https://iotlt.connpass.com/event/122499/

このネタを書くきっかけになったのは、以下の記事を拝見したからです。

【114106→アイシテル】平成の最後に最新のWebRTCでポケベルを作ってみた【体験編】

今年9月にいよいよポケベルが終わってしまうということで、ポケベルを知らない世代にもポケベルを知ってもらおうという趣旨ですね。

とても素敵です。

でもですね、電話と言ったらTwilioじゃないですか。

で、居ても立ってもいられなくなり、Twilioでポケベルを作ってみました。

肝心の配信先(ポケベル)はというと、IoT業界では有名なみんな大好きobnizを使わせてもらいました。


準備するもの


obniz

index-obniz-front.png

いわゆるマイコンボードなのですが、WiFiやBLEがついていて、簡単なディスプレイもあります。さらに、WiFiにさえ繋いでしまえば、外部からもアクセスが可能なので、今回のような使い方はとても向いています。

公式サイト経由でも購入が可能(Amazonで5,980円)ですので、ぜひ1台持っていると便利ですよ。


圧電ブザー

圧電ブザー.jpg

なくても構いませんが、本当のポケベルでもメッセージを着信したときにはブザー音がなるので、あるとよりリアルになります。

基本ほとんどの圧電ブザーが使えるはずですが、今回は秋葉原のマルツで調達しました(税込み302円)。秋葉原まで行けない方は、マルツオンラインでも購入可能です。


Twilioアカウント

今回は着信がメインとなりますので、Twilioのトライアルアカウントでも動作します。ただし、トライアルアカウントのポイントがなくなった時点で使えなくなりますので、継続して使いたい場合はアカウントのアップグレードをお願いいたします。

また、トライアルアカウントでは、電話をかけたときにトライアルアカウントである旨のガイダンスが流れます。

トライアルアカウントの作成は、こちらから行います。


obniz側の設定


圧電ブザーの取り付け

もし圧電ブザーをお持ちの方は、電源が刺さっていない状態で以下の作業を行います。


  • 圧電ブザーをお持ちの方は、ブザーの+(プラス)信号線(おそらく赤い方)をobnizの1番ピンに接続します。

  • 同じくー(GND)信号線(おそらく黒色)をobnizの0番ピンに接続します。

s_IMG_5616.JPG


マイページへの登録

もしobnizの初期設定が完了していない方は、こちらの記事を参考にセットアップを終わらせておいてください。

また、今回はobnizクラウドを利用しますので、こちらのページから購入したobnizをマイページ上に登録しておいてください。

obnizマイページ (1).png


AccessTokenの生成

先程書いたように、obnizはWiFiに接続するだけで外部からアクセスが可能になります。便利な半面、8桁のID(obniz ID)がわかれば、誰でも外部から接続できてしまうため、セキュリティ上の問題があります。

そのため、それぞれのobnizにAccessTokenを設定し、外部からのアクセス時にobniz IDと併せてAccessTokenも指定することでセキュリティを強化できます。

AccessTokenを生成したobnizについては、以後AccessTokenも一緒に設定しなければエラーになりますので注意してください。


  • obnizのマイページにログインします。

  • 登録したobnizの設定ボタンを押します。


  • Access Controlを選択し、Generate Access Tokenを押します。

obniz_APIKey_Before.png


  • AccessTokenが生成されるので、生成された文字列をメモ帳などに控えておきます。なお、画面には文字列の一部しか表示されていないので、かならずすべての文字列をコピーしておきます。

obniz_APIKey_After.png


WebAppの作成

次に、obnizクラウド上にWebアプリケーションを作成します。Webアプリケーションといっても、obniz上でホストされるHTMLページです。


  • obnizのマイページにログインします。


  • リポジトリ新規作成を押します。


  • 新しいプログラムダイアログが開くので、タイプは「WebApp」を選択、アクセスレベルは「公開」、ファイル名には「getCode」と入力して作成ボタンを押します。

スクリーンショット 2019-03-26 16.19.54.png


  • 表示されるHTMLをすべて削除し、以下のHTMLを貼り付けます。

<html>

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
<script src="https://unpkg.com/obniz@2.0.1/obniz.js" crossorigin="anonymous"></script>
</head>
<body>

<div id="obniz-debug"></div>

<script>
let request = {};
if (typeof req === "object") {
request = req;
}
console.log(request.body);

const dayOfWeek = new Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
const month = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");

const now = new Date();
const date = `${dayOfWeek[now.getDay()]} ${now.getDate()} ${month[now.getMonth()]} '${String(now.getFullYear()).slice(2)}`;
const time = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}\n`;

const obniz = new Obniz("[XXXX-XXXX]", {access_token: "[AccessToken]"});
obniz.onconnect = async () => {

// 圧電スピーカーが用意できているかたはこの後のコメントブロックを解除すると音がでます。
/*
const speaker = obniz.wired("Speaker", {signal: 1, gnd: 0});
speaker.play(440);
await obniz.wait(500);
speaker.stop();
*/

obniz.resetOnDisconnect(false);
obniz.display.clear();
obniz.display.print(date);
obniz.display.print(time);
obniz.display.print(request.body.message);

}
</script>
</body>
</html>

スクリーンショット 2019-03-26 16.31.20.png


  • 26行目の[XXXX-XXXXX]の部分をご自分のobniz IDに書き換えます。

  • 同じく26行目の[AccessToken]を、先程保存しておいたAccessTokenに書き換えます。

  • ブザーをお持ちの方は、途中のコメントアウト(/*と*/)を削除してください。

ここでobnizの電源を投入し、WiFiに接続されていることを確認します。


  • 右上にある実行ボタンを押します。

  • この時点ではメッセージを受け取っていないので、以下のようなエラー画面が表示されます。

  • ブザーを装着している場合は、ブザー音も鳴ります。

obniz_execute_error.png

これでポケベル部分はできました。

では次に、今作成したWebアプリケーションをTwilioから呼び出す際のWebhookを設定します。


  • obnizのマイページにログインします。


  • イベント新規作成を押します。

obniz_mypage_event.png



  • 名前欄には、「displayMessage」、実行するWebAppは、先ほど作成した「getCode.html」、きっかけは「Webhook」を選びます。

スクリーンショット 2019-03-27 12.34.39.png



  • 作成ボタンを押して設定を保存します。


  • WebhookEndpointが払い出させるので、URLを保存しておきます。


Twilio側の処理

今回、Twilioは以下の処理をします。


  1. 050番号に着信する。

  2. ガイダンスを流す。

  3. キーを受け付ける。

  4. 受け付けたキーを文字に変換して、obnizを呼び出す。

1について、電話番号を購入する必要があります。

4については、Twilio Functionsを使ってプログラミングをします。

2と3については、Twilio Studioを使います。


電話番号を購入する

すでに電話番号を購入している人は、その番号を使うこともできます。

もしまだ購入していない人は、以下の手順で番号を購入します。


  • ブラウザでログイン画面を表示し、ご自分のIDとパスワードでログインをします。

  • 管理コンソールの左側にあるボタンアイコンを押すと、スライドメニューが表示されます。

002 (1).png


  • スライドメニューの一覧から、電話番号を選択します。

005 (1).png

- Phone Numbersメニューの中のBuy a Numberを選択します。

- 国のプルダウンから「Japan(+81)」を選択し、検索ボタンを押します。

006.png


  • 一覧表示されたリストの中から、TYPEがローカルになっている(108円)の番号を一つ選び、購入ボタンを押します。

  • この番号を購入しますか?というダイアログが出たら、この番号を購入する。を押します。

  • Congratulationのダイアログが表示されたら、購入完了です。閉じるボタンを押します。

  • Phone Numbersの中のManage Numbersを選択し、今購入した電話番号が表示されることを確認します。


Functionsでobnizを呼び出す部分を作成する



  • DEVELOPER TOOLSの中にあるRuntimeを選択します。

  • その中のFunctionsを選択し、さらにConfigureを選びます。


  • Dependenciesの設定内にある+アイコンを1回押して、枠を追加します。


  • NAME欄に「request-promise」、VERSION欄に「4.2.4」と入力します。

  • 「Twilio」のVERSIONを「3.25.0」に変更します。

スクリーンショット 2019-03-27 13.59.00.png



  • Saveボタンを押して、設定を保存します。


Twilio Functionsの作成

つぎに、電話口で入力された数字をカタカナに変換して、obnizクラウドのWebhookを呼び出すためのFunctionを作ります。

ポケベルの変換表は機種によって若干の違いがあるようですが、今回はこちらの変換表を利用しました(小文字には対応していません)。


  • Twilioの管理コンソールで、先ほどと同じようにRuntimeFunctionsを選択します。

  • さらにManageを選択し、Create a new Functionもしくは、赤い+アイコンを押して、新しいFunctionを作成します。

  • テンプレートを選択するダイアログが表示されるので、Blankを選択した後、Createボタンを押します。

スクリーンショット 2018-08-08 21.56.20.png



  • FUNCTION NAME欄に「PostMessage」と入力します。


  • PATH欄に「/post-message」と入力します。


  • CODE欄に予め書かれているコードをすべて削除し、以下のコードを貼り付けます。

const rp = require('request-promise');

exports.handler = function(context, event, callback) {
// コードを受け取る
const code = event.code || '';

// 変換表
const pocketbellTable = {
"11":"ア", "12":"イ", "13":"ウ", "14":"エ", "15":"オ", "16":"A", "17":"B", "18":"C", "19":"D", "10":"E",
"21":"カ", "22":"キ", "23":"ク", "24":"ケ", "25":"コ", "26":"F", "27":"G", "28":"H", "29":"I", "20":"J",
"31":"サ", "32":"シ", "33":"ス", "34":"セ", "35":"ソ", "36":"K", "37":"L", "38":"M", "39":"N", "30":"O",
"41":"タ", "42":"チ", "43":"ツ", "44":"テ", "45":"ト", "46":"P", "47":"Q", "48":"R", "49":"S", "40":"T",
"51":"ナ", "52":"ニ", "53":"ヌ", "54":"ネ", "55":"ノ", "56":"U", "57":"V", "58":"W", "59":"X", "50":"Y",
"61":"ハ", "62":"ヒ", "63":"フ", "64":"ヘ", "65":"ホ", "66":"Z", "67":"?", "68":"!", "69":"-", "60":"/",
"71":"マ", "72":"ミ", "73":"ム", "74":"メ", "75":"モ", "76":"¥", "77":"&", "78":"", "79":"☎", "70":"",
"81":"ヤ", "82":"(", "83":"ユ", "84":")", "85":"ヨ", "86":"*", "87":"#", "88":" ", "89":"♥", "80":"",
"91":"ラ", "92":"リ", "93":"ル", "94":"レ", "95":"ロ", "96":"1", "97":"2", "98":"3", "99":"4", "90":"5",
"01":"ワ", "02":"ヲ", "03":"ン", "04":"゙", "05":"゚", "06":"6", "07":"7", "08":"8", "09":"9", "00":"0"
};

let message = '';
for (let i=0; i<code.length; i+=2) {
let str = code.substr(i, 2);
if (pocketbellTable[str]) {
message += pocketbellTable[str];
}
}

const options = {
uri: '[ここにobnizクラウドのイベントWebhookのURLを指定します]',
method: 'POST',
form: {
message: message
},
headers: {
}
};

rp(options)
.then((body) => {
callback(null, 'OK');
})
.catch((err) => {
callback(err);
});
};


  • 29行目の[ここにobnizクラウドのイベントWebhookのURLを指定します]の部分を、先程控えておいたobnizクラウドのイベントで払い出されたURLに置き換えます。


  • Saveボタンを押します。

  • デプロイが自動で実行され、しばらくするとデプロイ完了の緑色のバナーが表示されれば完成です。


Twilio Studioで着信フローを作成する


フローの読み込み


  • 管理コンソールの左側のスライドメニューから、Studioを選択します。

  • 赤いプラスアイコンをクリックするか、Create a new flowを選択して、新しいフローを作成します。


  • FLOW NAMEに「PostMessage」と入力して、NEXTボタンを押します。


  • New Flowダイアログが表示されるので、Import from JSONを選択して、Nextボタンを押します。

スクリーンショット 2019-01-07 16.57.36.png



  • New Flowダイアログが開きますので、1行目の{}を削除して、以下のJSONを貼り付けます。

{

"description": "Display Message",
"states": [
{
"type": "InitialState",
"name": "Trigger",
"properties": {
"offset": {
"x": 0,
"y": 0
},
"flow_url": "https://webhooks.twilio.com/v1/Accounts/ACxxxxx/Flows/FWxxxxx"
},
"transitions": [
{
"event": "incomingMessage",
"conditions": [],
"next": null,
"uuid": "5dcdc231-0b7c-42ab-8f11-68a8f5bf8997"
},
{
"event": "incomingCall",
"conditions": [],
"next": "FFff25f5b57237b689a4839d18d9b7caf9",
"uuid": "e3f190f6-1aa6-4c87-b2bf-770fb6314163"
},
{
"event": "incomingRequest",
"conditions": [],
"next": null,
"uuid": "f05c85d0-9a6a-47e3-a6a0-575cf5d803fe"
}
],
"sid": "FFd51d1d512e68f9099ffb6576265e7fff"
},
{
"type": "SayPlay",
"name": "SayGuide",
"properties": {
"offset": {
"x": 80,
"y": 160
},
"say": "こちらは東京テレメッセージです。送りたいメッセージをテンキーで入力してください。\n最後にシャープを押してください。",
"play": null,
"voice": "Polly.Takumi",
"language": "ja-JP",
"loop": 1
},
"transitions": [
{
"event": "audioComplete",
"conditions": [],
"next": "FFd279178a20fda6faa4b67dca30eaa759",
"uuid": "a4e308ee-0081-4074-84e7-3032084e5287"
}
],
"sid": "FFff25f5b57237b689a4839d18d9b7caf9"
},
{
"type": "Gather",
"name": "GatherCode",
"properties": {
"offset": {
"x": -170,
"y": 400
},
"timeout": 10,
"finish_on_key": "#",
"stop_gather": true,
"number_of_digits": 30,
"save_response_as": null,
"say": null,
"play": null,
"voice": "alice",
"language": "en",
"loop": 1,
"hints": null,
"gather_language": "en"
},
"transitions": [
{
"event": "keypress",
"conditions": [],
"next": "FF643f145fffa917d4565354d0585bdd13",
"uuid": "4b1d54d5-a701-4dbb-b006-85d95fbad42c"
},
{
"event": "speech",
"conditions": [],
"next": null,
"uuid": "61a3bea6-e71a-4875-a434-6329c110e67a"
},
{
"event": "timeout",
"conditions": [],
"next": null,
"uuid": "3218594f-e2e2-490b-894c-59a879fdb5eb"
}
],
"sid": "FFd279178a20fda6faa4b67dca30eaa759"
},
{
"type": "Function",
"name": "PostMessage",
"properties": {
"offset": {
"x": 290,
"y": 420
},
"url": "",
"timeout": null,
"parameters": [
{
"key": "code",
"value": "{{widgets.GatherCode.Digits}}"
}
]
},
"transitions": [
{
"event": "success",
"conditions": [],
"next": "FF85fc069cecba6fd448788cafe7a39b80",
"uuid": "612b61a7-639f-4a01-b19a-3191f5bd5bde"
},
{
"event": "fail",
"conditions": [],
"next": null,
"uuid": "e63e5fb1-9d4c-407f-b632-be42c9a1de88"
}
],
"sid": "FF643f145fffa917d4565354d0585bdd13"
},
{
"type": "SayPlay",
"name": "SayThanks",
"properties": {
"offset": {
"x": 270,
"y": 640
},
"say": "メッセージをお送りいたしました。ご利用ありがとうございました。",
"play": null,
"voice": "Polly.Takumi",
"language": "ja-JP",
"loop": 1
},
"transitions": [
{
"event": "audioComplete",
"conditions": [],
"next": null,
"uuid": "f538e4e1-7076-4602-a825-a482dd5a6f6e"
}
],
"sid": "FF85fc069cecba6fd448788cafe7a39b80"
}
]
}



  • Nextボタンを押します。

フローが読み込まれたら、一部のウィジェットを修正します。


  • PostMessageと書かれたウィジェットを選択します。

    スクリーンショット 2019-03-27 14.45.06.png


  • 右側に表示されるプロパティページのFUNCTION URLのプルダウンから「PostMessage」を選択します。


  • Saveボタンを押します。


  • 画面上部のPublishボタンを押します。


  • 確認ダイアログが開くので、Publishボタンを押します。



050番号に処理を割り当てる

最後に、今作成したStudioフローを050番号に割り当てます。


  • Twilioの管理コンソールからPhone Numbersを選択します。

  • 先程購入した050番号を選択します。


  • 通話着信時のプルダウンを「Studio Flow」にして、その右側のプルダウンは先程作成した「PostMessage」を選択します。

PhoneNumber_Setup.png



  • 保存ボタンを押します。


テスト

では早速テストをしてみましょう。

ポケベル変換表は次のとおりです。

スクリーンショット 2019-03-25 15.40.51.png


  • 購入した050番号に電話をかけます。

  • ガイダンスが流れるので、ガイダンスが終わったらメッセージを数字キーで入力して最後に#を押します(たとえば、40582937293089#)。

  • obnizのブザーがなって、メッセージが表示されることを確認します。

s_MicrosoftTeams-image.png


まとめ


  • obnizはディスプレイが標準でついているので、余計なコストがかかりません。

  • WiFiさえつながれば、たとえば社内に設置していても外部からアクセスが可能です。

  • Twilio StudioやFunctionsを使うと、サーバーを設置しなくても実現が可能です。

  • Twilioにかかる料金は、ほぼ電話番号代(108円/月)と着信料(1円/分)だけです。



Twilio(トゥイリオ)とは

https://twilio.kddi-web.com

Twilioは音声通話、メッセージング(SMS/チャット)、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウドAPIサービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。