Gesture-Controlled Smart Home System with NRF24L01 Wireless Communication

Touchless Appliance Control using APDS9960 Gesture Sensor, NRF24L01 2.4GHz RF Link, SH1106 OLED Transmitter, 20x4 LCD Receiver, and 4-Channel Relay Load Panel

Embedded Systems Home Automation Wireless Communication Arduino NRF24L01

Project Overview

This project delivers a fully wireless, touchless smart home control system built around two Arduino Nano units connected via an NRF24L01 2.4GHz RF link. The transmitter unit houses an APDS9960 gesture sensor that reads hand swipes in four directions and maps them to individual appliances. Each detected gesture toggles the corresponding appliance state and transmits a command string wirelessly to the receiver unit. The receiver drives a 4-channel active-LOW relay board to physically switch the connected loads: Fan, Socket, Lamp 1, and Lamp 2. A 20x4 I2C LCD on the receiver displays a live "SMART CONTROL" dashboard. The transmitter is battery-powered with a rechargeable Li-Polymer cell and a TP4056 charging circuit, making it fully portable with no tethered power cable needed.

A key engineering challenge was RF interference from the relay coil switching disturbing the NRF24L01 link. This was solved by setting aggressive retry logic (15 retries, 15 delay steps) on the transmitter radio, which ensures reliable command delivery even during relay transients. The transmitter also shows a splash screen on boot before entering gesture detection mode.


System Architecture

The system is composed of two independent, battery or mains-powered units that communicate over a peer-to-peer 2.4GHz RF link:


  [Hand Gesture]
       |
  APDS9960 Sensor (Transmitter Nano, I2C 0x39)
       |
  Map gesture -> command string ("U ON", "D OFF", etc.)
       |
  NRF24L01 TX (CE=D9, CSN=D10, pipe "ADDRESS01")
       |  ~~ 2.4GHz RF ~~
  NRF24L01 RX (CE=D9, CSN=D10, pipe "ADDRESS01")
       |
  Arduino Nano Receiver
       |
  Parse command -> toggle relay state
       |
  applyOutputs() -> Relay A0/A1/A2/A3
       |
  [Appliance ON/OFF]  +  LCD update  +  Beep
                

The transmitter also drives an SH1106 OLED that shows the detected gesture direction, device name, and ON/OFF state in real time. Four yellow LEDs on the transmitter face mirror the current state of each appliance so the user sees feedback without looking at the receiver panel.


Hardware Components

Component Specification Unit Role
Arduino Nano ATmega328P, USB-C, 5V logic 2 Transmitter MCU + Receiver MCU
APDS9960 Proximity, gesture, ambient light (I2C 0x39) 1 Gesture input on transmitter
NRF24L01 (SMA) 2.4GHz, SPI, SMA external antenna, CE=D9 CSN=D10 2 Wireless RF link (TX + RX)
SH1106 OLED 1.3", 128x64, I2C (U8g2 driver) 1 Status display on transmitter
20x4 I2C LCD Character LCD, I2C backpack 0x27, 5V 1 SMART CONTROL dashboard on receiver
4-Channel Relay Module 5V active-LOW, 250VAC/10A contacts 1 Switch Fan, Socket, Lamp 1, Lamp 2
Buzzer 5V passive/active, D2 1 Audible feedback on command received
LM2596 Buck Converter Input 4-40V, output adjustable, up to 3A 1 Step-down 12V to 5V for receiver logic
AC-DC Power Supply 230VAC to 12VDC, bare PCB module 1 Mains power for receiver unit
TP4056 Charger Module 1A Li-Po/Li-Ion charger, USB-C input 1 Transmitter battery charging circuit
Li-Polymer Battery Tecno BL-38AT, 3.85V, 3850mAh / 14.82Wh 1 Transmitter portable power source
120mm Fan AC fan, EQGIUN brand, 2-wire 1 Controlled load on receiver panel
E27 Lamp Holders Ceiling-mount base type, with LED bulbs 2 Lamp 1 and Lamp 2 controlled loads
Universal Mains Socket 3-pin/2-pin universal, wall-mount box type 1 Socket controlled load on receiver panel
White Plastic Junction Boxes Various sizes: small (transmitter), large flat panel (receiver) 2 Enclosures for both units
Yellow LEDs (5mm) 5V, with current-limiting resistors 4 Appliance state indicators on transmitter face
Red LED (5mm) 5V, with resistor 1 Power-ON indicator on transmitter face
Veroboard / Perfboard Standard copper-strip single-sided 2 Main circuit assembly boards
Toggle Switch SPST rocker, panel-mount 2 Power switches on both units

