ICT 360: Introduction to IoT
Mr. Seng Theara
Introduction to RFID (RC522)
Introduction to RFID (RC522)
RFID means radio-frequency identification. RFID uses electromagnetic fields to transfer data over short distances.
Tag / Card
Reader
It handles:
- Communication with the tag - Reading the UID - Sending data to ESP32Introduction to RFID (RC522)
Antenna
How it works
RC522 creates an electromagnetic field (13.56 MHz)
How it works
RC522 creates an electromagnetic field (13.56 MHz)
When the card comes close within short range (~3–5 cm)
The reader’s field induces voltage in the card’s coil which powers the card
How it works
1. RC522 creates an electromagnetic field (13.56 MHz)
2. When the card comes close within short range (~3–5 cm)
3. The reader’s field induces voltage in the card’s coil which powers the card
4. Once powered->The card’s chip wakes up and it prepares its UID
How it works
1. RC522 creates an electromagnetic field (13.56 MHz)
2. When the card comes close within short range (~3–5 cm)
3. The reader’s field induces voltage in the card’s coil which powers the card
4. Once powered->The card’s chip wakes up and it prepares its UID
5. The card sends data using a method called: Load Modulation
How it works
1. RC522 creates an electromagnetic field (13.56 MHz)
2. When the card comes close within short range (~3–5 cm)
3. The reader’s field induces voltage in the card’s coil which powers the card
4. Once powered->The card’s chip wakes up and it prepares its UID
5. The card sends data using a method called: Load Modulation
6. RC522 receives signal and converts it into: UID = AB 12 CD 34
Connection
Library RFID
DC Motor
from machine import Pin, SPI
import time
class MFRC522:
OK = 0
NOTAGERR = 1
ERR = 2
REQIDL = 0x26
REQALL = 0x52
AUTHENT1A = 0x60
AUTHENT1B = 0x61
def __init__(self, spi, gpioRst, gpioCs):
self.spi = spi
self.rst = gpioRst
self.cs = gpioCs
self.rst.init(Pin.OUT, value=1)
self.cs.init(Pin.OUT, value=1)
self.init()
def _wreg(self, reg, val):
self.cs.value(0)
self.spi.write(bytearray([(reg << 1) & 0x7E]))
self.spi.write(bytearray([val]))
self.cs.value(1)
def _rreg(self, reg):
self.cs.value(0)
self.spi.write(bytearray([((reg << 1) & 0x7E) | 0x80]))
val = self.spi.read(1)
self.cs.value(1)
return val[0]
def _sflags(self, reg, mask):
self._wreg(reg, self._rreg(reg) | mask)
def _cflags(self, reg, mask):
self._wreg(reg, self._rreg(reg) & (~mask))
def _tocard(self, cmd, send):
recv = []
bits = irq_en = wait_irq = n = 0
stat = self.ERR
if cmd == 0x0E:
irq_en = 0x12
wait_irq = 0x10
elif cmd == 0x0C:
irq_en = 0x77
wait_irq = 0x30
self._wreg(0x02, irq_en | 0x80)
self._cflags(0x04, 0x80)
self._sflags(0x0A, 0x80)
for c in send:
self._wreg(0x09, c)
self._wreg(0x01, cmd)
if cmd == 0x0C:
self._sflags(0x0D, 0x80)
i = 2000
while True:
n = self._rreg(0x04)
i -= 1
if not ((i != 0) and not (n & 0x01) and not (n & wait_irq)):
break
self._cflags(0x0D, 0x80)
if i != 0:
if (self._rreg(0x06) & 0x1B) == 0x00:
stat = self.OK
if n & irq_en & 0x01:
stat = self.NOTAGERR
elif cmd == 0x0C:
n = self._rreg(0x0A)
lbits = self._rreg(0x0C) & 0x07
if lbits != 0:
bits = (n - 1) * 8 + lbits
else:
bits = n * 8
if n == 0:
n = 1
if n > 16:
n = 16
for _ in range(n):
recv.append(self._rreg(0x09))
else:
stat = self.ERR
return stat, recv, bits
def request(self, mode):
self._wreg(0x0D, 0x07)
stat, recv, bits = self._tocard(0x0C, [mode])
if stat != self.OK or bits != 0x10:
stat = self.ERR
return stat, bits
def anticoll(self):
ser_chk = 0
ser = [0x93, 0x20]
self._wreg(0x0D, 0x00)
stat, recv, bits = self._tocard(0x0C, ser)
if stat == self.OK:
if len(recv) == 5:
for i in range(4):
ser_chk ^= recv[i]
if ser_chk != recv[4]:
stat = self.ERR
else:
stat = self.ERR
return stat, recv
def init(self):
self.reset()
self._wreg(0x2A, 0x8D)
self._wreg(0x2B, 0x3E)
self._wreg(0x2D, 30)
self._wreg(0x2C, 0)
self._wreg(0x15, 0x40)
self._wreg(0x11, 0x3D)
self.antenna_on()
def reset(self):
self._wreg(0x01, 0x0F)
def antenna_on(self):
if ~(self._rreg(0x14) & 0x03):
self._sflags(0x14, 0x03)
Save this file as mfrc522.py in the micropython device
Coding RFID
DC Motor
from machine import Pin, SPI
from mfrc522 import MFRC522
import time
spi = SPI(1, baudrate=1000000, polarity=0, phase=0,
sck=Pin(18), mosi=Pin(23), miso=Pin(19))
rdr = MFRC522(spi=spi, gpioRst=Pin(22), gpioCs=Pin(16))
print("Scan RFID...")
while True:
(stat, tag_type) = rdr.request(rdr.REQIDL)
if stat == rdr.OK:
(stat, uid) = rdr.anticoll()
if stat == rdr.OK:
uid_str = "".join([str(i) for i in uid])
print("UID:", uid_str)
time.sleep(1)
When we put tag/card on the RFID, it will print out the uid string data of that tag/card
SD Card Module
ESP32
Motor Driver
SD Card Module
ESP32
Motor Driver
An SD card module is a hardware device that allows a microcontroller (like ESP32) to store and retrieve data on a microSD card.
SD Card Module
Motor Driver
ESP32
SD Card Module
ESP32 sends instructions like:
The SD card module receives signals
Write Data
Read Data
ESP32
Connection
TB6612
ESP32
SD Card Library
L298N
from micropython import const
import time
_CMD_TIMEOUT = const(100)
_R1_IDLE_STATE = const(1 << 0)
# R1_ERASE_RESET = const(1 << 1)
_R1_ILLEGAL_COMMAND = const(1 << 2)
# R1_COM_CRC_ERROR = const(1 << 3)
# R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
# R1_ADDRESS_ERROR = const(1 << 5)
# R1_PARAMETER_ERROR = const(1 << 6)
_TOKEN_CMD25 = const(0xFC)
_TOKEN_STOP_TRAN = const(0xFD)
_TOKEN_DATA = const(0xFE)
class SDCard:
def __init__(self, spi, cs, baudrate=1320000):
self.spi = spi
self.cs = cs
self.cmdbuf = bytearray(6)
self.dummybuf = bytearray(512)
self.tokenbuf = bytearray(1)
for i in range(512):
self.dummybuf[i] = 0xFF
self.dummybuf_memoryview = memoryview(self.dummybuf)
# initialise the card
self.init_card(baudrate)
def init_spi(self, baudrate):
try:
master = self.spi.MASTER
except AttributeError:
# on ESP8266
self.spi.init(baudrate=baudrate, phase=0, polarity=0)
else:
# on pyboard
self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)
def init_card(self, baudrate):
# init CS pin
self.cs.init(self.cs.OUT, value=1)
# init SPI bus; use low data rate for initialisation
self.init_spi(100000)
# clock card at least 100 cycles with cs high
for i in range(16):
self.spi.write(b"\xff")
# CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
for _ in range(5):
if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
break
else:
raise OSError("no SD card")
# CMD8: determine card version
r = self.cmd(8, 0x01AA, 0x87, 4)
if r == _R1_IDLE_STATE:
self.init_card_v2()
elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
self.init_card_v1()
else:
raise OSError("couldn't determine SD card version")
# get the number of sectors
# CMD9: response R2 (R1 byte + 16-byte block read)
if self.cmd(9, 0, 0, 0, False) != 0:
raise OSError("no response from SD card")
csd = bytearray(16)
self.readinto(csd)
if csd[0] & 0xC0 == 0x40: # CSD version 2.0
self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024
elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB)
c_size = (csd[6] & 0b11) << 10 | csd[7] << 2 | csd[8] >> 6
c_size_mult = (csd[9] & 0b11) << 1 | csd[10] >> 7
read_bl_len = csd[5] & 0b1111
capacity = (c_size + 1) * (2 ** (c_size_mult + 2)) * (2**read_bl_len)
self.sectors = capacity // 512
else:
raise OSError("SD card CSD format not supported")
# print('sectors', self.sectors)
# CMD16: set block length to 512 bytes
if self.cmd(16, 512, 0) != 0:
raise OSError("can't set 512 block size")
# set to high data rate now that it's initialised
self.init_spi(baudrate)
def init_card_v1(self):
for i in range(_CMD_TIMEOUT):
self.cmd(55, 0, 0)
if self.cmd(41, 0, 0) == 0:
# SDSC card, uses byte addressing in read/write/erase commands
self.cdv = 512
# print("[SDCard] v1 card")
return
raise OSError("timeout waiting for v1 card")
def init_card_v2(self):
for i in range(_CMD_TIMEOUT):
time.sleep_ms(50)
self.cmd(58, 0, 0, 4)
self.cmd(55, 0, 0)
if self.cmd(41, 0x40000000, 0) == 0:
self.cmd(58, 0, 0, -4) # 4-byte response, negative means keep the first byte
ocr = self.tokenbuf[0] # get first byte of response, which is OCR
if not ocr & 0x40:
# SDSC card, uses byte addressing in read/write/erase commands
self.cdv = 512
else:
# SDHC/SDXC card, uses block addressing in read/write/erase commands
self.cdv = 1
# print("[SDCard] v2 card")
return
raise OSError("timeout waiting for v2 card")
def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
self.cs(0)
# create and send the command
buf = self.cmdbuf
buf[0] = 0x40 | cmd
buf[1] = arg >> 24
buf[2] = arg >> 16
buf[3] = arg >> 8
buf[4] = arg
buf[5] = crc
self.spi.write(buf)
if skip1:
self.spi.readinto(self.tokenbuf, 0xFF)
# wait for the response (response[7] == 0)
for i in range(_CMD_TIMEOUT):
self.spi.readinto(self.tokenbuf, 0xFF)
response = self.tokenbuf[0]
if not (response & 0x80):
# this could be a big-endian integer that we are getting here
# if final<0 then store the first byte to tokenbuf and discard the rest
if final < 0:
self.spi.readinto(self.tokenbuf, 0xFF)
final = -1 - final
for j in range(final):
self.spi.write(b"\xff")
if release:
self.cs(1)
self.spi.write(b"\xff")
return response
# timeout
self.cs(1)
self.spi.write(b"\xff")
return -1
def readinto(self, buf):
self.cs(0)
# read until start byte (0xff)
for i in range(_CMD_TIMEOUT):
self.spi.readinto(self.tokenbuf, 0xFF)
if self.tokenbuf[0] == _TOKEN_DATA:
break
time.sleep_ms(1)
else:
self.cs(1)
raise OSError("timeout waiting for response")
# read data
mv = self.dummybuf_memoryview
if len(buf) != len(mv):
mv = mv[: len(buf)]
self.spi.write_readinto(mv, buf)
# read checksum
self.spi.write(b"\xff")
self.spi.write(b"\xff")
self.cs(1)
self.spi.write(b"\xff")
def write(self, token, buf):
self.cs(0)
# send: start of block, data, checksum
self.spi.read(1, token)
self.spi.write(buf)
self.spi.write(b"\xff")
self.spi.write(b"\xff")
# check the response
if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05:
self.cs(1)
self.spi.write(b"\xff")
return
# wait for write to finish
while self.spi.read(1, 0xFF)[0] == 0:
pass
self.cs(1)
self.spi.write(b"\xff")
def write_token(self, token):
self.cs(0)
self.spi.read(1, token)
self.spi.write(b"\xff")
# wait for write to finish
while self.spi.read(1, 0xFF)[0] == 0x00:
pass
self.cs(1)
self.spi.write(b"\xff")
def readblocks(self, block_num, buf):
nblocks = len(buf) // 512
assert nblocks and not len(buf) % 512, "Buffer length is invalid"
if nblocks == 1:
# CMD17: set read address for single block
if self.cmd(17, block_num * self.cdv, 0, release=False) != 0:
# release the card
self.cs(1)
raise OSError(5) # EIO
# receive the data and release card
self.readinto(buf)
else:
# CMD18: set read address for multiple blocks
if self.cmd(18, block_num * self.cdv, 0, release=False) != 0:
# release the card
self.cs(1)
raise OSError(5) # EIO
offset = 0
mv = memoryview(buf)
while nblocks:
# receive the data and release card
self.readinto(mv[offset : offset + 512])
offset += 512
nblocks -= 1
if self.cmd(12, 0, 0xFF, skip1=True):
raise OSError(5) # EIO
def writeblocks(self, block_num, buf):
nblocks, err = divmod(len(buf), 512)
assert nblocks and not err, "Buffer length is invalid"
if nblocks == 1:
# CMD24: set write address for single block
if self.cmd(24, block_num * self.cdv, 0) != 0:
raise OSError(5) # EIO
# send the data
self.write(_TOKEN_DATA, buf)
else:
# CMD25: set write address for first block
if self.cmd(25, block_num * self.cdv, 0) != 0:
raise OSError(5) # EIO
# send the data
offset = 0
mv = memoryview(buf)
while nblocks:
self.write(_TOKEN_CMD25, mv[offset : offset + 512])
offset += 512
nblocks -= 1
self.write_token(_TOKEN_STOP_TRAN)
def ioctl(self, op, arg):
if op == 4: # get number of blocks
return self.sectors
if op == 5: # get block size in bytes
return 512
save this code as sdcard.py in the micropython device
ESP32
SD Card Coding
L298N
from machine import Pin, SPI
import os
import sdcard
spi = SPI(1, baudrate=1000000,
sck=Pin(14), mosi=Pin(15), miso=Pin(2))
cs = Pin(13)
print("Initializing SD card...")
sd = sdcard.SDCard(spi, cs)
vfs = os.VfsFat(sd)
os.mount(vfs, "/sd")
print("Mounted OK")
# Write file
with open("/sd/test.txt", "w") as f:
f.write("Hello everyone!")
print("Write done")
os.umount("/sd")
print("Unmounted")
SD Card Writing
If successful, you will see this
from machine import Pin, SPI
import os
import sdcard
# Setup SPI
spi = SPI(1, baudrate=1000000,
sck=Pin(14), mosi=Pin(15), miso=Pin(2))
cs = Pin(13)
print("Initializing SD card...")
# Mount
sd = sdcard.SDCard(spi, cs)
vfs = os.VfsFat(sd)
os.mount(vfs, "/sd")
print("Mounted OK")
print("Files:", os.listdir("/sd"))
with open("/sd/test.txt", "r") as f:
print("Content:", f.read())
os.umount("/sd")
print("Unmounted")
SD Card Reading
Data Reading from SD Card
Google Firestore
Google Firestore
Data is stored in the cloud and can be accessed anytime, anywhere
Google Firestore
Firestore Characteristic:
Example
Firestore Structure
In Cloud Firestore, you can define the structure of your data by creating collections and documents and specifying the fields and data types of each record. You can also create subcollections within documents to further organize your data.
Folder
File
Data
Click on this link
Log in with your google account and Go to Console
Create a new project
Give a project name
Select Default Account and Create project
Go database -> Firestore
then create Database with everything defualt
Go to Settings->General
This is your project ID
from machine import Pin, SPI
from mfrc522 import MFRC522
import network
import urequests
import ujson
import time
SSID = "SSID"
PASSWORD = "password"
wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect(SSID, PASSWORD)
print("Connecting WiFi", end="")
while not wifi.isconnected():
print(".", end="")
time.sleep(0.5)
print("\nConnected:", wifi.ifconfig())
PROJECT_ID = "firestore-ID"
url = "https://firestore.googleapis.com/v1/projects/{}/databases/(default)/documents/rfid_logs".format(PROJECT_ID)
spi = SPI(1, baudrate=1000000,
sck=Pin(18), mosi=Pin(23), miso=Pin(19))
rdr = MFRC522(spi=spi, gpioRst=Pin(22), gpioCs=Pin(16))
print("Scan RFID...")
def send_to_firestore(uid):
data = {
"fields": {
"uid": {"stringValue": uid},
"time": {"stringValue": str(time.time())}
}
}
try:
res = urequests.post(url, json=data)
print("Sent:", res.text)
res.close()
except Exception as e:
print("Error sending:", e)
while True:
(stat, tag_type) = rdr.request(rdr.REQIDL)
if stat == rdr.OK:
(stat, uid) = rdr.anticoll()
if stat == rdr.OK:
uid_str = "".join([str(i) for i in uid])
print("UID:", uid_str)
send_to_firestore(uid_str)
time.sleep(2)
In this code, we want to send the data of the RFID to the Firestore
from machine import Pin, SPI
from mfrc522 import MFRC522
import network
import urequests
import ujson
import time
SSID = "SSID"
PASSWORD = "password"
wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect(SSID, PASSWORD)
print("Connecting WiFi", end="")
while not wifi.isconnected():
print(".", end="")
time.sleep(0.5)
print("\nConnected:", wifi.ifconfig())
PROJECT_ID = "firestore-ID"
url = "https://firestore.googleapis.com/v1/projects/{}/databases/(default)/documents/rfid_logs".format(PROJECT_ID)
spi = SPI(1, baudrate=1000000,
sck=Pin(18), mosi=Pin(23), miso=Pin(19))
rdr = MFRC522(spi=spi, gpioRst=Pin(22), gpioCs=Pin(16))
print("Scan RFID...")
def send_to_firestore(uid):
data = {
"fields": {
"uid": {"stringValue": uid},
"time": {"stringValue": str(time.time())}
}
}
try:
res = urequests.post(url, json=data)
print("Sent:", res.text)
res.close()
except Exception as e:
print("Error sending:", e)
while True:
(stat, tag_type) = rdr.request(rdr.REQIDL)
if stat == rdr.OK:
(stat, uid) = rdr.anticoll()
if stat == rdr.OK:
uid_str = "".join([str(i) for i in uid])
print("UID:", uid_str)
send_to_firestore(uid_str)
time.sleep(2)
In this code, we want to send the data of the RFID to the Firestore
After we put the tag/card on the RFID, it will send to the Firestore as shown in here
After successfully publish, you will see the data in the Firestore