Communication Protocols in Embedded Systems.

Working with communication protocols (I2C,UART,SPI)

Sometimes a device may have more than one embedded system, each performing a different task and for the system to work efficiently, good communication must exist between the subsystems. A common communication protocol is chosen to allow exchange of data and information. There are numerous protocols to be selected but due to standardization, they are broadly categorised into 2:

  1. Parallel Interface - Multiple bits are transferred at the same time following a rising or falling clock edge.
  2. Serial Interface - Data is transmitted one bit at a time as a stream.Uses less wires than Parallel interface and is cheaper.
    The serial interface is further classified mainly into 3:
    • I2C (Inter Integrated Circuit)
    • SPI(Serial Peripheral Interface)
    • UART(Universal Asynchronous Receiver/Transmitter)
  1. I2C
    It uses only 2 bi-directional open-drain lines for data transmission called SDA and SCL.
    SDA(Serial Data)-facilitates the transfer of data.
    SCL(Serial Clock) - Carries the clock
    I2C operates in 2 modes: Master mode and Slave mode.

    Advantages of I2C

    1. Can have multiple masters
    2. Reduced complexity due to use of only 2 wires unlike SPI.
    3. Cost-efficient.
    4. Uses ACK(Acknowledge) bit which reduces error.

    Disadvantages of I2C

    1. Slower speed since bits are sent and received one at a time.
    2. Data can be bi-directional but supports only one direction at a time (Half-duplex).
  2. SPI
    It is used to facilitate communication between peripheral devices(input and output devices and microcontrollers).It featues full-duplex and synchronous communication between one or more slave devices and a microcontroller.
    The components of SPI include:
    • Master Device - It controls the data flow and generates the clock signal(Microcontroller).
    • Slave Device - Are peripheral devices controlles by the master device.
    • SPI Bus - It refers to the physical connections allowing the data to move between the slave devices and master device.
      SPI is made up of :
      1. Slave Select(SS) - Allows the master to communicate to a specific slave.
      2. Master Out Slave In(MOSI) - Allows communication from Master to slave.
      3. Master In Slave Out(MISO) - Allows communication from slave to Master.
      4. Serial Clock(SCK) - Clock used by master and slave devices to coordinate data transfer.
    • Data Transfer Protocol - SPI uses synchronous(controlled by a clock) serial communication for full duplex(data transfer in both ways simultaneously) data transfer.
    • Data Rate - Number of bits transferred per second .
    • Clock Polarity and Clock Phase - used to define the relationship between the data signals and the clock signals.

    Advantages of SPI.

    1. High speed data transfer
    2. Simple Hardware Requirements.(4 signal lines-SCK,MISO,MOSI,SS)
    3. Supports Full duplex communication.
    4. Supports multiple slave devices.

    Disadvantages of SPI.

    1. Used more wires than I2C.
  3. UART
    It allows for asynchronous serial communication between devices.UART is a block of circuitry responsible for implementing serial communication as it acts as an intermediary between parallel and serial interfaces. On one end of the UART is a bus of 8 or so data lines and control pins and the other is the two serial wires, RX and TX (Receiver and Transmitter)

    Although it has been greatly replaced by SPI and I2Cs, it's widely used for lower-speed applications because it is very simple, low-cost and easy to implement.

    Advantages of UART


    1. It supports full-duplex communication.
    2. It doesn't require a clock signal.
    3. Has error checking due to the presence parity bit
    4. Data format and transmission speeds are configurable.

    Disadvantages of UART

    1. Lower Data Rates
    2. Suitable for only 2 devices
    3. Has a distance limit of 15 meters.

Interfacing the RPi Pico and ESP32 using SPI

The following code was used in sending data from a raspberry pi pico (Master) to an ESP32 (slave) using SPI.

Raspberry pi pico code(SPI master)

                        
    import machine
    from machine import Pin
    from time import sleep
    
    
    led=machine.Pin(25,Pin.OUT)
    # Configure SPI pins
    spi_sck = Pin(18)
    spi_tx = Pin(19)
    spi_rx = Pin(16)
    slave_select_pin = Pin(10, Pin.OUT)
    
    # Initialize SPI as master
    spi = machine.SPI(0, baudrate=9600, polarity=0, phase=0, sck=spi_sck, mosi=spi_tx, miso=spi_rx)
    
    while True:
        # Select the slave (assert CS)
        slave_select_pin.value(0)
        led.value(1)
        sleep(1)
        # Send data to the slave
        tx_data = b'\x10'
        spi.write(tx_data)
        print("Sent data from master: ",tx_data[0])
    
        # Receive data from the slave
        rx_data = spi.read(1)
        
        # Deselect the slave (deassert CS)
        slave_select_pin.value(1)
        led.value(0)
    
        print("Received data from slave:", rx_data[0])
        
        sleep(1)      
                        
                    

