簡単にセットアップできて使いやすいLINE Beacon。LINEアプリ(アカウント)と連携させることができ非常に協力なのですが、shopで買うとは1個5400円とちょっと高いんですよね。
幸いLINE Simple Beaconという仕様が公開されているので、それを利用して安く、2000円弱でLINE Beaconを作ってみます。ハードウェアはあまり詳しくないのでもし間違い等ありましたらコメント等で指摘していただけると嬉しいです。
[追記]
雪山BOT with LINE Beaconでおなじみの久田さんがライブラリを作ってmicro:bitをLINE Simple Beacon 化するライブラリを作って下さいました!手順も丁寧に記載されていますので、お持ちの方は是非!
必要なもの
全部秋月で手にはいります。
- ESP32-DevKitC http://akizukidenshi.com/catalog/g/gM-11819/
- ブレッドボード6穴版 EIC-3901 http://akizukidenshi.com/catalog/g/gP-12366/
- USBケーブル Aオス-マイクロBオス http://akizukidenshi.com/catalog/g/gC-09314/
- 電池ボックス 単3×2本 リード線・間仕切 http://akizukidenshi.com/catalog/g/gP-10196/
しめて1930円。USBケーブルはもう持ってる方も多いでしょうしブレッドボードも最悪無くてもいいので最低限だと1600円位ですかね。
ESP32の開発環境
では最初にMacからESP32に書き込めるよう準備をしましょう。
Arduino IDE
からArduino IDEをダウンロード、インストールします。
mkdir -p ~/Documents/Arduino/hardware/espressif && \
cd ~/Documents/Arduino/hardware/espressif && \
git clone https://github.com/espressif/arduino-esp32.git esp32 && \
cd esp32/tools/ && \
python get.py
Pythonのバージョンによってはエラーになりますので、2.7で実行する等して対応して下さい。
python2.7 get.py
BLEライブラリのアップデート
これで動くはずだったのですが、私の環境だとArduino/hardware/espressif/esp32/libraries/BLEが無効なライブラリだという警告が出るので、最新版にアップデートします。
こちらに書いてある通りで大丈夫でした。
ドライバ
CP210x USB - UART ブリッジ VCP ドライバ
からMac用のドライバをインストールします。
ビーコンIDの払い出し
LINE@ Managerのビーコン登録画面へアクセスし、「ハードウェアIDの払い出し」をクリックします。ビーコンを紐付けたいアカウントを選択して発行します。IDは後で必要なので控えておいて下さい。
LINE DevelopersにてLINE Bot用の設定を済ませ、友だちになっておいて下さい。とりあえずWebhookにはwebhookを出力するようなコードだけ。
<?php
require_once __DIR__ . '/vendor/autoload.php';
$httpClient = new \LINE\LINEBot\HTTPClient\CurlHTTPClient(getenv('CHANNEL_ACCESS_TOKEN'));
$bot = new \LINE\LINEBot($httpClient, ['channelSecret' => getenv('CHANNEL_SECRET')]);
$signature = $_SERVER["HTTP_" . \LINE\LINEBot\Constant\HTTPHeader::LINE_SIGNATURE];
$inputString = file_get_contents("php://input");
error_log($inputString);
$events = $bot->parseEventRequest(file_get_contents('php://input'), $signature);
foreach ($events as $event) {
if ($event instanceof \LINE\LINEBot\Event\BeaconDetectionEvent) {
if ($event instanceof \LINE\LINEBot\Event\MessageEvent\TextMessage) {
$bot->replyMessage($event->getReplyToken(),
(new \LINE\LINEBot\MessageBuilder\MultiMessageBuilder())
->add(new \LINE\LINEBot\MessageBuilder\StickerMessageBuilder(1, 17))
->add(new \LINE\LINEBot\MessageBuilder\TextMessageBuilder('You are near beacon'))
);
}
continue;
}
}
?>
開発
MacとESP32をケーブルで接続、Arduino IDEを起動し、ツール > ボードから「ESP32 Dev Module」を、シリアルポートから「/dev/cu.SLAB_USBtoUART」を選択します。
Arduino IDEに以下のようなコードをペーストし、マイコンボードに書き込みます。
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
while (!Serial);
Serial.printf("Revision: %d\r\n" , ESP.getChipRevision());
}
void loop() {
// put your main code here, to run repeatedly:
Serial.write("loop\n");
delay(1000);
}
ターミナルで以下のコードを実行します。
screen /dev/tty.SLAB_USBtoUART 115200
以下のように、「loop」という文字列が1秒ごとに出力されます。
loop
loop
loop
書き込めることが確認できました。終了はctrl+a押してからk、yです。
では、実際にLINE Simple Beaconとして動くコードを書き込んでみます。といっても、公式から必要なコードだけを抜き出しただけです。
#include "bt.h"
#include "esp_gap_ble_api.h"
static const uint8_t HWID[5] = {0x12, 0x34, 0x56, 0x78, 0x90};// LINEから払い出されたhw idを2桁区切りにして0x__の部分に入れる
static const uint16_t UUID_FOR_LINECORP = 0xFE6F;
static const uint8_t MAX_SIMPLEBEACON_DEVICEMESSAGE_SIZE = 13;
static uint8_t deviceMessageSize = 1;
static uint8_t deviceMessage[MAX_SIMPLEBEACON_DEVICEMESSAGE_SIZE];
static const uint8_t MAX_BLE_ADVERTISING_DATA_SIZE = 31;
static const uint16_t HCI_LE_Set_Advertising_Data = (0x08 << 10) | 0x0008;
static const uint16_t HCI_LE_Set_Advertising_Enable = (0x08 << 10) | 0x000A;
static void _dump(const char * title, uint8_t *data, size_t dataSize) {
Serial.printf("%s [%d]:", title, dataSize);
for (size_t i = 0; i < dataSize; i++) {
Serial.printf(" %02x", data[i]);
}
Serial.println();
}
static void putUint8(uint8_t** bufferPtr, uint8_t data) {
*(*bufferPtr)++ = data;
}
static void putUint16LE(uint8_t** bufferPtr, uint16_t data) {
*(*bufferPtr)++ = lowByte(data);
*(*bufferPtr)++ = highByte(data);
}
static void putArray(uint8_t** bufferPtr, const void* data, size_t dataSize) {
memcpy(*bufferPtr, data, dataSize);
(*bufferPtr) += dataSize;
}
static void notifyHostSendAvailableHandler() {
// _dump("notify_host_send_available", NULL, 0);
}
static int notifyHostRecvHandler(uint8_t *data, uint16_t len) {
// _dump("notify_host_recv", data, len);
return 0;
}
static esp_vhci_host_callback_t vhciHostCallback = {
notifyHostSendAvailableHandler,
notifyHostRecvHandler
};
void setup() {
// put your setup code here, to run once:
Serial.begin(115200, SERIAL_8N1);
// bluetoothを初期化
btStart();
esp_vhci_host_register_callback(&vhciHostCallback);
updateSimpleBeaconDeviceMessage();
updateAdvertisingData();
uint8_t enable = 1;
executeBluetoothHCICommand(HCI_LE_Set_Advertising_Enable, &enable, 1);
}
static void updateSimpleBeaconDeviceMessage() {
memset(deviceMessage, 0x00, MAX_SIMPLEBEACON_DEVICEMESSAGE_SIZE);
uint8_t* deviceMessagePtr = deviceMessage;
const uint8_t messageStringSize = 8;
char messageString[messageStringSize];
snprintf(messageString, messageStringSize, "beac");
putArray(&deviceMessagePtr, messageString, messageStringSize);
deviceMessageSize = deviceMessagePtr - deviceMessage;
}
static void executeBluetoothHCICommand(uint16_t opCode, const uint8_t * hciData, uint8_t hciDataSize) {
uint8_t buf[4 + MAX_BLE_ADVERTISING_DATA_SIZE];
uint8_t* bufPtr = buf;
putUint8(&bufPtr, 1);// 1 = H4_TYPE_COMMAND
putUint16LE(&bufPtr, opCode);
putUint8(&bufPtr, hciDataSize);
putArray(&bufPtr, hciData, hciDataSize);
uint8_t bufSize = bufPtr - buf;
while (!esp_vhci_host_check_send_available());
esp_vhci_host_send_packet(buf, bufSize);
}
static void updateAdvertisingData() {
uint8_t data[MAX_BLE_ADVERTISING_DATA_SIZE];
uint8_t* dataPtr = data;
{
// flag
putUint8(&dataPtr, 1 + 1);
putUint8(&dataPtr, ESP_BLE_AD_TYPE_FLAG);
putUint8(&dataPtr, ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT);
}
{
// complete list of service uuid
putUint8(&dataPtr, 1 + 2);
putUint8(&dataPtr, ESP_BLE_AD_TYPE_16SRV_CMPL);
putUint16LE(&dataPtr, UUID_FOR_LINECORP);
}
{
// simple beacon
putUint8(&dataPtr, 1 + 9 + deviceMessageSize);
putUint8(&dataPtr, ESP_BLE_AD_TYPE_SERVICE_DATA);
putUint16LE(&dataPtr, UUID_FOR_LINECORP);
putUint8(&dataPtr, 0x02); // frame type
putArray(&dataPtr, HWID, 5);
putUint8(&dataPtr, 0x7F); // measured txpower
putArray(&dataPtr, deviceMessage, deviceMessageSize);
}
uint8_t dataSize = dataPtr - data;
_dump("simple beacon", data, dataSize);
uint8_t hciDataSize = 1 + MAX_BLE_ADVERTISING_DATA_SIZE;
uint8_t hciData[hciDataSize];
hciData[0] = dataSize;
memcpy(hciData + 1, data, dataSize);
executeBluetoothHCICommand(HCI_LE_Set_Advertising_Data, hciData, hciDataSize);
}
void loop() {
}
これで書き込むとビーコンがBLEのadvertiseを飛ばし続けるようになり、LINEがインストールされたスマホが新たに接近した(enter)と判断されたタイミングで指定したエンドポイントにイベントが送信されるようになります。
{
"events":[
{
"type":"beacon",
"replyToken":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"source":{
"userId":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"type":"user"
},
"timestamp":1513930225758,
"beacon":{
"hwid":"xxxxxxxxxx",
"dm":"62656163000000d0",
"type":"enter"
}
}
]
}
電池駆動
電池ボックスに電池を入れ、赤を3V3、黒をGNDにつなげばESP32の電源が入ります。抵抗とかも無くていいっぽい?起動時には書き込んだスケッチが読み込まれ、サービスが起動します。確認してみましたが正常に動作しているようです。
まとめ
便利なのは知っていても高くて手が出ないという声をよく聞くLINE Beacon。この通りにやれば少しは安く作れます。キリンやユニクロ等大手からはもう出ていますが、小規模サービスでもビーコンとLINEを連携させた革新的なLINE Botの登場が待ち遠しいですね!
もうすぐ年末休みですので、是非LINE Botを作って見て下さい。Bot関連のノウハウ集はQiitaで人気のLINE関連投稿紹介(保存版)によくまとまっています。
参考
https://engineering.linecorp.com/ja/blog/detail/149
https://qiita.com/zakkied/items/9a602878b9d178fa27b1
https://qiita.com/suigin/items/dcf704c05e98a00acae8
https://qiita.com/tomorrow56/items/afa06e206eec9fafcc7a
感謝です!