LINE Thingsを面白く使ってみたい
LINE Things は LINE 上から利用できる、新しい IoT プラットフォームです。Bluetooth LE を利用して、IoT デバイスに接続し、LIFF や LINE Bot からコミュニケーションができるようになります。
LINE Thingsを面白く使ってみたいですね。
LINE Thingsならではの場面を考えた
Wifiでもなく、LTEでもなく、Bluetooth LE でLINE通信する特徴を生かしたものは何かを考えました。
デバイスは、多人数の人が使うものでなく、個人持ちのデバイスに向いている。
使う場所は、Wifiがない場所で使うことで、他のデバイスとの差別化が図れる。
LINE Thingsの用途を考えた。
「外出している時に、個人持ちのデバイス」に絞り用途を考えた。
-
アイデア1:車の運転中。ワイパーでデバイスが反応して、LINE Botがいいことしてくれる。眠気を覚ましたり、簡単なゲームをしたり。運転中によそ見をせずに簡単に届くスイッチとして。
-
アイデア2:ゴルフ。スコアカウンターとして使用。スコアが悪いときにLINE Botが慰めてくれたり、スコアを合計してくれたりする。一緒にプレイしているメンバーのスコアも教えてくれる。
-
アイデア3:ベビーカーに乗って外出している赤ちゃん。おしっこ、ウンチしたときに「おむつを交換してね」のお知らせ。
-
アイデア4:工事現場で働く方の熱中症予防用に、温度・湿度センサーをヘルメットまたは作業着に取り付けて、集中監視。
アイデア1の事前調査
- アイデア1を実現できるか?
車に持ちこむデバイスはM5stackにする。
ワイバーにネオジウムマグネットをつけて、ガラス越しでも車内にあるM5stack内蔵のHall Sensorが反応するか確認した。
動画はこれ。
ワイパーにネオジウム磁石をつけて、ガラス越しに車内に取り付けたM5StackのHall Sensorが反応するか予備検討してみた。よし、反応する。#M5stack #LINEThings pic.twitter.com/19BgzzAG0j
— みきひろし (@miki_hiroshi_77) August 2, 2019
よし、反応した。
最終的なイメージは下の図。
M5stackをLINE Thingsのデバイスとして機能させ、enebularを使って、「眠気をさませ」の画像を送る練習。
templateノードのあたりに画像を送るよう指示
{ "replyToken": "{{replyToken}}", "messages": [ { "type": "image", "originalContentUrl": "https://xxxxxxxxxxxxxxx.xxxxx.xx/wakeup.JPG", "previewImageUrl": "https://xxxxxxxxxxxxxxx.xxxxx.xx/wakeup.JPG" } ] }
M5stackのボタンを押したら画像がLINEに送られるようになった。
動画はこれ。
M5Stack内蔵のHall sensorの反応値が高くなったら、LINEに画像を送る仕組みを考える。
Arduinoで以下の通り、LINE Thingsのプログラムと、Hall sensor動作のプログラムを、合体させたが今のところ動かない。
動かしたい。ここが今後の課題です。
# include <M5Stack.h>
# include <BLEServer.h>
# include <BLEDevice.h>
# include <BLEUtils.h>
# include <BLE2902.h>
// Device Name: Maximum 30 bytes
# define DEVICE_NAME "MyName_M5Stack"
// User service UUID: Change this to your generated service UUID
# define USER_SERVICE_UUID "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
// User service characteristics
# define WRITE_CHARACTERISTIC_UUID "E9062E71-9E62-4BC6-B0D3-35CDCD9B027B"
# define NOTIFY_CHARACTERISTIC_UUID "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
// PSDI Service UUID: Fixed value for Developer Trial
# define PSDI_SERVICE_UUID "E625601E-9E55-4597-A598-76018A0D293D"
# define PSDI_CHARACTERISTIC_UUID "26E2B12B-85F0-4F3F-9FDD-91D114270E6E"
# define TEXT_X (M5.Lcd.width() / 2)
# define TEXT_Y (M5.Lcd.height() / 2 / 2)
BLEServer* thingsServer;
BLESecurity *thingsSecurity;
BLEService* userService;
BLEService* psdiService;
BLECharacteristic* psdiCharacteristic;
BLECharacteristic* writeCharacteristic;
BLECharacteristic* notifyCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
class serverCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
class writeCallback: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *bleWriteCharacteristic) {
std::string value = bleWriteCharacteristic->getValue();
if ((char)value[0] <= 1) {
if ((char)value[0] == 1) {
M5.Lcd.fillRect(0, 0, M5.Lcd.width(), M5.Lcd.height() / 2, WHITE);
M5.Lcd.setTextColor(BLACK);
M5.Lcd.setTextSize(4);
M5.Lcd.drawString("LED: ON ", TEXT_X, TEXT_Y);
}
else {
M5.Lcd.fillRect(0, 0, M5.Lcd.width(), M5.Lcd.height() / 2, BLACK);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(4);
M5.Lcd.drawString("LED: OFF", TEXT_X, TEXT_Y);
}
}
}
};
void setup() {
M5.begin();
BLEDevice::init("");
BLEDevice::setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_NO_MITM);
// Security Settings
BLESecurity *thingsSecurity = new BLESecurity();
thingsSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND);
thingsSecurity->setCapability(ESP_IO_CAP_NONE);
thingsSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
setupServices();
startAdvertising();
// M5Stack LCD Setup
M5.Lcd.setTextDatum(MC_DATUM);
M5.Lcd.clear(BLACK);
M5.Lcd.qrcode("https://line.me/R/nv/things/deviceLink", 110, 130, 100, 3);
M5.Lcd.setTextColor(YELLOW);
M5.Lcd.setTextSize(2);
M5.Lcd.drawString("Ready to Connect", TEXT_X, TEXT_Y);
Serial.println("Ready to Connect");
}
void loop() {
M5.update();
if (M5.BtnB.wasPressed()) {
uint8_t btnValue = 1;
notifyCharacteristic->setValue(&btnValue, 1);
notifyCharacteristic->notify();
} else if (M5.BtnB.wasReleased()) {
//uint8_t btnValue = 0;
//notifyCharacteristic->setValue(&btnValue, 1);
//notifyCharacteristic->notify();
}
// Disconnection
if (!deviceConnected && oldDeviceConnected) {
delay(500); // Wait for BLE Stack to be ready
thingsServer->startAdvertising(); // Restart advertising
oldDeviceConnected = deviceConnected;
M5.Lcd.fillRect(0, 0, M5.Lcd.width(), M5.Lcd.height() / 2, BLACK);
M5.Lcd.setTextColor(YELLOW);
M5.Lcd.setTextSize(2);
M5.Lcd.drawString("Ready to Connect", TEXT_X, TEXT_Y);
}
// Connection
if (deviceConnected && !oldDeviceConnected) {
oldDeviceConnected = deviceConnected;
M5.Lcd.fillRect(0, 0, M5.Lcd.width(), M5.Lcd.height() / 2, BLACK);
M5.Lcd.setTextColor(GREEN);
M5.Lcd.setTextSize(2);
M5.Lcd.drawString("Connected", TEXT_X, TEXT_Y);
}
}
void setupServices(void) {
// Create BLE Server
thingsServer = BLEDevice::createServer();
thingsServer->setCallbacks(new serverCallbacks());
// Setup User Service
userService = thingsServer->createService(USER_SERVICE_UUID);
// Create Characteristics for User Service
writeCharacteristic = userService->createCharacteristic(WRITE_CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_WRITE);
writeCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
writeCharacteristic->setCallbacks(new writeCallback());
notifyCharacteristic = userService->createCharacteristic(NOTIFY_CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_NOTIFY);
notifyCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
BLE2902* ble9202 = new BLE2902();
ble9202->setNotifications(true);
ble9202->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
notifyCharacteristic->addDescriptor(ble9202);
// Setup PSDI Service
psdiService = thingsServer->createService(PSDI_SERVICE_UUID);
psdiCharacteristic = psdiService->createCharacteristic(PSDI_CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ);
psdiCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
// Set PSDI (Product Specific Device ID) value
uint64_t macAddress = ESP.getEfuseMac();
psdiCharacteristic->setValue((uint8_t*) &macAddress, sizeof(macAddress));
// Start BLE Services
userService->start();
psdiService->start();
}
void startAdvertising(void) {
// Start Advertising
BLEAdvertisementData scanResponseData = BLEAdvertisementData();
scanResponseData.setFlags(0x06); // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04
scanResponseData.setName(DEVICE_NAME);
thingsServer->getAdvertising()->addServiceUUID(userService->getUUID());
thingsServer->getAdvertising()->setScanResponseData(scanResponseData);
thingsServer->getAdvertising()->start();
}
/************************************************************************
M5StackFire internal Hall sensor
************************************************************************/
# define M5STACKFIRE_SPEAKER_PIN 25 // speaker DAC, only 8 Bit
# define HORIZONTAL_RESOLUTION 320
# define VERTICAL_RESOLUTION 240
# define POSITION_OFFSET_Y 20
# define SIGNAL_LENGTH HORIZONTAL_RESOLUTION
uint16_t oldSignal[SIGNAL_LENGTH];
uint16_t adcBuffer[SIGNAL_LENGTH];
float ESP32_hallRead()
{
float value = 0;
int count = 400;
// mean value filter
for (int n = 0; n < count; n++) value += hallRead();
return value / count * 10;
}
float HallOffset = 0;
void setup1()
{
dacWrite(M5STACKFIRE_SPEAKER_PIN, 0); // make sure that the speaker is quite
M5.Lcd.begin();
M5.Lcd.fillScreen( BLACK );
M5.Lcd.fillRect(10, 1, 150, 160, BLACK);
M5.Lcd.setCursor(0, 0);
M5.Lcd.setTextColor(BLUE, BLACK);
M5.Lcd.setTextSize(2);
M5.Lcd.println("ESP32 Hall sensor:");
HallOffset = ESP32_hallRead(); // callibrate the output value to the magnetic field at start up
M5.Lcd.setTextSize(2);
}
float LowPassFilteredValue=0;
void showSignal()
{
int n;
int oldx;
int oldy;
int oldSig;
int x, y;
for (n = 0; n < SIGNAL_LENGTH; n++)
{
x = n;
float value = ESP32_hallRead()- HallOffset;
LowPassFilteredValue+=(value-LowPassFilteredValue)*0.05;
M5.Lcd.setCursor(220, 1);
M5.Lcd.print((int)LowPassFilteredValue);M5.Lcd.print(" ");
y = map(value , -127, 127, VERTICAL_RESOLUTION, POSITION_OFFSET_Y);
if (n > 0)
{
// delete old line element
M5.Lcd.drawLine(oldx , oldSig, x, oldSignal[n], BLACK );
// draw new line element
if (n < SIGNAL_LENGTH - 1) // don't draw last element because it would generate artifacts
{
M5.Lcd.drawLine(oldx, oldy, x, y, GREEN );
}
}
oldx = x;
oldy = y;
oldSig = oldSignal[n];
oldSignal[n] = y;
//以下に磁石が近づいたらLINE BOTに通知する記述
if(LowPassFilteredValue>10){
notifyCharacteristic->notify();
}
//磁石が近づいたらLINE BOTに通知する記述ここまで
}
}
//void loop()
//{
// showSignal();
//}
アイデア1について今後の予定
- 運転中によそ見をせずに押せるスマートフォンのスイッチ、電気系統を改造することなく反応するスイッチとして、利用用途を考える。眠気防止。神経衰弱ゲーム。移動場所のマーク。docomo 基地局を利用した位置情報のAPIを使ったら、実現できるかも。でもカーナビがあるからいらない。
アイデア2について今後の予定
- データベースを使って、メンバー4人のスコアを互いにLINE BOTで知らせることができるようにする。腕時計タイプのカッコいい製品が2万円くらいでゴルフショップにすでに売ってる。でも、メンバー4人のスコアを互いに共有できる機能はないので、このアイデアを、動画にして、LINE Things Mini Awardに応募した(2019年9月8日応募)。コードの実装はできていません。アイデアのみで応募。
動画はこちら
アイデア3について今後の予定
- 漏水センサー、湿り気センサー、匂いセンサーを調査したが、そもそも紙おむつの中にセンサーは入れたら、痛かったり、怪我したりしないか?もっと、いい方法ないか?
アイデア4について今後の予定
- データベースに値が取り込めるようにする。Firebaseを学ぼう。LINE Things連携はないが、すでに世の中に多くの開発品があり競合多し。