ESP32 code(SPI slave)

                        
    #include <SPI.h>

        const int slaveSelectPin = 5; // GPIO pin connected to slave select (CS)
        const int led=2;
        void setup() {
            Serial.begin(9600);
            pinMode(slaveSelectPin, OUTPUT);
            pinMode(led,OUTPUT);
            
            SPI.begin(); // Initialize SPI
            digitalWrite(led,LOW);
            digitalWrite(slaveSelectPin,HIGH);
            delay(1000);
        }
        
        void loop() {
            // Wait for the master to select the slave (CS low)
            while (digitalRead(slaveSelectPin)) {
                Serial.print("Waiting .... ");
                Serial.println(digitalRead(slaveSelectPin));
                digitalWrite(led,HIGH);
                delay(1000);
            }
            // Send response back to the master
            uint8_t txData = 0x12;
            uint8_t rxData=SPI.transfer(txData);
            Serial.print("Sent data from slave: ");
            Serial.println(txData);
        
            // Receive data from the master
            Serial.print("Received data from master: ");
            Serial.println(rxData);
            // Process received data (e.g., perform an action)
        
            
        
            // Wait for the master to deselect the slave (CS high)
            while (!digitalRead(slaveSelectPin)) {
                digitalWrite(led,LOW);
                Serial.print("DESELECTING .... ");
                Serial.println(digitalRead(slaveSelectPin));
                delay(1000);
            }
            Serial.println("---------------------------");
        }
                        
                    

Interfacing the RPi Pico and ESP32 using I2C

The following code was used in sending data from a raspberry pi pico (Master) to an ESP32 (slave) using I2C.

Raspberry pi pico code (I2C Master)

                        
    # Raspberry Pi Pico (Master) - MicroPython

    from machine import Pin, SoftI2C
    import time
    
    # Initialize I2C communication
    i2c = SoftI2C(scl=Pin(5), sda=Pin(4))
    
    # I2C slave address of the ESP32
    slave_address = 0x42
    
    def send_data_to_slave(data):
        try:
            i2c.writeto(slave_address, data)
            print(f"Sent data to slave: {data}")
        except OSError:
            print("Error sending data to slave.")
    
    def main():
        while True:
            # Example: Send a byte (0xAA) to the slave
            send_data_to_slave(b'\xAA')
            time.sleep(1)
    
    if __name__ == "__main__":
        main()    
                        
                    

ESP32 code (I2C Slave)

                        
    // ESP32 (Slave) - C

    #include <Wire.h>
    
    // I2C address (must match the master's address)
    #define SLAVE_ADDRESS 0x42
    
    void setup() {
        Wire.begin(21,22,SLAVE_ADDRESS); // Initialize I2C as slave
        Wire.onReceive(receiveData);
        Serial.begin(115200);
    }
    
    void receiveData(int byteCount) {
        while (Wire.available()) {
            char receivedData = Wire.read();
            Serial.print("Received data from master: ");
            Serial.println(receivedData);
            // Process received data (e.g., control an LED, update sensor readings, etc.)
        }
        
    }
    
    void loop() {
        // Your code here
        // Continue processing or wait for more data from the master
    }                                                   
                        
                    

Interfacing the RPi Pico and ESP32 using Serial Communication.

The following code was used in sending data from a raspberry pi pico (Master) to an ESP32 (slave) using UART protocol.

Raspberry pi pico code (UART Master)

                        
    from machine import UART, Pin

    # Initialize UART
    uart = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))
    
    # Send data (master to slave)
    uart.write("Hello, ESP32!")
    
    # Receive data (slave to master)
    received_data = uart.read()
    print("Received data from ESP32:", received_data)
                        
                    

ESP32 code (UART Master)

                        
    #include <HardwareSerial.h>

        void setup() {
            Serial.begin(9600); // Initialize UART
        }
        
        void loop() {
            // Receive data from master (Raspberry Pi Pico)
            if (Serial.available()) {
                char receivedChar = Serial.read();
                // Process received data (e.g., control sensors, LEDs, etc.)
                // Send response back to the master if needed
            }
        }      
                        
                    

Communication between two raspberry pi pico Ws on the same network.

