LINE Thingsのアイデアを考え中

##LINE Thingsを面白く使ってみたい

LINE Things は LINE 上から利用できる、新しい IoT プラットフォームです。Bluetooth LE を利用して、IoT デバイスに接続し、LIFF や LINE Bot からコミュニケーションができるようになります。
LINE Thingsを面白く使ってみたいですね。


##LINE Thingsならではの場面を考えた
Wifiでもなく、LTEでもなく、Bluetooth LE でLINE通信する特徴を生かしたものは何かを考えました。
デバイスは、多人数の人が使うものでなく、個人持ちのデバイスに向いている。
使う場所は、Wifiがない場所で使うことで、他のデバイスとの差別化が図れる。

##LINE Thingsの用途を考えた。
「外出している時に、個人持ちのデバイス」に絞り用途を考えた。

- アイデア１：車の運転中。ワイパーでデバイスが反応して、LINE Botがいいことしてくれる。眠気を覚ましたり、簡単なゲームをしたり。運転中によそ見をせずに簡単に届くスイッチとして。

- アイデア２：ゴルフ。スコアカウンターとして使用。スコアが悪いときにLINE Botが慰めてくれたり、スコアを合計してくれたりする。一緒にプレイしているメンバーのスコアも教えてくれる。

- アイデア３：ベビーカーに乗って外出している赤ちゃん。おしっこ、ウンチしたときに「おむつを交換してね」のお知らせ。

- アイデア４：工事現場で働く方の熱中症予防用に、温度・湿度センサーをヘルメットまたは作業着に取り付けて、集中監視。


##アイデア１の事前調査

- アイデア１を実現できるか？
車に持ちこむデバイスはM5stackにする。
ワイバーにネオジウムマグネットをつけて、ガラス越しでも車内にあるM5stack内蔵のHall Sensorが反応するか確認した。


動画はこれ。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ワイパーにネオジウム磁石をつけて、ガラス越しに車内に取り付けたM5StackのHall Sensorが反応するか予備検討してみた。よし、反応する。<a href="https://twitter.com/hashtag/M5stack?src=hash&amp;ref_src=twsrc%5Etfw">#M5stack</a> <a href="https://twitter.com/hashtag/LINEThings?src=hash&amp;ref_src=twsrc%5Etfw">#LINEThings</a> <a href="https://t.co/19BgzzAG0j">pic.twitter.com/19BgzzAG0j</a></p>&mdash; みきひろし (@miki_hiroshi_77) <a href="https://twitter.com/miki_hiroshi_77/status/1157306136862711808?ref_src=twsrc%5Etfw">August 2, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



よし、反応した。

最終的なイメージは下の図。


![20190731_06-57-57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269534/99ae933d-ba14-7037-c954-a9ba3f6db026.png)

M5stackをLINE Thingsのデバイスとして機能させ、enebularを使って、「眠気をさませ」の画像を送る練習。

![20190731_04-11-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269534/a552067b-3a87-c474-7a48-2bdc3dfc4f01.png)

templateノードのあたりに画像を送るよう指示

` {
     "replyToken": "{{replyToken}}",
     "messages": [
        {
            "type": "image",
            "originalContentUrl": "https://xxxxxxxxxxxxxxx.xxxxx.xx/wakeup.JPG",
            "previewImageUrl": "https://xxxxxxxxxxxxxxx.xxxxx.xx/wakeup.JPG"
        }
     ]
 }
`


M5stackのボタンを押したら画像がLINEに送られるようになった。

![20190731_07-38-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269534/30dfdd12-59fd-af94-4f3d-008dd8077534.png)


動画はこれ。

https://youtu.be/kAvu-s7lKKM

##M5Stack内蔵のHall sensorの反応値が高くなったら、LINEに画像を送る仕組みを考える。

Arduinoで以下の通り、LINE Thingsのプログラムと、Hall sensor動作のプログラムを、合体させたが今のところ動かない。
動かしたい。ここが今後の課題です。


```setup.ino

#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();
//}
```



###アイデア１について今後の予定
- 運転中によそ見をせずに押せるスマートフォンのスイッチ、電気系統を改造することなく反応するスイッチとして、利用用途を考える。眠気防止。神経衰弱ゲーム。移動場所のマーク。docomo 基地局を利用した位置情報のAPIを使ったら、実現できるかも。でもカーナビがあるからいらない。

###アイデア２について今後の予定

- データベースを使って、メンバー4人のスコアを互いにLINE BOTで知らせることができるようにする。腕時計タイプのカッコいい製品が2万円くらいでゴルフショップにすでに売ってる。でも、メンバー4人のスコアを互いに共有できる機能はないので、このアイデアを、動画にして、LINE Things Mini Awardに応募した（2019年9月8日応募）。コードの実装はできていません。アイデアのみで応募。
動画は[こちら](https://youtu.be/amwXemyCyHI)




###アイデア３について今後の予定
- 漏水センサー、湿り気センサー、匂いセンサーを調査したが、そもそも紙おむつの中にセンサーは入れたら、痛かったり、怪我したりしないか？もっと、いい方法ないか？

###アイデア４について今後の予定
- データベースに値が取り込めるようにする。Firebaseを学ぼう。LINE Things連携はないが、すでに世の中に多くの開発品があり競合多し。