Send 2 smoothed analog values from ESP32 ESP-Now Master to ESP32 ESP-Now Slave

I've been trying to send 2 smoothed analog values using a multidimensional array from one master ESP32 to one slave ESP32. I am using the ESP-Now protocol.
Uber thanks to 6v6gt for help with multidimensional array.

I basically modified these master and slave examples below for my application.
https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/ESPNow/Multi-Slave/Master/Master.ino
https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/ESPNow/Multi-Slave/Slave/Slave.ino

Here is the working output using the original examples of code (mac addresses partially removed):

For my modified code,
I was finally able to properly send each of the values on the transmitter side I believe based on my serial output. However, the transmitter serial output reports no peer found.

My receiver side receives nothing.

I'm still unclear about all of the ESP-Now terminology, but I thought I was sending the data variable appropriately.

Any help is appreciated. Since the modified code is too long I am creating separate posts for transmitter and receiver codes that are currently not working.

Master code part 1:

/* Free and open source, CC BY-SA 4.0 */

/*
   ESPNOW - Basic communication - Master
   Date: 26th September 2017
   Author: Arvind Ravulavaru <https://github.com/arvindr21>
   Purpose: ESPNow Communication between a Master ESP32 and a Slave ESP32
   Description: This sketch consists of the code for the Master module.
   Resources: (A bit outdated)
   a. https://espressif.com/sites/default/files/documentation/esp-now_user_guide_en.pdf
   b. http://www.esploradores.com/practica-6-conexion-esp-now/

   << This Device Master >>

   Flow: Master
   Step 1 : ESPNow Init on Master and set it in STA mode
   Step 2 : Start scanning for Slave ESP32 (we have added a prefix of `slave` to the SSID of slave for an easy setup)
   Step 3 : Once found, add Slave as peer
   Step 4 : Register for send callback
   Step 5 : Start Transmitting data from Master to Slave

   Flow: Slave
   Step 1 : ESPNow Init on Slave
   Step 2 : Update the SSID of Slave with a prefix of `slave`
   Step 3 : Set Slave in AP mode
   Step 4 : Register for receive callback and wait for data
   Step 5 : Once data arrives, print it in the serial monitor

   Note: Master and Slave have been defined to easily understand the setup.
         Based on the ESPNOW API, there is no concept of Master and Slave.
         Any devices can act as master or slave.
*/

/*
  Smoothing

  Reads repeatedly from an analog input, calculating a running average and
  printing it to the computer. Keeps xx readings in an array and continually
  averages them.

  created 22 Apr 2007
  by David A. Mellis  <dam@mellis.org>
  modified 9 Apr 2012
  by Tom Igoe

  This example code is in the public domain.

  http://www.arduino.cc/en/Tutorial/Smoothing
*/

//Libs for espnow and wifi
#include <esp_now.h>
#include <WiFi.h>

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

#include <driver/adc.h>

// Define the number of samples to keep track of. The higher the number, the
// more the readings will be smoothed, but the slower the output will respond to
// the input. Using a constant rather than a normal variable lets us use this
// value to determine the size of the readings array.
const int numReadings = 64;
const uint8_t numSensors = 2;

uint32_t readings[numSensors][numReadings];   //multi-dimensional readings from analog input
uint32_t total[numSensors] = {0};    // the running total
uint32_t readIndex = 0;              // the index of the current reading
uint32_t average = 0;                // the average
uint8_t EspNowAverage;

// delay to process smoothing readings
uint16_t numReadingsInterval = 1;
uint32_t lastEventAtMs = 0 ;

//Earth rotates .25 degrees/minute. In 4 minutes Earth rotates 1 degree.
uint8_t delay_ScanForSlave = 10; //Scan for slave delay time

void setup() {

  //Set device in STA mode to begin with
  Serial.begin(115200);
  // initialize all the readings to 0:
  for (uint16_t thisReading = 0; thisReading < numReadings; thisReading++) {
    for ( int j = 0; j < numSensors; j++)  readings[j][thisReading] = 0;
  }

  WiFi.mode(WIFI_STA);
  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());
  // Init ESPNow with a fallback logic
  InitESPNow();
  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Transmitted packet
  esp_now_register_send_cb(OnDataSent);
}

