We have developed Smort, a smart 4WD platform designed for critical patient care. The system autonomously delivers medication at scheduled times, equipped with a rotating medicine dispenser and real-time monitoring using an inbuilt camera and microphone. The platform navigates towards patients, ensuring timely and accurate medication administration, while the camera monitors their condition.
The main microcontroller board I chose for this project is Sipeed Maixduino Dev Kit which has in-built camera and mems microphone that can detect the actions and sounds of patient for real-time monitoring of patient. The board also has AI computing power which will help detect the emergencies in real-time and send alerts to doctors and family accordingly. Additionally, I have used ESP32 which will connect to a hotspot or WiFi providing internet access to the device, which will then connect to Firebase Cloud for sending the emergency alerts, real-time monitoring data and accessing the medication schedule set by caregiver or doctor in the Smort Care Application.
Components Required
ESP32 module - 2
DC Motor with Wheel - 4
Servo Motor - 1
Buzzer - 1
IR Sensor - 2
L298N Motor Driver - 1
Breadboard mini - 1
Connecting Jumper Wires - Required Quantity
Power Supply - Required Batteries for Motors
Navigate to this YouTube Video for Full Demonstration of the Project
Circuit Diagram Explanation
ESP32 (Center Board):
The ESP32 is responsible for:
- Fetching Medication Data: Connects to Firestore through Wi-Fi to retrieve scheduled medication data.
- Controlling Peripheral Components: Manages the servo motor, buzzer, and motor driver through its GPIO pins.
- Line-Following Mode: Reads input from the IR sensors to control the movement of the 4WD platform.
Connections:
- Power Supply: Powered via 5v output from the motor driver or USB input.
- Servo Motor: Connected to GPIO 13 pin to rotate the lid of the medicine dispenser.
- Buzzer: Connected to GPIO 12 pin to alert the patients to take medicine.
- IR Sensors: Connected to GPIO 27 and GPIO 26 pins to detect the line on the ground and move towards the patient or its base station.
- Communication with Maixduino: GPIO 23 and GPIO 25 which detects for HIGH or LOW signal from the Maixduino board.
Maixduino (Top Board):
This microcontroller is the brain of our project, which includes AI capabilities, is used for:
- Voice Recognition: Detects commands from the patient, such as "thank you," "help", "water".
- Triggering Actions: Once it recognizes a command, it sends the HIGH signal to the ESP32 dedicated GPIO pin to trigger the appropriate response (e.g., stopping or returning to the base station).
Connections:
- Data Communication: Connected to the ESP32 through GPIO for sending detected commands by making that particular GPIO line HIGH.
- Power: Powered through USB cable.
Buzzer (Top Right):
The buzzer is used for:
- Audible Alerts: It beeps when the medication time is reached to alert the patient.
Connections:
- Power Supply: Connected to 3.3V or 5V for operation.
- Control Line: Connected to an ESP32 GPIO 12 pin that drives the buzzer ON/OFF.
- Ground: Connected to the common GND of ESP32
Servo Motor (Right Side):
The servo is responsible for:
- Dispensing Medication: Rotates to the correct position to present a section containing the medication for the patient.
Connections:
- Power Supply: Connected to a 5V line for stable operation.
- Signal Line: Connected to an ESP32 GPIO 13 pin for rotating the lid of medicine dispenser.
- Ground: Connected to the common GND of ESP32.
IR Line-Following Sensors (Bottom Left & Right):
These sensors are critical for:
- Line Following: Detecting the contrast between the line on the ground and the surrounding surface. The sensors read HIGH or LOW signals to guide the motors and steer the platform along the path.
Connections:
- Power Supply: Typically connected to 3.3V or 5V for operation.
- Signal Output: The output pins are connected to ESP32 GPIO 26 and ESP32 GPIO 27 pins to provide input signals based on the detected line.
- Ground: Connected to the system GND for consistent signal readings.
L298N Motor Driver (Center Bottom):
This dual H-bridge motor driver module controls:
- 4WD Platform Motors: Drives the four DC motors, allowing forward, backward, left, and right movements.
- Direction and Speed Control: Controlled by the input signals from the ESP32, allowing precise motor movements for line-following and returning to the base station.
Connections:
- Power Supply: VCC connected to the 9V battery; GND shared with the common ground.
- Input Pins (IN1-IN4): Connected to GPIO 5, GPIO 18, GPIO 19, GPIO 21 pins on the ESP32 for controlling motor direction.
- Motor Outputs: Connected to the four DC motors of the 4WD platform.
9V Battery (Bottom):
Supplies power specifically for:
- Motor Operations and ESP32: Powers the L298N motor driver to run the DC motors and ESP32 modules for controlling peripherals.
Connections:
- Positive Terminal: Connected to the L298N’s 12V input.
- Negative Terminal: Connected to the L298N's GND input.
Additional ESP Board (Top Left):
This ESP board handles:
- Patient Alert Notifications: Sends alerts through FCM (Firebase Cloud Messaging) when the patient requests help or if there’s a need for assistance.
Connections:
- Communication with Maixduino: Connected to the GPIO 13 of ESP32 to GPIO 2 of Maixduino.
Smort Care Application
The purpose of this application is to manage the medication schedule remotely and receive real-time alerts from the smort.
This application has following features:
At first the user needs to SignUp in the Smort Care Application, after the registration user will be allowed to login to the application with their credentials.
Once the user is registered the data of the user is stored in the users collection of firestore, and the authentication details are stored in firebase.
After the user registration the user will be logged in with the created account else the user can log in with existing account
Ex: username: test user, email: [email protected].
After the successful login the user will be navigated to the home screen.
Once the user is valid and gets the access to home screen, now he can connect to the smort device by tapping on the option "Connect Smort" with unique smort_id.
Every smort will have a unique smort_id which is a reference to store the medications and its status data in the firestore database.
After the user enters a smort_id as smort_test which is in the database added by me, he will get the status of the smort device and medications scheduled for that device.
If the smort_id is not available in the database that mean there is no such device with that id and user will be notified as invalid smort id if he enters wrong smort_id.Now, we can add medications and delete medications as per our need and schedule.
To add medication tap on the "Add Medication" option then it displays the Add Medication screen in which we can set the Medication Name, Section Number and Time.
After successful addition of medication the home screen will be updated with the latest schedule.
We can also delete the medications by tapping on the red dustbin icon in the medication details card, deleting it will remove that card and update the medications list in database accordingly.
We can also disconnect with the current smort device and connect to another smort device through the disconnect button.
Smort Code
The consists of 3 microcontrollers Maixduino and 2 ESP32's. Let's see the code section of each microcontroller in detail.
Maixduino Code:
Maixduino detects the commands from the patient.
Part 1: Header files and macros
#include "gpio.h"
#include "fpioa.h"
#include "Maix_Speech_Recognition.h"
#include "voice_model.h"
#define NOTIFY_PIN 21
#define THANK_YOU_PIN 22
#define WATER_PIN 23
#define NOTIFY_GPIOHS 0
#define THANK_YOU_GPIOHS 1
#define WATER_GPIOHS 2
SpeechRecognizer rec;
At first, we should include all the required libraries and define the macros in our code.
"
gpio.h
" and "fpioa.h
" header files used for mapping physical pins."Maix_Speech_Recognition.h" header file contains the speech recognizer object which will detect the voice models in the "voice_model.h" header files.
The required macros are defined.
Part 2: Setup
void setup()
{
fpioa_set_function(NOTIFY_PIN, FUNC_GPIOHS0);
fpioa_set_function(THANK_YOU_PIN, FUNC_GPIOHS1);
fpioa_set_function(WATER_PIN, FUNC_GPIOHS2);
gpiohs_set_drive_mode(NOTIFY_GPIOHS, GPIO_DM_OUTPUT);
gpiohs_set_drive_mode(THANK_YOU_GPIOHS, GPIO_DM_OUTPUT);
gpiohs_set_drive_mode(WATER_GPIOHS, GPIO_DM_OUTPUT);
rec.begin();
Serial.begin(115200);
Serial.println("init model...");
rec.addVoiceModel(0, 0, red_0, fram_num_red_0);
rec.addVoiceModel(0, 1, red_1, fram_num_red_1);
rec.addVoiceModel(0, 2, red_2, fram_num_red_2);
rec.addVoiceModel(0, 3, red_3, fram_num_red_3);
rec.addVoiceModel(1, 0, green_0, fram_num_green_0);
rec.addVoiceModel(1, 1, green_1, fram_num_green_1);
rec.addVoiceModel(1, 2, green_2, fram_num_green_2);
rec.addVoiceModel(1, 3, green_3, fram_num_green_3);
rec.addVoiceModel(2, 0, blue_0, fram_num_blue_0);
rec.addVoiceModel(2, 1, blue_1, fram_num_blue_1);
rec.addVoiceModel(2, 2, blue_2, fram_num_blue_2);
rec.addVoiceModel(2, 3, blue_3, fram_num_blue_3);
rec.addVoiceModel(3, 0, turnoff_0, fram_num_turnoff_0);
rec.addVoiceModel(3, 1, turnoff_1, fram_num_turnoff_1);
rec.addVoiceModel(3, 2, turnoff_2, fram_num_turnoff_2);
rec.addVoiceModel(3, 3, turnoff_3, fram_num_turnoff_3);
rec.addVoiceModel(4, 0, help_0, fram_num_help_0);
rec.addVoiceModel(4, 1, help_1, fram_num_help_1);
rec.addVoiceModel(4, 2, help_2, fram_num_help_2);
rec.addVoiceModel(5, 0, water_0, fram_num_water_0);
rec.addVoiceModel(5, 1, water_1, fram_num_water_1);
rec.addVoiceModel(5, 2, water_2, fram_num_water_2);
rec.addVoiceModel(6, 0, tq_0, fram_num_tq_0);
rec.addVoiceModel(6, 1, tq_1, fram_num_tq_1);
rec.addVoiceModel(6, 2, tq_2, fram_num_tq_2);
Serial.println("init model ok!");
}
In setup code we are doing the following operations:
The physical pins are mapped to the GPIOHS for the operation of the HIGH and LOW signals that can be sent to ESP32 for further operations.
The voice recognizer is being initialized and set to begin, the voice models from the "voice_models.h" header file are being added to the voice recognizer.
The serial monitor is initialized for debugging purposes.
Part 3: Loop
void loop()
{
int res = rec.recognize();
Serial.printf("res : %d ", res);
if (res > 0){
switch (res)
{
case 5:
gpiohs_set_pin(NOTIFY_GPIOHS, GPIO_PV_HIGH);
gpiohs_set_pin(THANK_YOU_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(WATER_GPIOHS, GPIO_PV_LOW);
Serial.println("rec : help! ");
delay(5000);
gpiohs_set_pin(NOTIFY_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(THANK_YOU_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(WATER_GPIOHS, GPIO_PV_LOW);
break;
case 6:
gpiohs_set_pin(WATER_GPIOHS, GPIO_PV_HIGH);
gpiohs_set_pin(NOTIFY_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(THANK_YOU_GPIOHS, GPIO_PV_LOW);
Serial.println("rec : water!");
delay(5000);
gpiohs_set_pin(NOTIFY_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(THANK_YOU_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(WATER_GPIOHS, GPIO_PV_LOW);
break;
case 7:
gpiohs_set_pin(WATER_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(NOTIFY_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(THANK_YOU_GPIOHS, GPIO_PV_HIGH);
Serial.println("rec : Thank you!");
delay(5000);
gpiohs_set_pin(NOTIFY_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(THANK_YOU_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(WATER_GPIOHS, GPIO_PV_LOW);
break;
default:
gpiohs_set_pin(NOTIFY_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(THANK_YOU_GPIOHS, GPIO_PV_LOW);
gpiohs_set_pin(WATER_GPIOHS, GPIO_PV_LOW);
Serial.println("rec : DEFAULT");
break;
}
} else {
Serial.println("recognize failed.");
}
delay(1000);
}
In the loop part we are doing following things:
Recognizing the sound and checking with the voice models added to the speech recognizer, it gives the model number and based on that we can know what word it is.
On recognizing the word, we get a result based on the result we are setting the notify, water, thankyou pins to high state.
The loop repeats every one second.
ESP32 Code:
This esp fetches the data from firestore and controls the medicine dispenser, 4wd platform.
Part 1: Header files and constants#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <NTPClient.h>
#include <ESP32Servo.h>
#include <WiFiUdp.h>
const char* ssid = "SmartMedic";
const char* password = "sm@1234567";
const char* apiKey = "api-key";
const char* baseURL = "https://firestore.googleapis.com/v1/projects/smort-care/databases/(default)/documents/";
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 19800, 60000);
Servo myServo;
const int servoPin = 13;
const int buzzerPin = 12;
const int leftSensorPin = 27;
const int rightSensorPin = 26;
const int leftMotorPin1 = 5;
const int leftMotorPin2 = 18;
const int rightMotorPin1 = 19;
const int rightMotorPin2 = 21;
const int bringWaterPin = 23;
const int thankyouPin = 25;
struct Medication {
int sectionNumber;
String time;
};
Medication medications[10];
int medicationCount = 0;
const int servoPositions[] = {30, 58, 86, 117, 145, 180};
const int numSections = sizeof(servoPositions) / sizeof(servoPositions[0]);
unsigned long lastFetchTime = 0;
const unsigned long fetchInterval = 5 * 60 * 1000; // 5 minutes
bool lineFollowingMode = false;
Header Files and Libraries
#include <WiFi.h>
: Used for connecting the ESP32 to a Wi-Fi network.#include <HTTPClient.h>
: Allows HTTP requests to be made to web services.#include <ArduinoJson.h>
: Used for parsing JSON responses from the Firestore API.#include <NTPClient.h>
: Handles Network Time Protocol (NTP) to get the current time.#include <ESP32Servo.h>
: Controls the servo motor connected to the ESP32.#include <WiFiUdp.h>
: Provides UDP support for the NTP client.
Constants and Global Variables
Wi-Fi and Firestore Settings:
const char* ssid
and const char* password
: Stores the Wi-Fi credentials.
const char* apiKey
and const char* baseURL
: Contains the API key and the base URL for Firestore data access.
NTP Client Setup:
WiFiUDP ntpUDP
: UDP object for time synchronization.
NTPClient timeClient
: NTP client initialized with the UDP object, time server ("pool.ntp.org"), timezone offset (19800 seconds for IST), and update interval (60000 ms).
Servo and Buzzer Setup:
Servo myServo
: Servo object for controlling the servo motor.
const int servoPin
and const int buzzerPin
: Pin assignments for the servo and buzzer.
Line Following Sensor and Motor Setup:
const int leftSensorPin
, rightSensorPin
: Pin assignments for line-following sensors.
Motor Pins (leftMotorPin1, leftMotorPin2, rightMotorPin1, rightMotorPin2
): Pin assignments for the motor control.
Additional Sensor Pins:
const int bringWaterPin
and thankyouPin
: Pin assignments for sensors that detect patient requests (e.g., for water or acknowledgment).
Medication Data Structure:
struct Medication
: Structure to store medication data (sectionNumber, time).
Medication medications [10]
: Array to hold multiple medication entries.
int medicationCount
: Number of medications fetched.
Servo Positions:
const int servoPositions[]
: Array holding predefined angles for the servo corresponding to each section.
const int numSections
: Number of available sections.
Timing Variables:
unsigned long lastFetchTime
: Used to track the last data fetch from Firestore.
const unsigned long fetchInterval
: Interval for data fetch (5 minutes).
Flags:
bool lineFollowingMode
: Indicates whether line-following mode is active.
Part 2: Setup
void setup() {
Serial.begin(9600);
WiFi.begin(ssid, password);
// WiFi Connection
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
// Start NTP Client
timeClient.begin();
// Setup Servo, Buzzer, and Additional Sensors
myServo.attach(servoPin);
pinMode(buzzerPin, OUTPUT);
pinMode(bringWaterPin, INPUT);
pinMode(thankyouPin, INPUT);
// Setup Line Following Sensors and Motors
pinMode(leftSensorPin, INPUT);
pinMode(rightSensorPin, INPUT);
pinMode(leftMotorPin1, OUTPUT);
pinMode(leftMotorPin2, OUTPUT);
pinMode(rightMotorPin1, OUTPUT);
pinMode(rightMotorPin2, OUTPUT);
// Fetch initial Firestore data
fetchFirestoreData();
}
In the setup section we do the following things:
Initialize Serial Communication:
Serial.begin(9600)
for debugging.Connect to Wi-Fi:
Attempts to connect to Wi-Fi using WiFi.begin()
and waits until the connection is established.
Start NTP Client:
timeClient.begin()
to begin fetching time data.Servo and Pin Initialization:
Attaches the servo to servoPin
using myServo.attach()
.
Configures buzzerPin, bringWaterPin, thankyouPin, leftSensorPin, rightSensorPin,
and motor pins using pinMode()
.
Initial Firestore Data Fetch: Calls
fetchFirestoreData()
to retrieve medication data from Firestore.
Part 3 : Loop
void loop() {
timeClient.update();
String currentTime = timeClient.getFormattedTime();
Serial.println("Current Time: " + currentTime);
// Check if bringWater or thankyou is triggered
if (digitalRead(bringWaterPin) == HIGH || digitalRead(thankyouPin) == HIGH) {
lineFollowingMode = true;
resumeMotion(); // Initiate motion
}
// Check for scheduled medication dispensing
for (int i = 0; i < medicationCount; i++) {
if (isTimeMatching(currentTime, medications[i].time))
{ Serial.print("Time for medication at section: ");
Serial.println(medications[i].sectionNumber);
lineFollowingMode = true;
digitalWrite(buzzerPin, HIGH);
rotateServo(medications[i].sectionNumber);
resumeMotion();
while(lineFollowingMode){
followLine();
}
delay(60000);
}
} // Execute line-following if mode is activated if (lineFollowingMode) { followLine();
currentMillis = millis();
if (currentMillis - lastFetchTime >= fetchInterval) {
lastFetchTime = currentMillis;
fetchFirestoreData();
}
delay(1000);
}
In the loop part we continuously do the following operations:
Time Update: Updates current time using
timeClient.update()
and prints it.Check Additional Sensor Inputs:
If bringWaterPin
or thankyouPin
reads HIGH, sets lineFollowingMode
to true and calls resumeMotion()
to initiate movement.
Medication Time Check:
Iterates through medications []
and checks if the current time matches any medication time using isTimeMatching()
.
If matched, activates line-following mode, starts the buzzer, rotates the servo to the corresponding section, and initiates the movement with resumeMotion()
.
Line-Following Mode Execution:
If lineFollowingMode
is true
, calls followLine()
to control motor movements based on sensor readings.
Firestore Data Refresh:
Checks if the time elapsed since the last data fetch exceeds fetchInterval
and calls fetchFirestoreData()
if true.
Part 4: Supporting Functions
void fetchFirestoreData() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = String(baseURL) + "smort/smort_test/medications?key=" + apiKey;
http.begin(url);
int httpCode = http.GET();
if (httpCode > 0) {
String payload = http.getString();
Serial.println("Fetched medication data: " + payload);
DynamicJsonDocument doc(4096);
DeserializationError error = deserializeJson(doc, payload);
if (!error) {
JsonArray medicationsArray = doc["documents"].as();
medicationCount = 0;
for (JsonObject medication : medicationsArray) {
JsonObject fields = medication["fields"].as();
medications[medicationCount].sectionNumber = fields["sectionNumber"]["integerValue"].as();
medications[medicationCount].time = fields["time"]["stringValue"] | "N/A";
medicationCount++;
}
} else {
Serial.println("JSON parsing failed");
}
}
http.end();
} else {
Serial.println("WiFi not connected");
}
}
void resumeMotion() {
moveForward();
delay(3000);
stopCar();
delay(200);
}
bool isTimeMatching(String currentTime, String medicationTime) {
int currentSeconds = timeToSeconds(currentTime);
int medicationSeconds = timeToSeconds(medicationTime);
return abs(currentSeconds - medicationSeconds) <= 5; }
int timeToSeconds(String time) {
int hours, minutes, seconds;
sscanf(time.c_str(), "%d:%d:%d", &hours, &minutes, &seconds);
return hours * 3600 + minutes * 60 + seconds; }
void followLine() {
int leftSensor = digitalRead(leftSensorPin);
int rightSensor = digitalRead(rightSensorPin);
if (leftSensor == HIGH && rightSensor == HIGH) {
stopCar();
digitalWrite(buzzerPin, LOW);
lineFollowingMode = false;
} else if (leftSensor == HIGH && rightSensor == LOW) {
turnLeft();
} else if (leftSensor == LOW && rightSensor == HIGH) {
turnRight();
} else {
moveForward();
}
}
void moveForward() {
digitalWrite(leftMotorPin1, LOW);
digitalWrite(leftMotorPin2, HIGH);
digitalWrite(rightMotorPin1, HIGH);
digitalWrite(rightMotorPin2, LOW);
}
void turnRight() {
digitalWrite(leftMotorPin1, LOW);
digitalWrite(leftMotorPin2, HIGH);
digitalWrite(rightMotorPin1, LOW);
digitalWrite(rightMotorPin2, LOW);
}
void turnLeft() {
digitalWrite(leftMotorPin1, LOW);
digitalWrite(leftMotorPin2, LOW);
digitalWrite(rightMotorPin1, HIGH);
digitalWrite(rightMotorPin2, LOW);
}
void stopCar() {
digitalWrite(leftMotorPin1, LOW);
digitalWrite(leftMotorPin2, LOW);
digitalWrite(rightMotorPin1, LOW);
digitalWrite(rightMotorPin2, LOW);
}
void rotateServo(int sectionNumber) {
if (sectionNumber >= 1 && sectionNumber <= numSections) {
int servoPosition = servoPositions[sectionNumber - 1];
myServo.write(servoPosition);
Serial.print("Rotating to section: ");
Serial.println(sectionNumber);
delay(1000);
} else {
Serial.println("Invalid section number");
}
}
void fetchFirestoreData()
:
Fetches medication data from Firestore using HTTP GET request.
Parses JSON response using ArduinoJson to populate the medications[] array.
void resumeMotion()
:
Moves the platform forward for a short duration to help detect the line again, then stops it momentarily.
bool isTimeMatching(String currentTime, String medicationTime)
:
Converts both currentTime and medicationTime to seconds and checks if they match within a 5-second tolerance.
int timeToSeconds(String time)
:
Converts a String time format (HH:MM:SS) into total seconds for easier comparison.
void followLine(
):
Reads sensor inputs to guide the platform:
Stops when both sensors detect the line (destination reached).
Turns left or right based on individual sensor readings.
Moves forward when neither sensor detects a line.
Motor Control Functions:
void moveForward()
: Activates motor pins for forward movement.
void turnRight()
: Activates motor pins to turn right.
void turnLeft()
: Activates motor pins to turn left.
void stopCar()
: Stops all motor activity.
void rotateServo(int sectionNumber)
: Rotates the servo to the angle corresponding to the sectionNumber using myServo.write().
Additional ESP32 for Sending Notifications to Caregiver
Part 1: Header files and Constants#include <WiFi.h>
#include <FirebaseESP32.h>
#include <addons/TokenHelper.h>
#define WIFI_SSID "SmartMedic"
#define WIFI_PASSWORD "sm@1234567"
#define notifyPin 13
#define FIREBASE_PROJECT_ID "project-id"
#define FIREBASE_CLIENT_EMAIL "firebase-client-email"
const char PRIVATE_KEY[] PROGMEM = "-----BEGIN PRIVATE KEY-----XXX-----END PRIVATE KEY-----n";
#define DEVICE_REGISTRATION_ID_TOKEN "device-id-token"
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
unsigned long lastTime = 0;
Header Files:
#include <Arduino.h>
: Essential for basic Arduino functions.#include <WiFi.h>
: Manages the Wi-Fi connectivity.#include <FirebaseESP32.h>
: Library for Firebase integration with ESP32.#include <addons/TokenHelper.h>
: Provides helper functions for Firebase token handling.The constants are defined with proper credentials.
Global Variables
FirebaseData fbdo;
: An instance to handle Firebase communication.FirebaseAuth auth;
: Handles authentication with Firebase.FirebaseConfig config;
: Configures Firebase client settings.unsigned long lastTime = 0;
: Used for timing theloop()
operations.
Part 2: Setup
void setup()
{
Serial.begin(115200);
pinMode(sensorPin, INPUT);
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();
Serial.printf("Firebase Client v%snn", FIREBASE_CLIENT_VERSION);
config.service_account.data.client_email = FIREBASE_CLIENT_EMAIL;
config.service_account.data.project_id = FIREBASE_PROJECT_ID;
config.service_account.data.private_key = PRIVATE_KEY;
config.token_status_callback = tokenStatusCallback;
Firebase.reconnectNetwork(true);
fbdo.setBSSLBufferSize(4096, 1024);
Firebase.begin(&config, &auth);
}
Starts the serial communication at a baud rate of 115200 for debugging.
Configures
sensorPin
as an input.Connects to Wi-Fi using
WIFI_SSID
andWIFI_PASSWORD
and displays connection progress in the Serial Monitor.Sets up the Firebase client with the provided service account credentials.
Assigns
tokenStatusCallback
to monitor Firebase token status.Enables automatic network reconnection in case of disconnection.
Sets the buffer size for SSL operations with
fbdo
.Initializes Firebase with the provided configuration and authentication details.
Part 3: Loopvoid loop()
{
if (Firebase.ready() && digitalRead(sensorPin) == HIGH)
{
sendMessage();
delay(10000);
}
}
Checks if Firebase is ready and if
sensorPin
reads HIGH (indicating a trigger).Calls
sendMessage()
if conditions are met and waits 10 seconds before repeating to avoid consecutive triggers.
Part 4: Supporting Functionsvoid sendMessage()
{
Serial.print("Send Firebase Cloud Messaging... ");
FCM_HTTPv1_JSON_Message msg;
msg.token = DEVICE_REGISTRATION_ID_TOKEN;
msg.notification.body = "Patient asked for help !!!";
msg.notification.title = "Smort Alert !!!";
if (Firebase.FCM.send(&fbdo, &msg))
Serial.printf("okn%snn", Firebase.FCM.payload(&fbdo).c_str());
else
Serial.println(fbdo.errorReason());
}
Creates an
FCM_HTTPv1_JSON_Message
object and sets the device token for message delivery.Defines the notification message body as "Patient asked for help !!!" and title as "Smort Alert !!!".
Sends the notification using Firebase Cloud Messaging and prints the response payload if successful.
Displays an error message if sending fails.