search
LoginSignup
0

More than 1 year has passed since last update.

posted at

updated at

LINEThingsとM5CoreInkを使ってO2Oマーケティングを体験してみる

概要

LINEThingsを使ってLINEとデバイスを連携させ、O2Oマーケティングの一例を再現してみる。今回は、M5Stack CoreInkという通信機能を持った電子ペーパーを値札として用い、LINEでアンケートに答えるとその値札の表示を変更し、購買を促す(本当かな?)といったものを作成した。

※注: 筆者がガス欠で説明不十分な点があるかと思います:pray:(2020/12/11)

やったこと

  • LINEThingsを用いて、LINEとM5Stack CoreInkを連携
  • LIFF上でアンケートに答えてもらったら、値札の表示を変更する

動作環境

  • maacOS Catalina ver.10.15.3
  • Python 3.7.0
  • python ライブラリ
    • line-bot-sdk==1.16.0
    • resspnder==1.3.2

うまくできなかったこと

  • M5Stack CoreInk側からLINE(スマホ)との接続を切断できなかった(すぐ再接続される)

説明しないこと

  • LINE BOTの作成方法
  • M5Stack CoreInkのsetup方法

LINE Thingsとは

getting-started-00.804e6cba.jpg

LINE Thingsは、LINEを介して、チャネルとBluetooth® Low Energy対応デバイスを連携し、操作を可能にするIoTプラットフォームです。
https://developers.line.biz/ja/docs/line-things/about-line-things/

M5Stack CoreInkとは

ざっと説明すると、WifiやBluetooth通信機能を備えた電子ペーパーデバイスです。

PXL_20201201_104424090.jpg
下記のページに詳細な説明があります。よければ、参考にしてください。
- スイッチサイエンス販売ページ

この記事でいうO2Oマーケティングとは

オンラインでの施策を、オフラインに反映させ、顧客の購買行動を促進させるマーケティンング手法。とします。

本当は、参考ページのようなオフライン、オンライン両方で相互誘導できるようなマーケティング手法のことを言うのだと思います。。。(ざっくり認識です。)

今回の記事で具体例を挙げてみると、服屋さんに来店した顧客に、LINE上で回答できるアンケートを配信する。そのアンケートに回答すると、店舗にある値札が値引きされたり、おすすめのコーディネートなどが表示されたりする。それによって、顧客の購買意欲促進を狙う。(て感じなのかな...)

参考ページ:
- O2Oの上位概念?「OMO」をLINEで実践! LINEプラットフォームを利用するメリットを徹底考察

フロー図

大体のフローはこんな感じです。
①今回作成するLINEBOTを友達登録している人のLINEと、値札(M5Stack CoreInk)がbluetooth接続される
②アンケートを送信する
③アンケートに回答する
④値札の表示が変更される

スクリーンショット 2020-12-11 10.14.32.png

手順

1. LINEBOTを作成する

適宜、LINEBOTを作成してください。
参考ページ:
- https://developers.line.biz/ja/docs/messaging-api/getting-started/#using-console

2. Webhookされるサーバーを作成する

今回はLINEThingsを用いて、デバイスから送信されたメッセージに合わせて、利用者に送信するメッセージを変更する必要があります。ですので、メッセージを受け取り、処理をする部分を担うサーバーを作成します。

公式SDK
Messaging API対応のLINE公式SDKはこちらです。下記の中でお好きな言語を選択してください。

  • Java
  • PHP
  • Go
  • Perl
  • Ruby
  • Python
  • Node.js

私は、Pythonを用い、サーバー側の処理を作成しました。
ソースコードはこちら

今回特筆すべき部分としては、以下の処理で、M5 CoreInkからメッセージを受信した際に実行されるメソッドです。

main.py
#---略---

###
# deviceからメッセージ受信時
###
@handler.add(ThingsEvent)
def handle_message(event):

    if event.things is None:
        print(event)
        return
    if event.things.type != "scenarioResult":
        print(event)
        return
    if event.things.result.result_code != "success":
        print(event)
        return

    # 届いたメッセージをdecode
    message = base64.b64decode(event.things.result.ble_notification_payload).decode()
    if message[0] == "S":
        # LIFFアンケートページをトーク画面に返信する。
        liff_url = "https://liff.line.me/1655338407-PZBX17Wz"
        line_bot_api.reply_message(
           event.reply_token,
           TextSendMessage(text="今アンケートに答えると割引チャンス!\n" + liff_url)
        )
    else:
        line_bot_api.reply_message(
           event.reply_token,
           TextSendMessage(text="対応していません")
        )

#---略---

3. LINEBotと2で作成したサーバーを接続する

ここでは、
参考ページ:
- https://developers.line.biz/ja/docs/messaging-api/building-bot/#setting-webhook-url

4. LINEBotでLINEThingsを使えるようにする

下記記事がめちゃくちゃ詳細に記載してくれているので、おすすめです。