Coming soon 😅

Configuring the raspberry pi pico W as a server in a network.

Raspberry pi pico W is equiped with wifi hence can act as a server of webpages to clients.This benefit allows us to build websites that allow us to control devices remotely provided there is an internet connection. Internet Of Things uses this ability extensively to allow for wireless communication to take place between devices and their users. Raspberry Pi Pico W when pregrammed as a server using Micropython, results in less bulky code as compared to ESP32 hence RPi Pico W is often preffered in IOT applications.
The following code shows the basic structure of a Pico W server that perfoms the action of turning on or off the onboard led. Ater the code has uploaded, proceed to your browser and enter the correct IP Address (printed in the terminal).

                        
    import network
    import socket
    from time import sleep
    from picozero import pico_temp_sensor, pico_led
    import machine

    ssid = 'GEARBOX MEMBERS'
    password = 'Members@Gearbox'

    def connect():
    #Connect to WLAN
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, password)
    while wlan.isconnected() == False:
        print('Waiting for connection...')
        sleep(1)
    ip = wlan.ifconfig()[0]
    print(f'Connected on {ip}')
    return ip

    def open_socket(ip):
    # Open a socket
    address = (ip,80)
    connection = socket.socket()
    connection.bind(address)
    connection.listen(1)
    return connection

    def webpage(temperature, state):
    #Template HTML
    html = f"""
            <!DOCTYPE html>
            <html>
            <form action="./lighton">
            <input type="submit" value="Light on" />
            </form>
            <form action="./lightoff">
            <input type="submit" value="Light off" />
            </form>
            <p>LED is {state}</p >
            <p>Temperature is {temperature}</p>
            </body>
            </html>
            """
    return str(html)

    def serve(connection):
    #Start a web server
    state = 'OFF'
    pico_led.off()
    temperature = 0
    while True:
        client = connection.accept()[0]
        request = client.recv(1024)
        request = str(request)
        
        try:
            request = request.split()[1]
        except IndexError:
            pass
        if request == '/lighton?':
            pico_led.on()
            state = 'ON'
        elif request =='/lightoff?':
            pico_led.off()
            state = 'OFF'
        temperature = pico_temp_sensor.temp
        html = webpage(temperature, state)
        client.send(html)
        client.close()

    try:
    ip = connect()
    connection = open_socket(ip)
    serve(connection)
    print(connection)
    except KeyboardInterrupt:
    machine.reset()

                        
                    
This basic structure remains constant as complexity increases hence it has fairly good scalability properties.

Configuring the ESP32 as a server in a network.

There are 2 types of web servers that can be implemented in ESP 32 :

  1. HTTP Web Server - It Lacks bi-directional capabilities meaning that multiple devices controlling the same website will fall out of sync.
  2. Web Socket Server It allows us to sync the website state on all devices if one changes something on the website.
The web socket is more reliable in situations where a Single website is accessed by multiple users and ensures that all the users see the same information on the webpage at all times.
For this lesson , I used the HTTP Web Server since only one device ( my laptop ) was controlling 2 leds in a circuit .

