Week13. Networking and communications

Hero shoot

This week was all about Networking and communications, we had the choice of trying out wired or wireless communication, so in my case, I decided to do wireless communication in order to get on with my final project. Our assignments were as follows:

Individual assignment:
design, build, and connect wired or wireless node(s)
with network or bus addresses and local input &/or output device(s)

Group assignment:
send a message between two projects

Group assignment

In order to carry out my group assignments, I decided to try and establish communication between my esp32 joystick and the screen used by Alexis, still using the ESPNOW protocol. To do this, I mainly used the code used by Alexis to send analog values from my joystick. I found it difficult to send analog values via his code and used Blackbox AI which quickly got me unstuck. You can find the code used below and the zip files at the bottom of the page.

Slave


#include "SPI.h"
#include "TFT_eSPI.h"
#include "ESP32_NOW.h"
#include "WiFi.h"
#include "esp_mac.h"  // For the MAC2STR and MACSTR macros
#include "vector"

TFT_eSPI tft = TFT_eSPI();

/* Definitions */
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define FONT_SIZE 4

#define ESPNOW_WIFI_CHANNEL 6

/* Classes */

// Creating a new class that inherits from the ESP_NOW_Peer class is required.

class ESP_NOW_Peer_Class : public ESP_NOW_Peer {
public:
  // Constructor of the class
  ESP_NOW_Peer_Class(const uint8_t *mac_addr,
                     uint8_t channel,
                     wifi_interface_t iface,
                     const uint8_t *lmk)
    : ESP_NOW_Peer(mac_addr, channel, iface, lmk) {}

  // Destructor of the class
  ~ESP_NOW_Peer_Class() {}

  // Function to register the control peer
  bool add_peer() {
    if (!add()) {
      log_e("Failed to register the broadcast peer");
      return false;
    }
    return true;
  }

  // Function to print the received messages from the control
  void onReceive(const uint8_t *data, size_t len, bool broadcast) {
    Serial.printf("Received a message from control " MACSTR " (%s)\n", MAC2STR(addr()), broadcast ? "broadcast" : "unicast");
    Serial.printf("  Message: %s\n", (char *)data);

    tft.init();          // Start the tft display
    tft.setRotation(1);  // Set the TFT display rotation in landscape mode

    // Clear the screen before writing to it
    tft.fillScreen(TFT_WHITE);
    tft.setTextColor(TFT_BLACK, TFT_WHITE);

    // Set X and Y coordinates for center of display
    int centerX = SCREEN_WIDTH / 2;
    int centerY = SCREEN_HEIGHT / 2;

    tft.drawCentreString("Communication test!", centerX, centerY - 100, FONT_SIZE);
    tft.drawCentreString("Received a message from controller :", centerX, centerY - 40, 2);
    tft.setCursor(70, 120, 2);
    tft.printf(MACSTR " (%s)\n", MAC2STR(addr()), broadcast ? "broadcast" : "unicast");
    tft.setCursor(70, 140, 2);
    tft.printf("Message: %s\n", (char *)data);

    tft.drawCentreString("From Alexis", 280, 220, 2);
  }
};

/* Global Variables */

// List of all the controls. It will be populated when a new control is registered
std::vector"ESP_NOW_Peer_Class" masters;

/* Callbacks */

// Callback called when an unknown peer sends a message
void register_new_master(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg) {
  if (memcmp(info->des_addr, ESP_NOW.BROADCAST_ADDR, 6) == 0) {
    Serial.printf("Unknown peer " MACSTR " sent a broadcast message\n", MAC2STR(info->src_addr));
    Serial.println("Registering the peer as a controller");

    ESP_NOW_Peer_Class new_master(info->src_addr, ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL);

    masters.push_back(new_master);
    if (!masters.back().add_peer()) {
      Serial.println("Failed to register the new controller");
      return;
    }
  } else {
    // The peripherals will only receive broadcast messages
    log_v("Received a unicast message from " MACSTR, MAC2STR(info->src_addr));
    log_v("Ignoring the message");
  }
}