void loop() {

  // Voltage divider analog in pins
  // https://dl.espressif.com/doc/esp-idf/latest/api-reference/peripherals/adc.html
  // set up A:D channels

  adc1_config_width(ADC_WIDTH_12Bit);
  adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11); //Pin34

  adc1_config_width(ADC_WIDTH_12Bit);
  adc1_config_channel_atten(ADC1_CHANNEL_7, ADC_ATTEN_DB_11); //Pin35

  // millis() -->Returns the number of milliseconds passed since the Arduino board began running the current program.
  // This number will overflow (go back to zero), after approximately 50 days.
  // timing resets to 0 when millis() - lastEventAtMS becomes greater than or equal to numReadingsInterval
  // then loop proceeds with remaining functions
  if ( millis() - lastEventAtMs >= numReadingsInterval ) {

    lastEventAtMs = millis();

    // Read the sensor
    uint16_t Azimuth = adc1_get_raw(ADC1_CHANNEL_6);
    uint16_t Elevation = adc1_get_raw(ADC1_CHANNEL_7);

    uint16_t  inputPin[ numSensors ] = {Azimuth, Elevation};

    uint16_t ai;

    for (ai = 0; ai < numSensors ; ai++) {
      // subtract the last reading:
      total[ ai ] = total[ ai ] - readings[ai][readIndex];
      // read from the sensor:
      readings[ai][readIndex] = inputPin[ai];
      // add the reading to the total:
      total[ai] = total[ai] + readings[ai][readIndex];

      // calculate the average:
      average = total[ai] / numReadings;

      // reduce results to 0-255
      EspNowAverage = average / 16; // send smoothed average of each sensor input to receiver

      sendData();
    }

    // advance to the next position in the array:
    readIndex = readIndex + 1;
    // if we're at the end of the array...
    if (readIndex >= numReadings) {
      // ...wrap around to the beginning:
      readIndex = 0;
    }
  }  
}

Master code part 2:

/***************************Void functions**********************************/

// 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() {
  int8_t scanResults = WiFi.scanNetworks();
  // 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("");
      }
      //wait approx. [period] ms}
      while (millis() < delay_ScanForSlave) {}
      // 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%c",  &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: ");
    const esp_now_peer_info_t *peer = &slave;
    const uint8_t *peer_addr = slave.peer_addr;
    // check if the peer exists
    bool exists = esp_now_is_peer_exist(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(peer);
      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() {
  const esp_now_peer_info_t *peer = &slave;
  const uint8_t *peer_addr = slave.peer_addr;
  esp_err_t delStatus = esp_now_del_peer(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() {
  uint8_t data = EspNowAverage;
  const uint8_t *peer_addr = slave.peer_addr;
  Serial.print("Sending: "); Serial.println(data);
  esp_err_t result = esp_now_send(peer_addr, &data, sizeof(data));
  Serial.print("Send Status: ");
  if (result == ESP_OK) {
    Serial.println("Success");
  } 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");
}

/***************************End of Void functions**********************************/

Slave Code:

I'm suspecting that this line of code is limiting my ability to receive more data, but I am unsure:

void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len)

My goal is to parse the two received analog inputs values on the slave side and then use each of the analog values to drive a total of two motors.

/* Free and open source, CC BY-SA 4.0 */

/*
   ESPNOW - Basic communication - Slave
   Date: 26th September 2017
   Author: Arvind Ravulavaru <https://github.com/arvindr21>
   Purpose: ESPNow Communication between a Master ESP32 and a Slave ESP32
   Description: This sketch consists of the code for the Slave module.
   Resources: (A bit outdated)
   a. https://espressif.com/sites/default/files/documentation/esp-now_user_guide_en.pdf
   b. http://www.esploradores.com/practica-6-conexion-esp-now/
   << This Device Slave >>
   Flow: Master
   Step 1 : ESPNow Init on Master and set it in STA mode
   Step 2 : Start scanning for Slave ESP32 (we have added a prefix of `slave` to the SSID of slave for an easy setup)
   Step 3 : Once found, add Slave as peer
   Step 4 : Register for send callback
   Step 5 : Start Transmitting data from Master to Slave
   Flow: Slave
   Step 1 : ESPNow Init on Slave
   Step 2 : Update the SSID of Slave with a prefix of `slave`
   Step 3 : Set Slave in AP mode
   Step 4 : Register for receive callback and wait for data
   Step 5 : Once data arrives, print it in the serial monitor
   Note: Master and Slave have been defined to easily understand the setup.
         Based on the ESPNOW API, there is no concept of Master and Slave.
         Any devices can act as master or salve.
*/

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

#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() {
  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));
  }
}

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);
  Serial.print("Last Packet Recv Data: "); Serial.println(*data);
  Serial.println("");
}

