Week 11
AI prompt:
Now need to create an image for FabAcademy week 11 'Networking and Communications'. This week, she wants to control RC with her designed Joystick. Need to only Drive Motors, and with the joystick set the direction to RC
Networking and Communications
Finally, the week has come when I connect my RC and joystick together 🤩
Even though the NFC reader on the RC is not working right now, I still decided to move forward and try to control the motors using the joystick.
JOYSTICK WiFi connection
To fully understand the system, I approached it step by step.
First, I connected my joystick to WiFi and displayed the IP address on the LCD.
Here are the parts of the code I added to my main joystick code (the full code is in Week 9 documentation).
Add Library and Credentials
#include <WiFi.h>
const char* ssid = "iPhonw";
const char* password = "Manuela20";
Initialize the Wi-Fi connection. Added this right before startAnimation();.
void setup() {
// ... my existing pinMode and Wire code ...
WiFi.begin(ssid, password); // Start connecting in the background
startAnimation();
analogReadResolution(12);
}
In my "DISPLAY TEXT" section, added this logic to show the Wi-Fi status at the bottom of the screen (around Y-coordinate 25).
// --- WIFI STATUS (Bottom Left) ---
display.setCursor(0, 25);
if (WiFi.status() == WL_CONNECTED) {
display.print("WiFi: ON ");
// Optional: show IP or Signal Strength
// display.print(WiFi.RSSI());
} else {
display.print("WiFi: OFF");
}
// --- ALERT SYSTEM (Shifted slightly or combined) ---
if(batV < 3.4 && batV > 1.0) {
display.setCursor(60, 25); // Move Low Bat alert to the right
display.print("! LOW !");
}
Here is the status displayed on the LCD.
The I2C LCD in this case is not just a simple screen — it works as a communication node on a shared network.
It is a classic example of Master–Slave communication:
instead of the microcontroller using many pins to control everything directly, it uses only 2 wires (SDA and SCL) to send data.
So basically, instead of micromanaging every detail, the controller sends high-level data, and the LCD handles the rest.
After that, the next step was to connect the joystick and PC to the same WiFi network.
This means I can read joystick movement data directly in the PC terminal.
To get the PC IP address, I used:
ipconfig
hostname -i
Then I added these code parts into my main program.
Added the WiFiUdp.h library and send a formatted string containing my X and Y values
#include <WiFiUdp.h>
WiFiUDP udp;
const char* pc_ip = "172.20.10.3"; // my PC IP address
const int udp_port = 4210;
Added this inside my loop() (after calculate xRaw and yRaw):
// --- SEND DATA TO PC ---
if (WiFi.status() == WL_CONNECTED) {
udp.beginPacket(pc_ip, udp_port);
String data = String(xRaw) + "," + String(yRaw);
udp.print(data);
udp.endPacket();
}
I used this simple Python script to "listen" for the joystick data and print it to my screen.
import socket
# Listen on all available interfaces at port 4210
UDP_IP = "0.0.0.0"
UDP_PORT = 4210
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))
print(f"Listening for Joystick data on port {UDP_PORT}...")
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
print(f"Joystick Position (X,Y): {data.decode('utf-8')}")
For reading the data, I ran this Python script in the Visual Studio Code terminal:
python3 read_joystick.py
And here is the result.
After that, it was time to program the RC to connect to the same WiFi…
But of course… something had to go wrong 😵💫
Somehow, I accidentally uploaded the RC code into my joystick 😭
And yes… this resulted in burning my second ESP32-C3 💀
AI prompt:
“Change this crying girl's face to a grandmother's face when she is also crying.”
While I was emotionally processing my joystick’s “death” 😄, Maksim was trying to understand if the ESP32-C3 was physically damaged or if it was just a software issue, because we kept getting this error:
Serial port /dev/ttyACM0:A fatal error occurred: Could not open /dev/ttyACM0, the port is busy or doesn't exist.([Errno 2] could not open port /dev/ttyACM0: [Errno 2] No such file or directory: '/dev/ttyACM0')Hint: Check if the port is correct and ESP connectedFailed uploading: uploading error: exit status 2
At the same time, Onik was thinking from a hardware safety perspective and suggested improvements:
- Add 100Ω resistors to the outputs of the pins
- Use a pull-down resistor for the switch button (connect pin 1 to GND through a resistor)
This is important because if, due to a software mistake, two connected pins are both set as OUTPUT, and one is HIGH while the other is LOW, it can create a short circuit.
The resistor will limit the current and protect the microcontroller from burning.
So… for now, let’s wait for Joystick version 2 😄🎮
RC WiFi connection
Now I am connecting the RC to WiFi as well.
- Libraries and Pin Setup
- Making the Web Page
- Getting the Speed Value
- Initialization (Setup)
- Driving the Motors (Loop)
- Showing the IP Address
First, I include the libraries for Wi-Fi and the Web Server. I define my motor pins exactly how they are connected to my RC PCB.
#include <WiFi.h>
#include <WebServer.h>
I created a function called handleRoot. This builds a simple website with a slider. When I move the slider on my PC, it sends the new speed value to the ESP32. I used a little bit of JavaScript so the page doesn't have to refresh every time I change the speed.
void handleRoot() {
String html = "<html><body><h1>Motor Speed Control</h1>";
html += "<input type='range' min='0' max='255' onchange='updateSpeed(this.value)'>";
// JavaScript to send speed to ESP32
html += "<script>function updateSpeed(val) { fetch('/setSpeed?val=' + val); }</script>";
server.send(200, "text/html", html);
}
This block handles the data coming from the PC. It looks for the "val" number from the slider and saves it into the currentSpeed variable so the motors can use it.
void handleSetSpeed() {
if (server.hasArg("val")) {
currentSpeed = server.arg("val").toInt();
}
server.send(200, "text/plain", "OK");
}
In the setup, I configure my motor pins as outputs. I connect to my home Wi-Fi and tell the server which functions to run when I visit the IP address in my browser.
void setup() {
pinMode(MOTOR_A_IN1, OUTPUT);
pinMode(MOTOR_A_IN2, OUTPUT);
// Connect WiFi
WiFi.begin(ssid, password);
// Routes for the web server
server.on("/", handleRoot);
server.on("/setSpeed", handleSetSpeed);
server.begin();
}
In the loop, server.handleClient() stays listening for my PC. I use analogWrite instead of digitalWrite. This uses PWM to change the speed of the motors based on the currentSpeed variable (0 to 255).
void loop() {
server.handleClient();
// Send PWM signal to motors
analogWrite(MOTOR_A_IN1, currentSpeed);
digitalWrite(MOTOR_A_IN2, LOW);
analogWrite(MOTOR_B_IN1, currentSpeed);
digitalWrite(MOTOR_B_IN2, LOW);
}
After the ESP32 connects to the Wi-Fi, I need to know which address to type into my browser. I added this part in the setup() to print the Local IP to the Serial Monitor. This way, I can see exactly where to visit to control my motors.
// Check if WiFi is connected
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print the IP address to the Serial Monitor
Serial.println("");
Serial.println("WiFi connected!");
Serial.print("Visit this link to control speed: http://");
Serial.println(WiFi.localIP());
Here is the whole process shown in video format.
And here is the full code.
#include <WiFi.h>
#include <WebServer.h>
// --- MOTOR PINS ---
#define MOTOR_A_IN1 2
#define MOTOR_A_IN2 3
#define MOTOR_B_IN1 5
#define MOTOR_B_IN2 6
// --- WIFI CREDENTIALS ---
const char* ssid = "iPhone";
const char* password = "Manuela20";
WebServer server(80);
int currentSpeed = 255; // 0 to 255
void handleRoot() {
String html = "<html><body><h1>Motor Speed Control</h1>";
html += "<input type='range' min='0' max='255' onchange='updateSpeed(this.value)'>";
// JavaScript to send speed to ESP32
html += "<script>function updateSpeed(val) { fetch('/setSpeed?val=' + val); }</script>";
server.send(200, "text/html", html);
}
void handleSetSpeed() {
if (server.hasArg("val")) {
currentSpeed = server.arg("val").toInt();
Serial.print("New Speed: "); Serial.println(currentSpeed);
}
server.send(200, "text/plain", "OK");
}
void setup() {
Serial.begin(115200);
pinMode(MOTOR_A_IN1, OUTPUT);
pinMode(MOTOR_A_IN2, OUTPUT);
pinMode(MOTOR_B_IN1, OUTPUT);
pinMode(MOTOR_B_IN2, OUTPUT);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
Serial.println("\nWiFi connected.");
Serial.print("Visit: http://"); Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/setSpeed", handleSetSpeed);
server.begin();
}
void loop() {
server.handleClient(); // Handle incoming web requests
// Move Forward using the variable speed from the slider
// On ESP32-C3, analogWrite sets the PWM duty cycle (0-255)
analogWrite(MOTOR_A_IN1, currentSpeed);
digitalWrite(MOTOR_A_IN2, LOW);
analogWrite(MOTOR_B_IN1, currentSpeed);
digitalWrite(MOTOR_B_IN2, LOW);
}
This week was kind of chaotic but also interesting. I tried to connect my RC car with the joystick using WiFi, step by step, first making the joystick send data and then trying to read it on my PC. That part actually worked and it was really satisfying to see the data coming live.
After that, I wanted to connect the RC too… but of course something went wrong 😵💫 I accidentally uploaded the wrong code and ended up burning my second ESP32-C3, which was really painful 😭
Also spent a lot of time trying to understand weird errors like the port not opening, and honestly that part was very annoying. At the same time, I started understanding more about how important hardware protection is, not just code.
So yeah… I liked the part where things actually worked and I could see real communication, but I really hated losing another board and wasting time on small mistakes. Still, I feel like I learned a lot from this week, even from the failures 😄
AI prompt:
“And Generate image when she fineshed Week 11”


