#概要
プロミスキャスモードのESP8266を用いることで、Amazon Dash Button (ADB)のイベント取得に成功しました。
これによりRaspberry Pi等を必要とせずにESP8266単独での検出が可能となりました。今回はESP-WROOM-02, Arduino 1.6.13を利用しています。
ESP8266とは
秋月電子等で購入可能なWiFi通信可能なモジュール(500~600円程度)。Arduinoで開発可能。
Wi-Fiモジュール ESP-WROOM-02 DIP化キット http://akizukidenshi.com/catalog/g/gK-09758/
Amazon Dash Buttonとは
ボタンを押すだけで、自動注文により商品を届けてくれるIoTデバイス。今回のように注文せずに、ボタン押下イベント取得だけをしたい場合は「ボタンが押されると届く商品の設定」をキャンセルする必要があります。
参考
- Amazon Dash Buttonの仕組みとハック: http://qiita.com/dkawashi/items/e6621c4b712b509c73ec
- Amazon Dash ButtonをただのIoTボタンとして使う: http://qiita.com/jsoizo/items/3b8bba4160f41aef20f4
WiFiチャンネルとボタンのMACアドレスを取得
プロミスキャスモードでの動作にはWiFiチャンネルが、またAmazon Dash Buttonのイベント取得にはMACアドレスが必要となります。
それらを取得するために、まず以下のスケッチをESP8266に書き込み、実行します。
#include <ESP8266WiFi.h>
#include "ESP_Promiscuous.h"
//プロミスキャスモードのAPIを利用するために必要
extern "C" {
#include <user_interface.h>
}
//WiFi接続部 参考:http://qiita.com/azusa9/items/7f78069cb09872cf6cbf
char toSSID[] = "SSID";
char ssidPASSWD[] = "Password";
//プロミスキャスモードでパケットを受け取ったときのcallback
static void ICACHE_FLASH_ATTR promisc_cb(uint8_t *buf, uint16_t len)
{
if (len != 128) return; //In order to discard both unknown packets and data packets
struct sniffer_buf2 *sniffer = (struct sniffer_buf2*) buf;
struct MAC_header *mac = (struct MAC_header*) sniffer->buf;
int i;
boolean beaconFlag = true;
for (i=0; i<6; i++) if (mac->addr2[i] != mac->addr3[i]) beaconFlag = false;
if (beaconFlag) return; //In order to remove beacon Packet
Serial.print("Possible MAC Address: ");
for (i=0; i<6; i++){
Serial.print(mac->addr2[i], HEX);
Serial.print(":");
}
Serial.println("");
}
void setup() {
byte channel;
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(toSSID, ssidPASSWD);
Serial.print("WiFi connecting.");
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
channel = wifi_get_channel();
Serial.print("Your WiFi Channel: ");
Serial.println(channel);
WiFi.disconnect();
Serial.println("");
Serial.println("ESP8266 promiscuous mode started.");
Serial.println("Please push Amazon Dash Button");
Serial.println("");
wifi_set_opmode(STATION_MODE);
wifi_set_channel(channel);
wifi_set_promiscuous_rx_cb(promisc_cb);
wifi_promiscuous_enable(1);
}
void loop() { }
struct RxControl {
signed rssi:8;
unsigned rate:4;
unsigned is_group:1;
unsigned:1;
unsigned sig_mode:2;
unsigned legacy_length:12;
unsigned damatch0:1;
unsigned damatch1:1;
unsigned bssidmatch0:1;
unsigned bssidmatch1:1;
unsigned MCS:7;
unsigned CWB:1;
unsigned HT_length:16;
unsigned Smoothing:1;
unsigned Not_Sounding:1;
unsigned:1;
unsigned Aggregation:1;
unsigned STBC:2;
unsigned FEC_CODING:1;
unsigned SGI:1;
unsigned rxend_state:8;
unsigned ampdu_cnt:8;
unsigned channel:4;
unsigned:12;
};
struct LenSeq {
uint16_t length;
uint16_t seq;
uint8_t address3[6];
};
struct sniffer_buf {
struct RxControl rx_ctrl;
uint8_t buf[36];
uint16_t cnt;
struct LenSeq lenseq[1];
};
struct sniffer_buf2{
struct RxControl rx_ctrl;
uint8_t buf[112];
uint16_t cnt;
uint16_t len;
};
struct MAC_header {
uint16_t frameControl;
uint16_t duration;
uint8_t addr1[6];
uint8_t addr2[6];
uint8_t addr3[6];
uint16_t sequenceControl;
uint8_t addr4[6];
};
そして実行し、ボタンを押すと検出してくれます。
2行目の5
がWiFiチャネル、12:34:56:78:9a:bc
がMACアドレスとなります。
Amazon Dash Buttonが関連する通信が10~15個程、表示されます。
ARPプローブ等が送受信されてると思いますが、詳細は把握しておりません。
WiFi connecting......
Your WiFi Channel: 5
ESP8266 promiscuous mode started.
Please push Amazon Dash Button.
Possible MAC Address: 12:34:56:78:9a:bc:
Possible MAC Address: 12:34:56:78:9a:bc:
(以下、続く)
イベント取得
続いて、得られたWiFiチャンネルとMACアドレスを用いて、Amazon Dash Buttonの通信を検知します。
2017/01/02 21:55修正: ご指摘頂きました方々にお礼申し上げます。また本ソースではESP8266WiFi.hの関数を用いていないので、include無しでもコンパイルが通ると思いますが如何でしょうか。不備等ございましたら、ご指摘いただけると幸いです。
#include "ESP_Promiscuous.h"
extern "C" {
#include <user_interface.h>
}
byte channel = 5; //WiFi channel (1-13)
uint8_t targetMAC1[6] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc}; //Amazon Dash Button MAC address
int detectInterval = 8000; //msec
unsigned long lastDetectedMillis = 0;
boolean ADBdetectingNow = false; //Amazon Dash Buttonイベント検出中フラグ
static void ICACHE_FLASH_ATTR promisc_cb(uint8_t *buf, uint16_t len)
{
if (ADBdetectingNow) return; //Amazon Dash Buttonイベント検知中(detectInterval指定ミリ秒間)であれば無視
if (len == 12){
//No accurate information about MAC address and length of the head of packet.
struct RxControl *sniffer = (struct RxControl*) buf;
return;
} else if (len == 128) {
//Management Packet
struct sniffer_buf2 *sniffer = (struct sniffer_buf2*) buf;
struct MAC_header *mac = (struct MAC_header*) sniffer->buf;
int i;
boolean MAC_Matching_Flag = true;
for (i=0; i<6; i++) if (mac->addr2[i] != targetMAC1[i]) MAC_Matching_Flag = false;
if (!MAC_Matching_Flag) return; //No hit
//Handle it.
Serial.println("ADB push detected.");
ADBdetectingNow = true;
lastDetectedMillis = millis();
return;
} else {
//Data Packet
struct sniffer_buf *sniffer = (struct sniffer_buf*) buf;
struct MAC_header *mac = (struct MAC_header*) sniffer->buf;
return;
}
}
void setup() {
Serial.begin(115200);
wifi_set_opmode(STATION_MODE);
wifi_set_channel(channel);
wifi_set_promiscuous_rx_cb(promisc_cb);
// Start!
wifi_promiscuous_enable(1);
}
void loop() {
unsigned long interval;
if (lastDetectedMillis > millis()) //稼働時間が49.7日を超えmillis()がオーバーフローした場合
interval = (0xffffffff - lastDetectedMillis) + millis();
else
interval = millis() - lastDetectedMillis;
if (interval > detectInterval)
ADBdetectingNow = false;
}
上記スケッチ起動後にADBのボタンを押すとADB push detected.
と表示されるはずです。
#結論
ADBのイベントを取得し、Lチカ等への展開ができそうです。
欠点としてESP8266はプロミスキャスモードでの動作時、WiFiの接続ができなくなってしまう点が挙げられます。
しかしプロミスキャスモードを解除すれば、WiFiの接続ができるようになるので、Milkcocoa等の他のサービスと連携も可能です。
後日、その詳細を書きたいと思います。
12/30 23:38追記 ESP8266によるAmazon Dash ButtonとMilkcocoaとの連携 書きました
またAPIリファレンスを眺めていると、WPSを利用してWiFi接続できそうだったり、WiFi経由でSleepから起こせそうだったりと色々と面白そうなAPIがありましたので、今後そういったことも記事にしていきたいと思います。