void setup() {
  Serial.begin(115200);
  
  // Initialize the Wi-Fi module
  WiFi.mode(WIFI_STA);
  WiFi.setChannel(ESPNOW_WIFI_CHANNEL);
  while (!WiFi.STA.started()) delay(100);

  Serial.println("ESP-NOW Example - Broadcast Peripherals");
  Serial.println("Wi-Fi parameters:");
  Serial.println("  Mode: STA");
  Serial.println("  MAC Address: " + WiFi.macAddress());
  Serial.printf("  Channel: %d\n", ESPNOW_WIFI_CHANNEL);

  // Initialize the ESP-NOW protocol
  if (!ESP_NOW.begin()) {
    Serial.println("Failed to initialize ESP-NOW");
    Serial.println("Reeboting in 5 seconds...");
    delay(5000);
    ESP.restart();
  }

  // Register the new peer callback
  ESP_NOW.onNewPeer(register_new_master, NULL);

  Serial.println("Setup complete. Waiting for a control to broadcast a message...");

  tft.init();          // Start the tft display
  tft.setRotation(1);  // Set the TFT display rotation in landscape mode

  // Clear the screen before writing to it
  tft.fillScreen(TFT_WHITE);
  tft.setTextColor(TFT_BLACK, TFT_WHITE);

  // Set X and Y coordinates for center of display
  int centerX = SCREEN_WIDTH / 2;
  int centerY = SCREEN_HEIGHT / 2;

  tft.drawCentreString("Communication test!", centerX, centerY - 100, FONT_SIZE);

  tft.drawCentreString("Communication will start soon", centerX, centerY, 2);

  tft.drawCentreString("From Alexis", 280, 220, 2);
}

void loop() {

  delay(1000);
}
                  

Master



#include "Wire.h"
#include "ESP32_NOW.h"
#include "WiFi.h"
#include "esp_mac.h" // For the MAC2STR and MACSTR macros

const int axisY = 35;
const int axisX = 34;
const int avance = 0;
const int direction = 0;

#define ESPNOW_WIFI_CHANNEL 6

class ESP_NOW_Broadcast_Peer : public ESP_NOW_Peer {
public:
  ESP_NOW_Broadcast_Peer(uint8_t channel, wifi_interface_t iface, const uint8_t *lmk)
    : ESP_NOW_Peer(ESP_NOW.BROADCAST_ADDR, channel, iface, lmk) {}

  bool begin() {
    if (!ESP_NOW.begin() || !add()) {
      log_e("Failed to initialize ESP-NOW or register the broadcast peer");
      return false;
    }
    return true;
  }

  bool send_message(const uint8_t *data, size_t len) {
    if (!send(data, len)) {
      log_e("Failed to broadcast message");
      return false;
    }
    return true;
  }
};

ESP_NOW_Broadcast_Peer broadcast_peer(ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL);

void setup() {
  Serial.begin(115200);

  // Initialize the Wi-Fi module
  WiFi.mode(WIFI_STA);
  WiFi.setChannel(ESPNOW_WIFI_CHANNEL);
  while (!WiFi.STA.started()) delay(100);

  Serial.println("ESP-NOW Example - Broadcast Master");
  Serial.println("Wi-Fi parameters:");
  Serial.println("  Mode: STA");
  Serial.println("  MAC Address: " + WiFi.macAddress());
  Serial.printf("  Channel: %d\n", ESPNOW_WIFI_CHANNEL);

  // Register the broadcast peer
  if (!broadcast_peer.begin()) {
    Serial.println("Failed to initialize broadcast peer");
    Serial.println("Reebooting in 5 seconds...");
    delay(5000);
    ESP.restart();
  }

  Serial.println("Setup complete. Broadcasting messages every 5 seconds.");

  pinMode(axisX, INPUT);
  pinMode(axisY, INPUT);

  delay(200); // delay to stabilize signal before the loop
}

void loop() {
  char data[32];
  snprintf(data, sizeof(data), "avance: %d", analogRead(axisX));
  Serial.printf("Broadcasting message= %s\n", data);
  

  if (!broadcast_peer.send_message((uint8_t *)data, sizeof(data))) {
    Serial.println("Failed to broadcast message");
  }

  delay(1000);
}
                  

The result was satisfactory, and communication was quickly established. You can see the quality of the results on video:

You can find all our group documentation on our page grilab.

Individual assignment

