19
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Amazon Dash ButtonイベントをWIFI_AP_STAモードのESP8266で取得する

Last updated at Posted at 2017-01-09

##はじめに
今回の記事では、Arduino IDEで開発可能で安価なWiFiモジュールESP8266を用いてAmazon Dashu Buttonのボタン押下イベントを取得しています。これまでのプロミスキャスモードによる手法ではなく、WIFI_AP_STAモードで取得することで外部サービスとの通信をサクッとすることができます。

Amazon Dash Buttonの通信は以下の通りになっています (rukihenaさんの記事より)

Amazon Dash Button は AmazonのサーバとHTTPSでしゃべるので、それに先立ち以下のような通信をしていると思われます。
①無線ネゴ→②WPA認証→③DHCP(ARP含む)→④DNS→⑤HTTPS

今回も①無線ネゴの段階で検出しています。①の段階でのAmazon Dash Buttonからのプローブ要求を検知しております。

##実装
(15:45追記 注意点: 1度ボタンを押すと2回程度の重複検知が生じますのでご注意ください。)
(20:28追記 スマホ等の他の機器からのWiFi接続が少し不安定になります。同じSSIDが2つあるため、WiFiの繋ぎ変えが生じていそうです。)

esp8266ADB-AP_STA.ino
#include <ESP8266WiFi.h>

extern "C" {
  #include <user_interface.h>
}

//-------------------------------------------------------------------------
//  WiFi Data
//-------------------------------------------------------------------------
#define WLAN_SSID       "yourSSID"
#define WLAN_PASS       "yourPassword"
//-------------------------------------------------------------------------

//String monitoringMAC1 = "12:34:56:78:9a:bc"; //各アドレスにおいて0F以下であれば、0は省略されます。
//String monitoringMAC2 = "a:b:c:d:e:f"; //例えば0a:0b:0c:0d:0e:0fだと"a:b:c:d:e:f"と表記する必要があります。
uint8_t monitoringMAC1[6] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};  //MACアドレスの比較法変更 (2017/01/11 9:15)
uint8_t monitoringMAC2[6] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};

boolean detectingMAC1 = false;
boolean detectingMAC2 = false;

String getStrMAC(uint8_t mac[6]){
  String res = String(mac[0], HEX) + ":" + String(mac[1], HEX) + ":" + String(mac[2], HEX) + ":" +
                String(mac[3], HEX) + ":" + String(mac[4], HEX) + ":" + String(mac[5], HEX);
  return res;
}

void ICACHE_FLASH_ATTR wifi_handle_event_cb(System_Event_t *evt) {
  if (evt->event != EVENT_SOFTAPMODE_PROBEREQRECVED) return; //プローブ要求以外は無視

  uint8_t* mac = evt->event_info.ap_probereqrecved.mac;

  if (memcmp(mac,monitoringMAC1,6) == 0) detectingMAC1 = true;  //Thanks to @rukihena san
  if (memcmp(mac,monitoringMAC2,6) == 0) detectingMAC2 = true;
}

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

  connectWiFi();

  //既設アクセスポイントと同一のSSIDでsoftAP作成
  WiFi.softAP(WLAN_SSID, "pleasechangethisdummypassword");
  wifi_set_event_handler_cb(wifi_handle_event_cb);
}

void loop() {
  //milkcocoa->loop();  milkcocoa用
  if (detectingMAC1) {
    detectingMAC1 = false;
    detectMAC1();
  }

  if (detectingMAC2) {
    detectingMAC2 = false;
    detectMAC2();
  }
}

void connectWiFi() {
  WiFi.disconnect();
  delay(10);
  
  WiFi.mode(WIFI_AP_STA);
  WiFi.begin(WLAN_SSID, WLAN_PASS);

  while (WiFi.status() != WL_CONNECTED) {
    delay(450);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected");
}

void detectMAC1() {
  Serial.println("ADB1 detected!");
}
void detectMAC2() {
  Serial.println("ADB2 detected!");
}

今回はWIFI_AP_STAモード(自身はアクセスポイントになりつつ、既設のアクセスポイントにクライアントとしても接続するモード)を用いています。また複数台検知も容易になりました。

まずsetup()で無線ルータに接続し、softAP()によりアクセスポイント作成後にwifi_set_event_handler_cb();でWiFiのイベントを取得します。

wifi_handle_event_cb()ではプローブ要求以外のイベントであれば無視し、プローブ要求してきたクライアントのMACアドレスを取得・比較します。今回でも、このコールバック関数内ではdetectingMAC1, detectingMAC2のフラグを立て、loop()内で処理します。

またWiFiイベントとして以下に示すイベントも取得できるようです。

(user_interface.hより)

EVENT_STAMODE_CONNECTED = 0,
EVENT_STAMODE_DISCONNECTED,
EVENT_STAMODE_AUTHMODE_CHANGE,
EVENT_STAMODE_GOT_IP,
EVENT_STAMODE_DHCP_TIMEOUT,
EVENT_SOFTAPMODE_STACONNECTED,
EVENT_SOFTAPMODE_STADISCONNECTED,
EVENT_SOFTAPMODE_PROBEREQRECVED,
EVENT_MAX 

IoTとして他のサービスとの連携

Milkcocoa

void detectMAC1(char *msg) {
    DataElement elem = DataElement();
    elem.setValue("Event", msg);
    milkcocoa->push(MILKCOCOA_DATASTORE, &elem);
    Serial.println("Done.");
}

例えばdetectMAC1()を上記のように変更すれば、milkcocoaとの連携も可能になります。

Line Notify (15:42追記)

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

extern "C" {
  #include <user_interface.h>
}

const char* host = "notify-api.line.me";
String LINE_TOKEN = "yourtokenishere";
WiFiClientSecure client;

(中略)

void detectMAC1(String msg) {
  LinePost("message=" + msg, LINE_TOKEN);
}

String LinePost(String data, String Token) {
  Serial.println("httpsPost");
  if (client.connect(host, 443)) {
    Serial.println("Connect");
    client.println("POST /api/notify HTTP/1.1");
    client.println("Host: " + (String)host);
    client.println("User-Agent: ESP8266/1.0");
    client.println("Connection: close");
    client.println("Content-Type: application/x-www-form-urlencoded;");
    client.print("Authorization: Bearer ");
    client.println(Token);
    client.print("Content-Length: ");
    client.println(data.length());
    client.println();
    client.println(data);
    delay(10);
    String response = client.readString();
    int bodypos =  response.indexOf("\r\n\r\n") + 4;
    return response.substring(bodypos);
  }
  else {
    return "ERROR";
  }
}

Line Notifyのトークン取得後、上記コードにより連携可能となります。

セキュリティ問題

これまでのプロミスキャスモードと同様に、MACアドレスのみの照合なのでセキュリティ上の問題あり!どうしよう・・・

しかしWIFI_AP_STAモードを使うことでWiFi接続したままにできるので、他のサービスとの連携が一層スピーディーに行えるようになりました。例えばMilkcocoaへのpush時間は1秒もかからないです。まだ少し問題は残っているものの、外部サービスとの連携も低遅延で行えるようになりました。今後は押下イベント検出にARPが使えないかを検討していこうと思います。

##ToDo

  • Line Notify, Twitter, IFTTT, Slack等の連携
  • SSID, パスワードでの認証, ARP? ちょっと難しそう。

##参考文献
今回、以下の記事の内容・アイディアを参考に実装しました。

19
19
5

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
  3. You can use dark theme
What you can do with signing up
19
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?