Helping making simplest ESP-NOW broadcast program (two way)

EP2003
Posts: 1
Joined: Fri Oct 25, 2024 12:53 am

Helping making simplest ESP-NOW broadcast program (two way)

Postby EP2003 » Fri Oct 25, 2024 1:31 am

Hello,

I'm trying to make a very simple ESP-NOW program to help understand the basics. All of the information I've found online to date is very poor and unhelpful for newcomers trying to get the hang of this stuff.

I am just trying to make a program that tells the ESP32 to transmit a single variable via an ESP-NOW broadcast (that is, to FF:FF:FF:FF:FF:FF), and I also want the same code to be able to receive such a broadcast so other ESP32 chips could participate in this as a form of two-way communication. I don't want to pick any specific ESP-32 and I don't want any master/slave stuff or identifying devices if at all possible. I'd rather take care of that through my own code.

So far, what little information I can find about ESP-NOW makes it seems like it requires a struct to transmit data. And for some reason or another, the callback functions for receiving and sending seem to require pointers.

I also have no clue what esp_now_recv_info means in the context of the parameter for the receiver callback. Documentation says it's a struct with three members, but doesn't go into any further detail. It's unclear what is even supposed to be sent in that parameter. There's also an 8-bit data parameter which is a pointer and I don't really get what that means.

Here's the simplest code I've found that is closest to what I'm looking for so far:

Code: Select all

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

// FF:FF:FF:FF:FF:FF is the broadcast address
uint8_t broadcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
esp_now_peer_info_t broadcastPeer;

typedef struct 
{
    int a;
    float b;
    char c;
} mymsg;