In order to use wireless communication, I had a number of options open to me, including radio frequency, Bluetooth and WiFi. As I've already started using ESP32 for my final project, I chose to use the protocol ESP-NOW developed by Espressif. You can also find out more about how this bookstore works at documentation by Espressif and on the Github repository of the library. This protocol is based on the wifi network, but does not obey the wifi protocol but its own protocol. This technology makes it possible to exploit wifi properties up to a range of 200m between two esp32s. However, this protocol is only applicable between esp32 devices, so it is already present in the ESP32 library examples. This communication mode can be used in both directions or only in one direction. In my case, it only works in one direction. I ask my esp32 master, which will be the remote control, to send data to my esp32 slave, which is the circuit I've created to control my robot. To access the examples proposed by espressif, simply follow the path File > Exemples > ESP32 > ESPNow > ESPNow_Basic_Master/Slave.

week13

To make the codes work, you'll first need to upload the code to the slave esp32, and when the code starts, you'll need to reserve the mac address which will be displayed. Each microcontroller has its own mac address, which will enable the master to send data only to this esp32 and not to all the others.

week13

(In this example, it's the mac address of the master I used for the final code at the bottom of the page, not the slave.)

Once we have the MAC address of our slave, we can enter it in the master's code, with this line in my case:


                  uint8_t broadcastAddress[] = {0xC4, 0xDE, 0xE2, 0xC0, 0x7E, 0xB9};
                  

Use the following codes for both slave and master to establish communication and send data. This code is based on the ESPNow example code proposed by Espressif, as well as another example from the site techtutorials. I also consulted the documentation on other websites, such as Raspberryme ou Random nerd tutorials. The esp32 I used are for the slave, the PCB I made during the electronic design week and as a master ESP32-DevKitC V4.
Below you will find the Master and Slav codes used.

Master


#include 
#include 
#include  // only for esp_wifi_set_channel()


// Global copy of slave
esp_now_peer_info_t slave;
#define CHANNEL 1
#define PRINTSCANRESULTS 0
#define DELETEBEFOREPAIR 0

uint8_t broadcastAddress[] = {0xAC, 0x67, 0xB2, 0x75, 0x42, 0xE1};
//uint8_t data = 0; //valeur envoyee
const int buttonPin = 27;
//uint8_t data[3] = {0, 0, 0}; //valeur envoyee
int buttonState = 0;
typedef struct test_struct {
  int x;
  int y;
} test_struct;


// Init ESP Now with fallback
void InitESPNow() {
  WiFi.disconnect();
  if (esp_now_init() == ESP_OK) {
    Serial.println("ESPNow Init Success");
  }
  else {
    Serial.println("ESPNow Init Failed");
    // Retry InitESPNow, add a counte and then restart?
    // InitESPNow();
    // or Simply Restart
    ESP.restart();
  }
}

// Scan for slaves in AP mode
void ScanForSlave() {
  int16_t scanResults = WiFi.scanNetworks(false, false, false, 300, CHANNEL); // Scan only on one channel
  // reset on each scan
  bool slaveFound = 0;
  memset(&slave, 0, sizeof(slave));

  Serial.println("");
  if (scanResults == 0) {
    Serial.println("No WiFi devices in AP Mode found");
  } else {
    Serial.print("Found "); Serial.print(scanResults); Serial.println(" devices ");
    for (int i = 0; i < scanResults; ++i) {
      // Print SSID and RSSI for each device found
      String SSID = WiFi.SSID(i);
      int32_t RSSI = WiFi.RSSI(i);
      String BSSIDstr = WiFi.BSSIDstr(i);

      if (PRINTSCANRESULTS) {
        Serial.print(i + 1);
        Serial.print(": ");
        Serial.print(SSID);
        Serial.print(" (");
        Serial.print(RSSI);
        Serial.print(")");
        Serial.println("");
      }
      delay(10);
      // Check if the current device starts with `Slave`
      if (SSID.indexOf("Slave") == 0) {
        // SSID of interest
        Serial.println("Found a Slave.");
        Serial.print(i + 1); Serial.print(": "); Serial.print(SSID); Serial.print(" ["); Serial.print(BSSIDstr); Serial.print("]"); Serial.print(" ("); Serial.print(RSSI); Serial.print(")"); Serial.println("");
        // Get BSSID => Mac Address of the Slave
        int mac[6];
        if ( 6 == sscanf(BSSIDstr.c_str(), "%x:%x:%x:%x:%x:%x",  &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5] ) ) {
          for (int ii = 0; ii < 6; ++ii ) {
            slave.peer_addr[ii] = (uint8_t) mac[ii];
          }
        }

        slave.channel = CHANNEL; // pick a channel
        slave.encrypt = 0; // no encryption

        slaveFound = 1;
        // we are planning to have only one slave in this example;
        // Hence, break after we find one, to be a bit efficient
        break;
      }
    }
  }

  if (slaveFound) {
    Serial.println("Slave Found, processing..");
  } else {
    Serial.println("Slave Not Found, trying again.");
  }

  // clean up ram
  WiFi.scanDelete();
}

