/*********************
Autenticação do cartão da fct
Vamos ler o cartão num RFID, verificar se este contém 11 caraceteres no seu UID e caso os tenha é inserido num vetor.
Este vetor é lido cada vez que um cartão é aceite para verificar o número de utilizações do mesmo.
É utilizado um clock para dar reset ao vetor mês a mês.
É também acendido um led assim que indica a necessidade da reposição (ldr)
*********************/
#include <Arduino.h>
#include <WiFi.h>
//#include <FirebaseESP32.h>
#include <Firebase_ESP_Client.h>
#include <time.h>
#include <string.h>
#include <addons/TokenHelper.h>
#include <addons/RTDBHelper.h>
/********************* FIREBASE STUFF **************************/
#define API_KEY "AIzaSyBfDzeTT9XfrTabYMQg662BechOLSaeIT0"
#define DATABASE_URL "https://p-dispens-default-rtdb.europe-west1.firebasedatabase.app/" //<databaseName>.firebaseio.com or <databaseName>.<region>.firebasedatabase.app
#define USER_EMAIL "[email protected]"
#define USER_PASSWORD "123456"
#define PROJECT_ID "p-dispens"
/********************************************************/
/********************* TO CHANGE **************************/
#define WIFI_SSID "OnePlus 8"
#define WIFI_PASSWORD "crocodilo%"
#define COLLEGE "sbe" // definir fac onde vai ser instalado o dispositivo
#define MAX_PER_MONTH 10
#include <string.h>
//Biblioteca do motor
#include <Stepper.h>
//Bibliotecas RFID
#include <SPI.h>
#include <MFRC522.h>
//Bibliotecas clock
//#include <Wire.h> //INCLUSÃO DA BIBLIOTECA
//#include <DS1307.h> //INCLUSÃO DA BIBLIOTECA
//Biblioteca LCD
#include <LiquidCrystal_I2C.h>
//Biblioteca tempo para passagem de cartão
#include <ctime>
#define NUM_SEG_ESPERA_CARTAO 5
#define NUM_MAXIMO_UTILIZACAO 3 //Numero de vezes de utilização
#define SS_PIN 5
#define RST_PIN 2
// Define Firebase Data object
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
const char *ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 0;
const int daylightOffset_sec = 3600;
unsigned long sendDataPrevMillis = 0;
unsigned long lastMonthCheck = 0;
int lastMonth = 0;
//Steps do motor
int stepsPerRevolutionTampao = 700;
int stepsPerRevolutionPenso= 2000;
// Define the pins for the stepper motor
const int motorPin1 =13;
const int motorPin2 = 12;
const int motorPin3 = 14;
const int motorPin4 = 27;
const int motorPin5 = 26;
const int motorPin6 = 25;
const int motorPin7 = 33;
const int motorPin8 = 32;
//Define os pins para o led de reposição e sensor de luz(ldr)
const int led = 2;
const int ldr = 34;
//Define pins para passagem para ir buscar tampoões mais longe
const int ldr2 = 39;
int valorluz_penso;
int valorluz_tampao;
Stepper stepper1(2048, motorPin1, motorPin3, motorPin2, motorPin4);
Stepper stepper2(2048, motorPin5, motorPin7, motorPin6, motorPin8);
// Create MFRC522 instance.
MFRC522 mfrc522(SS_PIN, 50);
LiquidCrystal_I2C lcd(0x27,16,2);
int numCartoes = 0, posVet = 0;
String vet1; //Vetor com UIDs
void setup()
{
Serial.begin(115200); // Initiate a serial communication
SPI.begin(); // Initiate SPI bus
mfrc522.PCD_Init();
setupWiFi();
setupFirebase();
// Init and get the time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
printLocalTime();
//Pins do motor
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
pinMode(motorPin3, OUTPUT);
pinMode(motorPin4, OUTPUT);
pinMode(motorPin5, OUTPUT);
pinMode(motorPin6, OUTPUT);
pinMode(motorPin7, OUTPUT);
pinMode(motorPin8, OUTPUT);
//Pins botões
pinMode(15, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
//Inicialização do LCD
lcd.init();
lcd.clear();
lcd.backlight();
//Pins luz
pinMode(led ,OUTPUT);
pinMode(ldr, INPUT);
pinMode(ldr2, INPUT);
Serial.println("Approximate your card to the reader...");
//Inicialização clock
/*
rtc.set(0, 25, 15, 20, 03, 2024); //sec, min, hour, day, month, year/
//rtc.stop();
rtc.start();
*/
}
void loop()
{
int j = 0, aux = 0, conta_card = 0, num_vezes_mes = 0;
String uid; //UID de cada cartão
int stepsPerRevolutionTampao = 700;
valorluz_tampao = analogRead(ldr2);
valorluz_penso = analogRead(ldr);
//Serial.println("A espera do cartao");
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Passe o cartao");
/*Serial.println("tampao");
Serial.println (valorluz_tampao);
Serial.println("penso");
Serial.println(valorluz_penso);
if( valorluz_penso < 1500)
{
digitalWrite(led, HIGH);
Serial.println(valorluz_penso);
}
else
{
digitalWrite(led,LOW);
}
if(valorluz_tampao< 1000)
{
stepsPerRevolutionTampao = 1150;
}
*/
//Código do RFID
if ( ! mfrc522.PICC_IsNewCardPresent())
{
return;
}
Serial.println("vi o cartao");
if ( ! mfrc522.PICC_ReadCardSerial())
{
return;
}
Serial.println("li o cartao");
delay (500);
//Concatenação UID cartão no vetor
for (byte i = 0; i < mfrc522.uid.size; i++)
{
posVet++;
byte byteValue = mfrc522.uid.uidByte[i];
String decimalString = String(byteValue, DEC);
uid.concat(decimalString);
}
int i = 0;
int k = 0;
// Firebase.ready() should be called repeatedly to handle authentication tasks.
checkIfMonthHasPassed();
if (Firebase.ready() /**&& (millis() - sendDataPrevMillis > 2000 || sendDataPrevMillis == 0)**/)
{
sendDataPrevMillis = millis();
}
Serial.println(uid);
processCard((char*)uid.c_str());
}
void dispensa(char* url, int amount)
{
int restantes = MAX_PER_MONTH-amount+1;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("clique no botao");
lcd.setCursor(0,1);
lcd.print("restantes:");
lcd.setCursor(13,1);
lcd.print(restantes);
int estadobotao1 = 1; //estado botao penso - se ativo HIGH
int estadobotao2 = 1; //estado botao tampao - se ativo HIGH
time_t start_time = time(nullptr); // Get current time
while(estadobotao1 && estadobotao2)
{
estadobotao1 = digitalRead(4);
estadobotao2 = digitalRead(15);
if (time(nullptr) - start_time > NUM_SEG_ESPERA_CARTAO)
{
break;
}
}
if (!estadobotao1)
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("penso");
stepper2.setSpeed(10);
stepper2.step(stepsPerRevolutionPenso);
delay(500);
stepper2.step(-stepsPerRevolutionPenso);
delay(500);
digitalWrite(motorPin5, LOW);
digitalWrite(motorPin6, LOW);
digitalWrite(motorPin7, LOW);
digitalWrite(motorPin8, LOW);
Serial.println("A funcionar motor 1");
incrementUsage(url, amount);
}
if (!estadobotao2)
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("tampao");
stepper1.setSpeed(10);
stepper1.step(-stepsPerRevolutionTampao);
delay(500);
stepper1.step(stepsPerRevolutionTampao);
delay(500);
//Mudança Led - passar para segunda parte dos tampoes
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
digitalWrite(motorPin3, LOW);
digitalWrite(motorPin4, LOW);
Serial.println("A funcionar motor 2");
incrementUsage(url, amount);
}
}
void processCard(char* card_id)
{
// count
Serial.println("antes url");
String url;
Serial.println("depois url");
url.concat("/college/");
url.concat(COLLEGE);
url.concat("/");
Serial.println("depois url 22222");
String studentId = retrieveStudentId(card_id);
if(studentId.equals("")){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("cartao invalido");
Serial.println("Invalid card. Student ID not found.");
delay(3000);
return;
}
Serial.println("depois retrieve");
url.concat(studentId);
//strcat(url, studentId); // Concatenate the student ID
Serial.println(url);
if (Firebase.RTDB.getInt(&fbdo, url))
{
int prevUse = fbdo.to<int>();
Serial.printf("prev: %d\n", prevUse);
// exceeded max
if (prevUse == MAX_PER_MONTH)
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Uso excedido");
Serial.println("NO PENSO! :(");
delay(3000);
// CHAMAR FUNÇAO PARA O QUE ACONTECE QUANDO NAO PODE
return;
}
dispensa((char*)url.c_str(), prevUse+1);
}
else
{
Serial.println(fbdo.errorReason().c_str());
// if does not exist count the first use
Serial.println("primeira vez");
dispensa((char*)url.c_str(), 1);
}
}
String retrieveStudentId(char* card_id)
{
Serial.println("Entrou funcao");
//char *lockerPath = "/";
String lockerPath;
lockerPath.concat("/");
lockerPath.concat(COLLEGE);
lockerPath.concat("_cacifo/");
lockerPath.concat(card_id);
String santanderPath;
santanderPath.concat("/");
santanderPath.concat(COLLEGE);
santanderPath.concat("_santander/");
santanderPath.concat(card_id);
/*
Serial.println("Entrou funcao1");
strcat(lockerPath, COLLEGE);
Serial.println("Entrou funcao 2");
strcat(lockerPath, "_cacifo/");
Serial.println("Entrou funcao 3");
strcat(lockerPath, card_id);
Serial.println("Entre strings");
char *santanderPath = "/";
strcat(santanderPath, COLLEGE);
strcat(santanderPath, "_santander/");
strcat(santanderPath, card_id);
Serial.println("antes get document");
*/if (Firebase.Firestore.getDocument(&fbdo, PROJECT_ID, "", lockerPath))
{
Serial.println("CARTAO CACIFO");
}
else if (Firebase.Firestore.getDocument(&fbdo, PROJECT_ID, "", santanderPath))
{
Serial.println("CARTAO SANTANDER");
}
else
{
Serial.println("CARTAO DESCONHECIDO");
return "";
}
FirebaseJson json;
FirebaseJsonData jsonData;
Serial.println(fbdo.payload().c_str());
json.setJsonData(fbdo.payload().c_str());
json.get(jsonData, "fields/student_id/referenceValue");
char *refValueStr = strtok((char *)(jsonData.stringValue.c_str()), "/");
Serial.println(refValueStr);
char *studentId;
while (refValueStr != NULL)
{
studentId = refValueStr;
refValueStr = strtok(NULL, "/");
}
Serial.println(studentId);
return String(studentId);
}
void incrementUsage(char *url, int amount)
{
Serial.println("a incrementarr..");
Serial.println(url);
if (Firebase.RTDB.setInt(&fbdo, url, amount))
{
Serial.printf("new: %d\n", fbdo.to<int>());
Serial.println("PENSO!!!!");
// CHAMAR FUNÇAO PARA DISPENSAR
}
else
{
Serial.println("nao deu");
Serial.println(fbdo.errorReason().c_str());
}
}
void deleteCollegeUsages()
{
char url[50];
strcpy(url, "/college/"); // Copy the first part
strcat(url, COLLEGE); // Concatenate the college
if (Firebase.RTDB.deleteNode(&fbdo, url))
{
Serial.println("Successfully resetted usages !");
}
else
{
Serial.println(fbdo.errorReason().c_str());
}
}
void setupWiFi()
{
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(300);
}
Serial.println();
Serial.print("Connected with IP: ");
Serial.println(WiFi.localIP());
Serial.println();
}
void setupFirebase()
{
Serial.printf("Firebase Client v%s\n\n", FIREBASE_CLIENT_VERSION);
config.api_key = API_KEY;
auth.user.email = USER_EMAIL;
auth.user.password = USER_PASSWORD;
config.database_url = DATABASE_URL;
config.token_status_callback = tokenStatusCallback; // see addons/TokenHelper.h
Firebase.reconnectNetwork(true);
fbdo.setBSSLBufferSize(4096 /* Rx buffer size in bytes from 512 - 16384 */, 1024 /* Tx buffer size in bytes from 512 - 16384 */);
Firebase.begin(&config, &auth);
Firebase.setDoubleDigits(5);
}
void checkIfMonthHasPassed()
{
unsigned long currentTime = millis();
// Check every hour if a month has passed
if (currentTime - lastMonthCheck >= 3600000)
{
lastMonthCheck = currentTime;
struct tm timeinfo;
if (!getLocalTime(&timeinfo))
{
Serial.println("Failed to obtain time");
return;
}
int currentMonth = timeinfo.tm_mon + 1;
if (lastMonth == 0)
{ // set the current month
lastMonth = currentMonth;
}
else if (currentMonth != lastMonth)
{
lastMonth = currentMonth;
monthlyCallback();
}
}
}
void monthlyCallback()
{
Serial.println("A month has passed!");
deleteCollegeUsages();
}
void printLocalTime()
{
struct tm timeinfo;
if (!getLocalTime(&timeinfo))
{
Serial.println("Failed to obtain time");
return;
}
Serial.printf("CURRENT MONTHTH:%d\n", timeinfo.tm_mon + 1);
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
Serial.print("Day of week: ");
Serial.println(&timeinfo, "%A");
Serial.print("Month: ");
Serial.println(&timeinfo, "%B");
Serial.print("Day of Month: ");
Serial.println(&timeinfo, "%d");
Serial.print("Year: ");
Serial.println(&timeinfo, "%Y");
Serial.print("Hour: ");
Serial.println(&timeinfo, "%H");
Serial.print("Hour (12 hour format): ");
Serial.println(&timeinfo, "%I");
Serial.print("Minute: ");
Serial.println(&timeinfo, "%M");
Serial.print("Second: ");
Serial.println(&timeinfo, "%S");
Serial.println("Time variables");
char timeHour[3];
strftime(timeHour, 3, "%H", &timeinfo);
Serial.println(timeHour);
char timeWeekDay[10];
strftime(timeWeekDay, 10, "%A", &timeinfo);
Serial.println(timeWeekDay);
Serial.println();
}