void OnDataRecv(const uint8_t* mac, const uint8_t* data, int len)
{
    // Check message length
    if (len != sizeof(mymsg))
    {
        Serial.println("Received invalid msg");
        return; // return if invalid
    }

    // Get mac string from uint array
    char macstr[32];
    snprintf(macstr, 31, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

    // Cast data to mymsg* and print it's values
    mymsg* msg = (mymsg*)data;
    Serial.printf("[%s] Received: %d, %f, %c\n", macstr, msg->a, msg->b, msg->c);
}

void OnDataSent(const uint8_t* mac, esp_now_send_status_t stat)
{
    // Get mac string from uint array (destination)
    char macstr[32];
    snprintf(macstr, 31, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

    // Log status
    Serial.printf("[%s] Last Packet Sent: %s\n", macstr, stat == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail");
}

void setup()
{
    // init serial
    Serial.begin(115200);

    // init wifi
    WiFi.mode(WIFI_STA);

    // init espnow
    if (esp_now_init() != ESP_OK)
    {
        Serial.println("Can't start espnow, rebooting");
        vTaskDelay(5000 / portTICK_PERIOD_MS);
        ESP.restart();
    }

    // populate peer info
    broadcastPeer.channel = 0; // 0 = any
    broadcastPeer.encrypt = false; // can't encrypt when broadcasting
    memcpy(broadcastPeer.peer_addr, broadcastAddr, 6); // copy broadcast address

    // register peer
    if (esp_now_add_peer(&broadcastPeer) != ESP_OK)
    {
        Serial.println("Can't register espnow broadcast peer, rebooting");
        vTaskDelay(5000 / portTICK_PERIOD_MS);
        ESP.restart();
    }
    esp_now_register_send_cb(OnDataSent);
    esp_now_register_recv_cb(OnDataRecv);
}
void loop() 
{
    // wait 5secs
    vTaskDelay(5000 / portTICK_PERIOD_MS);
    
    // populate message with random number
    mymsg msg;
    msg.a = random(10, 100);
    msg.b = msg.a / 10.0;
    msg.c = (msg.a % 26) + 'a';
    
    // send message
    esp_now_send(broadcastAddr, (uint8_t*)&msg, sizeof(msg));
}
Note that the receiver callback here receives an 8-bit value in the first parameter rather than a receiver info value. This gave me an error when I tried compiling it. In loop(), you can also see where the broadcastAddr (an 8-bit array) is sent. Strangely, the compiler gave me no errors when I fixed the receiver parameter even through I didn't change the arguments given to esp_now_send. Keep in mind that this program, which said it's for ESP32 ESP-NOW, was posted just a few months ago this year. So it shouldn't even be out of date or anything. Not sure if they made a mistake or what.

Can anyone help me figure out how to make the most bare-bones ESP-NOW program possible that is two-way and uses indiscriminate broadcasting? Can you explain what the parameters do on the callbacks? Can I send individual variables rather than structs? Thanks.

MicroController
Posts: 2668
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Helping making simplest ESP-NOW broadcast program (two way)

Postby MicroController » Tue Oct 29, 2024 10:14 pm

Maybe check the examples for Arduino.

ttennebb
Posts: 1
Joined: Fri Dec 13, 2024 11:20 pm

Re: Helping making simplest ESP-NOW broadcast program (two way)

Postby ttennebb » Sat Dec 14, 2024 5:51 pm

Code: Select all

//
// note: 
// when this callback fires, the mac_addr, data and data_len are passed in 
// i.e. some receive buffer filled and triggered some interrupt
//      now deal with it
// note:
// typedef void (*esp_now_recv_cb_t)(const esp_now_recv_info_t *esp_now_info, const uint8_t *data, int data_len)
// esp_now_info points to the source mac
// the destination mac and rx packet control is also avail with this pointer
//
// esp_now_info[0] source mac
// esp_now_info[6] destination mac
// esp_now_info[12] rx packet control : ref 
//
// see => struct esp_now_recv_info
//
The above are notes from espnow code I've been developing. Most times, the documentation will show the callback as:
some_receiver_cb(const uint8_t* mac, const uint8_t* data, int data_len). One important thing here is that the pointer to the mac address will not pass out properly, meaning the pointer in can't be passed out as a pointer. It needs to be copied to an array of 6 bytes. In C that means a memcpy.

data pointer can be passed out if I read the documentation correctly, but to be safe, I memcpy the data into some array data_len long.

From the above, esp_now_info[0..5] contains the source mac
The destination mac (the receiver of this packet) starts at esp_now_info[6]
Finally a pointer to some interesting transmission parameters. Just search on struct_esp_now_recv_info

The following snippet uses the ESP API and FreeRTOS kernal

Code: Untitled.c Select all


static void tr_espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len)
{
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
// test to be sure there are params to work on...
if (mac_addr == NULL || data == NULL || len <= 0)
{
ESP_LOGE(TAG, "Receive cb arg error");
return;
}
// len needs to be limited to the system data packet
int length_in = (len>trPACKET_SIZE)? trPACKET_SIZE : len;

tr_transfer_pkt_t xfer_pkt;
//check
memcpy(xfer_pkt.mac, mac_addr, trMAC_LEN);
xfer_pkt.len = length_in;
xfer_pkt.pdata = calloc(xfer_pkt.len, sizeof(uint8_t));
memcpy(xfer_pkt.pdata, data, xfer_pkt.len);

if (xQueueSendToBackFromISR(tr_espnow_transfer_queue, &xfer_pkt, &xHigherPriorityTaskWoken) != pdTRUE)
{
ESP_LOGW(TAG, "receive queue fail");
}
}
The structure tr_transfer_pkt_t looks like this:

Code: Untitled.c Select all


//
typedef struct
{
uint8_t mac[trMAC_LEN];
uint8_t *pdata;
int len;

}tr_transfer_pkt_t;
//
Look familiar? Every time the callback fires, a tr_transfer_pkt_t is instantiated. That creates an array where the source mac is stored. A pointer is also created, but needs some memory data_len in size. Once the incoming is copied to the outgoing, then pass it out via a queue to be processed further. The call to the queue then ends the callback (interrupt) routine. Outside the callback, time constraints are eased.

To transmit, all that is required is the destination mac, pointer to data, and data_len.

Yes there are pointers. I am not all that familiar with arduino, but C in general will require pointers as it does here. I could go on all day about this, but this demonstrates how I approached the problem.

Who is online

Users browsing this forum: Applebot, Qwantbot and 2 guests