// Check if the slave is already paired with the master.
// If not, pair the slave with master
bool manageSlave() {
  if (slave.channel == CHANNEL) {
    if (DELETEBEFOREPAIR) {
      deletePeer();
    }

    Serial.print("Slave Status: ");
    // check if the peer exists
    bool exists = esp_now_is_peer_exist(slave.peer_addr);
    if ( exists) {
      // Slave already paired.
      Serial.println("Already Paired");
      return true;
    } else {
      // Slave not paired, attempt pair
      esp_err_t addStatus = esp_now_add_peer(&slave);
      if (addStatus == ESP_OK) {
        // Pair success
        Serial.println("Pair success");
        return true;
      } else if (addStatus == ESP_ERR_ESPNOW_NOT_INIT) {
        // How did we get so far!!
        Serial.println("ESPNOW Not Init");
        return false;
      } else if (addStatus == ESP_ERR_ESPNOW_ARG) {
        Serial.println("Invalid Argument");
        return false;
      } else if (addStatus == ESP_ERR_ESPNOW_FULL) {
        Serial.println("Peer list full");
        return false;
      } else if (addStatus == ESP_ERR_ESPNOW_NO_MEM) {
        Serial.println("Out of memory");
        return false;
      } else if (addStatus == ESP_ERR_ESPNOW_EXIST) {
        Serial.println("Peer Exists");
        return true;
      } else {
        Serial.println("Not sure what happened");
        return false;
      }
    }
  } else {
    // No slave found to process
    Serial.println("No Slave found to process");
    return false;
  }
}

void deletePeer() {
  esp_err_t delStatus = esp_now_del_peer(slave.peer_addr);
  Serial.print("Slave Delete Status: ");
  if (delStatus == ESP_OK) {
    // Delete success
    Serial.println("Success");
  } else if (delStatus == ESP_ERR_ESPNOW_NOT_INIT) {
    // How did we get so far!!
    Serial.println("ESPNOW Not Init");
  } else if (delStatus == ESP_ERR_ESPNOW_ARG) {
    Serial.println("Invalid Argument");
  } else if (delStatus == ESP_ERR_ESPNOW_NOT_FOUND) {
    Serial.println("Peer not found.");
  } else {
    Serial.println("Not sure what happened");
  }
}


// send data
void sendData() {
  const uint8_t *peer_addr = slave.peer_addr;
  test_struct test;
  test.x = 10;
  test.y = 20;
  Serial.print("Sending: "); Serial.println(test.x, test.y);
  //esp_err_t result = esp_now_send(peer_addr, (uint8_t *) &test.x, sizeof(test.x));//&data, sizeof(data));
  esp_err_t result = esp_now_send(
    broadcastAddress, 
    (uint8_t *) &test,
    sizeof(test_struct));
  Serial.print("Send Status: ");
  if (result == ESP_OK) {
    Serial.println("Success");
  //  test_struct = 0;
  } else if (result == ESP_ERR_ESPNOW_NOT_INIT) {
    // How did we get so far!!
    Serial.println("ESPNOW not Init.");
  } else if (result == ESP_ERR_ESPNOW_ARG) {
    Serial.println("Invalid Argument");
  } else if (result == ESP_ERR_ESPNOW_INTERNAL) {
    Serial.println("Internal Error");
  } else if (result == ESP_ERR_ESPNOW_NO_MEM) {
    Serial.println("ESP_ERR_ESPNOW_NO_MEM");
  } else if (result == ESP_ERR_ESPNOW_NOT_FOUND) {
    Serial.println("Peer not found.");
  } else {
    Serial.println("Not sure what happened");
  }
}

