WiFiモジュール(ESP8266)で作るワンコイントラッカー(2)

[WiFiモジュール(ESP8266)で作るワンコイントラッカー(1)](https://qiita.com/ghibi/items/fed5664028ee0172cdc3)に続いて、ここでは、ESP8266で取得したWiFiアクセスポイント情報を、Sigfoxネットワークで送信するところまでを試します。

#用意するもの
|品名|数量|参考|
|:--|:--:|:--|
|ESP-WROOM-02開発ボード(ESP8266を搭載した開発ボード)|1|[ESPr® Developer](https://www.switch-science.com/catalog/2500/)|
|Sigfoxシールド(Sigfox通信モジュールを搭載したArduinoシールド)|1|[UnaShield](https://www.switch-science.com/catalog/3354/)|
|ブレッドボード|1||
|ジャンパーケーブル|数本||
すでに、ご予算的には、オーバーしていますが、ちゃんと基板をおこせる方は、ESP8266とSigfoxモジュールで作ってもらうと安価に作れます。

#ESP-WROOM-02をArduino化する
[前回](https://qiita.com/ghibi/items/fed5664028ee0172cdc3)は、ESP8266をPCからATコマンドを使って動作確認しましたが、今回は、ESP8266をArduinoとして動かし、WiFiアクセスポイント情報を取得したうえで、Sigfoxシールドに対してシリアルATコマンドを使ってデータ送信します。

##Arduino IDEにESP8266ボードをインストール
###ESP8266ボードのインストール
Arduino IDEを起動し、環境設定を開き、「追加のボードマネージャのURL」に下記URLを入力します。
http://arduino.esp8266.com/stable/package_esp8266com_index.json
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172313/11b8b2f0-9727-ca33-a5ee-75184add711e.png)
この設定を入れておくと、ボードマネジャにesp8266が追加されますので、最新版をインストールします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172313/8d94e50b-692e-7cc6-8701-70db845f65b5.png)
###ESP8266ボードの設定
ESP-WROOM-02をPCと接続しておき、ボードの一覧から"Generic ESP8266 Module"を選択し、ボードの設定をします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172313/61318934-0f56-f545-8389-e390d33eeb36.png)
これで、スケッチをマイコンボードに書き込むことが可能になりました。

#ESP8266とSigfoxシールドの接続
今回は、ESP8266をArduino化し、Sigfoxシールドと接続します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172313/60a3837e-bb7d-c894-510c-bf23b4036f20.png)
Sigfoxシールド(UnaShield)は、Arduinoボードとの複数のPIN接続がありますが、内蔵Sigfoxモジュールを動かすためだけであれば、下図の通り、3V3*,5V,GND,RXD*(D5),TXD*(D4)の5つのPINだけでも動作可能です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172313/db1587b7-8fe0-0952-eea9-fd327df957eb.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/172313/90f0ccdf-430f-907a-3e9a-b1f31eeff114.png)

#ESP8266 Sigfoxトラッカースケッチ
このサンプルスケッチでは、ESP8266で、`WiFi.scanNetworks`を行い、アクセスポイント一覧を取得し、String Atlas-WiFiのガイドラインに従い、上位2局のMACアドレスをSigfoxメッセージ送信コマンド`AT$SF=[payload]`で送信する例となっています。
EEPROMは、デザリング端末などのWiFiアクセスポイントを除外するために、そのSSID一覧を操作するために使用しています。RAMのサイズが十分にあるマイコンを使う場合は、EEPROMを使用する必要はありませんが、Arduino UnoはRAMが1024バイトしかないため、EEPROMを使用する必要があります。