There are 2 ways in which a web server can be programmed on the Arduino IDE :
  1. Using all the code in one file in the IDE.
  2. Using separate files depending on the the programming language before uploading all of them to the ESP32 microcontroller .
  1. All the code in one file.

    This method is easier to understand but more bulky in terms of the amount of code used . Since all the programming languages are in one file , it becomes harder to read and debug especially if the website is complex . If the website is simple eg . contains just one button used to toggle an Led,then this methed may be chosen .
    Steps :
    1. Import the required Libraries.ie wifi.h library only .
    2. Create ssid and password variables to store your wifi name and password.
    3. Create Server object and give it a name.
    4. Create a string string variable the the HTTP request
    5. Create string variables to store led states ( can be to store the state of whatever you wish to control e - g- ' on ' state , off state)
    6. Create integer variables to store the Pin numbers of the controlling the leds .
    7. Create a Setup and loop functions.
    8. Within the Setup function :
      • Initialize the Serial monitor ( to display the led statuses )
      • Initialize the led pins as output ping and set them to ' LOW ' state.
      • Initialize the Wifi class so that the ESP32 can connect to the wifi.
    9. Within the Loop function :
      • Check if the client has connected to the wifi and has accessed website continuing with the rest of the code .
      • Read the data from the client , and print it on the Serial monitor ( the led states ) and store it in the state variable created earlier .
      • Use if statements to check for specific states ('on' and 'off') and make the required changes on the website e.g change button color.
      • Attach all the code used in the website here using the phrase ' Client.println ( " ... " )' for every line of code (HTML, CSS and Javascript).
    10. Upload the code to your ESP32 microcontroller.
  2.                             
        // Load Wi-Fi library
        #include <WiFi.h>
    
        // Replace with your network credentials
        const char* ssid = "GEARBOX MEMBERS";
        const char* password = "Members@Gearbox";
    
        // Set web server port number to 80
        WiFiServer server(80);
    
        // Variable to store the HTTP request
        String header;
    
        // Auxiliar variables to store the current output state
        String output26State = "off";
        String output27State = "off";
    
        // Assign output variables to GPIO pins
        const int output26 = 26;
        const int output27 = 27;
    
        // Current time
        unsigned long currentTime = millis();
        // Previous time
        unsigned long previousTime = 0; 
        // Define timeout time in milliseconds (example: 2000ms = 2s)
        const long timeoutTime = 2000;
    
        void setup() {
        Serial.begin(115200);
        // Initialize the output variables as outputs
        pinMode(output26, OUTPUT);
        pinMode(output27, OUTPUT);
        // Set outputs to LOW
        digitalWrite(output26, LOW);
        digitalWrite(output27, LOW);
    
        // Connect to Wi-Fi network with SSID and password
        Serial.print("Connecting to ");
        Serial.println(ssid);
        WiFi.begin(ssid, password);
        while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
        }
        // Print local IP address and start web server
        Serial.println("");
        Serial.println("WiFi connected.");
        Serial.println("IP address: ");
        Serial.println(WiFi.localIP());
        server.begin();
        }
    
        void loop(){
        WiFiClient client = server.available();   // Listen for incoming clients
    
        if (client) {                             // If a new client connects,
        currentTime = millis();
        previousTime = currentTime;
        Serial.println("New Client.");          // print a message out in the serial port
        String currentLine = "";                // make a String to hold incoming data from the client
        while (client.connected() && currentTime - previousTime <= timeoutTime) {  // loop while the client's connected
        currentTime = millis();
        if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
        // if the current line is blank, you got two newline characters in a row.
        // that's the end of the client HTTP request, so send a response:
        if (currentLine.length() == 0) {
        // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
        // and a content-type so the client knows what's coming, then a blank line:
        client.println("HTTP/1.1 200 OK");
        client.println("Content-type:text/html");
        client.println("Connection: close");
        client.println();
    
        // turns the GPIOs on and off
        if (header.indexOf("GET /26/on") >= 0) {
            Serial.println("GPIO 26 on");
            output26State = "on";
            digitalWrite(output26, HIGH);
        } else if (header.indexOf("GET /26/off") >= 0) {
            Serial.println("GPIO 26 off");
            output26State = "off";
            digitalWrite(output26, LOW);
        } else if (header.indexOf("GET /27/on") >= 0) {
            Serial.println("GPIO 27 on");
            output27State = "on";
            digitalWrite(output27, HIGH);
        } else if (header.indexOf("GET /27/off") >= 0) {
            Serial.println("GPIO 27 off");
            output27State = "off";
            digitalWrite(output27, LOW);
        }
    
        // Display the HTML web page
        client.println("<!DOCTYPE html><html>");
        client.println("&lgthead><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
        //client.println("<link rel=\"icon\" href=\"data:,\">");
        // CSS to style the on/off buttons 
        // Feel free to change the background-color and font-size attributes to fit your preferences
        client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
        client.println(".button {
            background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");} 
        client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;");
        client.println(".button2 {background-color: #555555;}</style></head>");
    
        // Web Page Heading
        client.println("<body>>h1>ESP32 Web Server</h1>");
    
        // Display current state, and ON/OFF buttons for GPIO 26  
        client.println("<p>GPIO 26 - State " + output26State + "<p>");
        // If the output26State is off, it displays the ON button       
        if (output26State=="off") {
            client.println("<p><a href=\"/26/on\">>button class=\"button\">ON</button></a><p>");
        } else {
            client.println("<p><a href=\"/26/off\"><button class=\"button button2\">OFF</button></a><p>");
        } 
            
        // Display current state, and ON/OFF buttons for GPIO 27  
        client.println("<p>GPIO 27 - State " + output27State + "<p>");
        // If the output27State is off, it displays the ON button       
        if (output27State=="off") {
            client.println("<p><a href=\"/27/on\"><button class=\"button\">ON</button></a><p>");
        } else {
            client.println("<p><a href=\"/27/off\"><button class=\"button button2\">OFF</button></a&lgt<p>");
        }
        client.println("</body></html>");
    
        // The HTTP response ends with another blank line
        client.println();
        // Break out of the while loop
        break;
        } else { // if you got a newline, then clear currentLine
        currentLine = "";
        }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
        currentLine += c;      // add it to the end of the currentLine
        }
        }
        }
        // Clear the header variable
        header = "";
        // Close the connection
        client.stop();
        Serial.println("Client disconnected.");
        Serial.println("");
        }
        }
    
        
        
  3. Code in Separate files .

    This is much more efficient when complex websites are used or when the lines of code are many. It also involves the usage of more libraries that allow the web server to communicate with the various files uploaded to it. The files are organized as shown below :

    Inside the data folder is where we store all the external files used together with the code in the Arduino.
    Steps:
    1. Import all the required libraries
      • Wifi.h allow connection to wifi.
      • ESP Async Web Server.h allow creation of server object.
      • SPIFFS.h- used to allow the Server to access and coordinate different files of different programming languages.
    2. Create ssid and password variables to store the wifi name and password.
    3. Create variables store the led pin and led state.
    4. Create a server object.
    5. 5. Create a function called processor that takes the current led state, prints on the serial monitor and updates the previous state and returns the current led state.
    6. Create a Setup function:
      • Initialize the a Serial monitor and the led Pin as output.
      • Initialize SPIFFS.
      • Connect to wifi, using Wif.h , and use a while loop to ensure the code is excecuted only when connected to wifi.
      • Create 2 routes that connect to the HTML and CSS files to the arduino code.
      • Create 2 routes that check the state of the button from the HTML file and toggle the led accordingly.
      • Activate the server.
    7. Create a loop function : Nothing is written here.
    8. Upload your code to the ESP32 microcontroller

  4. NOTE : Ensure you arange your html css and arduino code files as describes earlier.
                                
                                    
        // Import required libraries
        #include "WiFi.h"
        #include "ESPAsyncWebServer.h"
        #include "SPIFFS.h"
        
        // Replace with your network credentials
        const char* ssid = "GEARBOX MEMBERS";
        const char* password = "Members@Gearbox";
        
        // Set LED GPIO
        const int ledPin = 2;
        // Stores LED state
        String ledState;
        
        // Create AsyncWebServer object on port 80
        AsyncWebServer server(80);
        
        // Replaces placeholder with LED state value
        String processor(const String& var){
        Serial.println(var);
        if(var == "STATE"){
            if(digitalRead(ledPin)){
            ledState = "ON";
            }
            else{
            ledState = "OFF";
            }
            Serial.print(ledState);
            return ledState;
        }
        return String();
        }
        
        void setup(){
        // Serial port for debugging purposes
        Serial.begin(115200);
        pinMode(ledPin, OUTPUT);
        
        // Initialize SPIFFS
        if(!SPIFFS.begin(true)){
            Serial.println("An Error has occurred while mounting SPIFFS");
            return;
        }
        
        // Connect to Wi-Fi
        WiFi.begin(ssid, password);
        while (WiFi.status() != WL_CONNECTED) {
            delay(1000);
            Serial.println("Connecting to WiFi..");
        }
        Serial.println("Connected !");
        // Print ESP32 Local IP Address
        Serial.println(WiFi.localIP());
        
        // Route for root / web page
        server.on("/index.html", HTTP_GET, [](AsyncWebServerRequest *request){
            request->send(SPIFFS, "/index.html", String(), false, processor);
        });
        
        // Route to load style.css file
        server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
            request->send(SPIFFS, "/style.css", "text/css");
        });
        
        // Route to set GPIO to HIGH
        server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
            digitalWrite(ledPin, HIGH);    
            request->send(SPIFFS, "/index.html", String(), false, processor);
        });
        
        // Route to set GPIO to LOW
        server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
            digitalWrite(ledPin, LOW);    
            request->send(SPIFFS, "/index.html", String(), false, processor);
        });
        
        // Start server
        server.begin();
        }
        
        void loop(){
        
        }
                                  
                                
                            