// callback when data is sent from Master to Slave
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  char macStr[18];
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.print("Last Packet Sent to: "); Serial.println(macStr);
  Serial.print("Last Packet Send Status: "); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

void setup() {
  Serial.begin(115200);
  //Set device in STA mode to begin with
  WiFi.mode(WIFI_STA);
  esp_wifi_set_channel(CHANNEL, WIFI_SECOND_CHAN_NONE);
  Serial.println("ESPNow/Basic/Master Example");
  // This is the mac address of the Master in Station Mode
  Serial.print("STA MAC: "); Serial.println(WiFi.macAddress());
  Serial.print("STA CHANNEL "); Serial.println(WiFi.channel());
  // Init ESPNow with a fallback logic
  InitESPNow();
  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_register_send_cb(OnDataSent);
}

void loop() {
  // In the loop we scan for slave
  ScanForSlave();
  // If Slave is found, it would be populate in `slave` variable
  // We will check if `slave` is defined and then we proceed further
  if (slave.channel == CHANNEL) { // check if slave channel is defined
    // `slave` is defined
    // Add slave as peer if it has not been added already
    bool isPaired = manageSlave();
    if (isPaired) {
      // pair success or already paired
      // Send data to device
      sendData();
    } else {
      // slave pair failed
      Serial.println("Slave pair failed!");
    }
  }
  //else {
    // No slave found to process
  //}

  //// wait for 3seconds to run the logic again
  delay(3000);

//  buttonState = digitalRead(buttonPin);
//  if (buttonState == HIGH) {
//    data[0] = 1;
//  } 

}

									

Slave


#include 
#include 

typedef struct test_struct {
  int x;
  int y;
} test_struct;

#define CHANNEL 1

// Init ESP Now with fallback
void InitESPNow() {
  WiFi.disconnect();
  if (esp_now_init() == ESP_OK) {
    Serial.println("ESPNow Init Success");
  }
  else {
    Serial.println("ESPNow Init Failed");
    // Retry InitESPNow, add a counte and then restart?
    // InitESPNow();
    // or Simply Restart
    ESP.restart();
  }
}

// config AP SSID
void configDeviceAP() {
  const char *SSID = "Slave_1";
  bool result = WiFi.softAP(SSID, "Slave_1_Password", CHANNEL, 0);
  if (!result) {
    Serial.println("AP Config failed.");
  } else {
    Serial.println("AP Config Success. Broadcasting with AP: " + String(SSID));
    Serial.print("AP CHANNEL "); Serial.println(WiFi.channel());
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("ESPNow/Basic/Slave Example");
  //Set device in AP mode to begin with
  WiFi.mode(WIFI_AP);
  // configure device AP mode
  configDeviceAP();
  // This is the mac address of the Slave in AP Mode
  Serial.print("AP MAC: "); Serial.println(WiFi.softAPmacAddress());
  // Init ESPNow with a fallback logic
  InitESPNow();
  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info.
  esp_now_register_recv_cb(OnDataRecv);
}

// callback when data is recv from Master
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len) {
  char macStr[18];
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.print("Last Packet Recv from: "); Serial.println(macStr);
  test_struct* test =(test_struct*) data;
  Serial.print("Last Packet Recv Data: "); Serial.print(test->x);
  Serial.print(", ");Serial.println(test->y);
  Serial.println("");
}

void loop() {
 
  // Chill
}

									

In this test I obtained the following results:

Joystick and driver

Once I'd managed to send information without too much difficulty, I reworked the code so as to be able to send concrete data. In my case, I connected the master to a joystick so as to use it as a remote control. The aim is to be able to remotely control the robot in my final project, and also use the remote control to switch from automatic to manual mode. To do this, I connected my master to a joystick to control the robot, as follows:

week13
Pin esp32 dev kit Pin joystick
GND GND
5V 5V
X axis 35
Y axis 34

As for the Slave part, I connected my driver in the same way as I did during the week. Output devices.
As far as the code is concerned, for the moment I'm only interested in the forward motion, so when I push the joystick, the robot moves forward. I used the map() in order to correlate the values read on the joystick with the PWM of my motor. The codes used were the same as below:

Master


#include 
#include 
#include  // only for esp_wifi_set_channel()