```
#include <SoftwareSerial.h>
#include <ESP8266WiFi.h>
#include <EEPROM.h>

int _wifiscan_ssidfilerlen = 12;

struct AP {
  String ssid = "";
  int rssi = -999;
  String mac = "";
};

SoftwareSerial SerialSig(14, 12);

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

  writeIgnoreSSID();
  
  setupWifi();
  setupSigfox();
}

void setupWifi() {
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);

  Serial.println("[Wifi] Setup done.");
}

void setupSigfox() {
  SerialSig.begin(9600);

  SerialSig.write("AT");
  SerialSig.write(10);

  SerialSig.write("ATS410=0");
  SerialSig.write(10);

  SerialSig.write("AT$IF=923200000");
  SerialSig.write(10);  
}

void loop() {
  AP ap[2];   //RSSI上位2局のWiFi AP情報
  
  Serial.println("[WiFi] scan start");

  int n = WiFi.scanNetworks();
  Serial.println("[Wifi] scan done");
  if (n == 0) {
    Serial.println("no networks found");
  } else {
    Serial.print(n);
    Serial.println(" networks found");
    for (int i = 0; i < n; i++) {
      // Print SSID and RSSI for each network found
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print(" (");
      Serial.print(WiFi.RSSI(i));
      Serial.print(")");
      String mac = WiFi.BSSIDstr(i);
      Serial.println(mac);
      mac.replace(":", "");
      
      filterAP(ap, WiFi.SSID(i), WiFi.RSSI(i), mac);
    }
  }
  Serial.println("");

  String msg = "AT$SF=" + ap[0].mac + ap[1].mac; //Sigfoxメッセージ
  Serial.println(msg);
  SerialSig.write(msg.c_str());
  SerialSig.write(10);

  delay(600000);
}

void filterAP(AP* ap, String ssid, int rssi, String mac) {
  if (filterMac(ssid, mac)) {
    if (ap[0].rssi < rssi) {
      ap[1] = ap[0];
      ap[0].ssid = ssid;
      ap[0].rssi = rssi;
      ap[0].mac = mac;
    } else if (ap[1].rssi < rssi) {
      ap[1].ssid = ssid;
      ap[1].rssi = rssi;
      ap[1].mac = mac;
    }
  }
}

bool filterMac(String ssid, String mac) {
  bool pass = true;
  pass = filterMacBySsid(ssid);
  if (pass) {
    pass = filterMacByMac(mac);
  }
  return pass;
}

bool filterMacBySsid(String ssid) {
  bool pass = true;
  for (int i = 0; i < _wifiscan_ssidfilerlen; i++) {
    String filteredSsid = read_data_from_rom(i);
    if (ssid.indexOf(filteredSsid) >= 0) {
      pass = false;
      break; 
    }
  }
  return pass;
}

bool filterMacByMac(String mac) {
  bool pass = true;
  if (mac.length() != 12) pass = false;
  //リザーブMACアドレスならフィルタリング
  if (pass) {
    if (mac.equalsIgnoreCase("000000000000") || mac.equalsIgnoreCase("FFFFFFFFFFFF")) {
      pass = false;
    }
  }
  //マルチキャストMACアドレスならフィルタリング
  if (pass) {
    long LSBbit_1stOct = strtol(mac.substring(0,2).c_str(), NULL, 16);
    if (LSBbit_1stOct % 2 == 1) {
      pass = false;
    }
  }
  return pass;
}

//EEPROMに文字列(6byte)を書き込む
//注意：EEPROMのサイズはEEPROM.beginで設定したサイズ
void write_data_to_rom(int address, String String_data) {
  const int MAX_LEN = 6;
  byte Byte_data[MAX_LEN+1];
  String_data.getBytes(Byte_data, MAX_LEN+1);
  int k;
  int start = address * (MAX_LEN+1);
  for (k = 0; k < MAX_LEN && Byte_data[k] != '\0'; k++) {
    EEPROM.write(start + k, Byte_data[k]);
  }
  EEPROM.write(start + k, '\0');
}

//EEPROMから文字列(6byte)を読み込む
String read_data_from_rom(int address) {
  const int MAX_LEN = 6;
  String data_from_rom;
  int start = address * (MAX_LEN+1);
  for (int i = 0; i < MAX_LEN && EEPROM.read(start + i) != '\0'; i++) {
    char c = EEPROM.read(start + i);
    data_from_rom = data_from_rom + String(c);
  }
  return data_from_rom;
}

void writeIgnoreSSID() {
  EEPROM.begin(512);

  write_data_to_rom(0, "android-");
  write_data_to_rom(1, "phone");
  write_data_to_rom(2, "samsung");
  write_data_to_rom(3, "huawei");
  write_data_to_rom(4, "iPad");
  write_data_to_rom(5, "iPhone");
  write_data_to_rom(6, "W01_");
  write_data_to_rom(7, "W03_");
  write_data_to_rom(8, "W04_");
  write_data_to_rom(9, "wx01-");
  write_data_to_rom(10, "wx02-");
  write_data_to_rom(11, "wx03-");
  
  EEPROM.commit();
}
```


###お薦めサンプル
ESP8266やSigfoxモジュールの低消費電力化のためのDeep Sleepや、その他お作法をしっかりとやっているサンプルが、下記GitHubにあがっていますので、参考にしてください。
https://github.com/disk91/esp8266-sigfox-trackr


#Sigfox Atlas-WiFiの利用にあたって
このスケッチを動かすと、定期的にWiFiスキャンしたデータをSigfoxクラウドに送信することができます。次は、**Altas-WiFiサービス**が必要となりますので、Sigfox Operator（日本は、[京セラコミュニケーションシステム](https://www.kccs.co.jp/sigfox/)）にお問い合わせください

[Sigfox Japan KCCS](https://qiita.com/organizations/sigfox)