Blynk Cloud Setup using ESP32

Blynk is an remote server that allows clients to build simple control panels for their projects without prior knowledge in website design. The panel has buttons sliders and many more which can send data to to your board or read and display data on the panel such as temperature readings.
The following is the process of linking your ESP32 to Blynk :

  1. Login to your blynk account.
  2. Create a new template then proceed to the datastream section and create a pin(digital,analog,virtualpin,input or output) which is used to send or receive data.
  3. Proceed to the home section while still in your template and create the control feature you wish to use such as a switch and attach it to a datastream you created.
  4. Copy your template's authentication token,template name and template id.Save your changes.
  5. Proceed to your 'Devices' and create a device from your template.Save it.
  6. Proceed to your Arduino IDE and upload the following code to your ESP32.Its a simple code of toggling the onboard LED from Blynk Website.
                        
    #define BLYNK_TEMPLATE_ID "TMPL280pQpoeq"
    #define BLYNK_TEMPLATE_NAME "blinkled"
    #define BLYNK_AUTH_TOKEN "ZmIs2CUiuUgsSQ5Rud4iohQJVUWS-jBi"
    
    #define BLYNK_PRINT Serial
    #include <Blynk.h>
    #include <BlynkSimpleEsp32.h>
    #include <WiFi.h>
    #include <WiFiClient.h>
    
    // Your WiFi credentials.
    const char auth[] = BLYNK_AUTH_TOKEN;
    const char ssid[] = "Your wifi name";
    const char pass[] = "********";
    
    void setup() {
        Serial.begin(9600);
        Blynk.begin(auth, ssid, pass,"blynk.cloud",80);
    }
    
    void loop() {
        Blynk.run();
        // Your other code here
    }      
                        
                    