参考資料:
- https://qiita.com/hktechno/items/12781e38b09e10c20da2#line-things-%E8%87%AA%E5%8B%95%E9%80%9A%E4%BF%A1%E6%A9%9F%E8%83%BD%E3%82%92%E4%BD%93%E9%A8%93%E3%81%97%E3%82%88%E3%81%86

5. M5Stack CoreInkがLINEやサーバーと接続するための処理を追加する

ソースコードが割と長いため、折りたたんでいます。

LINEやサーバーとの接続時に使用するソースコード
ble.ino
#include <BLEServer.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <M5CoreInk.h>

Ink_Sprite InkPageSprite(&M5.M5Ink);

// Device Name: Maximum 30 bytes
#define DEVICE_NAME "Trial M5Stack"

// User service UUID: Change this to your generated service UUID
#define USER_SERVICE_UUID "{USER_SERVICE_UUID}"

// User service characteristics
#define WRITE_CHARACTERISTIC_UUID "{WRITE_CHARACTERISTIC_UUID}"
#define NOTIFY_CHARACTERISTIC_UUID "{NOTIFY_CHARACTERISTIC_UUID}"

// PSDI Service UUID: Fixed value for Developer Trial
#define PSDI_SERVICE_UUID "{PSDI_SERVICE_UUI}"
#define PSDI_CHARACTERISTIC_UUID "{PSDI_CHARACTERISTIC_UUID}"

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;
  }
};
boolean flg = false;

class writeCallback: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *bleWriteCharacteristic) {
    std::string value = bleWriteCharacteristic->getValue();
    String read_value;
    if (value.length() > 0) {
        Serial.println("*********");
        Serial.print("New value: ");
        for (int i = 0; i < value.length(); i++)
          read_value = value[i];
        if (read_value == "1"){
            flg = true;
        }

        Serial.println();
        Serial.println("*********");
    }
  }
};

void setup() {
  M5.begin();
  Serial.begin(115200);

  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();

  digitalWrite(LED_EXT_PIN,LOW);
  if( !M5.M5Ink.isInit()){
      Serial.printf("Ink Init faild");
      while (1) delay(100);   
  }
  M5.M5Ink.clear();
  if( InkPageSprite.creatSprite(0,0,200,200,true) != 0 ){
    Serial.printf("Ink Sprite creat faild");
  }
  Serial.printf("Ink Sprite creat successful");

  draw_nehuda();
  draw_nedan(false);
  InkPageSprite.pushSprite();  

}

void loop() {
  if (flg) {
      InkPageSprite.clear();
      draw_nehuda();
      draw_nedan(true);
      InkPageSprite.pushSprite();
      flg = false;
  }
  if ( M5.BtnUP.wasPressed() ) {
    // デバッグ用
    String mes = "S"+(String)1;
    notifyCharacteristic->setValue(mes.c_str());
    notifyCharacteristic->notify();
    Serial.println("BtnUP.wasPressed() == TRUE");
  }
  if ( M5.BtnDOWN.wasPressed() ) {
    // 再接続用
    Serial.println("BtnDOWN.wasPressed() == TRUE");
    oldDeviceConnected = false;
  }
  // Connection
  if (deviceConnected && !oldDeviceConnected) {
    oldDeviceConnected = deviceConnected;
    delay(3000);
    String mes = "S"+(String)2;
    notifyCharacteristic->setValue(mes.c_str());
    notifyCharacteristic->notify();
    Serial.println("connected");
    delay(3000);
    uint16_t connId = thingsServer->getConnId();
    thingsServer->disconnect(connId);
    thingsServer->getAdvertising()->stop();
  }
  M5.update();
}

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

6. 2で作成したサーバーに、サーバー側からM5CoreInkにwriteする処理を追加

M5CoreInkに接続・writeする処理

write.py
import asyncio
import time
from bleak import BleakClient, BleakScanner

WRITE_CHARACTERISTIC_UUID = "WRITE_CHARACTERISTIC_UUID"
mac_addr = "M5 CoreInkのMACアドレス"

async def run(address, loop):
    async with BleakClient(address, loop=loop) as client:
        x = await client.is_connected()
        print("Connected: {0}".format(x))
        write_value = bytearray(b"1")
        await client.write_gatt_char(WRITE_CHARACTERISTIC_UUID, write_value)
        print("write")
        # 5秒後に終了
        await asyncio.sleep(5.0, loop=loop)

if __name__=="__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run(mac_addr, loop))

アンケートに回答したことを受信した際、上記処理を呼び出す

main.py
from write import run, mac_addr, WRITE_CHARACTERISTIC_UUID
#--- 略 ---

###
# メッセージ受信時
###
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    if event.message.text == "送信しました":
        loop = asyncio.get_event_loop()
        loop.run_until_complete(run(mac_addr, loop))
        line_bot_api.reply_message(
           event.reply_token,
           TextSendMessage(text="割引されるのでお待ちを")
        )

7. アンケートフォームを作成する

アンケートフォームを作成します。
ソースコードはこちら
スクリーンショット 2020-12-11 9.21.32.png

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0