Wio TerminalとSigfoxでWiFi位置情報推定

Wio Terminal内蔵のWi-FiがスキャンしたパブリックWi-Fiアクセスポイントの情報から、Sigfox Atlas Wifiを使用し、位置推定する方法を説明します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172313/f0cbe8dd-9690-8c7b-ed9a-c780f8562827.png)

#Wio Terminalの準備
##RTL8720ファームウェアアップロード
Wio Terminal搭載のWi-Fiチップ(RTL8720)のファームウェアアップデートが必要です。アップデート方法は、[公式Wiki](https://wiki.seeedstudio.com/Wio-Terminal-Network-Overview/)に記載されていますが、大きな流れは、

1. ファームウェア書き込み用ファイル（**[rtl8720_update_v2.uf2](http://files.seeedstudio.com/wiki/Wio-Terminal/res/rtl8720_update_v2.uf2)**）のダウンロード
2. ブートローダ起動（**Wio Terminal側面スイッチを2回スライド**: [here](https://wiki.seeedstudio.com/Wio-Terminal-Getting-Started/#faq)）
3. ブートローダモードになり、PC上に"Arduino"外部ドライブが表示されれば、上記1.でダウンロードした**uf2ファイルを移動**. (移動すると、Wio Terminalの画面に"Burn RTL8720 fw"と表示)
4. [最新ファームウェア](https://github.com/Seeed-Studio/seeed-ambd-firmware/releases)をダウンロード
5. ファームウェア書き込みツール（ambd_flash_tool）のダウンロード。
`git clone https://github.com/LynnL4/ambd_flash_tool`
6. 初期ファームウェアを削除 `ambd_flash_tool erase`
7. 新ファームウェアの書き込み `ambd_flash_tool flash -d [上記4.でダウンロードしたファイルフォルダのパス]`

##WiFi Scanning用のライブラリをインストール
1. [**Seeed_Arduino_rpcWiFi**](https://github.com/Seeed-Studio/Seeed_Arduino_rpcWiFi)リポジトリをZIPダウンロード
2. Arduino IDEの[スケッチ] _ [ライブラリをインクルード] _ [ZIP形式のライブラリをインストール]メニューから、1.でダウンロードしたファイルをインストール
3. インストールされると、[ライブラリをインクルード]メニューに**Seeed Arduino WiFi**が追加されます。
4. 同様に[**Seeed_Arduino_rpcUnified**](https://github.com/Seeed-Studio/Seeed_Arduino_rpcUnified)、[**Seeed_Arduino_mbedtls**](https://github.com/Seeed-Studio/Seeed_Arduino_mbedtls/tree/dev)、[**Wio Terminal FS**](https://github.com/Seeed-Studio/Seeed_Arduino_FS/tree/master)、[**Seeed_Arduino_SFUD**](https://github.com/Seeed-Studio/Seeed_Arduino_SFUD)、[**Seeed_Arduino_LCD**](https://github.com/Seeed-Studio/Seeed_Arduino_LCD)をインストール

#Sigfox Breakout BoardとWio Terminalの接続
Sigfox Breakout Board (BRKWS01 RC3)とWio Terminalの接続は、下図のように、3.3V電源入力とシリアルTx/Rxを接続してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172313/fbd049d7-e0f7-7426-ebd9-41358f050a9d.png)
![IMG_5546.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172313/214d9694-fe38-4495-c229-a3eae9cb23fc.jpeg)


#サンプルスケッチ
Wio Terminal内蔵のWi-FiモジュールでWi-Fiネットワークスキャンを行い、受信した上位2つのMACアドレスを、Sigfoxで送信するスケッチとなります。

```wio.ino.c
#include "rpcWiFi.h"
#include "TFT_eSPI.h"
#include <SoftwareSerial.h>

TFT_eSPI tft;

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

void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  
  // Set WiFi to station mode and disconnect from an AP if it was previously connected
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);
  
  //for button A
  pinMode(WIO_KEY_C, INPUT_PULLUP);

  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(TFT_BLACK);
  tft.setTextSize(2);
  debugDisplay("Setup Done");
  Serial1.print("AT$I=10\r");
}

void loop()
{
  displaySerial1();
  
  if (digitalRead(WIO_KEY_C) == LOW) {
    wifiScan();
  }
  delay(100);
}

void wifiScan()
{
  tft.fillScreen(TFT_BLACK);
  tft.setCursor(0,0);
  
  AP ap[2];   //RSSI上位2局のWiFi AP情報
  debugDisplay("Wifi Scan Start");
  
  // WiFi.scanNetworks will return the number of networks found
  int n = WiFi.scanNetworks();
  debugDisplay("Wifi Scan Done");
  if (n == 0) {
    debugDisplay("No Networks Found");
  } else {
    debugDisplay("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(":", "");
      // Sigfoxメッセージとして送信するWiFi MACアドレスを最大2つに絞る
      filterAP(ap, WiFi.SSID(i), WiFi.RSSI(i), mac);
    }
  }

  String msg = "AT$SF=" + ap[0].mac + ap[1].mac; //Sigfoxメッセージ
  debugDisplay("Sigfox Send Message");
  debugDisplay(msg);
  Serial1.println(msg);
  
  delay(5000);
}

void filterAP(AP* ap, String ssid, int rssi, String mac) {
  if (filterMac(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 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;
}

void displaySerial1()
{
  if (Serial1.available()) {
    int w = Serial1.read();
    tft.print(char(w));
    Serial.write(w);
  }
}

void debugDisplay(String s)
{
  tft.println(s);
  Serial.println(s);
}
```

上記サンプルスケッチをコンパイル・書き込みすることにより、Wio Terminalの"Cボタン"をプッシュすると、Wi-FiスキャンとSigfoxメッセージの送信ができ、下図のようにSigfoxクラウドに2つのMACアドレスが送信されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172313/90ce30f3-bd91-8dfe-ef5e-2eac69e1a995.png)

#Atlas Wi-Fiによる位置情報推定
スキャンしたMACアドレスをSigfoxクラウドに送信できるようになれば、最後に**Data Advanced Callback**を設定することにより、位置情報を取得する事ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172313/201646ae-a47f-145f-571f-41fe2326c03b.png)
CallbackされるJSON Bodyのうち、**[computedLocation]**という変数に計算された位置情報が取り込まれます。

```data_advanced_callback.json
{ 
  "deviceId": "73E6BB", 
  "time": 1607677923, 
  "seqNumber": 50, 
  "computedLocation": {"lat":35.75xxxxx,"lng":139.71xxxxx,"radius":250,"source":6,"status":1}
}
```

#Sigfox Atlas WiFiの仕組み
Sigfox Atlas WiFiは、IoTデバイスの位置情報をGPSよりも安価・低消費電力で提供可能なSigfox Atlasサービスのひとつです。
Atlas Wi-Fiの仕組みや注意事項は[こちら(HERE Positioning APIとSigfox Atlas WiFiの検証)](https://qiita.com/ghibi/items/e845832c7e5571787c1d)で確認してください。

[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172313/d6ad10bc-75ba-8124-14d4-f144657c6473.png)](https://twitter.com/ghibi)