PROJECT 2

This project utilizes the following components:
  1. DHT11 temperature and humidity sensor.
  2. Raspberry pi pico.
  3. ESP32.
  4. RFID card reader.
  5. OLED display
Its function is to use the RFID module to read tags and check if access is granted or not. If granted the OLED displays the temperature and humidity readings and this data is sent from the raspberry pi pico to ESP32 using UART communication protocol.If access is denied, no data is displayed or sent to ESP32.

                        
from machine import Pin,SPI,UART
from ssd1306 import SSD1306_SPI
from mfrc522 import MFRC522
import dht 
from time import sleep

'''
RSP Pico  |   RC522
0         |   RST
1         |   SDA
2         |   SCK
3         |   MOSI
4         |   MISO
'''
#Initializing the RFID Card reader
reader = MFRC522(spi_id=0,sck=2,miso=4,mosi=3,cs=1,rst=0)
cards=["412721497"]
 
'''
RSP Pico  |  OLED
16        |  CS
17        |  DC
18        |  SCK
19        |  MOSI
20        |  RST
'''
#Initializing the OLED display.
spi = SPI(0,100000,mosi=Pin(19),sck=Pin(18))
dc = Pin(17)   # data/command
rst = Pin(20)  # reset
cs = Pin(16)  # chip select, some modules do not have a pin for this
display = SSD1306_SPI(128, 64, spi, dc, rst, cs)

#initializing the cordinates of the text in OLED display
x=50
y=0
z=1
diff=5

#Initializing the DHT11 Sensor.
sensor = dht.DHT11(Pin(28))

# Initialize UART
uart = UART(0, baudrate=9600, tx=Pin(12), rx=Pin(13))

while True:
    display.fill(0)
    reader.init()
    (stat, tag_type) = reader.request(reader.REQIDL)
    if stat == reader.OK:
        (stat, uid) = reader.SelectTagSN()
        if stat == reader.OK:
            card = int.from_bytes(bytes(uid),"little",False)
            print('CARD ID: '+str(card))
            display.text('CARD ID: '+str(card),0,0,1)
            if str(card) in cards:              
                display.text("Access granted!",0,10,1)
                sensor.measure()
                temp = sensor.temperature
                hum = sensor.humidity
                temp_f = temp * (9/5) + 32.0
                #Display the temperature and humidity when access is granted.
                display.text('Temp.: %3.1f C' %temp,0,30,1)
                display.text('Temp.: %3.1f F' %temp_f,0,40,1)
                display.text('Humidity: %3.1f %%' %hum,0,50,1)
                display.show()
                
                #Send data (master to slave) when access is granted.
                temp1='Temp.: %3.1f C' %temp
                temp2='Temp.: %3.1f F' %temp_f
                humidity='Humidity: %3.1f %%' %hum
                uart.write(temp1+temp2+humidity)   
            else:
                display.text("Access DENIED!",0,10,1)
                display.show()
    else:
        display.text("BRING",x,y,z)
        display.text("CARD",x+2,y+10,z)
        display.text("CLOSER",x-4,y+20,z)
        y=y+diff
        if y>=35 or y<=0:
            diff=diff*-1
        display.show()  
    sleep(1)