Wireless Temperature and Humidity Monitoring with ESP-NOW: Automating a 12V DC Fan via ESP32

Discover how to use two ESP32 microcontrollers and the ESP-NOW protocol to create a peer-to-peer wireless climate monitoring and control system. A DHT22 sensor on a transmitter node provides real-time temperature and humidity readings, which are then streamed straight to a receiver node. When temperatures exceed predetermined limits, the receiver toggles a 5V relay module to activate a 12V DC cooling fan and shows real-time metrics on an I2C LCD screen.

Components Used

Transmitter (Tx) Node:

  • 1x ESP32 Development Board
  • 1x DHT22 (AM2302) Temperature & Humidity Sensor
  • Breadboard and Jumper Wires

Receiver (Rx) Node:

  • 1x ESP32 Development Board
  • 1x 16×2 LCD Module with I2C Backpack (PCF8574)
  • 1x 5V Single-Channel Relay Module
  • 1x 12V DC Fan
  • 1x 12V DC External Power Supply
  • Breadboard and Jumper Wires

Transmiter node: ESP32 & DHT2 Sensor

The ESP32’s 3.3V rail powers a DHT22 digital sensor used by the Transmitter node to measure temperature and humidity.

DHT22ESP32
GNDGND
OUTGPIO 22
VCC3.3V
Screenshot

Receiver Node: ESP32, Relay Module & 12V DC Fan

Incoming ESP-NOW telemetry packets are processed by the Receiver node, which also renders temperature and humidity readings on the 16×2 I2C LCD and toggles GPIO 19 to activate the 5V relay that controls the 12V DC fan.

ESP32I2C LCD Display5V Relay Module12V DC FanPower Supply(+)Power Supply(-)
VINVCC
GNDGND
SDAGPIO 21
SCLGPIO 22
5V (VIN)VCC
GNDGND
GPIO 19IN
COM+
NO+

Arduino C++ Code Setup (Peer-to-Peer Data Packets)

Transmitter Code (Tx)

#include <esp_now.h>
#include <WiFi.h>
#include "DHT.h"

#define DHTPIN 22     // Digital pin connected to the DHT sensor
#define DHTTYPE DHT22   // DHT 22 (AM2302)
DHT dht(DHTPIN, DHTTYPE);

// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x24, 0x62, 0xAB, 0xE0, 0xE7, 0x94};

struct __attribute__((packed)) dataPacket {
  float hum;   // Stores humidity value
  float temp;  // Stores temperature value
};

esp_now_peer_info_t peerInfo;

void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

void setup() {
  Serial.begin(115200);
  Serial.println(F("DHTxx test!"));
  dht.begin();

  WiFi.mode(WIFI_STA);

  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  esp_now_register_send_cb(OnDataSent);

  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;

  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}

void loop() {
  dataPacket packet;

  float h = dht.readHumidity();
  float t = dht.readTemperature();
  float f = dht.readTemperature(true);

  packet.hum = h;
  packet.temp = t;

  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &packet, sizeof(packet));
  delay(30);

  if (result == ESP_OK) {
    Serial.println("Sent with success");
  } else {
    Serial.println("Error sending the data");
  }

  if (isnan(h) || isnan(t) || isnan(f)) {
    Serial.println(F("Failed to read from DHT sensor!"));
    return;
  }

  float hif = dht.computeHeatIndex(f, h);
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print(F("Humidity: "));
  Serial.print(h);
  Serial.print(F("%  Temperature: "));
  Serial.print(t);
  Serial.print(F("°C "));
  Serial.print(f);
  Serial.print(F("°F  Heat index: "));
  Serial.print(hic);
  Serial.print(F("°C "));
  Serial.print(hif);
  Serial.println(F("°F"));

  delay(2000);
}

Receiver Code (Rx)

#include <esp_now.h>
#include <WiFi.h>
#include <LiquidCrystal_I2C.h>

#define Fan 19
LiquidCrystal_I2C lcd(0x27, 16, 2);

struct __attribute__((packed)) dataPacket {
  float hum;   // Stores humidity value
  float temp;  // Stores temperature value
};

void OnDataRecv(const esp_now_recv_info* info, const uint8_t *incomingData, int len) {
  dataPacket packet;

  memcpy(&packet, incomingData, sizeof(packet));
  
  lcd.setCursor(0, 0);
  lcd.print("Humidity: ");
  lcd.print(packet.hum);

  lcd.setCursor(0, 1);
  lcd.print(" %, Temp: ");
  lcd.print(packet.temp);
  lcd.print(" Celsius");
  
  delay(2000); // Delay 2 sec
  Serial.println(packet.temp);

  // Active Low Relay logic: LOW triggers relay ON, HIGH turns relay OFF
  if (packet.temp >= 21.60) { 
    digitalWrite(Fan, LOW);
  } else {
    digitalWrite(Fan, HIGH);
  }
}

void setup() {
  lcd.init(); 
  lcd.backlight();
  Serial.begin(115200);

  pinMode(Fan, OUTPUT);
  digitalWrite(Fan, HIGH); // Ensure relay is OFF at start

  WiFi.mode(WIFI_STA);

  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {
  // ESP-NOW relies on callback events; loop remains idle
}

How It Works

Protocol Initialization: To take advantage of ESP-NOW’s raw IEEE 802.11 MAC frame layer, both ESP32 modules set up Wi-Fi in Station Mode (WIFI_STA). As a result, a central Wi-Fi router or access point is no longer required.

Data Acquisition & Struct Packing: Every two seconds, the Transmitter takes temperature and humidity readings from the DHT22 sensor. Standard byte alignment (attribute((packed))) is used to pack the contents into a corresponding binary C-struct (dataPacket).

Peer Transmission: Using esp_now_send(), the Tx board sends the binary payload straight to the Rx board’s distinct MAC address.

Data Unpacking & Screen Output: The Rx board initiates the OnDataRecv callback method upon arrival. It writes real-time measurements to the I2C LCD screen and uses memcpy() to deserialize the memory payload back into variables.

Relay & Fan Control Logic: During the receive event, the Rx board evaluates the temperature value:

In order to complete the circuit and power the 12V DC fan, GPIO 19 drives LOW when the temperature rises above 21.60 °C, turning the active-low relay module ON.

GPIO 19 hits HIGH, opening the relay contacts and shutting off the cooling fan when the temperature falls below 21.60 °C.

Leave a Reply