3 min read

Chanel-less Remote Controller powered by ESP-NOW (Part 1)

This article discusses the development of a remote control system using ESP32-C3 Breadboard Adapter powered by ESP-NOW protocol. This protocol offers efficient, low-latency, and low-power communication between ESP32 devices without the need for a Wi-Fi network.
Chanel-less Remote Controller powered by ESP-NOW (Part 1)
A regular RC controller with a fixed number restricts the number of functions you can control, making it less versatile. These controllers lack flexibility, making it difficult to adapt to different devices or applications. Upgrading or adding new features can be complex and often requires significant modifications. Additionally, they typically have a limited range and battery life, which can be problematic for long-distance control.

ESP-NOW is a versatile communication protocol that allows for seamless data transmission between a Controller and a Receiver. This protocol is particularly advantageous due to its simplicity and efficiency, enabling devices to exchange information without the need for a complex network setup. By leveraging ESP-NOW, users can establish a direct, low-latency connection between devices, making it ideal for applications that require quick and reliable data transfer.

Building a remote controller using ESP-NOW involves several steps, including wiring up ESP32-C3 Breadboard Adapter, programming the microcontroller, and establishing communication between the transmitter and receiver.

Imagine you have a remote-controlled car. The transmitter (remote controller) reads the position of a joystick and sends this data to the receiver in the car using ESP-NOW. The receiver processes this data to control the car's motors, steering, and other functions, allowing you to drive the car wirelessly with minimal delay.

First Things First. Define the Data to be Sent

To begin, let's create Remote Controller for controlling the speed of four DC motors. To accomplish this, we may utilize Pulse Width Modulation (PWM) values that will define the the speed of the DC motors. The PWM values for four DC motors will be saved in a struct, which then will be sent to the receiving device. By defining a struct, we can encapsulate the PWM values into a single entity, making it easier to handle and transmit the data. This approach not only simplifies the code but also enhances its readability and maintainability. As we proceed, we’ll assign specific PWM values to each motor within the struct, enabling precise control over their speed.

struct motors_rpm {
    int motor1_rpm_pwm;
    int motor2_rpm_pwm;
    int motor3_rpm_pwm;
    int motor4_rpm_pwm;
};

Remote Controller (Transmitter)

A remote controller powered by ESP-NOW is a wireless control system that uses the ESP-NOW protocol for communication. For this purpose, we can use ESP32-C3 Breadboard Adapter that reads joystick position and sends this data over peer-to-peer network using the ESP-NOW wireless communication protocol.

// Struct holding sensors values
typedef struct {
    uint16_t    crc;                // CRC16 value of ESPNOW data
    uint8_t     x_axis;             // Joystick x-position
    uint8_t     y_axis;             // Joystick y-position
    bool        nav_bttn;           // Joystick push button
} __attribute__((packed)) sensors_data_t;
// Function to send data to the receiver
void sendData (void) {
    sensors_data_t buffer;              // Declare data struct

    buffer.crc = 0;
    buffer.x_axis = 240;
    buffer.y_axis = 256;
    buffer.nav_bttn = 0;
    buffer.motor1_rpm_pwm = 10;
    buffer.motor2_rpm_pwm = 0;
    buffer.motor3_rpm_pwm = 0;
    buffer.motor4_rpm_pwm = 0;

    // Display brief summary of data being sent.
    ESP_LOGI(TAG, "Joystick (x,y) position ( 0x%04X, 0x%04X )", (uint8_t)buffer.x_axis, (uint8_t)buffer.y_axis);  
    ESP_LOGI(TAG, "pwm 1, pwm 2 [ 0x%04X, 0x%04X ]", (uint8_t)buffer.pwm, (uint8_t)buffer.pwm);
    ESP_LOGI(TAG, "pwm 3, pwm 4 [ 0x%04X, 0x%04X ]", (uint8_t)buffer.pwm, (uint8_t)buffer.pwm);

    // Call ESP-NOW function to send data (MAC address of receiver, pointer to the memory holding data & data length)
    uint8_t result = esp_now_send(receiver_mac, &buffer, sizeof(buffer));

    // If status is NOT OK, display error message and error code (in hexadecimal).
    if (result != 0) {
        ESP_LOGE("ESP-NOW", "Error sending data! Error code: 0x%04X", result);
        deletePeer();
    }
    else
        ESP_LOGW("ESP-NOW", "Data was sent.");
}
#include "esp_wifi.h"

void app_main(void)
{
    // Initialize NVS to store Wi-Fi configurations
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK( nvs_flash_erase() );
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK( ret );

    // ESP-NOW
    wifi_init();                                    // Initialize Wi-Fi
    esp_now_init();                                 // Call ESP-NOW initialization function
    esp_now_register_recv_cb(onDataReceived);       // Define call back for the event when data is being received
    esp_now_register_send_cb(onDataSent);           // Define call back for the event when data is sent received

    // Set ESP-NOW receiver peer configuration values
    memcpy (peerInfo.peer_addr, receiver_mac, 6);   // Copy receiver MAC address
    peerInfo.channel = 1;                           // Define communication channel
    peerInfo.encrypt = false;                       // Keep data unencrypted
    esp_now_add_peer(&peerInfo);                    // Add peer to 
    xTaskCreate (rc_send_data_task, "RC", 2048, NULL, 15, NULL);
}

Receiving Device

// Call-back for the event when data is being received
void onDataReceived (uint8_t *mac_addr, uint8_t *data, uint8_t data_len) {

    buf = (sensors_data_t*)data;                            // Allocate memory for buffer to store data being received
    ESP_LOGW(TAG, "Data was received");
    ESP_LOGI(TAG, "x-axis: 0x%04x", buf->x_axis);
    ESP_LOGI(TAG, "x-axis: 0x%04x", buf->y_axis);
    ESP_LOGI(TAG, "PWM 1: 0x%04x", buf->motor1_rpm_pwm);
}

// Call-back for the event when data is being sent
void onDataSent (uint8_t *mac_addr, esp_now_send_status_t status) {
    ESP_LOGW(TAG, "Packet send status: 0x%04X", status);
}