- Programming for Final Project, 1-2
This page is the process of Programming for my final Project.
Notes:
When installing "Board Manager esp32" in the Arduino IDE, selecting version 2.0.8 caused an RFID-related compile error, so Please install "Board Manager esp32" version 2.0.7 or lower. (As of 2-May-2023)
1-2: RFID2 + SSR + LCD
Next, I generated an additional program in ChatGPT (GPT-4) that "displays text on the LCD depending on the situation, retrieves the exact time from the NTP server via a Wifi connection, and counts the time while the registrant's card is being authenticated.
- What I used
- Items used in 1-1, and add LCD Module (AE-AQM1602A)
- Library:
- Wire (for I2C communication)
- RFID_MFRC522v2 (for RFID reader)
- Wifi (for wireless connectivity)
- time (time-related processing)
Main instructions for ChatGPT (GPT-4)
- modify the original code generated in 1-1 by adding the LCD used for I2C and displaying the UID read.
- The original code must be adhered to and additional modifications must be made without changing the structure as much as possible.
- The board used is XIAO ESP32C3.
- Do not use the "LiquidCrystal_I2C" library from LCD, as it does not work with ESP32.
- Connect via Wifi Network and get the exact time from the NTP server.
- Count the elapsed time after the card is authenticated and display the elapsed time when the card is removed.
The completed code is as follows.
Programming code
#include <Wire.h>
#include <MFRC522v2.h>
#include <MFRC522DriverI2C.h>
#include <MFRC522Debug.h>
#include <WiFi.h>
#include <time.h>
const uint8_t RFID_Address = 0x28;
const uint8_t LCD_ADDRESS = 0x3E;
MFRC522DriverI2C driver{RFID_Address, Wire};
MFRC522 mfrc522{driver};
const char* registeredUids[] = {
"5E4ABC3E",
"DEE6B43E"
};
const char* registeredUserNames[] = {
"HIROE TAKEDA",
"KAZUNARI TAKEDA"
};
const uint8_t ssr = 8;
enum CardStatus {
NO_CARD,
REGISTERED_CARD,
UNREGISTERED_CARD
};
CardStatus currentStatus = NO_CARD;
char lastUid[9] = "";
char lastUserName[17] = "";
bool countTime = false;
time_t cardAuthStartTime;
bool uidMatches(MFRC522::Uid *uid1, const char *uid2) {
if (uid1->size != 4) {
return false;
}
for (uint8_t i = 0; i < uid1->size; i++) {
char buf[3] = {uid2[i * 2], uid2[i * 2 + 1], '\0'};
uint8_t uid2Byte = strtol(buf, nullptr, 16);
if (uid1->uidByte[i] != uid2Byte) {
return false;
}
}
return true;
}
const char* formatUid(MFRC522::Uid *uid) {
static char buf[9];
for (uint8_t i = 0; i < uid->size; i++) {
snprintf(&buf[i * 2], 3, "%02X", uid->uidByte[i]);
}
return buf;
}
void printCardDetails(MFRC522::Uid *uid) {
Serial.print("Card UID: ");
for (uint8_t i = 0; i < uid->size; i++) {
if (uid->uidByte[i] < 0x10) {
Serial.print("0");
}
Serial.print(uid->uidByte[i], HEX);
}
Serial.println();
}
void setup() {
Serial.begin(115200);
while (!Serial);
Wire.begin();
mfrc522.PCD_Init();
MFRC522Debug::PCD_DumpVersionToSerial(mfrc522, Serial);
Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks..."));
pinMode(ssr, OUTPUT);
init_LCD();
const char* ssid = "your wifi id"; // Input your wifi id
const char* password = "your wifi password"; // Inpur your wifi password
connectToWifi(ssid, password);
obtainTime();
}
void updateLCD(const char* line1, const char* line2) {
writeCommand(0x01); // Clear Display
// Write line1
for (int i = 0; line1[i] != '\0'; i++) {
writeCommand(0x80 + i);
writeData(line1[i]);
}
// Write line2
for (int i = 0; line2[i] != '\0'; i++) {
writeCommand(0x40 + 0x80 + i);
writeData(line2[i]);
}
}
void loop() {
bool cardPresent = mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial();
CardStatus newStatus;
if (cardPresent) {
printCardDetails(&(mfrc522.uid));
bool isRegistered = false;
for (uint8_t i = 0; i < sizeof(registeredUids) / sizeof(registeredUids[0]); i++) {
if (uidMatches(&(mfrc522.uid), registeredUids[i])) {
isRegistered = true;
strncpy(lastUid, registeredUids[i], sizeof(lastUid));
strncpy(lastUserName, registeredUserNames[i], sizeof(lastUserName));
break;
}
}
if (isRegistered) {
digitalWrite(ssr, HIGH);
Serial.println("Access granted.");
newStatus = REGISTERED_CARD;
while (mfrc522.PICC_IsNewCardPresent()) {
delay(100);
}
} else {
digitalWrite(ssr, LOW);
Serial.println("Access denied.");
newStatus = UNREGISTERED_CARD;
}
} else {
digitalWrite(ssr, LOW);
newStatus = NO_CARD;
}
char welcomeMessage[17] = "Welcome FabLab"; // Moved outside the switch statement
if (newStatus != currentStatus) {
currentStatus = newStatus;
switch (currentStatus) {
case NO_CARD:
if (countTime) {
time_t elapsedTime = time(nullptr) - cardAuthStartTime;
unsigned long hours = elapsedTime / 3600;
unsigned long minutes = (elapsedTime % 3600) / 60;
unsigned long seconds = elapsedTime % 60;
char elapsedTimeStr[17];
snprintf(elapsedTimeStr, sizeof(elapsedTimeStr), "%luh %lum %lus", hours, minutes, seconds);
updateLCD(elapsedTimeStr, lastUserName);
countTime = false;
} else {
updateLCD("Put your", "members card");
delay(2000); // Display "ERROR" for 2 seconds
}
break;
case REGISTERED_CARD:
updateLCD(welcomeMessage, lastUserName);
cardAuthStartTime = time(nullptr); // Start counting the elapsed time
countTime = true;
break;
case UNREGISTERED_CARD:
updateLCD("Card is not", "authorized");
delay(2000); // Display "Card is not authorized" for 2 seconds
break;
}
}
}
void updateLCD(const char* message) {
writeCommand(0x01); // Clear Display
for (int i = 0; message[i] != '\0'; i++) {
writeCommand(0x80 + i);
writeData(message[i]);
}
}
void writeData(byte t_data) {
Wire.beginTransmission(LCD_ADDRESS);
Wire.write(0x40);
Wire.write(t_data);
Wire.endTransmission();
delay(1);
}
void writeCommand(byte t_command) {
Wire.beginTransmission(LCD_ADDRESS);
Wire.write(0x00);
Wire.write(t_command);
Wire.endTransmission();
delay(10);
}
void init_LCD() {
delay(100);
writeCommand(0x38); // FUNCTION SET
delay(20);
writeCommand(0x39); // IS=1
delay(20);
writeCommand(0x14); // INT OSC FREQUENCY
delay(20);
writeCommand(0x7A); // CONTRAST SET 0,1,2,3
delay(20);
writeCommand(0x54); // CONTRAST SET 4,5
delay(20);
writeCommand(0x6C); // F0LLOWER CONTROL
delay(20);
writeCommand(0x38); // IS=0
delay(20);
writeCommand(0x0C); // Display ON
delay(20);
writeCommand(0x01); // Clear Display
delay(20);
writeCommand(0x06); // Entry Mode Set
delay(20);
}
void connectToWifi(const char* ssid, const char* password) {
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("Connected to WiFi.");
}
void obtainTime() {
configTime(0, 0, "pool.ntp.org");
Serial.print("Obtaining time from NTP server...");
while (time(nullptr) < 1619999999) {
delay(500);
Serial.print(".");
}
Serial.println(" Time obtained!");
}
The relevant codes for LCD
The relevant code to control LCD are described below:
- LCD_ADDRESS definition:
- This code defines the I2C address of the LCD. The address specified here is used to communicate from the microcontroller to the LCD.
const uint8_t LCD_ADDRESS = 0x3E; // Define the I2C address for the LCD
- updateLCD function (2 line version):
- This function is used to display text on the first and second lines of the LCD. Passing a string to be displayed as an argument causes the string to be displayed on each line. The
writeCommand
andwriteData
functions used here are described below and are responsible for sending commands to the LCD and writing data, respectively.
- This function is used to display text on the first and second lines of the LCD. Passing a string to be displayed as an argument causes the string to be displayed on each line. The
void updateLCD(const char* line1, const char* line2) {
writeCommand(0x01); // Clear Display
// Write line1
for (int i = 0; line1[i] != '\0'; i++) {
writeCommand(0x80 + i); // Set cursor to the appropriate location
writeData(line1[i]); // Send data (character) to be displayed
}
// Write line2
for (int i = 0; line2[i] != '\0'; i++) {
writeCommand(0x40 + 0x80 + i); // Set cursor to the appropriate location on second line
writeData(line2[i]); // Send data (character) to be displayed
}
}
- update LCD function (1 line version):
- This function is used to display text on the first line of the LCD. Passing a string to be displayed as an argument causes the string to be displayed on the first line of the LCD.
void updateLCD(const char* message) {
writeCommand(0x01); // Clear Display
for (int i = 0; message[i] != '\0'; i++) {
writeCommand(0x80 + i); // Set cursor to the appropriate location
writeData(message[i]); // Send data (character)
}
}
- writeData function:
- This function is used to write data to the LCD. This function uses
beginTransmission
in the Wire library to initiate I2C communication, then callswrite
twice to send data. The firstwrite
indicates to the LCD that what is to be sent next is data, and the nextwrite
is the actual data to be sent. Finally,endTransmission
is called to be end I2C communication.
- This function is used to write data to the LCD. This function uses
void writeData(byte t_data) {
Wire.beginTransmission(LCD_ADDRESS); // Begin I2C transmission to the LCD
Wire.write(0x40); // Specify that what follows is data
Wire.write(t_data); // Write the actual data (character) to the LCD
Wire.endTransmission(); // End I2C transmission
delay(1);
}
- writeCommand function:
- This function is used to send a command to the LCD.
void writeCommand(byte t_command) {
Wire.beginTransmission(LCD_ADDRESS); // Begin I2C transmission to the LCD
Wire.write(0x00); // Specify that what follows is a command
Wire.write(t_command); // Write the actual command to the LCD
Wire.endTransmission(); // End I2C transmission
delay(10);
}
- init_LCD function:
- This function is used to initialize the LCD and sends a series of commands necessary for the LCD to operate properly.
- This sequence of processing allows the LCD to begin operating in a given state and to properly display the text that follows. An appropriate delay is also placed after each command, which allows time for the LCD to process each command.
void init_LCD() {
delay(100); // Initial delay to allow LCD to power up
writeCommand(0x38); // Set LCD function (data length, number of lines, character font)
delay(20); // Allow command to be processed
writeCommand(0x39); // Extend function set (bias selection, instruction table)
delay(20);
writeCommand(0x14); // INT OSC FREQUENCY
delay(20);
writeCommand(0x7A); // Contrast set (low byte)
delay(20);
writeCommand(0x54); // Contrast set (high byte)
delay(20);
writeCommand(0x6C); // Follower control (amplifier ratio)
delay(20);
writeCommand(0x38); // Function set (switch back to basic instruction set)
delay(20);
writeCommand(0x0C); // Display ON
delay(20);
writeCommand(0x01); // Clear Display
delay(20);
writeCommand(0x06); // Entry Mode Set
delay(20);
}
Datasheet of LCD Module (AE-AQM1602A)
I2C address of LCD, "0x3E", is refer in Datasheet.
Upload
- Wired device, then I open Arduino IDE, choose "board ESP32C3". (Tools > Board > ESP32 Arduino > XIAO_ESP32C3) .Port is "/dev/cu.usbmodem1101" in this time, Then Upload the code.
I verified that the device works as programmed!!