RFID(Radio Frequency Identification, 無線射頻辨識)是一種無線通訊技術,可以通過無線電訊號識別特定目標並讀寫相關數據,而無需識別系統與特定目標之間建立機械或者光學接觸。
無線電的訊號是通過調成無線電頻率的電磁場,把數據從附著在物品上的標籤上傳送出去,以自動辨識與追蹤該物品。某些標籤在識別時從識別器發出的電磁場中就可以得到能量,並不需要電池(透過磁場變化產生電流,再將產生的電流輸出訊號給識別的機器);也有的標籤本身擁有電源,並可以主動發出無線電波。
註:「射頻」這個詞聽器來有點像中國用語,但在國教院名詞資訊網中查得到這個翻譯。(https://terms.naer.edu.tw/detail/178799/ )

在我們身上常見的就是悠遊卡與房間的磁卡,可以將悠遊卡用有機溶劑溶解後就能取得裡面的線圈。(下面是大學時將他與科技之夜的壓克力票卡結合)
RFID也有模組可以讓我們製作出一些小專題,而現在常見的模組是基於RC522 IC所設計出的模組,他支援13.56 MHz的頻率讀取,因此我們的感應卡也需要購買符合該頻率的感應卡才能使用。

這邊插入一下常見的RFID頻率,如果使用錯誤的卡片會無法讀取資料。

對RFID本身有基本的認識後,接下來我們就可以將RC522模組連接上開發板,並使用程式讓開發板做出反應囉。
連接方式:

SDA -> 10
SCK ->13
MOSI ->11
MISO ->12
IRQ -> (NONE)
GND ->GND
RST -> 9
連接好之後,將Arduino連接上電腦,並開啟Arduino。
接下來進入Arduino的程式管理員中,輸入「MFRC522」,然後可以看到很多人寫的程式庫,這邊我們先下載較多人使用的「GithubCommunity」的程式庫。


點選安裝後,我們就可以開始讓我們的RC522開始讀取RFID晶片卡中的內容囉。
RFID讀卡程式:
#include <SPI.h>
#include <MFRC522.h>
#define RST_PIN 9
#define SS_PIN 10 //就是模組上的SDA接腳
MFRC522 mfrc522; // 建立MFRC522實體
void setup() {
Serial.begin(9600);
SPI.begin(); // 初始化SPI介面
mfrc522.PCD_Init(SS_PIN, RST_PIN); // 初始化MFRC522卡
Serial.print(F("Reader "));
Serial.print(F(": "));
mfrc522.PCD_DumpVersionToSerial(); // 顯示讀卡設備的版本
}
void loop() {
// 檢查是不是一張新的卡
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
// 顯示卡片內容
Serial.print(F("Card HEX UID:"));
dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); // 顯示卡片的UID
Serial.println();
Serial.print(F("PICC type: "));
MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
Serial.println(mfrc522.PICC_GetTypeName(piccType)); //顯示卡片的類型
mfrc522.PICC_HaltA(); // 卡片進入停止模式
}
}
/**
* 這個副程式把讀取到的UID,用16進位顯示出來
*/
void dump_byte_array(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.print(buffer[i], HEX);
}
}

