Introduction to ESP32-S3 Communication via Wi-Fi
In this lesson, we'll explore how to establish communication between a Python script and an ESP32-S3 microcontroller using Wi-Fi in Access Point mode. We'll start with simple message exchange, move on to measuring ping times, and finally control GPIO pins and the onboard RGB LED remotely.
Table of Contents
- Setting Up the ESP32-S3 as an Access Point
- Sending Messages from Python to ESP32-S3
- Measuring Ping Times
- Controlling GPIO Pins and RGB LED
- Conclusion
Setting Up the ESP32-S3 as an Access Point
Before we begin, ensure you have the following:
- An ESP32-S3 development board.
- Arduino IDE installed with ESP32 board support.
- Python 3 installed on your computer.
- Your computer should have Wi-Fi capability.
We'll set up the ESP32-S3 to act as a Wi-Fi Access Point (AP) so that the Python script can connect to it directly.
Sending Messages from Python to ESP32-S3
In this section, we'll send a simple message from a Python script to the ESP32-S3 and display it on the Serial Monitor.
Python Code: Sending Messages
Copy and paste the following Python code into a file named send_message.py
:
import requests
# Replace with the IP address of your ESP32-S3 AP
esp32_ip = "192.168.4.1" # Default IP for ESP32 SoftAP
def send_message(message):
url = f"http://{esp32_ip}/send"
payload = {'message': message}
try:
response = requests.post(url, data=payload, timeout=5)
if response.status_code == 200:
print("Message sent successfully")
else:
print(f"Failed to send message. Status code: {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Error communicating with ESP32: {e}")
if __name__ == "__main__":
# Ensure your computer is connected to the ESP32 AP before running the script
while True:
message = input("Enter a message to send to ESP32 (type 'exit' to quit): ")
if message.lower() == 'exit':
break
send_message(message)
Description:
- The script prompts the user to enter a message.
- It sends the message to the ESP32-S3 over a TCP socket.
- Ensure your computer is connected to the ESP32-S3's Wi-Fi network.
Arduino Code: Receiving Messages
Copy and paste the following Arduino code into the Arduino IDE and upload it to your ESP32-S3:
#include <WiFi.h>
#include <WebServer.h>
// Replace with your desired AP credentials
const char* ssid = "ESP32-AP";
const char* password = "12345678"; // Minimum 8 characters for WPA2
WebServer server(80); // Create a web server on port 80
void handleRoot() {
// Simple response to indicate the server is running
server.send(200, "text/plain", "ESP32 Web Server is running");
}
void handleMessage() {
if (server.hasArg("message")) {
String message = server.arg("message");
Serial.print("Received message: ");
Serial.println(message);
server.send(200, "text/plain", "Message received");
} else {
server.send(400, "text/plain", "Bad Request: 'message' argument missing");
}
}
void setup() {
Serial.begin(115200);
// Configure ESP32 as an Access Point
Serial.println("Setting up Access Point...");
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
// Define routes
server.on("/", handleRoot);
server.on("/send", handleMessage);
// Start the server
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
}
Description:
- Sets up the ESP32-S3 as a Wi-Fi Access Point.
- Listens for incoming TCP connections on port 80.
- Receives messages and prints them to the Serial Monitor.
Instructions:
- Replace
ssid
andpassword
with your desired Wi-Fi credentials. - Upload the code to your ESP32-S3.
- Open the Serial Monitor at 115200 baud rate to view incoming messages.
- Connect your computer to the ESP32-S3's Wi-Fi network.
- Run the Python script and send messages.
Measuring Ping Times
Now, we'll create a ping test to measure the communication latency between the Python script and the ESP32-S3.
Python Code: Ping Test
Copy and paste the following Python code into a file named ping_test.py
:
import socket
import time
import matplotlib.pyplot as plt
# ESP32 Access Point IP and Port
ESP32_IP = '192.168.4.1'
PORT = 80
PING_COUNT = 1000
def main():
ping_times = []
for i in range(PING_COUNT):
start_time = time.time()
success = send_ping()
end_time = time.time()
if success:
ping_time = (end_time - start_time) * 1000 # Convert to milliseconds
ping_times.append(ping_time)
print(f"Ping {i+1}: {ping_time:.2f} ms")
else:
print(f"Ping {i+1}: Failed")
average_ping = sum(ping_times) / len(ping_times)
print(f"\nAverage Ping Time: {average_ping:.2f} ms")
# Plotting the ping times
plt.plot(ping_times)
plt.xlabel('Ping Number')
plt.ylabel('Ping Time (ms)')
plt.title('Ping Times over 1000 Samples')
plt.show()
def send_ping():
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1) # Timeout after 1 second
sock.connect((ESP32_IP, PORT))
sock.sendall(b'ping\n')
sock.close()
return True
except Exception as e:
return False
if __name__ == '__main__':
main()
Description:
- Sends 'ping' messages to the ESP32-S3.
- Measures the time taken for each ping.
- Plots the ping times over 1000 samples.
- Calculates the average ping time.
Arduino Code: Ping Response
Copy and paste the following Arduino code into the Arduino IDE and upload it to your ESP32-S3:
#include <WiFi.h>
// Wi-Fi credentials
const char* ssid = "ESP32_AP";
const char* password = "12345678";
// Create a Wi-Fi server on port 80
WiFiServer server(80);
void setup() {
Serial.begin(115200);
// Initialize Wi-Fi in AP mode
WiFi.softAP(ssid, password);
Serial.println();
Serial.print("Access Point \"");
Serial.print(ssid);
Serial.println("\" started");
Serial.print("IP address: ");
Serial.println(WiFi.softAPIP());
// Start the server
server.begin();
}
void loop() {
WiFiClient client = server.available(); // Listen for incoming clients
if (client) {
String currentLine = "";
while (client.connected()) {
if (client.available()) {
char c = client.read();
if (c == '\n') {
// End of message
if (currentLine == "ping") {
client.print("pong\n");
}
currentLine = "";
break; // Exit after responding
} else {
currentLine += c;
}
}
}
client.stop();
}
}
Description:
- Responds with 'pong' when it receives a 'ping'.
- Minimal processing to ensure accurate timing.
- No Serial Monitor output to reduce latency.
Instructions:
- Ensure the Wi-Fi credentials match between the Python and Arduino code.
- Install the
matplotlib
library for Python if you haven't already (pip install matplotlib
). - Run the Python script to start the ping test.
- Wait for the test to complete and observe the plotted results.
Controlling GPIO Pins and RGB LED
Finally, we'll implement a system to control GPIO pins and the onboard RGB LED by sending commands from the Python script.
Python Code: Remote Control
Copy and paste the following Python code into a file named remote_control.py
:
import socket
# ESP32 Access Point IP and Port
ESP32_IP = '192.168.4.1'
PORT = 80
def main():
while True:
command = input("Enter command: ").lower().strip()
send_command(command)
def send_command(command):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ESP32_IP, PORT))
sock.sendall((command + '\n').encode('utf-8'))
sock.close()
print("Command sent!")
except Exception as e:
print(f"Failed to send command: {e}")
if __name__ == '__main__':
main()
Description:
- Sends user-entered commands to the ESP32-S3.
- Converts commands to lowercase and strips whitespace.
- Commands can control GPIO pins and the RGB LED.
Arduino Code: Command Handling
Copy and paste the following Arduino code into the Arduino IDE and upload it to your ESP32-S3:
#include <WiFi.h>
// Wi-Fi credentials
const char* ssid = "ESP32_AP";
const char* password = "12345678";
// Create a Wi-Fi server on port 80
WiFiServer server(80);
// Avoid controlling pins 19 and 20
const int FORBIDDEN_PINS[] = {19, 20};
// RGB LED pins
const int LED_R_PIN = 21;
const int LED_G_PIN = 22;
const int LED_B_PIN = 23;
// Current LED color
int currentR = 0, currentG = 0, currentB = 0;
void setup() {
Serial.begin(115200);
// Initialize Wi-Fi in AP mode
WiFi.softAP(ssid, password);
Serial.println();
Serial.print("Access Point \"");
Serial.print(ssid);
Serial.println("\" started");
Serial.print("IP address: ");
Serial.println(WiFi.softAPIP());
// Start the server
server.begin();
// Set up RGB LED pins
pinMode(LED_R_PIN, OUTPUT);
pinMode(LED_G_PIN, OUTPUT);
pinMode(LED_B_PIN, OUTPUT);
// Turn off the LED
setLEDColor(0, 0, 0);
}
void loop() {
WiFiClient client = server.available(); // Listen for incoming clients
if (client) {
String currentLine = "";
while (client.connected()) {
if (client.available()) {
char c = client.read();
if (c == '\n') {
// End of command
handleCommand(currentLine);
currentLine = "";
break; // Exit after handling the command
} else {
currentLine += c;
}
}
}
client.stop();
}
}
// Handle incoming commands
void handleCommand(String command) {
command.trim();
command.toLowerCase();
// Control GPIO pins
if (command.startsWith("turn on pin ")) {
int pin = command.substring(11).toInt();
controlPin(pin, HIGH);
} else if (command.startsWith("turn off pin ")) {
int pin = command.substring(12).toInt();
controlPin(pin, LOW);
} else if (command.startsWith("pin ")) {
int spaceIndex = command.indexOf(' ', 4);
int pin = command.substring(4, spaceIndex).toInt();
String action = command.substring(spaceIndex + 1);
if (action == "on") {
controlPin(pin, HIGH);
} else if (action == "off") {
controlPin(pin, LOW);
} else if (action.endsWith("%")) {
int percent = action.substring(0, action.length() - 1).toInt();
setPinPWM(pin, percent);
}
}
// Control RGB LED
else if (command.startsWith("led turn on")) {
setLEDColor(255, 255, 255);
} else if (command.startsWith("led turn off")) {
setLEDColor(0, 0, 0);
} else if (command.startsWith("led ")) {
String colorValue = command.substring(4);
if (colorValue.indexOf('-') != -1) {
// RGB values
int r, g, b;
if (parseRGB(colorValue, r, g, b)) {
setLEDColor(r, g, b);
}
} else {
// Color name
int r, g, b;
if (getColorFromName(colorValue, r, g, b)) {
setLEDColor(r, g, b);
}
}
} else if (command.startsWith("set color ")) {
String colorName = command.substring(10);
int r, g, b;
if (getColorFromName(colorName, r, g, b)) {
setLEDColor(r, g, b);
}
} else if (command.startsWith("lower brightness by ")) {
int percent = command.substring(19).toInt();
lowerBrightness(percent);
} else {
Serial.println("Unknown command: " + command);
}
}
// Control GPIO pins
void controlPin(int pin, int state) {
if (isForbiddenPin(pin)) {
Serial.println("Cannot control pin " + String(pin));
return;
}
pinMode(pin, OUTPUT);
digitalWrite(pin, state);
Serial.println("Pin " + String(pin) + " set to " + (state == HIGH ? "HIGH" : "LOW"));
}
// Set PWM on a pin
void setPinPWM(int pin, int percent) {
if (isForbiddenPin(pin)) {
Serial.println("Cannot control pin " + String(pin));
return;
}
int dutyCycle = map(percent, 0, 100, 0, 255);
ledcAttachPin(pin, pin); // Use pin number as channel
ledcSetup(pin, 5000, 8); // 5 kHz frequency, 8-bit resolution
ledcWrite(pin, dutyCycle);
Serial.println("Pin " + String(pin) + " PWM set to " + String(percent) + "%");
}
// Check if pin is forbidden
bool isForbiddenPin(int pin) {
for (int i = 0; i < sizeof(FORBIDDEN_PINS) / sizeof(FORBIDDEN_PINS[0]); i++) {
if (pin == FORBIDDEN_PINS[i]) {
return true;
}
}
return false;
}
// Set RGB LED color
void setLEDColor(int r, int g, int b) {
currentR = r;
currentG = g;
currentB = b;
analogWrite(LED_R_PIN, r);
analogWrite(LED_G_PIN, g);
analogWrite(LED_B_PIN, b);
Serial.println("LED color set to R:" + String(r) + " G:" + String(g) + " B:" + String(b));
}
// Parse RGB values from string
bool parseRGB(String rgbStr, int& r, int& g, int& b) {
int firstDash = rgbStr.indexOf('-');
int secondDash = rgbStr.indexOf('-', firstDash + 1);
if (firstDash == -1 || secondDash == -1) {
return false;
}
r = rgbStr.substring(0, firstDash).toInt();
g = rgbStr.substring(firstDash + 1, secondDash).toInt();
b = rgbStr.substring(secondDash + 1).toInt();
return (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255);
}
// Get RGB values from color name
bool getColorFromName(String colorName, int& r, int& g, int& b) {
if (colorName == "red") {
r = 255; g = 0; b = 0;
} else if (colorName == "green") {
r = 0; g = 255; b = 0;
} else if (colorName == "blue") {
r = 0; g = 0; b = 255;
} else if (colorName == "yellow") {
r = 255; g = 255; b = 0;
} else if (colorName == "cyan") {
r = 0; g = 255; b = 255;
} else if (colorName == "magenta") {
r = 255; g = 0; b = 255;
} else if (colorName == "white") {
r = 255; g = 255; b = 255;
} else if (colorName == "purple") {
r = 128; g = 0; b = 128;
} else {
return false;
}
return true;
}
// Lower brightness by a percentage
void lowerBrightness(int percent) {
int factor = 100 - percent;
int r = (currentR * factor) / 100;
int g = (currentG * factor) / 100;
int b = (currentB * factor) / 100;
setLEDColor(r, g, b);
Serial.println("Brightness lowered by " + String(percent) + "%");
}
Description:
- Handles various commands to control GPIO pins and the RGB LED.
- Avoids controlling pins 19 and 20.
- Supports commands like:
turn on pin 4
pin 4 off
pin 5 50%
led turn on
led 255-0-255
set color blue
lower brightness by 50%
Instructions:
- Replace
LED_R_PIN
,LED_G_PIN
, andLED_B_PIN
with the actual GPIO pins connected to your RGB LED. - Upload the code to your ESP32-S3.
- Run the Python script and enter commands to control the microcontroller.
Conclusion
By following this lesson, you've learned how to:
- Establish a Wi-Fi connection between a Python script and an ESP32-S3 in Access Point mode.
- Send and receive messages between the computer and microcontroller.
- Measure and plot ping times to assess communication latency.
- Control GPIO pins and the onboard RGB LED remotely using custom commands.
Feel free to expand upon this foundation to build more complex IoT applications and remote control systems.
Note: Ensure your computer is connected to the ESP32-S3's Wi-Fi network before running the Python scripts. Adjust the IP address and port if necessary.