// Global copy of slave
esp_now_peer_info_t slave;
#define CHANNEL 1
#define PRINTSCANRESULTS 0
#define DELETEBEFOREPAIR 0
//uint8_t broadcastAddress[] = {0xC4, 0xDE, 0xE2, 0xC0, 0x7E, 0xB9}
uint8_t broadcastAddress[] = {0xAC, 0x67, 0xB2, 0x75, 0x42, 0xE1};

//uint8_t data = 0; //valeur envoyee
//uint8_t data[3] = {0, 0, 0}; //valeur envoyee
const int buttonPin = 27;
const int axisY = 34;
const int axisX = 35;
int buttonState = 0;
int button;
int avance=0;
int direction=0;
int test=0;
typedef struct joystick_data {
  int button;
  int axisY;
  int axisX;
} joystick_data;


// Init ESP Now with fallback
void InitESPNow() {
  WiFi.disconnect();
  if (esp_now_init() == ESP_OK) {
    Serial.println("ESPNow Init Success");
  }
  else {
    Serial.println("ESPNow Init Failed");
    // Retry InitESPNow, add a counte and then restart?
    // InitESPNow();
    // or Simply Restart
    ESP.restart();
  }
}

// Scan for slaves in AP mode
void ScanForSlave() {
  int16_t scanResults = WiFi.scanNetworks(false, false, false, 300, CHANNEL); // Scan only on one channel
  // reset on each scan
  bool slaveFound = 0;
  memset(&slave, 0, sizeof(slave));

  Serial.println("");
  if (scanResults == 0) {
    Serial.println("No WiFi devices in AP Mode found");
  } else {
    Serial.print("Found "); Serial.print(scanResults); Serial.println(" devices ");
    for (int i = 0; i < scanResults; ++i) {
      // Print SSID and RSSI for each device found
      String SSID = WiFi.SSID(i);
      int32_t RSSI = WiFi.RSSI(i);
      String BSSIDstr = WiFi.BSSIDstr(i);

      if (PRINTSCANRESULTS) {
        Serial.print(i + 1);
        Serial.print(": ");
        Serial.print(SSID);
        Serial.print(" (");
        Serial.print(RSSI);
        Serial.print(")");
        Serial.println("");
      }
      delay(10);
      // Check if the current device starts with `Slave`
      if (SSID.indexOf("Slave") == 0) {
        // SSID of interest
        Serial.println("Found a Slave.");
        Serial.print(i + 1); Serial.print(": "); Serial.print(SSID); Serial.print(" ["); Serial.print(BSSIDstr); Serial.print("]"); Serial.print(" ("); Serial.print(RSSI); Serial.print(")"); Serial.println("");
        // Get BSSID => Mac Address of the Slave
        int mac[6];
        if ( 6 == sscanf(BSSIDstr.c_str(), "%x:%x:%x:%x:%x:%x",  &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5] ) ) {
          for (int ii = 0; ii < 6; ++ii ) {
            slave.peer_addr[ii] = (uint8_t) mac[ii];
          }
        }

        slave.channel = CHANNEL; // pick a channel
        slave.encrypt = 0; // no encryption

        slaveFound = 1;
        // we are planning to have only one slave in this example;
        // Hence, break after we find one, to be a bit efficient
        break;
      }
    }
  }

  if (slaveFound) {
    Serial.println("Slave Found, processing..");
  } else {
    Serial.println("Slave Not Found, trying again.");
  }

  // clean up ram
  WiFi.scanDelete();
}

// Check if the slave is already paired with the master.
// If not, pair the slave with master
bool manageSlave() {
  if (slave.channel == CHANNEL) {
    if (DELETEBEFOREPAIR) {
      deletePeer();
    }

    Serial.print("Slave Status: ");
    // check if the peer exists
    bool exists = esp_now_is_peer_exist(slave.peer_addr);
    if ( exists) {
      // Slave already paired.
      Serial.println("Already Paired");
      return true;
    } else {
      // Slave not paired, attempt pair
      esp_err_t addStatus = esp_now_add_peer(&slave);
      if (addStatus == ESP_OK) {
        // Pair success
        Serial.println("Pair success");
        return true;
      } else if (addStatus == ESP_ERR_ESPNOW_NOT_INIT) {
        // How did we get so far!!
        Serial.println("ESPNOW Not Init");
        return false;
      } else if (addStatus == ESP_ERR_ESPNOW_ARG) {
        Serial.println("Invalid Argument");
        return false;
      } else if (addStatus == ESP_ERR_ESPNOW_FULL) {
        Serial.println("Peer list full");
        return false;
      } else if (addStatus == ESP_ERR_ESPNOW_NO_MEM) {
        Serial.println("Out of memory");
        return false;
      } else if (addStatus == ESP_ERR_ESPNOW_EXIST) {
        Serial.println("Peer Exists");
        return true;
      } else {
        Serial.println("Not sure what happened");
        return false;
      }
    }
  } else {
    // No slave found to process
    Serial.println("No Slave found to process");
    return false;
  }
}

