Firestore with ESP32

ICT 360: Introduction to IoT

Mr. Seng Theara

Learning Objectives

Learning Objectives

  • Develop an IoT attendance system using ESP32 and RFID
  • Acquire and process RFID data using embedded programming
  • Transmit and manage data using Google Firebase Firestore
  • Design a mobile interface using MIT App Inventor
  • Store data locally using an SD card for backup
  • Implement real-time feedback using a buzzer
  • Integrate hardware and cloud services into a complete IoT system

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

  • The MFRC522 RFID Module is the brain of the system
  • It generates a radio frequency field (13.56 MHz)
  • It handles:

      - Communication with the tag                                        - Reading the UID                                                                      - Sending data to ESP32
  • Passive RFID card (no battery)
  • Contains:
    • Small chip (memory + UID)
    • Antenna coil
  • Each card has a unique UID (like ID card)
  • Can also store small data

Introduction to RFID (RC522)

Antenna

  • Both reader and card have coils
  • Works using electromagnetic induction

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

  • DC Motor usually need 6V, 9V, 12V (more)
  • Needs hundreds of mA to several A
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

  • DC Motor usually need 6V, 9V, 12V (more)
  • Needs hundreds of mA to several A
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

  • Output Voltage is only 3.3V
  • Max Current from the pin ~12mA

Motor Driver

SD Card Module

ESP32

  • Output Voltage is only 3.3V
  • Max Current from the pin ~12mA

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:

  • Open file
  • Write data
  • Read data

The SD card module receives signals

  • The SD card saves data in files (like .txt or .csv)
  • Data is written line by line

Write Data

Read Data

ESP32

  • Output Voltage is only 3.3V
  • Max Current from the pin ~12mA

Connection

TB6612

ESP32

  • Output Voltage is only 3.3V
  • Max Current from the pin ~12mA

SD Card Library

L298N

from machine import SPI
import time

class SDCard:
    def __init__(self, spi, cs):
        self.spi = spi
        self.cs = cs

        self.cs.init(self.cs.OUT, value=1)

        self.init_card()

    def init_card(self):
        self.cs.value(1)
        for _ in range(10):
            self.spi.write(b'\xff')

        self.cs.value(0)
        if self.cmd(0, 0, 0x95) != 1:
            raise OSError("No SD card")

        while self.cmd(1, 0, 0xff) != 0:
            pass

        self.cs.value(1)
        self.spi.write(b'\xff')

    def cmd(self, cmd, arg, crc):
        self.cs.value(0)

        self.spi.write(bytearray([
            0x40 | cmd,
            arg >> 24,
            arg >> 16,
            arg >> 8,
            arg,
            crc
        ]))

        for _ in range(10):
            response = self.spi.read(1)[0]
            if not (response & 0x80):
                return response
        return -1

    def readblocks(self, block_num, buf):
        raise NotImplementedError

    def writeblocks(self, block_num, buf):
        raise NotImplementedError

    def ioctl(self, op, arg):
        if op == 4: return 512
        if op == 5: return 0

save this code as sdcard.py in the micropython device

ESP32

  • Output Voltage is only 3.3V
  • Max Current from the pin ~12mA

SD Card Coding

L298N

from machine import SDCard
import os


sd = SDCard(slot=2)
os.mount(sd, "/sd")


with open("/sd/data_csv.csv", "a") as f:
    f.write("Justin Bieber,85\n")
    f.write("Taylor Swift,90\n")

print("CSV written")

SD Card Writing

If successful, you will see this

from machine import SDCard
import os


sd = SDCard(slot=2)
os.mount(sd, "/sd")


with open("/sd/data_csv.csv", "a") as f:
    f.write("Justin Bieber,85\n")
    f.write("Taylor Swift,90\n")

print("CSV written")

SD Card Reading

Data Reading from SD Card

Google Firestore

Google Firestore

  • Cloud Firestore is a NoSQL document-oriented database that stores data in the form of documents organized into collections.
  • Each document can contain multiple fields, and the data type of each field can be any of the supported data types.

Data is stored in the cloud and can be accessed anytime, anywhere

Google Firestore

Firestore Characteristic:

  • Real-time updates 
  • No server needed 
  • Easy to use API
  • Scalable (small → large system)

Example

  • Temperature monitoring
  • RFID attendance
  • Smart home control

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

Cloud Firestore

Click on this link

Log in with your google account and Go to Console

Cloud Firestore

Create a new project

Give a project name

Select Default Account and Create project

Cloud Firestore

Go database -> Firestore 

then create Database with everything defualt

 Firestore ID

Go to Settings->General

This is your project ID

 Firestore Coding

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 

 Firestore Coding

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

Firestore Data

After successfully publish, you will see the data in the Firestore