Introduction
ESP-NOW is yet another protocol developed by Espressif, which enables multiple devices to communicate with one another without using Wi-Fi. The protocol is similar to the low-power 2.4GHz wireless connectivity that is often deployed in wireless mouses. So, the pairing between devices is needed prior to their communication. After the pairing is done, the connection is safe and peer-to-peer, with no handshake being required.
Before proceed to the tutorial, please go through following tutorial first:
Video
This video shows how to send data wirelessly between 2 units of ESP32 board using ESP-NOW.
Hardware Preparation
This is the list of items used in the video.
The custom PCB is created using CNC on Snapmaker. Below is the list of components used in the PCB.
Schematic and PCB files
If you have Snapmaker, you can download 👉 THIS FILES, and use it for CNC your PCB.
Sample Program
This is Arduino sample program. Before compile, please install following libraries through Library Manager:
- MD_MAX72XX by majicDesigns Version 3.2.1
- MD_Parola by majicDesigns Version 3.3.0
- TFT_eSPI by Bodmer Version 1.4.20
- MLX90614 Library by Adafruit Version 1.0.1
/* | |
Tutorial: Build Your Own Portable Contactless Temperature Reader | |
Hardware: | |
– TTGO T-Display ESP32 | |
https://my.cytron.io/p-ttgo-t-display-esp32-1.14-display-module-presolder-header?tracking=idris | |
– MLX90614 Non-Contact Infrared Temperature Sensor | |
https://my.cytron.io/p-mlx90614-non-contact-infrared-temperature-sensor?tracking=idris | |
External libraries: | |
– TFT_eSPI by Bodmer Version 1.4.20 | |
– Adafruit MLX90614 Library by Adafruit Version 1.0.1 | |
Created by: | |
24 Mar 2020 Idris Zainal Abidin, Cytron Technologies | |
*/ | |
#include <esp_now.h> | |
#include <WiFi.h> | |
#include <TFT_eSPI.h> | |
#include <SPI.h> | |
#include <Wire.h> | |
#include <Adafruit_MLX90614.h> | |
const byte broadcastAddress[] = {0x30, 0xAE, 0xA4, 0xF6, 0xCD, 0xB8}; // Replace with your ESP board MAC Address | |
#define ADC_PIN 34 | |
#define VREF 1100 | |
#define BUTTON1 35 | |
#define BUTTON2 0 | |
#define FF17 &FreeSans9pt7b | |
#define FF21 &FreeSansBold9pt7b | |
#define ROW1 0,16 | |
#define ROW2 0,38 | |
#define ROW3 0,60 | |
#define ROW4 0,82 | |
#define ROW5 0,104 | |
#define ROW6 0,126 | |
TFT_eSPI tft = TFT_eSPI(); | |
Adafruit_MLX90614 mlx = Adafruit_MLX90614(); | |
float ambientCelsius = 0.0; | |
float objectCelsius = 0.0; | |
float objectCelsiusTotal = 0.0; | |
long prevMillis = 0; | |
int interval = 1000; | |
// callback when data is sent | |
void OnDataSent(const byte *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() | |
{ | |
pinMode(BUTTON1, INPUT_PULLUP); | |
pinMode(BUTTON2, INPUT_PULLUP); | |
Serial.begin(115200); | |
Serial.println(); | |
Serial.println("Build your own Portable Contactless Temperature Reader for Covid-19"); | |
Serial.println(); | |
WiFi.mode(WIFI_STA); | |
// Init ESP-NOW | |
if (esp_now_init() != ESP_OK) { | |
Serial.println("Error initializing ESP-NOW"); | |
return; | |
} | |
// Once ESPNow is successfully Init, we will register for Send CB to | |
// get the status of Trasnmitted packet | |
esp_now_register_send_cb(OnDataSent); | |
// Register peer | |
esp_now_peer_info_t peerInfo; | |
memcpy(peerInfo.peer_addr, broadcastAddress, 6); | |
peerInfo.channel = 0; | |
peerInfo.encrypt = false; | |
// Add peer | |
if (esp_now_add_peer(&peerInfo) != ESP_OK){ | |
Serial.println("Failed to add peer"); | |
return; | |
} | |
tft.init(); | |
tft.setRotation(1); | |
tft.fillScreen(TFT_BLACK); | |
tft.fillRect(0, 0, 240, 43, TFT_DARKGREY); | |
tft.setFreeFont(FF21); | |
tft.setTextColor(TFT_BLACK); | |
tft.setCursor(ROW1); | |
tft.print(" Portable Contactless"); | |
tft.setCursor(0, 35); // Row 2 | |
tft.print(" Temperature Reader"); | |
tft.setTextColor(TFT_WHITE); | |
tft.setCursor(ROW3); | |
tft.print(" Battery:"); | |
tft.setTextColor(TFT_CYAN); | |
tft.setCursor(ROW4); | |
tft.print(" < Press button to read >"); | |
tft.setTextColor(TFT_WHITE); | |
tft.setCursor(ROW5); | |
tft.print(" Celsius:"); | |
tft.fillRect(0, 112, 240, 23, TFT_MAROON); | |
tft.setTextColor(TFT_BLACK); | |
tft.setCursor(0, 128); // Row 6 | |
tft.print(" #Covid19 by Idris"); | |
mlx.begin(); | |
} | |
void loop() | |
{ | |
if (millis() – prevMillis > interval) { | |
int adc = analogRead(ADC_PIN); | |
float batteryVoltage = ((float)adc/4095.0)*2.0*3.3*(VREF/1000.0); | |
tft.fillRect(140, 48, 100, 20, TFT_BLACK); | |
if (batteryVoltage > 4.2) { | |
tft.setTextColor(TFT_BLUE); | |
tft.setCursor(140, 60); | |
tft.print("Charge"); | |
} | |
else { | |
batteryVoltage = constrain(batteryVoltage, 3, 4); | |
float batteryPercent = ((batteryVoltage – 3) / 1) * 100; | |
if (batteryPercent < 50) { | |
tft.setTextColor(TFT_RED); | |
} | |
else if (batteryPercent < 80) { | |
tft.setTextColor(TFT_YELLOW); | |
} | |
else { | |
tft.setTextColor(TFT_GREEN); | |
} | |
tft.setCursor(150, 60); | |
tft.print((int)batteryPercent); | |
tft.print(" %"); | |
} | |
prevMillis = millis(); | |
} | |
if (digitalRead(BUTTON2) == LOW) { | |
objectCelsiusTotal = 0.0; | |
for (int i = 0; i < 20; i++) { | |
// ambientCelsius = mlx.readAmbientTempC(); | |
objectCelsius = mlx.readObjectTempC(); | |
objectCelsiusTotal += objectCelsius; | |
delay(50); | |
} | |
objectCelsius = objectCelsiusTotal / 20; | |
int celsius = objectCelsius; | |
tft.fillRect(140, 92, 100, 14, TFT_BLACK); | |
tft.setTextColor(TFT_GREEN); | |
tft.setCursor(150, 104); | |
tft.print(objectCelsius); | |
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &celsius, sizeof(celsius)); | |
if (result == ESP_OK) { | |
Serial.println("Sent with success"); | |
} | |
else { | |
Serial.println("Error sending the data"); | |
} | |
} | |
} |
/* | |
Project: Wireless Display with ESPNow | |
Board: ESP32 Dev Module | |
Mac Address: 30:AE:A4:F6:CD:B8 | |
Hardware: | |
– Node32 Lite | |
https://my.cytron.io/p-node32-lite-wifi-and-bluetooth-development-kit?tracking=idris | |
– 4 In 1 MAX7219 Dot Matrix Display Module | |
https://my.cytron.io/p-4-in-1-max7219-dot-matrix-display-module?tracking=idris | |
Connections: | |
ESP32 | Dot Matrix | |
RAW – VCC | |
GND – GND | |
27 – DIN | |
26 – CS | |
25 – CLK | |
External libraries: | |
– MD_MAX72XX by majicDesigns Version 3.2.1 (Manager) | |
– MD_Parola by majicDesigns Version 3.3.0 (Manager) | |
Created by: | |
24 Mar 2020 Idris Zainal Abidin, Cytron Technologies | |
*/ | |
#include <esp_now.h> | |
#include <WiFi.h> | |
#include <MD_Parola.h> | |
#include <MD_MAX72xx.h> | |
#include <SPI.h> | |
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW | |
//#define HARDWARE_TYPE MD_MAX72XX::ICSTATION_HW | |
#define MAX_DEVICES 4 | |
#define CLK_PIN 25 // 18 or 25 | |
#define DATA_PIN 27 // 16 or 27 | |
#define CS_PIN 26 // 17 or 26 | |
MD_Parola DotMatrix = MD_Parola(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); | |
#define BUZZER 23 | |
#define NOTE_C4 262 | |
#define NOTE_D4 294 | |
#define NOTE_G4 392 | |
#define NOTE_A4 440 | |
#define playReadyMelody() playTone(melody1, melody1Dur, 2) | |
#define playNote1() playTone(melody2, melody2Dur, 1) | |
#define playNote2() playTone(melody3, melody3Dur, 1) | |
#define playBatteryWeak() playTone(melody4, melody1Dur, 2) | |
int melody1[] = {NOTE_C4, NOTE_G4}; | |
int melody1Dur[] = {12, 8}; | |
int melody2[] = {NOTE_A4}; | |
int melody2Dur[] = {8}; | |
int melody3[] = {NOTE_D4}; | |
int melody3Dur[] = {8}; | |
int melody4[] = {NOTE_G4, NOTE_C4}; | |
int melody4Dur[] = {12, 8}; | |
int celsius; | |
char textDotMatrix[6]; | |
// callback when data is recv from Master | |
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *incomingData, int data_len) { | |
char macStr[18]; | |
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", | |
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); | |
memcpy(&celsius, incomingData, sizeof(celsius)); | |
String text = String(celsius) + " °C"; | |
Serial.print("Last Packet Recv from: "); Serial.println(macStr); | |
Serial.print("Last Packet Recv Data: "); Serial.println(text); | |
Serial.println(" °C"); | |
sprintf(textDotMatrix, "%d\x90""C", (int)celsius); | |
DotMatrix.write(textDotMatrix); | |
playNote1(); | |
} | |
void setup() | |
{ | |
Serial.begin(115200); | |
DotMatrix.begin(); | |
DotMatrix.setIntensity(1); | |
DotMatrix.setTextAlignment(PA_CENTER); | |
DotMatrix.print("…"); | |
WiFi.mode(WIFI_STA); | |
// Init ESP-NOW | |
if (esp_now_init() != ESP_OK) { | |
Serial.println("Error initializing ESP-NOW"); | |
return; | |
} | |
DotMatrix.setTextAlignment(PA_CENTER); | |
DotMatrix.print("Ready"); | |
// Once ESPNow is successfully Init, we will register for recv CB to | |
// get recv packer info | |
esp_now_register_recv_cb(OnDataRecv); | |
playReadyMelody(); | |
} | |
void loop() | |
{ | |
} | |
const int FREQUENCY = 2000; | |
const int CHANNEL = 0; | |
const int RESOLUTION = 8; | |
void playTone(int *melody, int *melodyDur, int notesLength) | |
{ | |
for (int i = 0; i < notesLength; i++) { | |
ledcAttachPin(BUZZER, CHANNEL); | |
int noteDuration = 1000 / melodyDur[i]; | |
ledcWriteTone(CHANNEL, melody[i]); | |
int pauseBetweenNotes = noteDuration * 1.30; | |
delay(pauseBetweenNotes); | |
ledcDetachPin(BUZZER); | |
ledcWrite(CHANNEL, 0); | |
} | |
} |
Thank You
References:
Thanks for reading this tutorial. If you have any technical inquiries, please post at Cytron Technical Forum.
“Please be reminded, this tutorial is prepared for you to try and learn.
You are encouraged to improve the code for better application.“
2 thoughts on “Send Data Wirelessly Between ESP32 Using ESP-NOW”
its hard to find esp_now library
can you please tell me
where to find library
Can ESP32 become web server (join any WiFi) and doing ESP-NOW communication at same time?