Custom Packet Ping Pong

This example demonstrates the custom packet functionality of MeshNOW.

We set everything up, and then broadcast a PING message from the root to all nodes in the mesh. Each node will respond with a PONG message, and the root will print the responses.

  1#include <esp_err.h>
  2#include <esp_event.h>
  3#include <esp_log.h>
  4#include <esp_mac.h>
  5#include <esp_wifi.h>
  6#include <freertos/FreeRTOS.h>
  7#include <freertos/task.h>
  8#include <meshnow.h>
  9#include <nvs_flash.h>
 10
 11// Waitbits
 12#define CONNECTED_BIT BIT0
 13
 14static const char *TAG = "ping-pong";
 15
 16// Fixed MAC address of the root node
 17static const uint8_t root_mac[MESHNOW_ADDRESS_LENGTH] = {0x24, 0x6f, 0x28, 0x4a, 0x63, 0x3c};
 18
 19// MAC address of the current node
 20static uint8_t node_mac[MESHNOW_ADDRESS_LENGTH];
 21
 22// Whether this node is the root
 23static bool is_root = false;
 24
 25// Event group for various waiting processes
 26static EventGroupHandle_t my_event_group;
 27
 28// Custom packet types
 29typedef enum {
 30    PING = 0,
 31    PONG = 1,
 32} custom_packet_type_t;
 33
 34// Custom packet for ping-ponging
 35typedef struct {
 36    custom_packet_type_t type;
 37    uint32_t timestamp;
 38} __attribute__((__packed__)) custom_packet_t;
 39
 40// Event handler for MESHNOW_EVEMT
 41static void meshnow_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
 42    assert(event_base == MESHNOW_EVENT);
 43
 44    switch (event_id) {
 45        case MESHNOW_EVENT_PARENT_CONNECTED: {
 46            meshnow_event_parent_connected_t *data = event_data;
 47            ESP_LOGI(TAG, "Parent connected: " MACSTR, MAC2STR(data->parent_mac));
 48            xEventGroupSetBits(my_event_group, CONNECTED_BIT);
 49            break;
 50        }
 51        case MESHNOW_EVENT_PARENT_DISCONNECTED: {
 52            meshnow_event_parent_disconnected_t *data = event_data;
 53            ESP_LOGI(TAG, "Parent disconnected: " MACSTR, MAC2STR(data->parent_mac));
 54            xEventGroupClearBits(my_event_group, CONNECTED_BIT);
 55            break;
 56        }
 57    }
 58}
 59
 60// Sends custom ping messages to every node in the mesh
 61static void perform_ping() {
 62    // since we are root, we can send right away
 63    while (true) {
 64        custom_packet_t data = {
 65            .type = PING,
 66            .timestamp = xTaskGetTickCount(),
 67        };
 68        ESP_ERROR_CHECK(meshnow_send(&MESHNOW_BROADCAST_ADDRESS, (uint8_t *)&data, sizeof(data));
 69        vTaskDelay(pMS_TO_TICKS(5000));
 70    }
 71}
 72
 73// Callback, reacts to pings and pongs depending on the node type
 74static void my_data_callback(uint8_t *src, uint8_t *buffer, size_t len) {
 75    // usually, you would do input sanitization here
 76    // we skip it for the sake of simplicity
 77
 78    // cast buffer to custom packet
 79    custom_packet_t *data = (custom_packet_t *)buffer;
 80
 81    ESP_LOGI(TAG, "Received packet of type %d from " MACSTR " with timestamp %lu", data->type, MAC2STR(src),
 82             data->timestamp);
 83
 84    // if we are not the root, we send a pong
 85    if (!is_root) {
 86        custom_packet_t pong = {
 87            .type = PONG,
 88            .timestamp = data->timestamp,
 89        };
 90        ESP_ERROR_CHECK(meshnow_send(src, (uint8_t *)&pong, sizeof(pong)));
 91    }
 92}
 93
 94// Initializes NVS, Event Loop, Wi-Fi, and Netif
 95static void pre_init(void) {
 96    // nvs
 97    esp_err_t ret = nvs_flash_init();
 98    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
 99        ESP_ERROR_CHECK(nvs_flash_erase());
100        ret = nvs_flash_init();
101    }
102    ESP_ERROR_CHECK(ret);
103
104    // event loop
105    ESP_ERROR_CHECK(esp_event_loop_create_default());
106
107    // wifi
108    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
109    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
110
111    // netif
112    ESP_ERROR_CHECK(esp_netif_init());
113}
114
115void app_main(void) {
116    pre_init();
117
118    // get MAC address
119    ESP_ERROR_CHECK(esp_read_mac(node_mac, ESP_MAC_WIFI_STA));
120
121    // check if root
122    is_root = memcmp(node_mac, root_mac, MESHNOW_ADDRESS_LENGTH) == 0;
123
124    // create event group
125    my_event_group = xEventGroupCreate();
126    assert(my_event_group != NULL);
127
128    // initialize meshnow
129    meshnow_config_t config = {
130        .root = is_root,
131        .router_config =
132            {
133                .should_connect = fale,  // do not connect to internet, we want to use inter-node communication
134                .sta_config = NULL,
135            },
136    };
137    ESP_ERROR_CHECK(meshnow_init(&config));
138
139    // register event handler for MESHNOW_EVENT
140    ESP_ERROR_CHECK(
141        esp_event_handler_instance_register(MESHNOW_EVENT, ESP_EVENT_ANY_IID, &meshnow_event_handler, NULL, NULL));
142
143    // register data callback
144    meshnow_data_cb_handle_t data_cb_handle;  // unused, as we do not unregister the callback
145    ESP_ERROR_CHECK(meshnow_register_data_cb(&my_data_callback, &data_cb_handle));
146
147    // start meshnow
148    ESP_ERROR_CHECK(meshnow_start());
149
150    // wait for IP
151    // when we get an IP address, the node has to have connected to a parent/router
152    // in a real application, you would want to handle disconnects/lost IP as well and restart MQTT
153    EventBits_t bits = xEventGroupWaitBits(my_event_group, GOT_IP_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
154    assert((bits & GOT_IP_BIT) != 0);
155
156    if (is_root) {
157        perform_ping();
158    }
159}