# 期末模組與函式庫
###### tags: `物聯網實境遊戲應用課程`
## 期末戰提供資源
### 模組
* KSM111_ESP8266 WiFi module
* MF-RC522 RFID reader
### [函式庫](https://github.com/LanKuDot/iot_brc)
* KSM111_ESP8266:與 KSM111_ESP8266 WiFi 模組直接溝通的基本 API
* BRCClient:建構在 KSM111_ESP8266 之上,用來與中控台溝通的 API
* RFID:與 MF-RC522 模組溝通的 API
#### 將函式庫匯入 Arduino IDE
1. 找到 Arduino IDE 存放使用者專案的地方 (eg. C:/My Documents/Arduino/),進入 `libraries` 資料夾
2. 將上述三個函式庫的資料夾直接放到這個資料夾中
3. 重新開啟 Arduino IDE,檢查選單中的 library 有無剛剛放入的函式庫名稱
4. 在程式碼中使用函式庫:`#include <library_name.h>`
5. 每個函式庫都有附範例,可以在 IDE 的 Example -> Custom Libraries 中打開
* 範例可以直接修改與編譯,但 IDE 不允許範例直接存檔,必須需要另存新檔
### App
* BRC Server ([Android](https://drive.google.com/file/d/0B8l9tuYB4QokR25yTnd4U1VhTFU/view?usp=sharing)):模擬中控台運作,供測試用的 APP
#### 匯入 RFID 地圖資訊檔 (v1.3 之後)
* 檔名:`map.dat`
* 放置路徑:[內部 或 SD卡 或 `storage/emulated/0`] `/Android/data/com.LanKuDot.iotBRC/files`
* 第一次啟用會自動建立對應的資料夾,如果找不到對應資料夾可以先啟動一次 APP
* 當 APP 找不到檔案時會提示檔案放置的路徑
* [已知 Bug] 然而提示的檔案路徑不一定正確,像助教的手機測試提示 `storage/emulated/0/`,但實際位置為內存,因此助教列出三個可能的檔案路徑,正確的檔案路徑會有上述路徑的資料夾
* 地圖資訊檔格式
* 一行一個地圖資訊
* `8 字元 16 進制 RFID SN (大寫)` `空格` `2 字元 x 座標` `空格` `2 字元 y 座標` `空格` `4 字元地圖類型 0xNN`
* 例如:`C5B538D5 01 02 0x22`
* [第三戰的地圖資訊檔](https://drive.google.com/open?id=0B8l9tuYB4QokNGZOQVNUcklQMnM)
#### 第三戰相關 RFID 的寶藏內容與 server 行為
* 當 APP 讀完地圖後,會列出所有寶藏的 serial number
* 只要 BRCClient request 寶藏 serial number,就會視為找到
* 按 Start Round 可以將所有寶藏設為未找到,當所有寶藏 serial number 都被找到後,Server 自動發送 `MSG_ROUND_END`
## 模組使用教學
### WiFi module
* 供電:5V/3.3V
* 通訊:UART 配合 [SoftwareSerial](https://www.arduino.cc/en/Reference/SoftwareSerial)
* 接線:
* VCC:5V/3.3V
* GND:接地
* TXD:接 SoftwareSerial 的 RX
* RXD:接 SoftwareSerial 的 TX
* RST:可不接,與接地短路一下可以重啟模組 (Hard reset)
* 由於 WiFi 模組在收送訊息時需要較多的耗電量,所以需要獨立供電。以下提供一些隊伍的解決方案:
* 電源為電池,利用 L298N 的穩壓 IC 讓 +5V 可以**當 5V 輸出**用
* L298N 使用的穩壓 IC 為 78M05,輸出電壓為 5V,最大輸出電流為 500mA (瞬間為 750 mA)。輸入輸出的壓差 (dropdown voltage) 要有 2V 以上才會有穩定的 5V 輸出
* ESP8266 發訊息時最大耗電流為 215 mA
* 接法一:+5V 只供應 WiFi 模組
![](https://i.imgur.com/GTUfJsO.png)
* 接法二:+5V 同時供應 Arduino 與 WiFi 模組
![](https://i.imgur.com/Mx3iugV.png)
* 利用三用電表確認 Arduino 運作時的電流量,以確認接法二是否適合使用
![](https://i.imgur.com/bvnrCWW.png)
### RFID Reader
* **供電:3.3V**
* 通訊:[SPI](https://www.arduino.cc/en/Reference/SPI)
* 接線:
* VCC:3.3V
* GND:接地
* MOSI:SPI 的 MOSI
* MISO:SPI 的 MISO
* SCL:SPI 的 SCK
* SDA:SPI 的 SS (SPI 的 Slave Select 是 Low active)
* RST:Reset and Power down pin,用來讓模組重設的 pin 腳
* 接線圖 (右鍵檢視圖片都可以看原始大小圖片)
* 由於 Arduino 的 SPI 是以 master mode 運行,所以模組的 SDA (SPI 的 SS) 腳位可以自由選擇,不一定要接到 Arduino 的 SS 腳位
* 模組的 RST 腳位接線也可以自由選擇
* UNO
![1](https://i.imgur.com/zPIvcJP.png =400x)
* MEGA
![](https://i.imgur.com/pBICRxn.png =x400)
## BRCClient
與中控台連線並能夠與所有其他連線的 BRCClient 傳遞訊息的 API。
由於 `BRCClient` 是 public 繼承自 `KSM111_ESP8266`,所以可以直接透過 `BRCClient` 的物件直接使用 `KSM111_ESP8266` 的成員函式。
這個函式庫使用 `SoftwareSerial` 做為與 WiFi 模組溝通的介面,注意 `SoftwareSerial` 做為 `RX` [腳位的限制](https://www.arduino.cc/en/Reference/SoftwareSerial),Arduino Uno 只有 pin 2, 3 可以做為 RX。
### 常用 API 說明
* 使用函式庫:`#include <BRCClient.h>`
* 建構子:`BRCClient(int Rx, int Tx, int resetPin = -1)`
* 建立 `BRCClient` 物件並初始化 `SoftwareSerial`
* `Rx`:`SoftwareSerial` 的 RX pin,與模組的 TXD pin 相接
* `Tx`:`SoftwareSerial` 的 TX pin,與模組的 RXD pin 相接
* `resetPin`:可不指定,與模組的 RST pin 相接
* 連線到 BRC Server:`bool beginBRCClient(const char *ssid, const char *passwd, const char *serverIP, const int port)`
* 與 server 所在的 AP 連線,並連到 server 上
* 注意本函式不會開啟 `SoftwareSerial`,需要呼叫 `KSM111_ESP8266::begin()` 來啟用
* `ssid`:AP 的名稱
* `passwd`:AP 的密碼,`""` 代表為無密碼
* `serverIP`:server 的 IP
* `port`:server 的 port
* 回傳:`true` 成功連線到 server
* 離開 BRC Server:`bool endBRCClient()`
* 離開 server 並退出連線的 AP
* 發訊息:`bool sendMessage(CommMsg *msg)`
* 傳送訊息給 BRC Server 交由 Server 處理訊息
* 回傳:`true` 傳送成功
* 收訊息:`bool receiveMessage(CommMsg *msg)`
* 從 BRC Server 收取訊息,並存放到 `msg` 中
* 回傳:`true` 有訊息傳入
### 範例
* RegisterID:如何註冊到 Server 中
* DataReceiver:顯示從 Server 收到的 raw data
* DataSender:週期性的傳遞自訂訊息給其他裝置
* RoundTimer:示範回合開始與結束的功能
### 訊息資料結構:`CommMsg`
BRCServer 與 BRCClient 之間主要的通訊資料結構,所有通訊資料都會以此資料結構打包傳送。
```c
#define COMM_MSG_BUF_LEN 30
typedef struct COMM_MESSAGE {
char type; // 記錄資料類型
char ID; // 記錄發送/接收者 ID
char buffer[COMM_MSG_BUF_LEN]; // 額外資料區,至少要保留 1 byte 給 null character
} CommMsg;
```
* 注意收發訊息遇到 `0x00` (`\0`, null terminated character) 會視為訊息結束
#### 資料類型說明
* 資料 ID 有對應的 macro 定義在 `CommMsg.h` 中
* **MSG_REGISTER**:0x01,註冊通訊裝置的代表 ID
* 發送資料格式
* `type`:`MSG_REGISTER`
* `ID`:要註冊的 ID (1 byte)
* `buffer`:無
* 中控台會紀錄該 IP 對應的 ID,之後透過中控台與其他通訊裝置只需指定 ID
* 中控台回應
* `type`:`MSG_REGISTER`
* `ID`:剛剛註冊的 ID
* `buffer`:"OK"/"FAIL"
* "FAIL":代表 ID 重覆或無效 ID
* 被保留的 ID
* `0xFF`:尚未有有效 ID
* `0x01~0x0F`:特殊用途
* `0x01`:BRC server
* **MSG_REQUSET_RFID**:0x10,詢問 RFID SN 對應的座標
* 發送資料格式
* `type`:`MSG_REQUEST_RFID`
* `ID`:無
* `buffer`:4 bytes 的 RFID serial number
* 中控台回應
* `type`:`MSG_REQUEST_RFID`
* `ID`:0x01
* `buffer`:4 bytes RFID serial number + 1 byte x 座標 + 1 byte y 座標 + 1 byte 類型
* 如果收到座標為 0xFF, 0xFF 代表請求的 RFID 是無效的。而且只有發送者才會收到 0xFF, 0xFF
* 地圖類型定義
* header file:`MapMsg.h`
* MAP_NORMAL = 0x01
MAP_TREASURE = 0x02
MAP_PARK_1 = 0x21
MAP_PARK_2 = 0x22
MAP_PARK_3 = 0x23
MAP_PARK_4 = 0x24
MAP_INVAILD = 0xFF
* App Server 對於 RFID 管理的應對見「期末戰提供資源 → APP」
* **MSG_ROUND_COMPLETE**:0x11,回傳回合完成
* 用於認為自走車完成任務後傳送,讓 Server 停止計時
* 發送資料格式
* `type`:`MSG_ROUND_COMPLETE`
* `ID`:無
* `buffer`:無
* 當中控台收到所有回合完成後,會發送 `MSG_ROUND_END` 給所有通訊裝置 (包含最後發送回合完成的裝置)
* **MSG_ROUND_START**:0x20,回合開始
* 只能由中控台發送
* 中控台發送格式
* `type`:`MSG_ROUND_START`
* `ID`:`0x01`
* `buffer`:無
* **MSG_ROUND_END**:0x21,回合結束
* 只能由中控台發送
* 中控台發送格式
* `type`:`MSG_ROUND_END`
* `ID`:`0x01`
* `buffer`:無
* **MSG_CUSTOM**:0x70,發送自訂訊息給其他連線的裝置
* 發送資料格式
* `type`:`MSG_CUSTOM`
* `ID`:接收訊息的裝置 ID
* `buffer`:自定訊息,長度不能超過 `COMM_MSG_BUF_LEN - 1`
* 發送者收到中控台回應
* `type`:`MSG_CUSTOM`
* `ID`:發送者的 ID,以區分來自其他 client 的訊息
* `buffer`:"OK"/"FAIL"
* "FAIL":代表目標 ID 不存在或已經斷線
* 接收者會收到
* `type`:`MSG_CUSTOM`
* `ID`:發送者的 ID
* `buffer`:自定訊息,長度不能超過 `COMM_MSG_BUF_LEN - 1`
* **MSG_CUSTOM_BROADCAST**:`0x71`,廣播自訂訊息給所有連線裝置
* 發送資料格式
* `type`:`MSG_CUSTOM_BROADCAST`
* `ID`:無
* `buffer`:自定訊息,長度不能超過 `COMM_MSG_BUF_LEN - 1`
* 發送者收到中控台回應
* `type`:`MSG_CUSTOM`
* `ID`:發送者的 ID,以區分來自其他 client 的訊息
* `buffer`:"OK"
* 接收者會收到
* `type`:`MSG_CUSTOM`
* `ID`:發送者的 ID
* `buffer`:自定訊息,長度不能超過 `COMM_MSG_BUF_LEN - 1`
## KSM111_ESP8266
與 WiFi 模組溝通的基本 API,使用 AT command 操作。
### 常用 API 說明
* 啟用 `SoftwareSerial`:`bool begin(long baudrate)`
* 啟用 `SoftwareSerial` 並測試模組有無正確啟動
* `baudrate`:設定 `SoftwareSerial` 的 baudrate
* 回傳:`true` 模組整確啟動
* 重設與重啟模組:`bool softreset()`
* 透過指令讓 WiFi 模組重啟,需等待 5 秒
* 回傳:`true` 成功重啟
* 設定預設 baudrate:`bool setBaudrate(long baudrate)`
* 指定 WiFi 的預設 baudrate 並且更新 `SoftwareSerial` 的 baudrate
* 注意:重啟並不會重設 baudrate
* 由於 baudrate 更換時可能會收到一些亂碼而造成函式判斷錯誤,所以可以呼叫兩次確認有沒有設定成功
```c
// Change baudrate from 115200 to 9600
setBaudrate(9600); // May return false becasue of changing to new baudrate.
serBaudrate(9600); // It will return true.
```
* 回傳:`true` 成功設定
### 範例
* SetBaudrate:設定 WiFi module 的預設 baudrate
## RFID
`MFRC522` 是與 MF-RC522 模組溝通的基本 API,而 `RFID` 則是以 public 繼承自 `MFRC522`
### 常用 API 說明
* 建構子:`RFID(int selectPin, int resetPowerDownPin)`
* 初始化 pin 腳
* `selectPin`:與模組的 SDA pin 相接
* `resetPowerDownPin`:與模組的 RTPD pin 相接
* 初始化並啟用模組:`void begin()`
* 該函式會呼叫 `pcdReset()`、`pcdInit()`、`pcdAntennaOn()` 來重設與啟用模組
* 模組天線:`void pcdAntennaOn()`, `void pcdAntennaOff()`
* 啟用/關閉模組天線,要啟用才能讀取 RFID tag
* 偵測有無 RFID tag:`uint8_t find_tag(uint16_t *card_type)`
* `card_type`:輸出記錄 16-bit 的卡片類型資訊
* 回傳:
* `STATUS_OK`:有偵測到 tag
* `STATUS_TIMEOUT`:沒有偵測到 tag
* `STATUS_ERROR`:tag 資訊讀取出錯
* `STATUS_COLLISION`:有多組 tag 出現
* `STATUS_PCD_NO_RESPONSE`:模組出錯
* 讀取 tag serial number:`uint8_t readTagSN(uint8_t *sn, uint8_t *snBytes)`
* `sn`:存放讀取到的 serial number 的 buffer,至少 4 bytes,至多 10 bytes
* `snBytes`:記錄在 `sn` 中有多少 bytes 是有效的
* 回傳:`STATUS_OK`,正確讀出 serial number
### 範例
* readTagSN:讀取 tag 的 serial number