# 期末模組與函式庫 ###### 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