// ============================================================================
// SMART MOTOR PROTECTION & HEALTH MONITORING SYSTEM
// ESP32 + FreeRTOS + DHT22 + MPU6050 + Pot + Relay + LCD 20x4
//
// Features:
// - Multi-fault detection (Temperature, Load, Vibration)
// - Baseline-based vibration monitoring
// - Fault latch with manual reset
// - Relay-based motor cut-off (fail-safe)
// - LCD status display & serial diagnostics
//
// Developer: Sujeet
// Level : Advanced Embedded (Level-3)
// ============================================================================
#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "DHT.h"
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
// ---------------- LCD ----------------
LiquidCrystal_I2C lcd(0x27, 20, 4);
// ---------------- DHT ----------------
#define DHTPIN 15
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// ---------------- MPU ----------------
Adafruit_MPU6050 mpu;
// ---------------- Pins ----------------
#define POT_PIN 34
#define BUZZER_PIN 18
#define RELAY_PIN 26
#define RESET_BTN 27
// ---------------- Thresholds ----------------
#define TEMP_WARN 60
#define TEMP_FAULT 70
#define LOAD_WARN 75
#define LOAD_FAULT 90
#define VIB_WARN 0.20
#define VIB_FAULT 0.40
// ---------------- Motor State ----------------
enum MotorState { NORMAL, WARNING, FAULT };
MotorState motorState = NORMAL;
// ---------------- Shared Data ----------------
float motorTemp = 0.0;
float motorVib = 0.0;
float motorLoad = 0.0;
String motorStatus = "NORMAL";
String faultReason = "NONE";
bool faultLatched = false;
SemaphoreHandle_t dataMutex;
// -------- Vibration baseline --------
float baseAx = 0, baseAy = 0, baseAz = 0;
bool baselineSet = false;
// ============================================================================
// WELCOME SCREENS
// ============================================================================
void showWelcomeScreen() {
lcd.clear();
lcd.setCursor(0,0); lcd.print(" SMART MOTOR CTRL ");
lcd.setCursor(0,1); lcd.print(" Health Monitoring");
lcd.setCursor(0,2); lcd.print(" Initializing... ");
lcd.setCursor(0,3); lcd.print(" Please Wait ");
delay(2500);
}
void showSystemReady() {
lcd.clear();
lcd.setCursor(0,0); lcd.print(" SYSTEM READY ");
lcd.setCursor(0,1); lcd.print(" Sensors OK ");
lcd.setCursor(0,2); lcd.print(" Motor Protected ");
lcd.setCursor(0,3); lcd.print(" Starting... ");
delay(2000);
}
// ============================================================================
// SENSOR TASK
// ============================================================================
void SensorTask(void *pv) {
sensors_event_t a, g, t;
while (1) {
xSemaphoreTake(dataMutex, portMAX_DELAY);
motorTemp = dht.readTemperature();
mpu.getEvent(&a, &g, &t);
if (!baselineSet) {
baseAx = a.acceleration.x;
baseAy = a.acceleration.y;
baseAz = a.acceleration.z;
baselineSet = true;
}
float dx = a.acceleration.x - baseAx;
float dy = a.acceleration.y - baseAy;
float dz = a.acceleration.z - baseAz;
motorVib = sqrt(dx*dx + dy*dy + dz*dz);
motorLoad = map(analogRead(POT_PIN), 0, 4095, 0, 100);
xSemaphoreGive(dataMutex);
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
// ============================================================================
// MOTOR LOGIC TASK (MULTI-FAULT + LATCH FIXED)
// ============================================================================
void MotorLogicTask(void *pv) {
while (1) {
xSemaphoreTake(dataMutex, portMAX_DELAY);
String activeFaults = "";
if (motorTemp > TEMP_FAULT) activeFaults += "TEMP ";
if (motorLoad > LOAD_FAULT) activeFaults += "LOAD ";
if (motorVib > VIB_FAULT) activeFaults += "VIB ";
if (activeFaults.length() > 0) {
motorState = FAULT;
motorStatus = "FAULT";
faultLatched = true;
faultReason = activeFaults;
}
else if (!faultLatched &&
(motorTemp > TEMP_WARN ||
motorLoad > LOAD_WARN ||
motorVib > VIB_WARN)) {
motorState = WARNING;
motorStatus = "WARNING";
faultReason = "WARNING";
}
else if (!faultLatched) {
motorState = NORMAL;
motorStatus = "NORMAL";
faultReason = "NONE";
}
xSemaphoreGive(dataMutex);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
// ============================================================================
// LCD TASK
// ============================================================================
void LCDTask(void *pv) {
char l0[21], l1[21], l2[21], l3[21];
while (1) {
xSemaphoreTake(dataMutex, portMAX_DELAY);
snprintf(l0, 21, "T:%5.1fC L:%3.0f%% ", motorTemp, motorLoad);
snprintf(l1, 21, "VIB:%6.3f g ", motorVib);
snprintf(l2, 21, "STATE: %-10s ", motorStatus.c_str());
if (motorState == FAULT)
snprintf(l3, 21, "FAULT: %-15s", faultReason.c_str());
else
snprintf(l3, 21, "UPTIME: %lus ", millis()/1000);
xSemaphoreGive(dataMutex);
lcd.setCursor(0,0); lcd.print(l0);
lcd.setCursor(0,1); lcd.print(l1);
lcd.setCursor(0,2); lcd.print(l2);
lcd.setCursor(0,3); lcd.print(l3);
vTaskDelay(pdMS_TO_TICKS(300));
}
}
// ============================================================================
// SERIAL TASK
// ============================================================================
void SerialTask(void *pv) {
while (1) {
if (Serial.available()) {
String cmd = Serial.readStringUntil('\n');
cmd.trim();
if (cmd == "reset") {
xSemaphoreTake(dataMutex, portMAX_DELAY);
faultLatched = false;
motorState = NORMAL;
motorStatus = "NORMAL";
faultReason = "NONE";
xSemaphoreGive(dataMutex);
Serial.println(">>> FAULT RESET DONE <<<");
}
}
xSemaphoreTake(dataMutex, portMAX_DELAY);
Serial.println("===== MOTOR STATUS =====");
Serial.print("Temp : "); Serial.print(motorTemp); Serial.println(" C");
Serial.print("Load : "); Serial.print(motorLoad); Serial.println(" %");
Serial.print("Vibration : "); Serial.print(motorVib, 3); Serial.println(" g");
Serial.print("State : "); Serial.println(motorStatus);
Serial.print("Faults : "); Serial.println(faultReason);
Serial.println("========================");
xSemaphoreGive(dataMutex);
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
// ============================================================================
// ALERT + RELAY + RESET TASK
// ============================================================================
void AlertTask(void *pv) {
pinMode(BUZZER_PIN, OUTPUT);
pinMode(RELAY_PIN, OUTPUT);
pinMode(RESET_BTN, INPUT_PULLUP);
while (1) {
if (digitalRead(RESET_BTN) == LOW) {
xSemaphoreTake(dataMutex, portMAX_DELAY);
faultLatched = false;
motorState = NORMAL;
motorStatus = "NORMAL";
faultReason = "NONE";
xSemaphoreGive(dataMutex);
delay(500);
}
xSemaphoreTake(dataMutex, portMAX_DELAY);
if (motorState == FAULT) {
digitalWrite(RELAY_PIN, LOW); // Motor OFF
tone(BUZZER_PIN, 2000);
}
else {
digitalWrite(RELAY_PIN, HIGH); // Motor ON (NORMAL + WARNING)
noTone(BUZZER_PIN);
}
xSemaphoreGive(dataMutex);
vTaskDelay(pdMS_TO_TICKS(200));
}
}
// ============================================================================
// SETUP
// ============================================================================
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
lcd.init();
lcd.backlight();
showWelcomeScreen();
dht.begin();
mpu.begin();
showSystemReady();
dataMutex = xSemaphoreCreateMutex();
xTaskCreatePinnedToCore(SensorTask, "Sensor", 4096, NULL, 2, NULL, 1);
xTaskCreatePinnedToCore(MotorLogicTask, "Logic", 2048, NULL, 3, NULL, 1);
xTaskCreatePinnedToCore(LCDTask, "LCD", 4096, NULL, 1, NULL, 1);
xTaskCreatePinnedToCore(SerialTask, "Serial", 4096, NULL, 1, NULL, 0);
xTaskCreatePinnedToCore(AlertTask, "Alert", 2048, NULL, 2, NULL, 0);
}
void loop() {
// FreeRTOS only
}