點選右上方的「序列阜監控視窗」就會出現RFID卡中的內容囉。
接下來我們讓RFID卡感應後做一些反應吧。
注意,第一段程式碼讀出的HEX數值,只要在前方加上0x即可,如上方讀出F7,則下面辨識的卡片即可改為0xF7。
/*本程式參考趙英傑老師書中內容進行修改,歡迎大家上網搜尋「趙英傑 Arduino」 */
#include <SPI.h>
#include <MFRC522.h> // 引用程式庫
#define RST_PIN 9 // 讀卡機的重置腳位
#define SS_PIN 10 // 晶片選擇腳位
struct RFIDTag { // 定義結構
byte uid[4];
char *name;
};
struct RFIDTag tags[] = { // 初始化結構資料
{{0xCD, 0xE8, 0x2D, 0x01}, "Light"},
{{0x1A, 0x75, 0x3D, 0x1B}, "Dark"},
};
byte totalTags = sizeof(tags) / sizeof(RFIDTag);
MFRC522 mfrc522(SS_PIN, RST_PIN); // 建立MFRC522物件
void setup() {
Serial.begin(9600);
Serial.println();
Serial.println("RFID reader is ready!");
SPI.begin();
mfrc522.PCD_Init(); // 初始化MFRC522讀卡機模組
pinMode(4, OUTPUT); //定義輸出馬達4,5,6,7
pinMode(5, OUTPUT);
}
void light(){
digitalWrite(4, 1);
digitalWrite(5, 0);
}
void dark(){
digitalWrite(4, 0);
digitalWrite(5, 0);
}
void loop() {
// 確認是否有新卡片
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
byte *id = mfrc522.uid.uidByte; // 取得卡片的UID
byte idSize = mfrc522.uid.size; // 取得UID的長度
bool foundTag = false; // 是否找到紀錄中的標籤,預設為「否」。
for (byte i=0; i<totalTags; i++) {
if (memcmp(tags[i].uid, id, idSize) == 0) {
Serial.println(tags[i].name); // 顯示標籤的名
foundTag = true; // 設定成「找到標籤了!」
if (tags[i].name == "Light")light();
else dark();
break; // 退出for迴圈
}
}
/* if (!foundTag) { // 若掃描到紀錄之外的標籤,則顯示"Wrong card!"。
Serial.println("Wrong card!");
}
*/
mfrc522.PICC_HaltA(); // 讓卡片進入停止模式
}
}
接下來如果將他與車子做結合呢?
這邊我們使用4, 5, 6, 7 四個腳位作為馬達控制。
/*本程式參考趙英傑老師書中內容進行修改,歡迎大家上網搜尋「趙英傑 Arduino」 */
#include <SPI.h>
#include <MFRC522.h> // 引用程式庫
#define RST_PIN 9 // 讀卡機的重置腳位
#define SS_PIN 10 // 晶片選擇腳位
struct RFIDTag { // 定義結構
byte uid[4];
char *name;
};
struct RFIDTag tags[] = { // 初始化結構資料
{{0xCD, 0xE8, 0x2D, 0x01}, "Left"},
{{0x1A, 0x75, 0x3D, 0x1B}, "Go"},
{{0x3E, 0x36, 0x2D, 0x01}, "Right"},
{{0x8B,0x36,0x2D,0x01}, "Stop"}
};
byte totalTags = sizeof(tags) / sizeof(RFIDTag);
MFRC522 mfrc522(SS_PIN, RST_PIN); // 建立MFRC522物件
void setup() {
Serial.begin(9600);
Serial.println();
Serial.println("RFID reader is ready!");
SPI.begin();
mfrc522.PCD_Init(); // 初始化MFRC522讀卡機模組
pinMode(4, OUTPUT); //定義輸出馬達4,5,6,7
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
}
void go(){
digitalWrite(6, 1);
digitalWrite(7, 0);
digitalWrite(4, 1);
digitalWrite(5, 0);
}
void left(){
digitalWrite(6, 0);
digitalWrite(7, 0);
digitalWrite(4, 1);
digitalWrite(5, 0);
}
void right(){
digitalWrite(6, 1);
digitalWrite(7, 0);
digitalWrite(4, 0);
digitalWrite(5, 0);
}
void back(){
digitalWrite(6, 0);
digitalWrite(7, 1);
digitalWrite(4, 0);
digitalWrite(5, 1);
}
void stop(){
digitalWrite(6, 0);
digitalWrite(7, 0);
digitalWrite(4, 0);
digitalWrite(5, 0);
}
void loop() {
// 確認是否有新卡片
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
byte *id = mfrc522.uid.uidByte; // 取得卡片的UID
byte idSize = mfrc522.uid.size; // 取得UID的長度
bool foundTag = false; // 是否找到紀錄中的標籤,預設為「否」。
for (byte i=0; i<totalTags; i++) {
if (memcmp(tags[i].uid, id, idSize) == 0) {
Serial.println(tags[i].name); // 顯示標籤的名
foundTag = true; // 設定成「找到標籤了!」
if (tags[i].name == "Go")go();
else if (tags[i].name == "Back")back();
else if (tags[i].name == "Right")right();
else if (tags[i].name == "Left")left();
else stop();
break; // 退出for迴圈
}
}
/* if (!foundTag) { // 若掃描到紀錄之外的標籤,則顯示"Wrong card!"。
Serial.println("Wrong card!");
}
*/
mfrc522.PICC_HaltA(); // 讓卡片進入停止模式
}
}
透過以上的方式可以做出小車的移動控制,如先設定好到某個地方需要往前走幾秒、右轉、左轉等等,碰到另一個感測器之後再停下。
另外我們也可以使用藍牙進行控制。
#include <SoftwareSerial.h>
char BTcmd;
SoftwareSerial BT(7,8); // RX, TX 這邊大家可以查看看SoftwareSerial的資料,他其實是可以自己定義要用哪些接腳作為Rx, Tx傳輸所用。
int MotorRight1=3;
int MotorRight2=4;
int MotorLeft1=5;
int MotorLeft2=6;
int MotorRight3=10;
int MotorRight4=11;
int MotorLeft3=12;
int MotorLeft4=13; //這邊我定義了4組直流馬達,大家使用時如果沒有打算用這麼多馬達的話,可以將後面4排刪掉
void setup()
{
Serial.begin(9600); //這行是原本的Serial,我沒刪掉
BT.begin(9600); //這是新定義的
pinMode(MotorRight1, OUTPUT);
pinMode(MotorRight2, OUTPUT);
pinMode(MotorLeft1, OUTPUT);
pinMode(MotorLeft2, OUTPUT);
pinMode(MotorRight3, OUTPUT);
pinMode(MotorRight4, OUTPUT);
pinMode(MotorLeft3, OUTPUT);
pinMode(MotorLeft4, OUTPUT); //將所有的馬達都設定為「輸出」
}
void go()// 前進,void 函數執行完成後,不傳回任何數值,後方go為自訂的函數名稱。
{
digitalWrite(MotorRight1,HIGH);
digitalWrite(MotorRight2,LOW);
digitalWrite(MotorLeft1,HIGH);
digitalWrite(MotorLeft2,LOW);
digitalWrite(MotorRight3,HIGH);
digitalWrite(MotorRight4,LOW);
digitalWrite(MotorLeft3,HIGH);
digitalWrite(MotorLeft4,LOW);
}
void left() //右轉
{
digitalWrite(MotorRight1,HIGH);
digitalWrite(MotorRight2,LOW);
digitalWrite(MotorLeft1,LOW);
digitalWrite(MotorLeft2,HIGH);
digitalWrite(MotorRight3,HIGH);
digitalWrite(MotorRight4,LOW);
digitalWrite(MotorLeft3,LOW);
digitalWrite(MotorLeft4,HIGH);
}
void right() //左轉
{
digitalWrite(MotorRight1,LOW);
digitalWrite(MotorRight2,HIGH);
digitalWrite(MotorLeft1,HIGH);
digitalWrite(MotorLeft2,LOW);
digitalWrite(MotorRight3,LOW);
digitalWrite(MotorRight4,HIGH);
digitalWrite(MotorLeft3,HIGH);
digitalWrite(MotorLeft4,LOW);
}
void back()//
{
digitalWrite(MotorRight1,LOW);
digitalWrite(MotorRight2,HIGH);
digitalWrite(MotorLeft1,LOW);
digitalWrite(MotorLeft2,HIGH);
digitalWrite(MotorRight3,LOW);
digitalWrite(MotorRight4,HIGH);
digitalWrite(MotorLeft3,LOW);
digitalWrite(MotorLeft4,HIGH);
}
void stop() //停止
{
digitalWrite(MotorRight1,LOW);
digitalWrite(MotorRight2,LOW);
digitalWrite(MotorLeft1,LOW);
digitalWrite(MotorLeft2,LOW);
digitalWrite(MotorRight3,LOW);
digitalWrite(MotorRight4,LOW);
digitalWrite(MotorLeft3,LOW);
digitalWrite(MotorLeft4,LOW);
}
void loop()
{
if (BT.available()) //如果BT收讀取到資料則會跑以下的內容
{
BTcmd= BT.read(); //BT
if ('F' == BTcmd) go(); //如果讀取的數值為F,則執行go裡面的動作
else if ('L' ==BTcmd) left();
else if ('B' ==BTcmd) back();
else if ('R' == BTcmd) right();
else stop(); //else stop()
}
else{
delay(200);
}
Serial.flush();
}
Android上面個人推薦:https://play.google.com/store/apps/details?id=braulio.calle.bluetoothRCcontroller
也可以另外做遙控器出來控制: