//----- Library definition -----//
#include <WiFi.h>
#include "PubSubClient.h"
#include <Keypad.h>
//----- Connectivity configuration -----//
const char* ssid = "Wokwi-GUEST"; //WiFi network identification AP
const char* password = ""; // Password for the WiFi connectivity
const char* mqttServer = "broker.emqx.io"; // MQTT Server URL
const int port = 1883; // MQTT Port for non secure connection. Use 8883 for TLS.
const char* topicController = "sdg/accessControl/controller"; // Topic used for the controller
const char* topicValidator = "sdg/accessControl/validator"; // Topic used for the access keypad
char clientId[50]; // Client ID used for the MQTT client connecition
WiFiClient espClient; // Creates an instance of the WiFiClient to be used for MQTT connection
PubSubClient client(espClient); // Creates an instance of PubSubClient to handle the MQTT connection
//----- GPIO Pinout -----//
const int redLedPin = 2; // Pin used for the Red LED as output
const int greenLedPin = 15; // Pin used for the Green LED as output
const int buzzerPin = 4; // Ping used for the buzzer as output
const int distanceTrigger = 18; // Pin used to generate emitter signal to the ultrasonic sensor as output
const int distanceEcho = 5; // Pin used to receive the emitter signal from the ultrasonic sensor as input
//----- Keypad configuration -----//
const uint8_t ROWS = 4;
const uint8_t COLS = 4;
char keys[ROWS][COLS] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
uint8_t colPins[COLS] = { 26, 25, 33, 32 }; // Pins connected to C1, C2, C3, C4
uint8_t rowPins[ROWS] = { 13, 12, 14, 27 }; // Pins connected to R1, R2, R3, R4
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); // Creates the instance of the keypad
//----- Global variables -----//
float distance = 0.0; // Variable to allocate the distance data of the ultrasonic sensor
String currentCode = ""; // Variable to allocate the code introduced by the keypad
int idCode = 0; // Variable to allocate an ID of the code introduce to match the response
void setup() {
Serial.begin(115200); // Initialize the serial communication to 115200 bauds
randomSeed(analogRead(0)); // Initialize the seed for random numbers according to the analog input 0
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid); // Prints the AP to be connected
wifiConnect(); // Call the method to connect to the WiFi network
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP()); // Prints the IP given by the router during the WiFi connection
Serial.println(WiFi.macAddress()); // Prints the MAC address of the board network adapter
client.setServer(mqttServer, port); // Configures the MQTT client to its later connection
client.setCallback(callback); // Sets the callback of the MQTT client to listen to events
pinMode(redLedPin, OUTPUT); // Sets the Red LED pin 2 as output
pinMode(greenLedPin, OUTPUT); // Sets the Greed LED pin 15 as output
pinMode(distanceTrigger, OUTPUT); // Sets the Ultrasonic emitter pin 18 as output
pinMode(distanceEcho, INPUT); // Sets the Ultrasonic receiver pin 5 as input
pinMode(buzzerPin, OUTPUT); // Sets the buzzer pin 4 as output
}
//----- WiFi Connectivity method -----//
void wifiConnect() {
WiFi.mode(WIFI_STA); // Configures the WiFi in station mode to get to internet through the router
WiFi.begin(ssid, password); // Connects to the WiFi with the credentials given
while (WiFi.status() != WL_CONNECTED) { // Waits until the connection is stablished
delay(500); // Delay of 500ms
Serial.print("."); // Prints in the serial port until the WiFi connectivity is stablished
}
}
//----- MQTT Reconnection method -----//
void mqttReconnect() {
while (!client.connected()) { // Checks if the client is connected to the MQTT Server
Serial.print("Attempting MQTT connection...");
long r = random(1000); // Generate a random number from 1 to 1000
sprintf(clientId, "clientId-%ld", r); // Concatenate a string with the random number for the MQTT client ID
if (client.connect(clientId)) { // Checks if the MQTT client is already connected
Serial.print(clientId);
Serial.println(" connected");
client.subscribe(topicController); // Subscribe to the controller topic to receive the responses
} else {
Serial.print("failed, rc=");
Serial.print(client.state()); // Prints the status of the MQTT connection
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
//----- Check the MQTT Connection method -----//
bool checkConnection() {
delay(10);
if (!client.connected()) { // Checks if the MQTT client is connected
mqttReconnect(); // Call the MQTT reconnection method in case the client disconnects
}
client.loop(); // Makes sure the connection is stablished and checks if any event has occured
return client.connected(); // Return the boolean status of the MQTT connection
}
//----- Ultrasonic sensor reading method -----//
float readDistanceCM() {
digitalWrite(distanceEcho, LOW); // Sets the ultrasonic sensor receiver pin to 0
delayMicroseconds(2); // Wait 2 miliseconds to make sure the receiver is in low state
digitalWrite(distanceTrigger, HIGH); // Sets the ultrasonic sensor emitter to 1
delayMicroseconds(10); // Wait 10 miliseconds to make sure the emitter is in high state
digitalWrite(distanceTrigger, LOW); // Sets the ultrasonic sensor emitter to 0
int duration = pulseIn(distanceEcho, HIGH); // Reads the pulse received and calculate its duration
int distance = int(duration * 0.034 / 2); // Algorithm to convert pulse duration in distance
return distance;
}
//----- Manage the user feedback of the controller -----//
void feedbackAccessValidation(bool accessGranted) {
if(accessGranted == true) { // Checks if the access is granted
Serial.print("Access granted"); // Prints in the serial comms the granted message
digitalWrite(greenLedPin, HIGH); // Turns the green LED on
digitalWrite(redLedPin, LOW); // Turns the red LED off
tone(buzzerPin, 800, 250); //Create a tone in output buzzerPin of 200Hz and for 250ms.
delay(2000); // Maintain the GPIO status for 2 seconds
digitalWrite(greenLedPin, LOW); // Turns the green led off
}else{
Serial.print("Access denied"); // Prints in the serial comms the denied message
digitalWrite(greenLedPin, LOW); // Turns the green LED off
digitalWrite(redLedPin, HIGH); // Turns the red LED on
tone(buzzerPin, 200, 1000); //Create a tone in output buzzerPin of 800Hz and for 1000ms.
delay(2000); // Maintain the GPIO status for 2 seconds
digitalWrite(redLedPin, LOW); // Turns the red LED off
}
}
//----- Callback for MQTT Listener events ----//
void callback(char* topic, byte* message, unsigned int length) {
Serial.print("Message arrived on topic: "); // Prints that a new message has arrived
Serial.print(topic); // Prints the topic of the arrived message
Serial.print(". Message: ");
String stMessage; // Define the variable to store the message received in String format
for (int i = 0; i < length; i++) { // Compose the string message byte by byte
Serial.print((char)message[i]);
stMessage += (char)message[i];
}
Serial.println();
if (String(topic) == topicController) { // Checks if the message come from the controller topic
// JSON Manual decodification //
int index = stMessage.indexOf(','); // Searches for a , simbol position
String sub_S1 = stMessage.substring(0,index); // Gets the substring from position 0 to the previous calculated index
int index_S1 = sub_S1.indexOf(':'); // Searches for a : simbol position
String id = sub_S1.substring(index_S1+1,sub_S1.length()); // Gets the id code from substring calculated from indexes
String sub_S2 = stMessage.substring(index,stMessage.length()); // Gets a substring to obtain the code
int index_S2 = sub_S2.indexOf(':'); // Searches for a : simbol position
String resp = sub_S2.substring(index_S2+1,sub_S2.length()-1); // Gets the response of the controller from the calculated indexes
//End JSON Manual decodification//
Serial.print("Validation received - idCode: ");
Serial.print(id); // Prints the ID Code received by the controller
Serial.print(" - Response: ");
Serial.println(resp); // Prints the response received by the controller
if(id == String(idCode)) { // Checks if the ID code received is the latest one sent
if(resp == "True"){ // If positive match
feedbackAccessValidation(true); // Call the method to manage the positive GPIO outputs using true as parameter
}else{
feedbackAccessValidation(false); // Call the method to manage the negative GPIO outputs using false as parameter
}
}else{
Serial.println("ID Code not match the last one. Validation rejected");
}
}else{
Serial.print("Topic does not match");
}
}
//----- Publishing message method -----//
void publishMessage(String code) {
String packet = ""; // creates the variable used to be published
// Compose the JSON object by concatenatic strings //
packet.concat(("{\"reader\":\"001""\""));
packet.concat((",\"idCode\":"));
idCode = random(1000000); // Generate a random number from 1 to 1000000 as ID Code
packet.concat(idCode);
packet.concat((",\"code\":\""));
packet.concat(currentCode);
packet.concat("\"}");
// End of JSON Composer //
Serial.println(packet); // Prints in the serial port the message composed
client.publish(topicValidator, packet.c_str()); // Publish the composed message as a pointer to a null-terminated character array
}
//----- Main programe method -----//
void loop() {
checkConnection(); // Checks if the MQTT connection is stablished
if(distance != readDistanceCM()) { // Checks if latest distance is the same to avoid excesive debugging
distance = readDistanceCM(); // Sets the latest read distance to the new reading
Serial.print("Distance: ");
Serial.println(distance); // Prints in the serial port the distance read
}
if(distance < 100) { // Checks if the distance read is less than 100cm.
char key = keypad.getKey(); // If the distance is less than 1m it enables the keypad reading
if (key != NO_KEY) { // Checks if a key is pressed in the keypad
currentCode += String(key); // Concatenate the keys pressed to componse a code
Serial.println(currentCode); // Prints the current introduced keys
if('#' == key){ // Check if the # simbol is pressed, which means the code is going to be sent to the controller
publishMessage(currentCode); // Publishes the message with the code introduced
currentCode = ""; // Reset the currentCode introduced
}
}
}
}