Pin Assignments

Transmitter Arduino Nano

PinConnected ToFunction
D9 (CE)NRF24L01 CEChip Enable for RF module
D10 (CSN)NRF24L01 CSNSPI Chip Select for RF module
D11 (MOSI)NRF24L01 MOSISPI data out
D12 (MISO)NRF24L01 MISOSPI data in
D13 (SCK)NRF24L01 SCKSPI clock
A4 (SDA)APDS9960 SDA + OLED SDAI2C data bus
A5 (SCL)APDS9960 SCL + OLED SCLI2C clock bus
A0Socket status LED + LED stateSocket appliance indicator
A1Fan status LED + LED stateFan appliance indicator
A2Lamp 2 status LED + LED stateLamp 2 appliance indicator
A3Lamp 1 status LED + LED stateLamp 1 appliance indicator
3.3VNRF24L01 VCC + APDS9960 VCC3.3V power rail
5VOLED VCC5V power rail
GNDAll module GNDCommon ground

Receiver Arduino Nano

PinConnected ToFunction
D9 (CE)NRF24L01 CEChip Enable for RF module
D10 (CSN)NRF24L01 CSNSPI Chip Select
D11 (MOSI)NRF24L01 MOSISPI data out
D12 (MISO)NRF24L01 MISOSPI data in
D13 (SCK)NRF24L01 SCKSPI clock
D2BuzzerAudible command-received beep
A4 (SDA)LCD I2C SDAI2C data to LCD backpack
A5 (SCL)LCD I2C SCLI2C clock to LCD backpack
A0Relay IN1 (Socket)Active-LOW relay for socket
A1Relay IN2 (Fan)Active-LOW relay for fan
A2Relay IN3 (Lamp 2)Active-LOW relay for Lamp 2
A3Relay IN4 (Lamp 1)Active-LOW relay for Lamp 1
3.3VNRF24L01 VCC3.3V for RF module
5VRelay VCC + LCD VCC5V power rail
GNDAll module GNDCommon ground

Circuit Diagram

Full Proteus circuit diagram showing both units: the transmitter Arduino Nano with APDS-9960 gesture sensor, SH1106 OLED, NRF24L01 TX, TP4056 Li-Ion charger, boost converter, and four status LEDs; and the receiver Arduino Nano with NRF24L01 RX, 4-channel BC547 relay driver circuit, 20x4 I2C LCD, buzzer, and a 220V AC to 5V DC LM7805 power supply.


Gesture-to-Command Mapping

The APDS9960 recognises four gestures. Each gesture toggles the assigned appliance and transmits an explicit command string via NRF24L01:

Hand Gesture Command Sent Appliance Transmitter OLED Display
Swipe UP "U ON" / "U OFF" Lamp 1 (Relay A3) Action: SWIPE UP | LAMP 1 | State: ON/OFF
Swipe DOWN "D ON" / "D OFF" Lamp 2 (Relay A2) Action: SWIPE DOWN | LAMP 2 | State: ON/OFF
Swipe LEFT "L ON" / "L OFF" Fan (Relay A1) Action: SWIPE LEFT | FAN | State: ON/OFF
Swipe RIGHT "R ON" / "R OFF" Socket (Relay A0) Action: SWIPE RIGHT | SOCKET | State: ON/OFF
Serial: ALL ON "ALL ON" All four appliances ALL DEVICES | State: ON
Serial: ALL OFF "ALL OFF" All four appliances ALL DEVICES | State: OFF
Note: Active-LOW Relay Logic
The 4-channel relay module uses active-LOW inputs. The receiver firmware inverts the state before writing to each relay pin: digitalWrite(RELAY_SOCKET, !sSocket). This means a true appliance state drives the pin LOW to energise the relay coil.

Firmware Code Excerpts

Transmitter: Gesture Detection and Command Transmission

The transmitter loop reads the APDS9960 gesture register, maps the direction to a command string, updates the local LED state and OLED, then fires the NRF24L01 write:

void loop() {
  String cmdStr = "";
  const char* gestureDetected = "NONE";

  // Read gesture from APDS9960
  uint8_t gesture = apds.readGesture();

  if (gesture > 0) {
    if (gesture == APDS9960_UP) {
      stateA3 = !stateA3;                    // Toggle Lamp 1
      cmdStr = stateA3 ? "U ON" : "U OFF";
      gestureDetected = "SWIPE UP";
    } else if (gesture == APDS9960_DOWN) {
      stateA2 = !stateA2;                    // Toggle Lamp 2
      cmdStr = stateA2 ? "D ON" : "D OFF";
      gestureDetected = "SWIPE DOWN";
    } else if (gesture == APDS9960_LEFT) {
      stateA1 = !stateA1;                    // Toggle Fan
      cmdStr = stateA1 ? "L ON" : "L OFF";
      gestureDetected = "SWIPE LEFT";
    } else if (gesture == APDS9960_RIGHT) {
      stateA0 = !stateA0;                    // Toggle Socket
      cmdStr = stateA0 ? "R ON" : "R OFF";
      gestureDetected = "SWIPE RIGHT";
    }
  }

  if (cmdStr.length() > 0) {
    // Update LED outputs to mirror appliance state
    digitalWrite(A3, stateA3);
    digitalWrite(A2, stateA2);
    digitalWrite(A1, stateA1);
    digitalWrite(A0, stateA0);

    // Pack into fixed 10-byte array (avoids garbage from memory)
    char payload[10] = { 0 };
    cmdStr.toCharArray(payload, sizeof(payload));

    // Transmit via NRF24L01 (15 retries set in setup for relay noise)
    radio.write(&payload, sizeof(payload));

    // Update OLED with gesture name, device, and new state
    showOLED(gestureDetected, device, status);
    delay(200);
  }
}

Receiver: Command Parsing and Relay Control

The receiver listens on the NRF24L01 pipe. On each received payload, it parses the command, updates relay outputs (active-LOW), refreshes the 20x4 LCD, and beeps the buzzer:

void loop() {
  if (radio.available()) {
    char payload[10] = {0};
    radio.read(&payload, sizeof(payload));

    String cmdStr(payload);
    cmdStr.trim();

    const char* line1 = "";
    const char* line2 = "";
    bool validCommand = true;

    if      (cmdStr == "U ON")  { sLamp1 = true;  line1 = "LAMP 1";       line2 = "ON";  }
    else if (cmdStr == "U OFF") { sLamp1 = false;  line1 = "LAMP 1";       line2 = "OFF"; }
    else if (cmdStr == "D ON")  { sLamp2 = true;   line1 = "LAMP 2";       line2 = "ON";  }
    else if (cmdStr == "D OFF") { sLamp2 = false;  line1 = "LAMP 2";       line2 = "OFF"; }
    else if (cmdStr == "L ON")  { sFan   = true;   line1 = "FAN";          line2 = "ON";  }
    else if (cmdStr == "L OFF") { sFan   = false;  line1 = "FAN";          line2 = "OFF"; }
    else if (cmdStr == "R ON")  { sSocket = true;  line1 = "SOCKET";       line2 = "ON";  }
    else if (cmdStr == "R OFF") { sSocket = false; line1 = "SOCKET";       line2 = "OFF"; }
    else if (cmdStr == "ALL ON")  { sLamp1=sLamp2=sFan=sSocket=true;  line1="ALL DEVICES"; line2="ON";  }
    else if (cmdStr == "ALL OFF") { sLamp1=sLamp2=sFan=sSocket=false; line1="ALL DEVICES"; line2="OFF"; }
    else { validCommand = false; }

    if (validCommand) {
      applyOutputs();     // Write active-LOW states to relay pins
      delay(50);          // Settle before beeping to avoid relay noise
      updateLCD(line1, line2);
      beep();
    }
  }
}

void applyOutputs() {
  // Active-LOW: true (ON) -> LOW, false (OFF) -> HIGH
  digitalWrite(RELAY_SOCKET, !sSocket);
  digitalWrite(RELAY_FAN,    !sFan);
  digitalWrite(RELAY_LAMP2,  !sLamp2);
  digitalWrite(RELAY_LAMP1,  !sLamp1);
}
Relay Interference Note
The NRF24L01 is sensitive to EMI from relay switching transients. The transmitter was configured with radio.setRetries(15, 15) to retry aggressively. A 50ms delay was also inserted in the receiver between applyOutputs() and beep() to prevent the relay coil surge from disrupting the beeper.

Project Photos

Components and Procurement

Enclosure Design and Panel Layout

Electronics Assembly and Testing

Transmitter Unit Assembly

Completed System and Demonstration


Video Demonstration

Video Coming Soon
A demonstration video showing gesture-based touchless control of the fan, lamps, and socket in real time will be published on YouTube shortly.

Project Links


Thank You for Visiting My Portfolio

I sincerely appreciate you taking the time to explore my portfolio and learn about my work and expertise. If you have any questions or wish to discuss potential collaborations, please feel free to reach out via the Contact section.

Best regards,
Damilare Lekan, Adekeye.