← All projects

IEIndustrial Automation & Embedded

IoT Scalable Smart Automation System

A localized IoT network — a NodeMCU (ESP8266) edge controller driving a 16-channel opto-isolated relay module to safely switch high-voltage appliances and lab equipment, with PIR/LDR smart-lighting energy optimization and a custom MIT App Inventor Android dashboard.

  • IoT
  • Power Electronics
  • Energy Optimization
  • Mobile UI/UX

Hardware & Architecture

  • NodeMCU ESP8266 edge controller acting as a local web server and GPIO manager.
  • 16-channel 5V relay module with optocoupler isolation to safely toggle high-voltage loads.
  • PIR occupancy sensor + LDR ambient-light sensor for the smart-lighting subsystem.
  • Dedicated external 5V/3A PSU to drive 16 relay coils without browning out the MCU.
  • Custom Android app (MIT App Inventor) issuing HTTP GET requests to the NodeMCU API.

Key Highlights

  • REST-style endpoints toggle individual relays and expose a JSON /status feed for the app to mirror hardware state.
  • Autonomous smart-lighting loop turns lab lights on only when it's dark AND motion is detected, off otherwise, to save energy.
  • Polling app UI updates button colors every 2s to stay in sync with the relays.

Images

Code

iot_automation.inoESP8266 Web Server + Smart Lightingcpp
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

// --- Network Configuration ---
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

// --- Hardware Pins ---
// Array to hold the GPIO pins connected to the 16-channel relay
// Note: ESP8266 has limited GPIO. For 16 channels, I/O expanders
// (two 74HC595 shift registers or an MCP23017 via I2C) are industry standard.
// This code assumes an I2C expander is used for scalability, but
// simplifies the logic for demonstration.
const int relayPins[16] = { /* Insert your specific expanded/multiplexed pins here */ };

// Smart Lighting Sensors
const int pirPin = 12; // D6 - Motion Sensor
const int ldrPin = A0; // Analog LDR Sensor
const int smartLightRelayIndex = 0; // Assign Relay 1 to the Smart Light

ESP8266WebServer server(80);

// Relay state array (Active LOW for most relay modules)
bool relayStates[16];

void setup() {
  Serial.begin(115200);
  // Initialize Relays to OFF (HIGH)
  for (int i = 0; i < 16; i++) {
    pinMode(relayPins[i], OUTPUT);
    digitalWrite(relayPins[i], HIGH);
    relayStates[i] = false;
  }
  pinMode(pirPin, INPUT);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConnected! IP: " + WiFi.localIP().toString());

  // --- Define API Endpoints ---
  // Toggle a specific relay: e.g., http://<IP>/toggle?relay=3&state=1
  server.on("/toggle", HTTP_GET, []() {
    if (server.hasArg("relay") && server.hasArg("state")) {
      int relayNum = server.arg("relay").toInt();
      int state = server.arg("state").toInt();
      if (relayNum >= 0 && relayNum < 16) {
        // Relays are active low
        digitalWrite(relayPins[relayNum], state ? LOW : HIGH);
        relayStates[relayNum] = state;
        server.send(200, "text/plain", "Relay " + String(relayNum) + " set to " + String(state));
      } else {
        server.send(400, "text/plain", "Invalid Relay Number");
      }
    } else {
      server.send(400, "text/plain", "Missing Arguments");
    }
  });

  // Get all states for App UI updating
  server.on("/status", HTTP_GET, []() {
    String json = "{";
    for (int i = 0; i < 16; i++) {
      json += "\"" + String(i) + "\":" + String(relayStates[i]);
      if (i < 15) json += ",";
    }
    json += "}";
    server.send(200, "application/json", json);
  });

  server.begin();
}

void loop() {
  server.handleClient();
  handleSmartLighting();
}

// --- Smart Lighting Logic (Energy Optimization) ---
void handleSmartLighting() {
  int lightLevel = analogRead(ldrPin);
  bool motionDetected = digitalRead(pirPin);
  // Thresholds: Dark environment AND motion detected
  if (lightLevel < 400 && motionDetected) {
    // Turn ON light
    digitalWrite(relayPins[smartLightRelayIndex], LOW);
    relayStates[smartLightRelayIndex] = true;
  } else if (!motionDetected) {
    // Turn OFF light to save energy
    digitalWrite(relayPins[smartLightRelayIndex], HIGH);
    relayStates[smartLightRelayIndex] = false;
  }
}
app_inventor_notes.txtMIT App Inventor Architecture (Mobile UI)text
UI Layout Components
--------------------
1. Web Component (Web1): Sends HTTP requests to the NodeMCU.
2. Clock Component (Clock1): Fires every 2000ms to poll /status and update
   button colors (Green = ON, Gray = OFF).
3. Buttons (Btn_Relay1 .. Btn_Relay16): Arranged in a scrollable Table Arrangement.

Logic Blocks (pseudo-code)
--------------------------
1. Global Variables:
   - NodeMCU_IP: "http://192.168.1.xxx"

2. Button Click (example, Relay 1):
   When Btn_Relay1.Click:
     If Relay1_State is ON  -> set Web1.Url = join(NodeMCU_IP, "/toggle?relay=0&state=0")
     If Relay1_State is OFF -> set Web1.Url = join(NodeMCU_IP, "/toggle?relay=0&state=1")
     call Web1.Get

3. Status Sync:
   When Clock1.Timer:
     set Web1.Url = join(NodeMCU_IP, "/status")
     call Web1.Get
   When Web1.GotText:
     parse JSON response
     loop keys 0..15
     update background colors of Btn_Relay1 .. Btn_Relay16 from the boolean values
     so the app matches the actual hardware state.