ESP NOW Joystick Controlled Servo Transmitter & Receiver Project

https://ogbugbu-technologies.com.ng

Controlling multiple servos wirelessly using ESP-NOW is the gold standard for high-performance RC projects. Unlike Wi-Fi, which requires a router and a slow “handshake,” ESP-NOW allows two ESP32 or ESP8266 boards to talk directly to each other with near-zero latency.

In this project, we’ll build a transmitter with a joystick and a receiver that controls two servo motors (ideal for a pan-tilt camera or a robotic arm).

Video Explanation


1. Project Requirements

  • Microcontrollers: 2x ESP32 (recommended) or ESP8266.
  • Input: 1x Analog Joystick Module.
  • Output: 2x SG90 or MG996R Servo Motors.
  • Power: 5V/2A power supply (Servos draw high current).
  • Library: ESP32Servo (if using ESP32).

2. Wiring Diagram

Transmitter (Remote Control)

  • VCC/GND → ESP32 3.3V and GND.
  • VRx (X-axis) → GPIO 34 (ADC1).
  • VRy (Y-axis) → GPIO 35 (ADC1).

Receiver (Servo Node)

  • Servo 1 (Pan) → Signal to GPIO 18.
  • Servo 2 (Tilt) → Signal to GPIO 19.
  • External Power → Connect the servos’ Red/Brown wires to an external 5V source. Common Ground: Connect the external GND to the ESP32 GND.

3. The Code Logic

ESP-NOW works by sending a “Data Structure.” We bundle the X and Y coordinates into one package and send it to the receiver’s MAC Address.

Part A: Get the Receiver’s MAC Address

Mac Address Code

#include "esp_mac.h"

void setup() {
  Serial.begin(115200);
  
  uint8_t baseMac[6];
  // Get the factory-assigned MAC address
  esp_read_mac(baseMac, ESP_MAC_WIFI_STA);
  
  Serial.printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", 
                baseMac[0], baseMac[1], baseMac[2], 
                baseMac[3], baseMac[4], baseMac[5]);
}

void loop() {}

Upload a simple “MAC Address Finder” sketch to your receiver board. Open the Serial Monitor to find the address (e.g., B4:E6:2D:E2:43:8C). You will need this for the transmitter code.

Part B: Transmitter Code (Joystick)

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

typedef struct struct_message {
  int xValue;
  int yValue;
} struct_message;

struct_message dataToSend;

uint8_t receiverAddress[] = {0x00, 0x4B, 0x12, 0xE7, 0xA8}; // Replace with your receiver MAC

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);

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

  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, receiverAddress, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

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

void loop() {
  dataToSend.xValue = analogRead(34); // Joystick X pin
  dataToSend.yValue = analogRead(35); // Joystick Y pin

  esp_now_send(receiverAddress, (uint8_t *) &dataToSend, sizeof(dataToSend));
  Serial.print("Sent X: ");
  Serial.print(dataToSend.xValue);
  Serial.print(" | Y: ");
  Serial.println(dataToSend.yValue);
  delay(100);
}

Part C: Receiver Code (Servos)

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

Servo servo1;
Servo servo2;

int servo1Pin = 18;  // Adjust as needed
int servo2Pin = 19;

typedef struct struct_message {
  int xValue;
  int yValue;
} struct_message;

struct_message incomingData;

void onDataReceive(const esp_now_recv_info_t *info, const uint8_t *data, int len) {
  memcpy(&incomingData, data, sizeof(incomingData));

  Serial.print("X: ");
  Serial.print(incomingData.xValue);
  Serial.print(" | Y: ");
  Serial.println(incomingData.yValue);

  // Map joystick to servo angle
  int angleX = map(incomingData.xValue, 0, 4095, 0, 180);
  int angleY = map(incomingData.yValue, 0, 4095, 0, 180);

  servo1.write(angleX);
  servo2.write(angleY);
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);

  servo1.attach(servo1Pin);
  servo2.attach(servo2Pin);

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

  esp_now_register_recv_cb(onDataReceive);
}

void loop() {
  // Nothing to do here
}

🚀 Pro Tips for Smooth Movement

  1. Deadzone: Joysticks rarely return to a perfect center. Add a small “deadzone” in your code (e.g., if the value is between 85 and 95, set it to 90) to prevent jitter.
  2. External Power: Never power two servos directly from the ESP32’s 3.3V or 5V pins. The voltage sag will crash the ESP-NOW connection.
  3. Antenna Orientation: For maximum range, keep the PCB antennas of both ESP32 boards parallel to each other.

Would you like me to show you how to add a failsafe so the servos return to a neutral position if the signal is lost?

About ogbugbu-technologies.com.ng

View all posts by ogbugbu-technologies.com.ng →

Leave a Reply

Your email address will not be published. Required fields are marked *