2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
はじめに
本記事は、2017年11月1日に開催された、SIer版のIoTLTでライブコーディングしたネタの一部を解説したものです。
当日使ったスライドはこちらにあります。
何をするのか
Twilio Wirelessを使って、ラズパイに繋いだLEDを点灯させます(見た目は凄い地味w)。
3G USBドングル経由でTwilioから指示が飛んで、黄色いLEDが光ります。
それだけですw
ラズパイに対してプッシュする技術は色々ありますが、代表的なのは常に通信状態をキープしつつ、サーバーから指示を出す方法です。しかし、この方法だと通信状態をキープするためのしくみが必要になるので、あまり美しくありません。
今回の方法は、Twilio WirelessのCommand機能を使っているので、通信が切れている状態でもサーバーからプッシュすることができます。
準備
- USBドングル: AK-020
- Raspberry Pi 3 Model B
- ミニブレッドボード
- 抵抗(抵抗値はいくつでも大丈夫です)
- LED
- ジャンパーケーブル(2本)
- Twilio SIM: こちらから購入可能です(購入には、Twilioアカウントが必要です)
作業手順
今回は以下の手順でセットアップしていきます。
- Twilio SIMの設定
- Raspberry Piの設定
- 配線
- プログラミング
- テスト
では、はじめましょう。
ハンズオン
1. Twilio SIMの設定
- Twilioの管理コンソールにログインします。
- Programmable Wireless -> 料金プラン を選択します。
-
Create a Rate Plan
ボタンを押します。 - UNIQUE NAME欄に、
CommandOnly
と入力します。 - Enabled Servicesでは、MESSAGING(Programmable SMS and Commands)のみチェックをして、他は外します。
- International RoamingのENABLEDをチェックし、MESSAGING(Programmable SMS and Commands)のみチェックを入れます。
-
Create
ボタンを押します。 - Programmable Wireless -> SIM を選択します。
-
Register a SIM Card
ボタンを押します。 - REGISTRATION CODE欄に、SIMカードの裏面にかかれている10文字の文字列を入力します。
-
Register SIM Card
ボタンを押します。 - 利用規約のダイアログが表示されるので、
Accept and Continue
ボタンを押します。 -
Next: Add a Unique Name
ボタンを押します。 - UNIQUE NAME欄に、
SIM01
と入力し、Next: Add a Rate Plan
ボタンを押します。
多分、このあたりで管理コンソールがハングアップするので、あとはCurlコマンドで設定をします。
- まずは、SIMカードに割り当てられた、DEから始まる文字列(SID)を調べます。
- 次に、管理コンソールのホーム -> 設定を選択します。
- APIクレデンシャルにかかれている、ACCOUNT SIDとAUTH TOKENを調べます。
- 以下のCurlコマンドを実行します。
curl -X POST https://wireless.twilio.com/v1/Sims/DEから始まるSIMのSID \
-d 'UniqueName=SIM01' \
-d 'FriendlyName=SIM01' \
-d 'RatePlan=CommandOnly' \
-d 'Status=active' \
-u 'ACから始まるACCOUNT SID:AUTH TOKEN'
これでSIMが使えるようになりました。
USBドングルに、SIMカードを装着しましょう。
Raspberry Piの設定
- USBドングルを、Raspberry PiのUSBポート(外側の上段)に接続します。
- Raspberry Piにログインします。
ログインまでの手順は今回は割愛しますので、詳しくは以下のリンクなどを参考にしてください。 - http://hirazakura.hatenablog.com/entry/raspberrypi/setup/first
- https://qiita.com/takabye/items/03ad86a23226a12e4417
USBドングルを使えるようにするために、以下の手順を実施します。
以下のサイトを参考にさせていただきました。
http://mag.switch-science.com/2016/08/22/raspberry-pi-soracom-air-sms/
ejectのインストールとusbの設定
sudo apt-get install eject
eject /dev/sr0
sudo modprobe usbserial vendor=0x15eb product=0x7d0e
まずはここまででエラーが出ないことを確認したら、毎回起動時にUSBの初期化を行うために、/etc/rc.localのexit 0
の直前に以下のコードを記載しておきます。
i=0
while [ $i -lt 15 ]; do
if [ -e /dev/sr0 ]; then
break
else
sleep 1
fi
i=$((i+1))
done
eject /dev/sr0
sudo modprobe usbserial vendor=0x15eb product=0x7d0e
USBが正しく認識されると、ドングルのライトが緑色に点灯します。
配線
続いて配線をしていきます。
基本的に次の配線図通りに繋いでいただければOKです。
LEDのプラスとマイナスを間違えないようにしてください。足の長い方がプラスになり、抵抗につながっています。
プログラミング
では最後にプログラムを作っていきます。
今回作成が必要なのは、Twilio側の指示を出すためのプログラムと、Raspberry Pi側で指示を受けてLEDのON/OFFを行うプログラムの2つです。
まずは、Raspberry Piのプログラムから作っていきましょう。
Raspberry Piにログインした状態で、以下の作業をします。
cd
vi sms.py
エディタが開くので、まずはアルファベットのi
を入力して、挿入モードにします。
続いて、以下のコードをコピペします。
# coding: utf-8
'''Twilio Programmable Wireless Command using Raspberry Pi, Twilio Wireless SIM and ABIT AK-020
'''
import sys
import time
import serial
import re
import threading
import RPi.GPIO as GPIO
LED = 3 # ボード上の3番ピン(GPIO2)
class Board:
# コンストラクタ
def __init__(self):
# ピンのモードをそれぞれ出力用と入力用に設定
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(LED, GPIO.OUT)
self.ledoff()
def ledon(self):
# LEDを点ける
GPIO.output(LED, GPIO.HIGH)
def ledoff(self):
# LEDを消す
GPIO.output(LED, GPIO.LOW)
class Sms:
# コンストラクタ
def __init__(self):
# SMSメッセージかどうかをチェックする文字列パターン
self.pattern = re.compile('\+CMGL: [0-9]+,"REC UNREAD"')
# シリアル通信の設定
self.serial = serial.Serial(
'/dev/ttyUSB0',
460800,
timeout = 5,
xonxoff = False,
rtscts = False,
dsrdtr = False,
bytesize = serial.EIGHTBITS,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE
)
# ATZ:モデム初期化
self.serial.write('ATZ\r')
self.check_response_isok()
# AT+CFUN:機能モードを設定(1:Full Functionaly)
self.serial.write('AT+CFUN=1\r')
self.check_response_isok()
# AT+CGDCONT:APNの書き込み
self.serial.write('AT+CGDCONT=1,"IP","wireless.twilio.com"\r')
self.check_response_isok()
# AT+CMGF:SMSを設定(1:テキストモード)
self.serial.write('AT+CMGF=1\r')
self.check_response_isok()
# ディストラクタ
def __del__(self):
# シリアル通信を閉じる
self.serial.close()
# ウェイト
def wait_response(self):
time.sleep(1)
while self.serial.inWaiting() == 0:
time.sleep(0.5)
# OK応答の確認
def check_response_isok(self):
self.wait_response()
r = self.serial.read(self.serial.inWaiting()).split('\r\n')
if len(r) < 2 or r[-2] != 'OK':
raise Exception(r)
# プロンプト応答の確認
def check_response_isprompt(self):
self.wait_response()
r = self.serial.read(self.serial.inWaiting()).split('\r\n')
if len(r) < 1 or r[-1] != '> ':
raise Exception(r)
# 読み捨て
def dispose_response(self):
self.wait_response()
self.serial.read(self.serial.inWaiting())
# メッセージの受信
def receive_message(self):
self.serial.write('AT+CMGL="REC UNREAD"\r\n')
self.wait_response()
r = self.serial.read(self.serial.inWaiting()).split('\r\n')
if len(r) < 2 or r[-2] != 'OK':
raise Exception(r)
messages = []
is_message = False
for line in r:
if is_message:
messages.append(line)
if line and self.pattern.match(line):
is_message = True
else:
is_message = False
return messages
class MainThread(threading.Thread):
def __init__(self, n):
super(MainThread, self).__init__()
self.n = n
def run(self):
print " === start thread === "
# 指定された回数だけ受信メッセージ取得をループ
for i in range(self.n):
messages = sms.receive_message()
for message in messages:
if message.upper() == 'ON':
board.ledon()
elif message.upper() == 'OFF':
board.ledoff()
print message
board.ledoff()
print " === end thread === "
if __name__ == '__main__':
board = Board()
sms = Sms()
th_main = MainThread(30) # 30 * ループ1回につき2秒 = 60秒間実行
th_main.start()
貼り付けが終わったら、ESC
を押して挿入モードを抜けて、wq
で保存して終了します。
では、念のために実行をしてみましょう。
python sms.py
しばらくすると、 === start thread ===
と表示されれば、待受開始です。
=== end thread ===
でプログラムは終了となります。 なおプログラムは数分で終了します。
次にTwilio側のプログラムを作成していきます。
-
Twilioの管理コンソールにログインし、Runtime -> Functions -> Configureを選択します。
-
Enable ACCOUNT_SID and AUTH_TOKEN
にチェックをいれて、Save
ボタンを押します。 -
Runtime -> Functions -> Manageを選択します。
-
Create a Function
ボタンを押します。 -
New Functionsダイアログが開いたら、
Blank
を選択して、Create
ボタンを押します。 -
Functions Name欄に、
sendCommand
と入力します。 -
PATH欄には、
/sendCommand
と入力します。 -
PATH欄の右側にある、コピーアイコンを押して、URLをメモ帳にコピーしておきます。
-
事前にCODE欄に書かれていたコードを一度消して、以下のコードをコピペします。
exports.handler = function(context, event, callback) {
const command = event.command || 'On'
const request = require('request')
const options = {
method: 'POST',
url: 'https://wireless.twilio.com/v1/Commands',
headers: {
'cache-control': 'no-cache',
authorization: 'Basic ' + new Buffer(context.ACCOUNT_SID + ':' + context.AUTH_TOKEN).toString('base64'),
'content-type': 'application/x-www-form-urlencoded' },
form: {
Sim: 'SIM01',
Command: command
}
};
request(options, function (error, response, body) {
if (error) {
console.log(error)
callback(error)
} else {
console.log(response)
callback(null, 'OK')
}
});
};
Save
ボタンを押して、コードをデプロイします。しばらくして、デプロイ完了の緑色のバナーが表示されれば、準備は完了です。
テスト
ではいよいよテストをしましょう。
- まずは、Raspberry Pi側で、先程のsms.pyを実行しておきます。
- 次に、新しくブラウザを開き、先程メモしておいたTwilio側のプログラムのURLを入力して実行します。
LEDが点灯すれば成功です。
ちなみに、LEDを消灯させるには、メモしておいたURLに、?command=Off
を付加して実行します。