void loop() {
  // Chill
}

I just read this:

https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/network/esp_now.html

Although I don't have a lot of data. I'm sending two analog values with a range of 0-255, since the data variable is a uint8_t for the esp now protocol.

If there is a lot of ESP-NOW data to send, call esp_now_send() to send less than or equal to 250 bytes of data once a time. Note that too short interval between sending two ESP-NOW datas may lead to disorder of sending callback function. So, it is recommended that sending the next ESP-NOW data after the sending callback function of previous sending has returned. The sending callback function runs from a high-priority WiFi task. So, do not do lengthy operations in the callback function. Instead, post necessary data to a queue and handle it from a lower priority task.

FYI... the readings I'm taking have an interval of 1 ms. A total of 64 readings occur so there should be at least a 64 ms interval before the next reading is sent. Correct? Wouldn't this be good enough for requirement mentioned above? I don't know how else to delay the sending of void loop code. My previous attempts of using millis() to delay loop were not good since someone mentioned I was using blocking code.

Were you ever able to send and receive an array of values using ESP-NOW? I've seen code online that seems to imply that it's possible, but I haven't yet been able to make it actually work.

I was able to successfully do this, but did not get the end result I wanted:

https://forum.arduino.cc/index.php?topic=620107.msg4201745#msg4201745

The code is in post numbers 2 and 3.
It was probably too long for most people to go over.

I was getting data on the receiver side but still have to work on familiarizing myself better with arrays.
I don't think I was properly splitting or assigning array values on the slave end code based on the motor output results I was getting.
I was covering up my sensors with my fingers to get the proper data output from the sensors but the proper data was not showing up on the receiver side. I think it was only receiving data from 1 or 2 sensors rather than all 4.

Please don't post code in parts - mistakes are easily made joining the parts. or long programs just add the .ino file as an attachment.

I wonder if this ESP-NOW thread will be any help. Sorry, I've forgotten all about it so I can't give any more support.

...R

Ok. I'm taking a look at the thread right now. Since the code is for the ESP8266 it's a little bit different for the ESP32 I believe.

I've attached my files since the code in them is too long.

I was able to successfully transmit the array values, but I think I need to send them as an array and receive them properly on the receiver side.

I believe if I understood it correctly I need to add another layer in the multidimensional array.
Don't know if that's the right terminology for it.
I may need to nest it one more time.

Serial output of master before slave is powered and uploaded with receiver code:

Fail
33
22
Fail
Fail
35
23

Serial output of master after slave is powered and uploaded with receiver code:

106
95
Success
Success
106
95
Success
Success
106
95
Success
Success

Serial output of slave with succesfull tranmission of data:

121
100
AZIMUTH MOTOR MOVES COUNTERCLOCKWISE
 
ELEVATION MOTOR MOVES COUNTERCLOCKWISE
 
118
97
AZIMUTH MOTOR MOVES COUNTERCLOCKWISE
 
ELEVATION MOTOR MOVES COUNTERCLOCKWISE
 
115
95
AZIMUTH MOTOR MOVES COUNTERCLOCKWISE
 
ELEVATION MOTOR MOVES COUNTERCLOCKWISE
 
113
92
AZIMUTH MOTOR MOVES COUNTERCLOCKWISE
 
ELEVATION MOTOR MOVES COUNTERCLOCKWISE

The problem I have is that on the receiver side I don't know how to assign the data variable the proper array values that are being received. I believe that is the reason why the motors will only spin counterclockwise.

I tried to follow the example of the code in this instructables tutorial (he has an array of digital on/off values I believe), his website code is more uniform and joined together. His instructable code is a bit incomplete I believe. His video is in Portugese so that makes it more harder to understand.

Esp32EspNowDataSentDAST_R1.ino (5.39 KB)

Esp32EspNowDataRcvdDAST_R6.ino (7.18 KB)