void deletePeer() {
  esp_err_t delStatus = esp_now_del_peer(slave.peer_addr);
  Serial.print("Slave Delete Status: ");
  if (delStatus == ESP_OK) {
    // Delete success
    Serial.println("Success");
  } else if (delStatus == ESP_ERR_ESPNOW_NOT_INIT) {
    // How did we get so far!!
    Serial.println("ESPNOW Not Init");
  } else if (delStatus == ESP_ERR_ESPNOW_ARG) {
    Serial.println("Invalid Argument");
  } else if (delStatus == ESP_ERR_ESPNOW_NOT_FOUND) {
    Serial.println("Peer not found.");
  } else {
    Serial.println("Not sure what happened");
  }
}

void setup() {
  Serial.begin(115200);
  //Set device in STA mode to begin with
  WiFi.mode(WIFI_STA);
  esp_wifi_set_channel(CHANNEL, WIFI_SECOND_CHAN_NONE);
  Serial.println("ESPNow/Basic/Master Example");
  // This is the mac address of the Master in Station Mode
  Serial.print("STA MAC: "); Serial.println(WiFi.macAddress());
  Serial.print("STA CHANNEL "); Serial.println(WiFi.channel());
  // Init ESPNow with a fallback logic
  InitESPNow();
  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
 //esp_now_register_send_cb(OnDataSent);
}

void loop() {
  // In the loop we scan for slave
  ScanForSlave();
  // If Slave is found, it would be populate in `slave` variable
  // We will check if `slave` is defined and then we proceed further
  if (slave.channel == CHANNEL) { // check if slave channel is defined
    // `slave` is defined
    // Add slave as peer if it has not been added already
    bool isPaired = manageSlave();
    if (isPaired) {
      // pair success or already paired
      // Send data to device
      sendData();
    } else {
      // slave pair failed
      Serial.println("Slave pair failed!");
    }
  }
  //else {
    // No slave found to process
  //}
joystick_data test;
    button=buttonPin;
    avance=analogRead(axisX);
    direction=analogRead(axisY);
    

  //// wait for 3seconds to run the logic again
  delay(100);

//  buttonState = digitalRead(buttonPin);
//  if (buttonState == HIGH) {
//    data[0] = 1;
//  } 


}

// send data
void sendData() {
  const uint8_t *peer_addr = slave.peer_addr;
//  joystick_data test;
//  avance=analogRead(axisX);
//  direction=analogRead(axisY);
  Serial.print("Sending: "); Serial.println(test);
  //esp_err_t result = esp_now_send(peer_addr, (uint8_t *) &test.x, sizeof(test.x));//&data, sizeof(data));
  esp_err_t result = esp_now_send(
    broadcastAddress, 
    (uint8_t *) &test,
    sizeof(joystick_data));
 
  Serial.print("Send Status: ");
  if (result == ESP_OK) {
    Serial.println("Success");
  //  test_struct = 0;
  } else if (result == ESP_ERR_ESPNOW_NOT_INIT) {
    // How did we get so far!!
    Serial.println("ESPNOW not Init.");
  } else if (result == ESP_ERR_ESPNOW_ARG) {
    Serial.println("Invalid Argument");
  } else if (result == ESP_ERR_ESPNOW_INTERNAL) {
    Serial.println("Internal Error");
  } else if (result == ESP_ERR_ESPNOW_NO_MEM) {
    Serial.println("ESP_ERR_ESPNOW_NO_MEM");
  } else if (result == ESP_ERR_ESPNOW_NOT_FOUND) {
    Serial.println("Peer not found.");
  } else {
    Serial.println("Not sure what happened");
  }
  delay(100);
}
                  

