Apr 01, 2026

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:

For Windows:
ipconfig
For Linux/Mac:
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
  • 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>
    

  • Making the Web Page
  • 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);
    }

  • Getting the Speed Value
  • 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");
    }

  • Initialization (Setup)
  • 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();
    }
    

  • Driving the Motors (Loop)
  • 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);
    }

  • Showing the IP Address
  • 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”

© Copyright 2026 Mariam Daghbashyan - Creative Commons Attribution Non Commercial
Design: HTML, CSS