Slave


#include 
#include 
// Define pins for steering control
const int DIR = 33; 
//  PWM spindle for speed control
const int PWM_PIN = 32;
int button;
int avance_value = 0;
int direction_value = 0;

int test=0;
typedef struct joystick_data {
  int button;//première ligne du struct n'est pas lu est attribue directement la valeur 0
  int avance;
  int direction;
} joystick_data;

#define CHANNEL 1

// Init ESP Now with fallback
void InitESPNow() {
  WiFi.disconnect();
  if (esp_now_init() == ESP_OK) {
    Serial.println("ESPNow Init Success");
  }
  else {
    Serial.println("ESPNow Init Failed");
    // Retry InitESPNow, add a counte and then restart?
    // InitESPNow();
    // or Simply Restart
    ESP.restart();
  }
}

// config AP SSID
void configDeviceAP() {
  const char *SSID = "Slave_1";
  bool result = WiFi.softAP(SSID, "Slave_1_Password", CHANNEL, 0);
  if (!result) {
    Serial.println("AP Config failed.");
  } else {
    Serial.println("AP Config Success. Broadcasting with AP: " + String(SSID));
    Serial.print("AP CHANNEL "); Serial.println(WiFi.channel());
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("ESPNow/Basic/Slave Example");
  //Set device in AP mode to begin with
  WiFi.mode(WIFI_AP);
  // configure device AP mode
  configDeviceAP();
  // This is the mac address of the Slave in AP Mode
  Serial.print("AP MAC: "); Serial.println(WiFi.softAPmacAddress());
  // Init ESPNow with a fallback logic
  InitESPNow();
  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info.
  esp_now_register_recv_cb(OnDataRecv);

  //Driver
  pinMode(DIR, OUTPUT);//direction
  pinMode(PWM_PIN, OUTPUT);//speed
  
}

// callback when data is recv from Master
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len) {
  char macStr[18];
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.print("Last Packet Recv from: "); Serial.println(macStr);
  joystick_data* test =(joystick_data*) data;
  Serial.print("Last Packet Recv Data: Avancement= "); Serial.print(test->avance);
  Serial.print(", Direction=");Serial.println(test->direction);
  //Serial.print(", ");Serial.println(test->button);
  
  avance_value = test->avance;
  direction_value = test->direction;
}

void loop() {
int value_pwm = map(avance_value, 0, 4095, 0, 250);
  analogWrite(PWM_PIN, value_pwm);
  digitalWrite(DIR, HIGH);
  /*int value_pwm = map(dir, 0, 4095, O, 250);
  analogWrite(PWM_PIN, value_pwm);*/
  delay(100);
}
                  

The result was as follows:

I then wanted to test it remotely, so I used an extension cord to power my master, and even at a distance of a dozen meters in Agrilab, even though you can't really see it on the video, it received the signal very well.

Following this test I slightly modified the code again, so you'll find the final code above, but it's not exactly the same as in the video test. In the master code, I've removed the function.


                    void OnDataSent()
                  

This function let the master know whether the slave had received the data. The latter considerably increased the exchange time between the two, and I managed to drastically reduce the frequency of data transmission by removing it.

During this week, I had a lot of difficulty modifying my program to my liking, I don't have a very good level in programming and I spent a lot of time understanding and modifying the program to make it execute the actions I wanted.

Electronic design

Once my code was working well, I started designing a circuit on Kicad in order to integrate a remote control with all the components I would need. It has a microcontroller Xiao esp32C3. In addition to the microcontroller, I've added:
-Two switch slide buttons
-One Conn pinheader femaleX5
Two resistors 10kOhm

The aim is to use one of the buttons to tell me whether the robot should be running or switched off, while the second is designed to indicate whether the robot is running using the remote control or whether it should go into automatic mode. You can find the kicad design file here here.

week13

The result wasn't very satisfactory, the milling wasn't done properly on the top of the PCB and so I cut the tracks with the ultrasonic blade, which took a lot of time. The final result after soldering was as follows:

week13

I'm not really satisfied with the result, the ESP32 doesn't really work satisfactorily, once the code has been uploaded, nothing is displayed in the serial monitor, I can't send any communication.

Files

Group assignment Slave
Group assignment Master
Master
Slave