From 7fdad90eda4c1c396f2cecf8a0a017a50470a26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Sat, 8 Aug 2020 22:27:32 +0200 Subject: [PATCH 01/13] Occasionally receiving something, ACK temporarily disabled --- src/PJON.h | 3 +- src/interfaces/ARDUINO/ESPNOWHelper.h | 405 ++++++++++++-------------- src/interfaces/ARDUINO/PacketQueue.h | 85 ++++++ src/strategies/ESPNOW/ESPNOW.h | 10 +- 4 files changed, 287 insertions(+), 216 deletions(-) create mode 100644 src/interfaces/ARDUINO/PacketQueue.h diff --git a/src/PJON.h b/src/PJON.h index 01d6121062..274bc76065 100644 --- a/src/PJON.h +++ b/src/PJON.h @@ -563,7 +563,8 @@ class PJON { !(payload[1] & PJON_ACK_REQ_BIT) || _mode == PJON_SIMPLEX ) return PJON_ACK; - return (strategy.receive_response() == PJON_ACK) ? PJON_ACK : PJON_FAIL; + //return (strategy.receive_response() == PJON_ACK) ? PJON_ACK : PJON_FAIL; + return PJON_ACK; }; /* Compose and transmit a packet passing its info as parameters: */ diff --git a/src/interfaces/ARDUINO/ESPNOWHelper.h b/src/interfaces/ARDUINO/ESPNOWHelper.h index ed25ce60cf..726fc7205f 100644 --- a/src/interfaces/ARDUINO/ESPNOWHelper.h +++ b/src/interfaces/ARDUINO/ESPNOWHelper.h @@ -1,56 +1,47 @@ #pragma once -#ifndef ESP32 - #error "ESP32 constant is not defined." +#if !defined(ESP8266) && !defined(ESP32) + #error "ESP8266 or ESP32 constant is not defined." #endif -// ESP includes #include #include #include #include -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "freertos/timers.h" -#include "nvs_flash.h" -#include "esp_event_loop.h" -#include "tcpip_adapter.h" -#include "esp_wifi.h" -#include "esp_log.h" -#include "esp_system.h" -#include "esp_now.h" -#include "rom/ets_sys.h" -#include "rom/crc.h" -#include "esp_wifi_types.h" - -static const char *TAG = "espnow"; - -/* ESPNOW can work in both station and softap mode. - It is configured in menuconfig. */ -#if CONFIG_STATION_MODE - #define ESPNOW_WIFI_MODE WIFI_MODE_STA - #define ESPNOW_WIFI_IF ESP_IF_WIFI_STA +#include "PacketQueue.h" + +#if defined(ESP8266) + #include + #include + #include +#elif defined(ESP32) + #include +#endif + +#if CONFIG_STATION_MODE // ESPNOW can work in both station and softap mode. It is configured in menuconfig. + #if defined(ESP8266) + #define ESPNOW_WIFI_MODE ESP_NOW_ROLE_CONTROLLER + #elif defined(ESP32) + #define ESPNOW_WIFI_MODE WIFI_MODE_STA + #define ESPNOW_WIFI_IF ESP_IF_WIFI_STA + #endif + #else - #define ESPNOW_WIFI_MODE WIFI_MODE_AP - #define ESPNOW_WIFI_IF ESP_IF_WIFI_AP + #if defined(ESP8266) + #define ESPNOW_WIFI_MODE ESP_NOW_ROLE_COMBO + #elif defined(ESP32) + #define ESPNOW_WIFI_MODE WIFI_MODE_AP + #define ESPNOW_WIFI_IF ESP_IF_WIFI_AP + #endif #endif #define ESPNOW_MAX_PACKET 250 -#define ESPNOW_QUEUE_SIZE 6 + static uint8_t espnow_broadcast_mac[ESP_NOW_ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; #define IS_BROADCAST_ADDR(addr) (memcmp(addr, espnow_broadcast_mac, ESP_NOW_ETH_ALEN) == 0) -typedef struct { - uint8_t mac_addr[ESP_NOW_ETH_ALEN]; - esp_now_send_status_t status; -} espnow_event_send_cb_t; -typedef struct { - uint8_t mac_addr[ESP_NOW_ETH_ALEN]; - uint8_t *data; - int data_len; -} espnow_packet_t; enum { ESPNOW_DATA_BROADCAST, @@ -59,192 +50,184 @@ enum { }; static uint8_t last_mac[ESP_NOW_ETH_ALEN]; -static TaskHandle_t pjon_task_h = NULL; -static xQueueHandle espnow_recv_queue = NULL; - -static void espnow_send_cb(const uint8_t *mac_addr, esp_now_send_status_t status) { - // The only thing we do in the send callback is unblock the - // other thread which blocks after posting data to the MAC - xTaskNotifyGive(pjon_task_h); - if(mac_addr == NULL) ESP_LOGE(TAG, "Send cb arg error"); - return; -}; +static PacketQueue packetQueue; +volatile bool sendingDone = true; -static void espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len) { - espnow_packet_t packet; - if(mac_addr == NULL || data == NULL || len <= 0) { - ESP_LOGE(TAG, "Receive cb arg error"); - return; - } - memcpy(packet.mac_addr, mac_addr, ESP_NOW_ETH_ALEN); - packet.data = (uint8_t *)malloc(len); - if(packet.data == NULL) { - ESP_LOGE(TAG, "Malloc receive data fail"); - return; - } - memcpy(packet.data, data, len); - packet.data_len = len; - // Post to the queue, but don't wait - if(xQueueSend(espnow_recv_queue, &packet, 0) != pdTRUE) { - ESP_LOGW(TAG, "Send receive queue fail"); - free(packet.data); - } +static void espnow_send_cb(const uint8_t *mac_addr, uint8_t status) { + // The only thing we do in the send callback is unblock the other thread which blocks after posting data to the MAC + sendingDone = true; }; -class ENHelper { - uint8_t _magic_header[4]; - uint8_t _channel = 14; - uint8_t _esp_pmk[16]; +static void espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len) { + espnow_packet_t* packet = new espnow_packet_t(); -public: - void add_node_mac(uint8_t mac_addr[ESP_NOW_ETH_ALEN]) { - ESP_ERROR_CHECK(add_peer(mac_addr)); - }; + memcpy(packet->mac_addr, mac_addr, ESP_NOW_ETH_ALEN); - esp_err_t add_peer(uint8_t mac_addr[ESP_NOW_ETH_ALEN]) { - if(esp_now_is_peer_exist(mac_addr)) - return ESP_OK; - /* Add broadcast peer information to peer list. */ - esp_now_peer_info_t *peer = - (esp_now_peer_info_t *)malloc(sizeof(esp_now_peer_info_t)); - if(peer == NULL) { - ESP_LOGE(TAG, "Malloc peer information fail"); - vSemaphoreDelete(espnow_recv_queue); - esp_now_deinit(); - return ESP_FAIL; - } - memset(peer, 0, sizeof(esp_now_peer_info_t)); - peer->channel = _channel; - peer->ifidx = ESPNOW_WIFI_IF; - if(IS_BROADCAST_ADDR(mac_addr)) - peer->encrypt = false; - // else { - // peer->encrypt = true; - // memcpy(peer->lmk, _esp_pmk, 16); - // } - memcpy(peer->peer_addr, mac_addr, ESP_NOW_ETH_ALEN); - ESP_ERROR_CHECK(esp_now_add_peer(peer)); - free(peer); - return ESP_OK; - }; + packet->data = (uint8_t *)malloc(len); + memcpy(packet->data, data, len); + packet->data_len = len; - bool begin(uint8_t channel, uint8_t *espnow_pmk) { - esp_err_t ret = nvs_flash_init(); - if( - ret == ESP_ERR_NVS_NO_FREE_PAGES // || - // ret == ESP_ERR_NVS_NEW_VERSION_FOUND - // error: ESP_ERR_NVS_NEW_VERSION_FOUND was not declared in this scope - ) { - ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); - } - ESP_ERROR_CHECK(ret); - pjon_task_h = xTaskGetCurrentTaskHandle(); - _channel = channel; - memcpy(_esp_pmk, espnow_pmk, 16); - if(espnow_recv_queue != NULL) - return ESP_FAIL; - espnow_recv_queue = - xQueueCreate(ESPNOW_QUEUE_SIZE, sizeof(espnow_packet_t)); - if(espnow_recv_queue == NULL) { - ESP_LOGE(TAG, "Create mutex fail"); - return ESP_FAIL; - } - tcpip_adapter_init(); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - ESP_ERROR_CHECK(esp_wifi_set_country(&wifi_country)); - ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); - ESP_ERROR_CHECK(esp_wifi_set_mode(ESPNOW_WIFI_MODE)); - /* These two steps are required BEFORE the channel can be set - As per the documentation from Espressif: - docs.espressif.com/projects/esp-idf/en/latest/api-reference/network/ - esp_wifi.html#_CPPv420esp_wifi_set_channel7uint8_t18wifi_second_chan_t */ - ESP_ERROR_CHECK(esp_wifi_start()); - ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); - ESP_ERROR_CHECK(esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE)); - // Initialize ESPNOW and register sending & receiving callback function - ESP_ERROR_CHECK(esp_now_init()); - ESP_ERROR_CHECK(esp_now_register_send_cb(espnow_send_cb)); - ESP_ERROR_CHECK(esp_now_register_recv_cb(espnow_recv_cb)); - // Set primary master key - ESP_ERROR_CHECK(esp_now_set_pmk(_esp_pmk)); - // Add broadcast peer information to peer list - add_peer(espnow_broadcast_mac); - return true; - }; + if (!packetQueue.push(packet)) { // queue is full - drop the packet + free(packet->data); + delete packet; + } +}; - uint16_t receive_frame(uint8_t *data, uint16_t max_length) { - // see if there's any received data waiting - espnow_packet_t packet; - if(xQueueReceive(espnow_recv_queue, &packet, 0) == pdTRUE) { - if(packet.data_len >= 4) { - uint8_t len = packet.data_len - 4; - if( - (packet.data[0] ^ len) != _magic_header[0] || - (packet.data[1] ^ len) != _magic_header[1] || - (packet.data[packet.data_len - 2] ^ len) != _magic_header[2] || - (packet.data[packet.data_len - 1] ^ len) != _magic_header[3] - ) { - ESP_LOGE(TAG, "magic mismatch"); - free(packet.data); - return PJON_FAIL; - } - if(len > max_length) { - free(packet.data); - ESP_LOGE(TAG, "buffer overflow - %d bytes but max is %d", len, max_length); - return PJON_FAIL; - } - memcpy(data, packet.data + 2, len); - free(packet.data); - // Update last mac received from - memcpy(last_mac, packet.mac_addr, ESP_NOW_ETH_ALEN); - return len; - } else { - ESP_LOGE(TAG, "packet < 4 received"); - free(packet.data); - return PJON_FAIL; // No data waiting - } - } - return PJON_FAIL; - }; +class ENHelper { + uint8_t _magic_header[4]; + uint8_t _channel = 14; + uint8_t _esp_pmk[16]; - void send_frame( - uint8_t *data, - uint16_t length, - uint8_t dest_mac[ESP_NOW_ETH_ALEN] - ) { - uint8_t packet[ESPNOW_MAX_PACKET]; - if(length + 4 > ESPNOW_MAX_PACKET) { - ESP_LOGE(TAG, "Packet send error - too long :%d", length + 4); - return; - } - uint8_t len = length; - packet[0] = _magic_header[0] ^ len; - packet[1] = _magic_header[1] ^ len; - memcpy(packet + 2, data, len); - packet[len + 2] = _magic_header[2] ^ len; - packet[len + 3] = _magic_header[3] ^ len; - if(esp_now_send(dest_mac, packet, len + 4) != ESP_OK) - ESP_LOGE(TAG, "Send error"); - else // Wait for notification that the data has been received by the MAC - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); +public: + void add_node_mac(uint8_t mac_addr[ESP_NOW_ETH_ALEN]) { + add_peer(mac_addr); + }; + + void add_peer(uint8_t mac_addr[ESP_NOW_ETH_ALEN]) { + if(esp_now_is_peer_exist(mac_addr)) + return; + + #if defined(ESP8266) // TODO: Fix encryption! ESP_NOW_ROLE_SLAVE doesn't seem to matter. From the doc: The peer's Role does not affect any function, but only stores the Role information for the application layer. + esp_now_add_peer(mac_addr, ESP_NOW_ROLE_SLAVE,_channel, NULL, 0); + + #elif defined(ESP32) // TODO: Why malloc a local variable? Add broadcast peer information to peer list. + esp_now_peer_info_t *peer = (esp_now_peer_info_t *)malloc(sizeof(esp_now_peer_info_t)); + + memset(peer, 0, sizeof(esp_now_peer_info_t)); + peer->channel = _channel; + peer->ifidx = ESPNOW_WIFI_IF; + if(IS_BROADCAST_ADDR(mac_addr)) + peer->encrypt = false; // else { peer->encrypt = true; memcpy(peer->lmk, _esp_pmk, 16); } + memcpy(peer->peer_addr, mac_addr, ESP_NOW_ETH_ALEN); + esp_now_add_peer(peer); + free(peer); + #endif + }; + + bool begin(uint8_t channel, uint8_t *espnow_pmk) { + #if defined(ESP32) // TODO: Is this necessary? + esp_err_t ret = nvs_flash_init(); + if(ret == ESP_ERR_NVS_NO_FREE_PAGES) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + #endif + + _channel = channel; + memcpy(_esp_pmk, espnow_pmk, 16); + + #ifdef ESP8266 + WiFi.channel(_channel); + esp_now_init(); + esp_now_set_self_role(ESPNOW_WIFI_MODE); + esp_now_register_send_cb(reinterpret_cast(espnow_send_cb)); + esp_now_register_recv_cb(reinterpret_cast(espnow_recv_cb)); + #else + tcpip_adapter_init(); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + esp_wifi_init(&cfg); //esp_wifi_set_country(&wifi_country); + esp_wifi_set_storage(WIFI_STORAGE_RAM); + esp_wifi_set_mode(ESPNOW_WIFI_MODE); + esp_wifi_start(); // These two steps are required BEFORE the channel can be set + esp_wifi_set_promiscuous(true); // and this + esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE); + esp_now_init()); + esp_now_register_send_cb(reinterpret_cast(espnow_send_cb)); + esp_now_register_recv_cb(reinterpret_cast(espnow_recv_cb)); + ESP_ERROR_CHECK(esp_now_set_pmk(_esp_pmk); + #endif + + add_peer(espnow_broadcast_mac); // Add broadcast peer information to peer list + + return true; + }; + + uint16_t receive_frame(uint8_t *data, uint16_t max_length) { + espnow_packet_t* packet = packetQueue.pop(); // see if there's any received data waiting + if (packet == NULL) + return PJON_FAIL; + + if (packet->data_len < 4) { // The packet is too small + Serial.println("too small"); + free(packet->data); + delete(packet); + return PJON_FAIL; + } + + uint8_t len = packet->data_len - 4; + + if( + (packet->data[0] ^ len) != _magic_header[0] || + (packet->data[1] ^ len) != _magic_header[1] || + (packet->data[packet->data_len - 2] ^ len) != _magic_header[2] || + (packet->data[packet->data_len - 1] ^ len) != _magic_header[3] + ) { + Serial.println("Magic mismatch"); + free(packet->data); + delete(packet); + return PJON_FAIL; + } + + //Serial.print("H"); Serial.println(packet->data[2]); + + if (len > max_length) { + free(packet->data); + delete(packet); + return PJON_FAIL; + } + + memcpy(last_mac, packet->mac_addr, ESP_NOW_ETH_ALEN); // Update last received mac + + memcpy(data, packet->data + 2, len); + + free(packet->data); + delete(packet); + + return len; + }; + + void send_frame(uint8_t *data, uint16_t length, uint8_t dest_mac[ESP_NOW_ETH_ALEN]) { + if (!sendingDone) + return; // we are still sending the previous frame - discard this one + + uint8_t packet[ESPNOW_MAX_PACKET]; + if (length + 4 > ESPNOW_MAX_PACKET) { + return; + } + + uint8_t len = length; + packet[0] = _magic_header[0] ^ len; + packet[1] = _magic_header[1] ^ len; + memcpy(packet + 2, data, len); + packet[len + 2] = _magic_header[2] ^ len; + packet[len + 3] = _magic_header[3] ^ len; + + ATOMIC() + { + sendingDone = false; + } + if(esp_now_send(dest_mac, packet, len + 4) == 0) { + do { // do nothing, wait for interrupt to be called + //delay(1); + yield(); + } while(!sendingDone); + } }; - void send_response(uint8_t response) { - send_frame(&response, 1, last_mac); - }; + void send_response(uint8_t response) { + send_frame(&response, 1, last_mac); + }; - void send_frame(uint8_t *data, uint16_t length) { - // Broadcast - send_frame(data, length, espnow_broadcast_mac); - }; + void send_frame(uint8_t *data, uint16_t length) { // Broadcast + send_frame(data, length, espnow_broadcast_mac); + }; - void set_magic_header(uint8_t *magic_header) { - memcpy(_magic_header, magic_header, 4); - }; + void set_magic_header(uint8_t *magic_header) { + memcpy(_magic_header, magic_header, 4); + }; - void get_sender(uint8_t *ip) { - memcpy(ip, last_mac, ESP_NOW_ETH_ALEN); - }; + void get_sender(uint8_t *ip) { + memcpy(ip, last_mac, ESP_NOW_ETH_ALEN); + }; }; diff --git a/src/interfaces/ARDUINO/PacketQueue.h b/src/interfaces/ARDUINO/PacketQueue.h new file mode 100644 index 0000000000..a263f63366 --- /dev/null +++ b/src/interfaces/ARDUINO/PacketQueue.h @@ -0,0 +1,85 @@ +#pragma once + +#if defined(ESP8266) + #include + #include +#elif defined(ESP32) + #include +#endif + +#include // https://github.com/wizard97/SimplyAtomic + +#ifndef ESP_NOW_ETH_ALEN + #define ESP_NOW_ETH_ALEN 6 +#endif + +#define ESPNOW_QUEUE_SIZE 200 + +typedef struct +{ + uint8_t mac_addr[ESP_NOW_ETH_ALEN]; + uint8_t *data; + int data_len; +} espnow_packet_t; + +/** + * A queue of incoming packets of max capacity ESPNOW_QUEUE_SIZE. + * The queue should be atomic (not tested). + * This was done to remove a dependency on FreeRTOS - replacement of xQueueHandle (and related functions). + */ +class PacketQueue +{ + private: + espnow_packet_t* queue[ESPNOW_QUEUE_SIZE + 1]; + volatile uint8_t firstElement = 0; + volatile uint8_t firstSpace = 0; + + public: + /** + * Adds a packet to the queue. + * @return true if successfull, false if the queue was full and the packet was not added. + */ + bool push(espnow_packet_t* packet); + + /** + * Remove the first packet from the queue and return it. + * @return first packet or NULL if the queue is empty + */ + espnow_packet_t* pop(); +}; + +bool PacketQueue::push(espnow_packet_t* packet) +{ + bool isFull; + + ATOMIC() + { + int firstSpacePlus1 = (firstSpace + 1) % ESPNOW_QUEUE_SIZE; + isFull = firstSpacePlus1 == firstElement; + if (!isFull) + { + queue[firstSpace] = packet; + firstSpace = firstSpacePlus1; + } + } + + return !isFull; +} + +espnow_packet_t* PacketQueue::pop() { + if (firstElement == firstSpace) { + return NULL; + } + else + { + espnow_packet_t* packet; + + ATOMIC() + { + packet = queue[firstElement]; + firstElement = (firstElement + 1) % ESPNOW_QUEUE_SIZE; + } + + return packet; + } +} \ No newline at end of file diff --git a/src/strategies/ESPNOW/ESPNOW.h b/src/strategies/ESPNOW/ESPNOW.h index d5696c5560..f351f65c6c 100644 --- a/src/strategies/ESPNOW/ESPNOW.h +++ b/src/strategies/ESPNOW/ESPNOW.h @@ -76,7 +76,7 @@ class ESPNOW { PJONTools::parse_header(message, packet_info); uint8_t sender_id = packet_info.tx.id; if(sender_id == 0) { - ESP_LOGE("ESPNOW", "AutoRegister parsing failed"); + //ESP_LOGE("ESPNOW", "AutoRegister parsing failed"); return; // If parsing fails, it will be 0 } @@ -87,19 +87,19 @@ class ESPNOW { // See if PJON id is already registered, add if not int16_t pos = find_remote_node(sender_id); if(pos == -1) { - ESP_LOGI("ESPNOW", "Autoregister new sender %d",sender_id); + //ESP_LOGI("ESPNOW", "Autoregister new sender %d",sender_id); add_node(sender_id, sender_mac); } else if(memcmp(_remote_mac[pos], sender_mac, ESP_NOW_ETH_ALEN) != 0) { // Update mac of existing node - ESP_LOGI( + /*ESP_LOGI( "ESPNOW", "Update sender mac %d %d:%d:%d", sender_id, sender_mac[1], sender_mac[2], sender_mac[3] - ); + );*/ memcpy(_remote_mac[pos], sender_mac, ESP_NOW_ETH_ALEN); } } @@ -195,6 +195,8 @@ class ESPNOW { if(result[0] == PJON_ACK) return result[0]; + yield(); + } while ((uint32_t)(PJON_MICROS() - start) < EN_RESPONSE_TIMEOUT); return PJON_FAIL; }; From f2375ed3852c86d7c28baecb7d06a415269f6236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Sat, 8 Aug 2020 22:58:36 +0200 Subject: [PATCH 02/13] Adding debug outputs, ACK is back --- src/PJON.h | 3 +-- src/interfaces/ARDUINO/ESPNOWHelper.h | 16 +++++++++++++++- src/interfaces/ARDUINO/PacketQueue.h | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/PJON.h b/src/PJON.h index 274bc76065..01d6121062 100644 --- a/src/PJON.h +++ b/src/PJON.h @@ -563,8 +563,7 @@ class PJON { !(payload[1] & PJON_ACK_REQ_BIT) || _mode == PJON_SIMPLEX ) return PJON_ACK; - //return (strategy.receive_response() == PJON_ACK) ? PJON_ACK : PJON_FAIL; - return PJON_ACK; + return (strategy.receive_response() == PJON_ACK) ? PJON_ACK : PJON_FAIL; }; /* Compose and transmit a packet passing its info as parameters: */ diff --git a/src/interfaces/ARDUINO/ESPNOWHelper.h b/src/interfaces/ARDUINO/ESPNOWHelper.h index 726fc7205f..afdbe33fea 100644 --- a/src/interfaces/ARDUINO/ESPNOWHelper.h +++ b/src/interfaces/ARDUINO/ESPNOWHelper.h @@ -52,13 +52,20 @@ enum { static uint8_t last_mac[ESP_NOW_ETH_ALEN]; static PacketQueue packetQueue; volatile bool sendingDone = true; +volatile uint32_t sendCount = 0; +volatile uint32_t recCalled = 0; +volatile uint32_t failCount = 0; static void espnow_send_cb(const uint8_t *mac_addr, uint8_t status) { + sendCount++; + if (status != 0) + failCount++; // The only thing we do in the send callback is unblock the other thread which blocks after posting data to the MAC sendingDone = true; }; static void espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len) { + recCalled++; espnow_packet_t* packet = new espnow_packet_t(); memcpy(packet->mac_addr, mac_addr, ESP_NOW_ETH_ALEN); @@ -68,6 +75,7 @@ static void espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len packet->data_len = len; if (!packetQueue.push(packet)) { // queue is full - drop the packet + failCount++; free(packet->data); delete packet; } @@ -188,11 +196,14 @@ class ENHelper { }; void send_frame(uint8_t *data, uint16_t length, uint8_t dest_mac[ESP_NOW_ETH_ALEN]) { - if (!sendingDone) + if (!sendingDone) { + failCount++; return; // we are still sending the previous frame - discard this one + } uint8_t packet[ESPNOW_MAX_PACKET]; if (length + 4 > ESPNOW_MAX_PACKET) { + failCount++; return; } @@ -213,6 +224,9 @@ class ENHelper { yield(); } while(!sendingDone); } + else { + failCount++; + } }; void send_response(uint8_t response) { diff --git a/src/interfaces/ARDUINO/PacketQueue.h b/src/interfaces/ARDUINO/PacketQueue.h index a263f63366..324c79ea7f 100644 --- a/src/interfaces/ARDUINO/PacketQueue.h +++ b/src/interfaces/ARDUINO/PacketQueue.h @@ -13,7 +13,7 @@ #define ESP_NOW_ETH_ALEN 6 #endif -#define ESPNOW_QUEUE_SIZE 200 +#define ESPNOW_QUEUE_SIZE 6 typedef struct { From 598b411aa210aabd084f5a15b3030f35167cca06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Sun, 9 Aug 2020 16:25:04 +0200 Subject: [PATCH 03/13] Setting channel and mode properly. It works! --- src/interfaces/ARDUINO/ESPNOWHelper.h | 36 +++++++++++---------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/interfaces/ARDUINO/ESPNOWHelper.h b/src/interfaces/ARDUINO/ESPNOWHelper.h index afdbe33fea..2b4528a3ee 100644 --- a/src/interfaces/ARDUINO/ESPNOWHelper.h +++ b/src/interfaces/ARDUINO/ESPNOWHelper.h @@ -18,9 +18,10 @@ #include #endif -#if CONFIG_STATION_MODE // ESPNOW can work in both station and softap mode. It is configured in menuconfig. +#if defined(CONFIG_STATION_MODE) // ESPNOW can work in both station and softap mode. It is configured in menuconfig. #if defined(ESP8266) - #define ESPNOW_WIFI_MODE ESP_NOW_ROLE_CONTROLLER + #define ESPNOW_WIFI_MODE WIFI_STA + #define ESPNOW_WIFI_ROLE ESP_NOW_ROLE_CONTROLLER #elif defined(ESP32) #define ESPNOW_WIFI_MODE WIFI_MODE_STA #define ESPNOW_WIFI_IF ESP_IF_WIFI_STA @@ -28,7 +29,8 @@ #else #if defined(ESP8266) - #define ESPNOW_WIFI_MODE ESP_NOW_ROLE_COMBO + #define ESPNOW_WIFI_MODE WIFI_AP + #define ESPNOW_WIFI_ROLE ESP_NOW_ROLE_COMBO #elif defined(ESP32) #define ESPNOW_WIFI_MODE WIFI_MODE_AP #define ESPNOW_WIFI_IF ESP_IF_WIFI_AP @@ -52,20 +54,13 @@ enum { static uint8_t last_mac[ESP_NOW_ETH_ALEN]; static PacketQueue packetQueue; volatile bool sendingDone = true; -volatile uint32_t sendCount = 0; -volatile uint32_t recCalled = 0; -volatile uint32_t failCount = 0; static void espnow_send_cb(const uint8_t *mac_addr, uint8_t status) { - sendCount++; - if (status != 0) - failCount++; // The only thing we do in the send callback is unblock the other thread which blocks after posting data to the MAC sendingDone = true; }; static void espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len) { - recCalled++; espnow_packet_t* packet = new espnow_packet_t(); memcpy(packet->mac_addr, mac_addr, ESP_NOW_ETH_ALEN); @@ -75,7 +70,6 @@ static void espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len packet->data_len = len; if (!packetQueue.push(packet)) { // queue is full - drop the packet - failCount++; free(packet->data); delete packet; } @@ -126,19 +120,24 @@ class ENHelper { memcpy(_esp_pmk, espnow_pmk, 16); #ifdef ESP8266 - WiFi.channel(_channel); + WiFi.mode(ESPNOW_WIFI_MODE); + wifi_set_channel(_channel); esp_now_init(); - esp_now_set_self_role(ESPNOW_WIFI_MODE); + esp_now_set_self_role(ESPNOW_WIFI_ROLE); esp_now_register_send_cb(reinterpret_cast(espnow_send_cb)); esp_now_register_recv_cb(reinterpret_cast(espnow_recv_cb)); #else tcpip_adapter_init(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - esp_wifi_init(&cfg); //esp_wifi_set_country(&wifi_country); + esp_wifi_init(&cfg); + //esp_wifi_set_country(&wifi_country); esp_wifi_set_storage(WIFI_STORAGE_RAM); esp_wifi_set_mode(ESPNOW_WIFI_MODE); - esp_wifi_start(); // These two steps are required BEFORE the channel can be set - esp_wifi_set_promiscuous(true); // and this + + // These two steps are required BEFORE the channel can be set + esp_wifi_start(); + esp_wifi_set_promiscuous(true); + esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE); esp_now_init()); esp_now_register_send_cb(reinterpret_cast(espnow_send_cb)); @@ -197,13 +196,11 @@ class ENHelper { void send_frame(uint8_t *data, uint16_t length, uint8_t dest_mac[ESP_NOW_ETH_ALEN]) { if (!sendingDone) { - failCount++; return; // we are still sending the previous frame - discard this one } uint8_t packet[ESPNOW_MAX_PACKET]; if (length + 4 > ESPNOW_MAX_PACKET) { - failCount++; return; } @@ -224,9 +221,6 @@ class ENHelper { yield(); } while(!sendingDone); } - else { - failCount++; - } }; void send_response(uint8_t response) { From 881512ed9d74b329cfba42c5e0096eebac3f70f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Sun, 9 Aug 2020 17:00:18 +0200 Subject: [PATCH 04/13] Fixing ESP32 compilation, cleanup of includes --- src/interfaces/ARDUINO/ESPNOWHelper.h | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/interfaces/ARDUINO/ESPNOWHelper.h b/src/interfaces/ARDUINO/ESPNOWHelper.h index 2b4528a3ee..90620ef1b0 100644 --- a/src/interfaces/ARDUINO/ESPNOWHelper.h +++ b/src/interfaces/ARDUINO/ESPNOWHelper.h @@ -4,10 +4,6 @@ #error "ESP8266 or ESP32 constant is not defined." #endif -#include -#include -#include -#include #include "PacketQueue.h" #if defined(ESP8266) @@ -16,12 +12,14 @@ #include #elif defined(ESP32) #include + #include + #include #endif #if defined(CONFIG_STATION_MODE) // ESPNOW can work in both station and softap mode. It is configured in menuconfig. #if defined(ESP8266) #define ESPNOW_WIFI_MODE WIFI_STA - #define ESPNOW_WIFI_ROLE ESP_NOW_ROLE_CONTROLLER + #define ESPNOW_WIFI_ROLE ESP_NOW_ROLE_CONTROLLER #elif defined(ESP32) #define ESPNOW_WIFI_MODE WIFI_MODE_STA #define ESPNOW_WIFI_IF ESP_IF_WIFI_STA @@ -30,7 +28,7 @@ #else #if defined(ESP8266) #define ESPNOW_WIFI_MODE WIFI_AP - #define ESPNOW_WIFI_ROLE ESP_NOW_ROLE_COMBO + #define ESPNOW_WIFI_ROLE ESP_NOW_ROLE_COMBO #elif defined(ESP32) #define ESPNOW_WIFI_MODE WIFI_MODE_AP #define ESPNOW_WIFI_IF ESP_IF_WIFI_AP @@ -120,7 +118,7 @@ class ENHelper { memcpy(_esp_pmk, espnow_pmk, 16); #ifdef ESP8266 - WiFi.mode(ESPNOW_WIFI_MODE); + WiFi.mode(ESPNOW_WIFI_MODE); wifi_set_channel(_channel); esp_now_init(); esp_now_set_self_role(ESPNOW_WIFI_ROLE); @@ -130,19 +128,19 @@ class ENHelper { tcpip_adapter_init(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(&cfg); - //esp_wifi_set_country(&wifi_country); + //esp_wifi_set_country(&wifi_country); esp_wifi_set_storage(WIFI_STORAGE_RAM); esp_wifi_set_mode(ESPNOW_WIFI_MODE); - // These two steps are required BEFORE the channel can be set + // These two steps are required BEFORE the channel can be set esp_wifi_start(); esp_wifi_set_promiscuous(true); esp_wifi_set_channel(_channel, WIFI_SECOND_CHAN_NONE); - esp_now_init()); + esp_now_init(); esp_now_register_send_cb(reinterpret_cast(espnow_send_cb)); esp_now_register_recv_cb(reinterpret_cast(espnow_recv_cb)); - ESP_ERROR_CHECK(esp_now_set_pmk(_esp_pmk); + ESP_ERROR_CHECK(esp_now_set_pmk(_esp_pmk)); #endif add_peer(espnow_broadcast_mac); // Add broadcast peer information to peer list From 94e5b67a60e191a1982c4d33779a121af6490cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Sun, 9 Aug 2020 17:08:14 +0200 Subject: [PATCH 05/13] Re-enabling error logging, updating readme --- src/strategies/ESPNOW/ESPNOW.h | 26 ++++++++++++++++---------- src/strategies/ESPNOW/README.md | 9 ++++++++- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/strategies/ESPNOW/ESPNOW.h b/src/strategies/ESPNOW/ESPNOW.h index f351f65c6c..f739529ee3 100644 --- a/src/strategies/ESPNOW/ESPNOW.h +++ b/src/strategies/ESPNOW/ESPNOW.h @@ -76,7 +76,9 @@ class ESPNOW { PJONTools::parse_header(message, packet_info); uint8_t sender_id = packet_info.tx.id; if(sender_id == 0) { - //ESP_LOGE("ESPNOW", "AutoRegister parsing failed"); + #if defined(ESP32) + ESP_LOGE("ESPNOW", "AutoRegister parsing failed"); + #endif return; // If parsing fails, it will be 0 } @@ -87,19 +89,23 @@ class ESPNOW { // See if PJON id is already registered, add if not int16_t pos = find_remote_node(sender_id); if(pos == -1) { - //ESP_LOGI("ESPNOW", "Autoregister new sender %d",sender_id); + #if defined(ESP32) + ESP_LOGI("ESPNOW", "Autoregister new sender %d",sender_id); + #endif add_node(sender_id, sender_mac); } else if(memcmp(_remote_mac[pos], sender_mac, ESP_NOW_ETH_ALEN) != 0) { // Update mac of existing node - /*ESP_LOGI( - "ESPNOW", - "Update sender mac %d %d:%d:%d", - sender_id, - sender_mac[1], - sender_mac[2], - sender_mac[3] - );*/ + #if defined(ESP32) + ESP_LOGI( + "ESPNOW", + "Update sender mac %d %d:%d:%d", + sender_id, + sender_mac[1], + sender_mac[2], + sender_mac[3] + ); + #endif memcpy(_remote_mac[pos], sender_mac, ESP_NOW_ETH_ALEN); } } diff --git a/src/strategies/ESPNOW/README.md b/src/strategies/ESPNOW/README.md index 361c12a15c..edc73624de 100644 --- a/src/strategies/ESPNOW/README.md +++ b/src/strategies/ESPNOW/README.md @@ -4,7 +4,7 @@ |--------|-----------|--------------------| | ESPNOW over WiFi | NA | `#include ` | -With the `ESPNOW` PJON strategy, up to 10 ESP32 devices can use PJON to communicate with each other over +With the `ESPNOW` PJON strategy, up to 10 ESP32 or ESP8266 devices can use PJON to communicate with each other over the [Espressif ESPNOW protocol](https://www.espressif.com/en/products/software/esp-now/overview) (peer-to-peer 802.11). PJON over ESPNOW has the following benefits: @@ -38,6 +38,13 @@ void setup() { } ``` +You can also choose between Access Point and Station mode. To switch to Station mode, define CONFIG_STATION_MODE before +including PJONESPNOW.h. +```cpp + #define CONFIG_STATION_MODE + #include +``` + The ESPNOW strategy will send a broadcast message if the device_id is not already registered. Once a response is received (assuming auto-registration is enabled) the device automatically adds the node id and mac in its table. Sender auto-registration is enabled by default and can be disabled using the following setter: ```cpp From 4af981647fc6d93e7256149628c232c1bdf95f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Mon, 10 Aug 2020 07:35:59 +0200 Subject: [PATCH 06/13] Using PJON max packet length and buffer capacity constants --- src/interfaces/ARDUINO/ESPNOWHelper.h | 44 +++++++++++++++------------ src/interfaces/ARDUINO/PacketQueue.h | 18 +++++------ src/strategies/ESPNOW/ESPNOW.h | 10 +++--- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/interfaces/ARDUINO/ESPNOWHelper.h b/src/interfaces/ARDUINO/ESPNOWHelper.h index 90620ef1b0..5650bcaba8 100644 --- a/src/interfaces/ARDUINO/ESPNOWHelper.h +++ b/src/interfaces/ARDUINO/ESPNOWHelper.h @@ -5,6 +5,7 @@ #endif #include "PacketQueue.h" +#include "PJONDefines.h" #if defined(ESP8266) #include @@ -35,11 +36,11 @@ #endif #endif -#define ESPNOW_MAX_PACKET 250 +#define ESPNOW_PACKET_HEADER_LENGTH 2 +#define ESPNOW_PACKET_FOOTER_LENGTH 2 -static uint8_t espnow_broadcast_mac[ESP_NOW_ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -#define IS_BROADCAST_ADDR(addr) (memcmp(addr, espnow_broadcast_mac, ESP_NOW_ETH_ALEN) == 0) +static uint8_t espnow_broadcast_mac[ESP_NOW_MAC_LENGTH] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; @@ -49,7 +50,7 @@ enum { ESPNOW_DATA_MAX, }; -static uint8_t last_mac[ESP_NOW_ETH_ALEN]; +static uint8_t last_mac[ESP_NOW_MAC_LENGTH]; static PacketQueue packetQueue; volatile bool sendingDone = true; @@ -61,7 +62,7 @@ static void espnow_send_cb(const uint8_t *mac_addr, uint8_t status) { static void espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len) { espnow_packet_t* packet = new espnow_packet_t(); - memcpy(packet->mac_addr, mac_addr, ESP_NOW_ETH_ALEN); + memcpy(packet->mac_addr, mac_addr, ESP_NOW_MAC_LENGTH); packet->data = (uint8_t *)malloc(len); memcpy(packet->data, data, len); @@ -79,11 +80,11 @@ class ENHelper { uint8_t _esp_pmk[16]; public: - void add_node_mac(uint8_t mac_addr[ESP_NOW_ETH_ALEN]) { + void add_node_mac(uint8_t mac_addr[ESP_NOW_MAC_LENGTH]) { add_peer(mac_addr); }; - void add_peer(uint8_t mac_addr[ESP_NOW_ETH_ALEN]) { + void add_peer(uint8_t mac_addr[ESP_NOW_MAC_LENGTH]) { if(esp_now_is_peer_exist(mac_addr)) return; @@ -96,9 +97,10 @@ class ENHelper { memset(peer, 0, sizeof(esp_now_peer_info_t)); peer->channel = _channel; peer->ifidx = ESPNOW_WIFI_IF; - if(IS_BROADCAST_ADDR(mac_addr)) - peer->encrypt = false; // else { peer->encrypt = true; memcpy(peer->lmk, _esp_pmk, 16); } - memcpy(peer->peer_addr, mac_addr, ESP_NOW_ETH_ALEN); + if( (memcmp(mac_addr, espnow_broadcast_mac, ESP_NOW_MAC_LENGTH) == 0)) + peer->encrypt = false; + // else { peer->encrypt = true; memcpy(peer->lmk, _esp_pmk, 16); } + memcpy(peer->peer_addr, mac_addr, ESP_NOW_MAC_LENGTH); esp_now_add_peer(peer); free(peer); #endif @@ -182,7 +184,7 @@ class ENHelper { return PJON_FAIL; } - memcpy(last_mac, packet->mac_addr, ESP_NOW_ETH_ALEN); // Update last received mac + memcpy(last_mac, packet->mac_addr, ESP_NOW_MAC_LENGTH); // Update last received mac memcpy(data, packet->data + 2, len); @@ -192,20 +194,23 @@ class ENHelper { return len; }; - void send_frame(uint8_t *data, uint16_t length, uint8_t dest_mac[ESP_NOW_ETH_ALEN]) { + void send_frame(uint8_t *data, uint16_t length, uint8_t dest_mac[ESP_NOW_MAC_LENGTH]) { if (!sendingDone) { - return; // we are still sending the previous frame - discard this one + // we are still sending the previous frame - discard this one + return; } - uint8_t packet[ESPNOW_MAX_PACKET]; - if (length + 4 > ESPNOW_MAX_PACKET) { + if (length > PJON_PACKET_MAX_LENGTH) { + // the data is too long return; } + uint8_t packet[PJON_PACKET_MAX_LENGTH + ESPNOW_PACKET_HEADER_LENGTH + ESPNOW_PACKET_FOOTER_LENGTH]; + uint8_t len = length; packet[0] = _magic_header[0] ^ len; packet[1] = _magic_header[1] ^ len; - memcpy(packet + 2, data, len); + memcpy(packet + ESPNOW_PACKET_HEADER_LENGTH, data, len); packet[len + 2] = _magic_header[2] ^ len; packet[len + 3] = _magic_header[3] ^ len; @@ -213,9 +218,8 @@ class ENHelper { { sendingDone = false; } - if(esp_now_send(dest_mac, packet, len + 4) == 0) { - do { // do nothing, wait for interrupt to be called - //delay(1); + if(esp_now_send(dest_mac, packet, len + ESPNOW_PACKET_HEADER_LENGTH + ESPNOW_PACKET_FOOTER_LENGTH) == 0) { + do { // wait for sending finished interrupt to be called yield(); } while(!sendingDone); } @@ -234,6 +238,6 @@ class ENHelper { }; void get_sender(uint8_t *ip) { - memcpy(ip, last_mac, ESP_NOW_ETH_ALEN); + memcpy(ip, last_mac, ESP_NOW_MAC_LENGTH); }; }; diff --git a/src/interfaces/ARDUINO/PacketQueue.h b/src/interfaces/ARDUINO/PacketQueue.h index 324c79ea7f..4f45249168 100644 --- a/src/interfaces/ARDUINO/PacketQueue.h +++ b/src/interfaces/ARDUINO/PacketQueue.h @@ -8,29 +8,29 @@ #endif #include // https://github.com/wizard97/SimplyAtomic +#include "PJONDefines.h" -#ifndef ESP_NOW_ETH_ALEN - #define ESP_NOW_ETH_ALEN 6 +#ifndef ESP_NOW_MAC_LENGTH + #define ESP_NOW_MAC_LENGTH 6 #endif -#define ESPNOW_QUEUE_SIZE 6 - typedef struct { - uint8_t mac_addr[ESP_NOW_ETH_ALEN]; + uint8_t mac_addr[ESP_NOW_MAC_LENGTH]; uint8_t *data; int data_len; } espnow_packet_t; /** - * A queue of incoming packets of max capacity ESPNOW_QUEUE_SIZE. + * A queue of incoming packets of max capacity PJON_MAX_PACKETS. * The queue should be atomic (not tested). * This was done to remove a dependency on FreeRTOS - replacement of xQueueHandle (and related functions). */ class PacketQueue { private: - espnow_packet_t* queue[ESPNOW_QUEUE_SIZE + 1]; + // 1 extra capacity is necessary to distinguisch between the queue being full and empty + espnow_packet_t* queue[PJON_MAX_PACKETS + 1]; volatile uint8_t firstElement = 0; volatile uint8_t firstSpace = 0; @@ -54,7 +54,7 @@ bool PacketQueue::push(espnow_packet_t* packet) ATOMIC() { - int firstSpacePlus1 = (firstSpace + 1) % ESPNOW_QUEUE_SIZE; + int firstSpacePlus1 = (firstSpace + 1) % (PJON_MAX_PACKETS + 1); isFull = firstSpacePlus1 == firstElement; if (!isFull) { @@ -77,7 +77,7 @@ espnow_packet_t* PacketQueue::pop() { ATOMIC() { packet = queue[firstElement]; - firstElement = (firstElement + 1) % ESPNOW_QUEUE_SIZE; + firstElement = (firstElement + 1) % (PJON_MAX_PACKETS + 1); } return packet; diff --git a/src/strategies/ESPNOW/ESPNOW.h b/src/strategies/ESPNOW/ESPNOW.h index f739529ee3..1cce4ed26e 100644 --- a/src/strategies/ESPNOW/ESPNOW.h +++ b/src/strategies/ESPNOW/ESPNOW.h @@ -48,7 +48,7 @@ class ESPNOW { // Remote nodes uint8_t _remote_node_count = 0; uint8_t _remote_id[EN_MAX_REMOTE_NODES]; - uint8_t _remote_mac[EN_MAX_REMOTE_NODES][ESP_NOW_ETH_ALEN]; + uint8_t _remote_mac[EN_MAX_REMOTE_NODES][ESP_NOW_MAC_LENGTH]; ENHelper en; @@ -83,7 +83,7 @@ class ESPNOW { } // Then get the mac address of the sender - uint8_t sender_mac[ESP_NOW_ETH_ALEN]; + uint8_t sender_mac[ESP_NOW_MAC_LENGTH]; en.get_sender(sender_mac); // See if PJON id is already registered, add if not @@ -94,7 +94,7 @@ class ESPNOW { #endif add_node(sender_id, sender_mac); } - else if(memcmp(_remote_mac[pos], sender_mac, ESP_NOW_ETH_ALEN) != 0) { + else if(memcmp(_remote_mac[pos], sender_mac, ESP_NOW_MAC_LENGTH) != 0) { // Update mac of existing node #if defined(ESP32) ESP_LOGI( @@ -106,7 +106,7 @@ class ESPNOW { sender_mac[3] ); #endif - memcpy(_remote_mac[pos], sender_mac, ESP_NOW_ETH_ALEN); + memcpy(_remote_mac[pos], sender_mac, ESP_NOW_MAC_LENGTH); } } }; @@ -129,7 +129,7 @@ class ESPNOW { ) { if(_remote_node_count == EN_MAX_REMOTE_NODES) return -1; _remote_id[_remote_node_count] = remote_id; - memcpy(_remote_mac[_remote_node_count], remote_mac, ESP_NOW_ETH_ALEN); + memcpy(_remote_mac[_remote_node_count], remote_mac, ESP_NOW_MAC_LENGTH); en.add_node_mac(remote_mac); _remote_node_count++; return _remote_node_count - 1; From 9c684db464c026784f45531712964d8f2045176b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Mon, 10 Aug 2020 08:48:20 +0200 Subject: [PATCH 07/13] Static allocation of packet queue --- src/interfaces/ARDUINO/ESPNOWHelper.h | 73 ++---------- src/interfaces/ARDUINO/PacketQueue.h | 161 ++++++++++++++++---------- src/strategies/ESPNOW/ESPNOW.h | 3 +- 3 files changed, 111 insertions(+), 126 deletions(-) diff --git a/src/interfaces/ARDUINO/ESPNOWHelper.h b/src/interfaces/ARDUINO/ESPNOWHelper.h index 5650bcaba8..a80ecc40eb 100644 --- a/src/interfaces/ARDUINO/ESPNOWHelper.h +++ b/src/interfaces/ARDUINO/ESPNOWHelper.h @@ -36,8 +36,7 @@ #endif #endif -#define ESPNOW_PACKET_HEADER_LENGTH 2 -#define ESPNOW_PACKET_FOOTER_LENGTH 2 + static uint8_t espnow_broadcast_mac[ESP_NOW_MAC_LENGTH] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; @@ -60,22 +59,12 @@ static void espnow_send_cb(const uint8_t *mac_addr, uint8_t status) { }; static void espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len) { - espnow_packet_t* packet = new espnow_packet_t(); - - memcpy(packet->mac_addr, mac_addr, ESP_NOW_MAC_LENGTH); - - packet->data = (uint8_t *)malloc(len); - memcpy(packet->data, data, len); - packet->data_len = len; - - if (!packetQueue.push(packet)) { // queue is full - drop the packet - free(packet->data); - delete packet; - } + // Add the packet to the incoming queue. + // no error checking, if queue is full just ignore the packet. + packetQueue.push(mac_addr, data, len); }; class ENHelper { - uint8_t _magic_header[4]; uint8_t _channel = 14; uint8_t _esp_pmk[16]; @@ -151,47 +140,7 @@ class ENHelper { }; uint16_t receive_frame(uint8_t *data, uint16_t max_length) { - espnow_packet_t* packet = packetQueue.pop(); // see if there's any received data waiting - if (packet == NULL) - return PJON_FAIL; - - if (packet->data_len < 4) { // The packet is too small - Serial.println("too small"); - free(packet->data); - delete(packet); - return PJON_FAIL; - } - - uint8_t len = packet->data_len - 4; - - if( - (packet->data[0] ^ len) != _magic_header[0] || - (packet->data[1] ^ len) != _magic_header[1] || - (packet->data[packet->data_len - 2] ^ len) != _magic_header[2] || - (packet->data[packet->data_len - 1] ^ len) != _magic_header[3] - ) { - Serial.println("Magic mismatch"); - free(packet->data); - delete(packet); - return PJON_FAIL; - } - - //Serial.print("H"); Serial.println(packet->data[2]); - - if (len > max_length) { - free(packet->data); - delete(packet); - return PJON_FAIL; - } - - memcpy(last_mac, packet->mac_addr, ESP_NOW_MAC_LENGTH); // Update last received mac - - memcpy(data, packet->data + 2, len); - - free(packet->data); - delete(packet); - - return len; + return packetQueue.pop(last_mac, data, max_length); }; void send_frame(uint8_t *data, uint16_t length, uint8_t dest_mac[ESP_NOW_MAC_LENGTH]) { @@ -208,11 +157,11 @@ class ENHelper { uint8_t packet[PJON_PACKET_MAX_LENGTH + ESPNOW_PACKET_HEADER_LENGTH + ESPNOW_PACKET_FOOTER_LENGTH]; uint8_t len = length; - packet[0] = _magic_header[0] ^ len; - packet[1] = _magic_header[1] ^ len; + packet[0] = ESP_NOW_MAGIC_HEADER[0] ^ len; + packet[1] = ESP_NOW_MAGIC_HEADER[1] ^ len; memcpy(packet + ESPNOW_PACKET_HEADER_LENGTH, data, len); - packet[len + 2] = _magic_header[2] ^ len; - packet[len + 3] = _magic_header[3] ^ len; + packet[len + 2] = ESP_NOW_MAGIC_HEADER[2] ^ len; + packet[len + 3] = ESP_NOW_MAGIC_HEADER[3] ^ len; ATOMIC() { @@ -233,10 +182,6 @@ class ENHelper { send_frame(data, length, espnow_broadcast_mac); }; - void set_magic_header(uint8_t *magic_header) { - memcpy(_magic_header, magic_header, 4); - }; - void get_sender(uint8_t *ip) { memcpy(ip, last_mac, ESP_NOW_MAC_LENGTH); }; diff --git a/src/interfaces/ARDUINO/PacketQueue.h b/src/interfaces/ARDUINO/PacketQueue.h index 4f45249168..0f9c2819c5 100644 --- a/src/interfaces/ARDUINO/PacketQueue.h +++ b/src/interfaces/ARDUINO/PacketQueue.h @@ -1,25 +1,63 @@ #pragma once #if defined(ESP8266) - #include - #include + #include + #include #elif defined(ESP32) - #include + #include #endif #include // https://github.com/wizard97/SimplyAtomic #include "PJONDefines.h" -#ifndef ESP_NOW_MAC_LENGTH - #define ESP_NOW_MAC_LENGTH 6 -#endif +#define ESP_NOW_MAC_LENGTH 6 +#define ESPNOW_PACKET_HEADER_LENGTH 2 +#define ESPNOW_PACKET_FOOTER_LENGTH 2 +uint8_t* const ESP_NOW_MAGIC_HEADER = (uint8_t*)"\xEE\xFE\x0E\xEF"; -typedef struct +class EspNowPacket { - uint8_t mac_addr[ESP_NOW_MAC_LENGTH]; - uint8_t *data; - int data_len; -} espnow_packet_t; + private: + uint8_t mac_address[ESP_NOW_MAC_LENGTH]; + uint8_t data[PJON_PACKET_MAX_LENGTH + ESPNOW_PACKET_HEADER_LENGTH + ESPNOW_PACKET_FOOTER_LENGTH]; + int data_len; + + public: + void set(const uint8_t *mac_address, const uint8_t *data, int len) { + memcpy(this->mac_address, mac_address, ESP_NOW_MAC_LENGTH); + memcpy(this->data, data, len); + this->data_len = len; + } + + uint16_t checkAndGet(uint8_t *out_mac_address, uint8_t *out_data, uint16_t max_length) { + if (data_len < ESPNOW_PACKET_HEADER_LENGTH + ESPNOW_PACKET_FOOTER_LENGTH) { + // The packet is too small + return PJON_FAIL; + } + + uint8_t len = data_len - ESPNOW_PACKET_HEADER_LENGTH - ESPNOW_PACKET_FOOTER_LENGTH; + + if( + (data[0] ^ len) != ESP_NOW_MAGIC_HEADER[0] || + (data[1] ^ len) != ESP_NOW_MAGIC_HEADER[1] || + (data[data_len - 2] ^ len) != ESP_NOW_MAGIC_HEADER[2] || + (data[data_len - 1] ^ len) != ESP_NOW_MAGIC_HEADER[3] + ) { + // Magic mismatch + return PJON_FAIL; + } + + if (len > max_length) { + // Too short buffer + return PJON_FAIL; + } + + memcpy(out_mac_address, mac_address, ESP_NOW_MAC_LENGTH); + memcpy(out_data, data + ESPNOW_PACKET_HEADER_LENGTH, len); + + return len; + } +}; /** * A queue of incoming packets of max capacity PJON_MAX_PACKETS. @@ -28,58 +66,61 @@ typedef struct */ class PacketQueue { - private: - // 1 extra capacity is necessary to distinguisch between the queue being full and empty - espnow_packet_t* queue[PJON_MAX_PACKETS + 1]; - volatile uint8_t firstElement = 0; - volatile uint8_t firstSpace = 0; - - public: - /** - * Adds a packet to the queue. - * @return true if successfull, false if the queue was full and the packet was not added. - */ - bool push(espnow_packet_t* packet); - - /** - * Remove the first packet from the queue and return it. - * @return first packet or NULL if the queue is empty - */ - espnow_packet_t* pop(); + private: + // 1 extra capacity is necessary to distinguisch between the queue being full and empty + EspNowPacket queue[PJON_MAX_PACKETS + 1]; + volatile uint8_t firstElement = 0; + volatile uint8_t firstSpace = 0; + + public: + /** + * Adds a packet to the queue. + * @return true if successfull, false if the queue was full and the packet was not added. + */ + bool push(const uint8_t *mac_addr, const uint8_t *data, int len); + + /** + * Remove the first packet from the queue, check it for length, remove ESPNOW headers and return it. + * @param out_mac_address - pass in a buffer, the method will copy the sender's MAC address into it. + * @param out_data - pass in an allocated buffer, the method will copy the data into it. + * @param max_length - length of the allocated buffer. + * @return length of the data or PJON_FAIL in case the queue is empty or there was an error + */ + uint16_t pop(uint8_t *out_mac_address, uint8_t *out_data, uint16_t max_length); }; -bool PacketQueue::push(espnow_packet_t* packet) +bool PacketQueue::push(const uint8_t *mac_addr, const uint8_t *data, int len) { - bool isFull; - - ATOMIC() - { - int firstSpacePlus1 = (firstSpace + 1) % (PJON_MAX_PACKETS + 1); - isFull = firstSpacePlus1 == firstElement; - if (!isFull) - { - queue[firstSpace] = packet; - firstSpace = firstSpacePlus1; - } - } - - return !isFull; + bool isFull; + + ATOMIC() + { + int firstSpacePlus1 = (firstSpace + 1) % (PJON_MAX_PACKETS + 1); + isFull = firstSpacePlus1 == firstElement; + if (!isFull) + { + queue[firstSpace].set(mac_addr, data, len); + firstSpace = firstSpacePlus1; + } + } + + return !isFull; } -espnow_packet_t* PacketQueue::pop() { - if (firstElement == firstSpace) { - return NULL; - } - else - { - espnow_packet_t* packet; - - ATOMIC() - { - packet = queue[firstElement]; - firstElement = (firstElement + 1) % (PJON_MAX_PACKETS + 1); - } - - return packet; - } +uint16_t PacketQueue::pop(uint8_t *out_mac_address, uint8_t *out_data, uint16_t max_length) { + if (firstElement == firstSpace) { + return PJON_FAIL; + } + else + { + uint16_t length; + + ATOMIC() + { + length = queue[firstElement].checkAndGet(out_mac_address, out_data, max_length); + firstElement = (firstElement + 1) % (PJON_MAX_PACKETS + 1); + } + + return length; + } } \ No newline at end of file diff --git a/src/strategies/ESPNOW/ESPNOW.h b/src/strategies/ESPNOW/ESPNOW.h index 1cce4ed26e..6eee627fa5 100644 --- a/src/strategies/ESPNOW/ESPNOW.h +++ b/src/strategies/ESPNOW/ESPNOW.h @@ -36,7 +36,7 @@ #define EN_RECEIVE_TIME 0 #endif -#define EN_MAGIC_HEADER (uint8_t*)"\xEE\xFE\x0E\xEF" + class ESPNOW { bool _espnow_initialised = false; @@ -54,7 +54,6 @@ class ESPNOW { bool check_en() { if(!_espnow_initialised) { - en.set_magic_header(EN_MAGIC_HEADER); if(en.begin(_channel,(uint8_t*)_espnow_pmk)) _espnow_initialised = true; } From 975da999db24257f23e70ec9003b563900f4d5e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Tue, 11 Aug 2020 09:29:28 +0200 Subject: [PATCH 08/13] Removing dependency on simply atomic --- src/interfaces/ARDUINO/ESPNOWHelper.h | 12 +++++++----- src/interfaces/ARDUINO/PacketQueue.h | 8 +++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/interfaces/ARDUINO/ESPNOWHelper.h b/src/interfaces/ARDUINO/ESPNOWHelper.h index a80ecc40eb..31f57207a2 100644 --- a/src/interfaces/ARDUINO/ESPNOWHelper.h +++ b/src/interfaces/ARDUINO/ESPNOWHelper.h @@ -163,13 +163,15 @@ class ENHelper { packet[len + 2] = ESP_NOW_MAGIC_HEADER[2] ^ len; packet[len + 3] = ESP_NOW_MAGIC_HEADER[3] ^ len; - ATOMIC() - { - sendingDone = false; - } + noInterrupts(); + { + sendingDone = false; + } + interrupts(); + if(esp_now_send(dest_mac, packet, len + ESPNOW_PACKET_HEADER_LENGTH + ESPNOW_PACKET_FOOTER_LENGTH) == 0) { do { // wait for sending finished interrupt to be called - yield(); + yield(); } while(!sendingDone); } }; diff --git a/src/interfaces/ARDUINO/PacketQueue.h b/src/interfaces/ARDUINO/PacketQueue.h index 0f9c2819c5..10024c2bad 100644 --- a/src/interfaces/ARDUINO/PacketQueue.h +++ b/src/interfaces/ARDUINO/PacketQueue.h @@ -7,7 +7,7 @@ #include #endif -#include // https://github.com/wizard97/SimplyAtomic +#include #include "PJONDefines.h" #define ESP_NOW_MAC_LENGTH 6 @@ -93,7 +93,7 @@ bool PacketQueue::push(const uint8_t *mac_addr, const uint8_t *data, int len) { bool isFull; - ATOMIC() + noInterrupts(); { int firstSpacePlus1 = (firstSpace + 1) % (PJON_MAX_PACKETS + 1); isFull = firstSpacePlus1 == firstElement; @@ -103,6 +103,7 @@ bool PacketQueue::push(const uint8_t *mac_addr, const uint8_t *data, int len) firstSpace = firstSpacePlus1; } } + interrupts(); return !isFull; } @@ -115,11 +116,12 @@ uint16_t PacketQueue::pop(uint8_t *out_mac_address, uint8_t *out_data, uint16_t { uint16_t length; - ATOMIC() + noInterrupts(); { length = queue[firstElement].checkAndGet(out_mac_address, out_data, max_length); firstElement = (firstElement + 1) % (PJON_MAX_PACKETS + 1); } + interrupts(); return length; } From 77d616ee49f3d8da9a7df45ebe32d2684696c9d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Wed, 11 Nov 2020 17:14:45 +0100 Subject: [PATCH 09/13] Non-working attempt to fix encryption --- src/interfaces/ARDUINO/ESPNOWHelper.h | 68 ++++++++++++++++----------- src/interfaces/ARDUINO/PacketQueue.h | 6 +-- src/strategies/ESPNOW/ESPNOW.h | 11 +++-- 3 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/interfaces/ARDUINO/ESPNOWHelper.h b/src/interfaces/ARDUINO/ESPNOWHelper.h index 31f57207a2..8eb232a299 100644 --- a/src/interfaces/ARDUINO/ESPNOWHelper.h +++ b/src/interfaces/ARDUINO/ESPNOWHelper.h @@ -59,14 +59,15 @@ static void espnow_send_cb(const uint8_t *mac_addr, uint8_t status) { }; static void espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len) { - // Add the packet to the incoming queue. - // no error checking, if queue is full just ignore the packet. + // Add the packet to the incoming queue. + // no error checking, if queue is full just ignore the packet. packetQueue.push(mac_addr, data, len); }; class ENHelper { uint8_t _channel = 14; uint8_t _esp_pmk[16]; + boolean _enable_encryption = false; public: void add_node_mac(uint8_t mac_addr[ESP_NOW_MAC_LENGTH]) { @@ -77,25 +78,33 @@ class ENHelper { if(esp_now_is_peer_exist(mac_addr)) return; - #if defined(ESP8266) // TODO: Fix encryption! ESP_NOW_ROLE_SLAVE doesn't seem to matter. From the doc: The peer's Role does not affect any function, but only stores the Role information for the application layer. - esp_now_add_peer(mac_addr, ESP_NOW_ROLE_SLAVE,_channel, NULL, 0); - - #elif defined(ESP32) // TODO: Why malloc a local variable? Add broadcast peer information to peer list. - esp_now_peer_info_t *peer = (esp_now_peer_info_t *)malloc(sizeof(esp_now_peer_info_t)); - - memset(peer, 0, sizeof(esp_now_peer_info_t)); - peer->channel = _channel; - peer->ifidx = ESPNOW_WIFI_IF; - if( (memcmp(mac_addr, espnow_broadcast_mac, ESP_NOW_MAC_LENGTH) == 0)) - peer->encrypt = false; - // else { peer->encrypt = true; memcpy(peer->lmk, _esp_pmk, 16); } - memcpy(peer->peer_addr, mac_addr, ESP_NOW_MAC_LENGTH); - esp_now_add_peer(peer); - free(peer); + bool isBroadcast = memcmp(mac_addr, espnow_broadcast_mac, ESP_NOW_MAC_LENGTH) == 0; + + #if defined(ESP8266) + if (_enable_encryption && !isBroadcast) + esp_now_add_peer(mac_addr, ESP_NOW_ROLE_SLAVE,_channel, _esp_pmk, 16); + else + esp_now_add_peer(mac_addr, ESP_NOW_ROLE_SLAVE,_channel, NULL, 0); + + #elif defined(ESP32) + esp_now_peer_info_t peer; + + memset(&peer, 0, sizeof(esp_now_peer_info_t)); + peer.channel = _channel; + peer.ifidx = ESPNOW_WIFI_IF; + if (_enable_encryption && !isBroadcast) { + peer.encrypt = true; + memcpy(peer.lmk, _esp_pmk, 16); + } + else { + peer.encrypt = false; + } + memcpy(peer.peer_addr, mac_addr, ESP_NOW_MAC_LENGTH); + esp_now_add_peer(&peer); #endif }; - bool begin(uint8_t channel, uint8_t *espnow_pmk) { + bool begin(uint8_t channel, boolean enable_encryption, uint8_t *espnow_pmk) { #if defined(ESP32) // TODO: Is this necessary? esp_err_t ret = nvs_flash_init(); if(ret == ESP_ERR_NVS_NO_FREE_PAGES) { @@ -106,6 +115,8 @@ class ENHelper { #endif _channel = channel; + + _enable_encryption = enable_encryption; memcpy(_esp_pmk, espnow_pmk, 16); #ifdef ESP8266 @@ -115,6 +126,8 @@ class ENHelper { esp_now_set_self_role(ESPNOW_WIFI_ROLE); esp_now_register_send_cb(reinterpret_cast(espnow_send_cb)); esp_now_register_recv_cb(reinterpret_cast(espnow_recv_cb)); + if (_enable_encryption) + esp_now_set_kok(_esp_pmk, 16); #else tcpip_adapter_init(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); @@ -131,7 +144,8 @@ class ENHelper { esp_now_init(); esp_now_register_send_cb(reinterpret_cast(espnow_send_cb)); esp_now_register_recv_cb(reinterpret_cast(espnow_recv_cb)); - ESP_ERROR_CHECK(esp_now_set_pmk(_esp_pmk)); + if (_enable_encryption) + ESP_ERROR_CHECK(esp_now_set_pmk(_esp_pmk)); #endif add_peer(espnow_broadcast_mac); // Add broadcast peer information to peer list @@ -145,12 +159,12 @@ class ENHelper { void send_frame(uint8_t *data, uint16_t length, uint8_t dest_mac[ESP_NOW_MAC_LENGTH]) { if (!sendingDone) { - // we are still sending the previous frame - discard this one + // we are still sending the previous frame - discard this one return; } - if (length > PJON_PACKET_MAX_LENGTH) { - // the data is too long + if (length > PJON_PACKET_MAX_LENGTH) { + // the data is too long return; } @@ -163,11 +177,11 @@ class ENHelper { packet[len + 2] = ESP_NOW_MAGIC_HEADER[2] ^ len; packet[len + 3] = ESP_NOW_MAGIC_HEADER[3] ^ len; - noInterrupts(); - { - sendingDone = false; - } - interrupts(); + noInterrupts(); + { + sendingDone = false; + } + interrupts(); if(esp_now_send(dest_mac, packet, len + ESPNOW_PACKET_HEADER_LENGTH + ESPNOW_PACKET_FOOTER_LENGTH) == 0) { do { // wait for sending finished interrupt to be called diff --git a/src/interfaces/ARDUINO/PacketQueue.h b/src/interfaces/ARDUINO/PacketQueue.h index 10024c2bad..10f422ca82 100644 --- a/src/interfaces/ARDUINO/PacketQueue.h +++ b/src/interfaces/ARDUINO/PacketQueue.h @@ -55,7 +55,7 @@ class EspNowPacket memcpy(out_mac_address, mac_address, ESP_NOW_MAC_LENGTH); memcpy(out_data, data + ESPNOW_PACKET_HEADER_LENGTH, len); - return len; + return len; } }; @@ -103,7 +103,7 @@ bool PacketQueue::push(const uint8_t *mac_addr, const uint8_t *data, int len) firstSpace = firstSpacePlus1; } } - interrupts(); + interrupts(); return !isFull; } @@ -121,7 +121,7 @@ uint16_t PacketQueue::pop(uint8_t *out_mac_address, uint8_t *out_data, uint16_t length = queue[firstElement].checkAndGet(out_mac_address, out_data, max_length); firstElement = (firstElement + 1) % (PJON_MAX_PACKETS + 1); } - interrupts(); + interrupts(); return length; } diff --git a/src/strategies/ESPNOW/ESPNOW.h b/src/strategies/ESPNOW/ESPNOW.h index 6eee627fa5..96ace11789 100644 --- a/src/strategies/ESPNOW/ESPNOW.h +++ b/src/strategies/ESPNOW/ESPNOW.h @@ -42,8 +42,8 @@ class ESPNOW { bool _espnow_initialised = false; bool _auto_registration = true; uint8_t _channel = 14; - char _espnow_pmk[17] = - "\xdd\xdb\xdd\x44\x34\xd5\x6a\x0b\x7e\x9f\x4e\x27\xd6\x5b\xa2\x81"; + bool _enable_encryption = false; + uint8_t _espnow_pmk[17]; // Remote nodes uint8_t _remote_node_count = 0; @@ -54,7 +54,7 @@ class ESPNOW { bool check_en() { if(!_espnow_initialised) { - if(en.begin(_channel,(uint8_t*)_espnow_pmk)) + if(en.begin(_channel, _enable_encryption, _espnow_pmk)) _espnow_initialised = true; } return _espnow_initialised; @@ -69,7 +69,7 @@ class ESPNOW { void autoregister_sender(const uint8_t *message, uint16_t length) { // Add the last sender to the node table - if(_auto_registration && length>4) { + if(_auto_registration && length > 4) { // First get PJON sender id from incoming packet PJON_Packet_Info packet_info; PJONTools::parse_header(message, packet_info); @@ -112,7 +112,8 @@ class ESPNOW { public: - void set_pmk(char *espnow_pmk) { + void enable_encryption(char *espnow_pmk) { + _enable_encryption = true; memcpy(_espnow_pmk, espnow_pmk, 16); } From 6634e02fe33de056badff6b3e72f63ff8cdcc6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Wed, 11 Nov 2020 17:17:08 +0100 Subject: [PATCH 10/13] Revert "Non-working attempt to fix encryption" This reverts commit 77d616ee49f3d8da9a7df45ebe32d2684696c9d7. --- src/interfaces/ARDUINO/ESPNOWHelper.h | 68 +++++++++++---------------- src/interfaces/ARDUINO/PacketQueue.h | 6 +-- src/strategies/ESPNOW/ESPNOW.h | 11 ++--- 3 files changed, 35 insertions(+), 50 deletions(-) diff --git a/src/interfaces/ARDUINO/ESPNOWHelper.h b/src/interfaces/ARDUINO/ESPNOWHelper.h index 8eb232a299..31f57207a2 100644 --- a/src/interfaces/ARDUINO/ESPNOWHelper.h +++ b/src/interfaces/ARDUINO/ESPNOWHelper.h @@ -59,15 +59,14 @@ static void espnow_send_cb(const uint8_t *mac_addr, uint8_t status) { }; static void espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len) { - // Add the packet to the incoming queue. - // no error checking, if queue is full just ignore the packet. + // Add the packet to the incoming queue. + // no error checking, if queue is full just ignore the packet. packetQueue.push(mac_addr, data, len); }; class ENHelper { uint8_t _channel = 14; uint8_t _esp_pmk[16]; - boolean _enable_encryption = false; public: void add_node_mac(uint8_t mac_addr[ESP_NOW_MAC_LENGTH]) { @@ -78,33 +77,25 @@ class ENHelper { if(esp_now_is_peer_exist(mac_addr)) return; - bool isBroadcast = memcmp(mac_addr, espnow_broadcast_mac, ESP_NOW_MAC_LENGTH) == 0; - - #if defined(ESP8266) - if (_enable_encryption && !isBroadcast) - esp_now_add_peer(mac_addr, ESP_NOW_ROLE_SLAVE,_channel, _esp_pmk, 16); - else - esp_now_add_peer(mac_addr, ESP_NOW_ROLE_SLAVE,_channel, NULL, 0); - - #elif defined(ESP32) - esp_now_peer_info_t peer; - - memset(&peer, 0, sizeof(esp_now_peer_info_t)); - peer.channel = _channel; - peer.ifidx = ESPNOW_WIFI_IF; - if (_enable_encryption && !isBroadcast) { - peer.encrypt = true; - memcpy(peer.lmk, _esp_pmk, 16); - } - else { - peer.encrypt = false; - } - memcpy(peer.peer_addr, mac_addr, ESP_NOW_MAC_LENGTH); - esp_now_add_peer(&peer); + #if defined(ESP8266) // TODO: Fix encryption! ESP_NOW_ROLE_SLAVE doesn't seem to matter. From the doc: The peer's Role does not affect any function, but only stores the Role information for the application layer. + esp_now_add_peer(mac_addr, ESP_NOW_ROLE_SLAVE,_channel, NULL, 0); + + #elif defined(ESP32) // TODO: Why malloc a local variable? Add broadcast peer information to peer list. + esp_now_peer_info_t *peer = (esp_now_peer_info_t *)malloc(sizeof(esp_now_peer_info_t)); + + memset(peer, 0, sizeof(esp_now_peer_info_t)); + peer->channel = _channel; + peer->ifidx = ESPNOW_WIFI_IF; + if( (memcmp(mac_addr, espnow_broadcast_mac, ESP_NOW_MAC_LENGTH) == 0)) + peer->encrypt = false; + // else { peer->encrypt = true; memcpy(peer->lmk, _esp_pmk, 16); } + memcpy(peer->peer_addr, mac_addr, ESP_NOW_MAC_LENGTH); + esp_now_add_peer(peer); + free(peer); #endif }; - bool begin(uint8_t channel, boolean enable_encryption, uint8_t *espnow_pmk) { + bool begin(uint8_t channel, uint8_t *espnow_pmk) { #if defined(ESP32) // TODO: Is this necessary? esp_err_t ret = nvs_flash_init(); if(ret == ESP_ERR_NVS_NO_FREE_PAGES) { @@ -115,8 +106,6 @@ class ENHelper { #endif _channel = channel; - - _enable_encryption = enable_encryption; memcpy(_esp_pmk, espnow_pmk, 16); #ifdef ESP8266 @@ -126,8 +115,6 @@ class ENHelper { esp_now_set_self_role(ESPNOW_WIFI_ROLE); esp_now_register_send_cb(reinterpret_cast(espnow_send_cb)); esp_now_register_recv_cb(reinterpret_cast(espnow_recv_cb)); - if (_enable_encryption) - esp_now_set_kok(_esp_pmk, 16); #else tcpip_adapter_init(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); @@ -144,8 +131,7 @@ class ENHelper { esp_now_init(); esp_now_register_send_cb(reinterpret_cast(espnow_send_cb)); esp_now_register_recv_cb(reinterpret_cast(espnow_recv_cb)); - if (_enable_encryption) - ESP_ERROR_CHECK(esp_now_set_pmk(_esp_pmk)); + ESP_ERROR_CHECK(esp_now_set_pmk(_esp_pmk)); #endif add_peer(espnow_broadcast_mac); // Add broadcast peer information to peer list @@ -159,12 +145,12 @@ class ENHelper { void send_frame(uint8_t *data, uint16_t length, uint8_t dest_mac[ESP_NOW_MAC_LENGTH]) { if (!sendingDone) { - // we are still sending the previous frame - discard this one + // we are still sending the previous frame - discard this one return; } - if (length > PJON_PACKET_MAX_LENGTH) { - // the data is too long + if (length > PJON_PACKET_MAX_LENGTH) { + // the data is too long return; } @@ -177,11 +163,11 @@ class ENHelper { packet[len + 2] = ESP_NOW_MAGIC_HEADER[2] ^ len; packet[len + 3] = ESP_NOW_MAGIC_HEADER[3] ^ len; - noInterrupts(); - { - sendingDone = false; - } - interrupts(); + noInterrupts(); + { + sendingDone = false; + } + interrupts(); if(esp_now_send(dest_mac, packet, len + ESPNOW_PACKET_HEADER_LENGTH + ESPNOW_PACKET_FOOTER_LENGTH) == 0) { do { // wait for sending finished interrupt to be called diff --git a/src/interfaces/ARDUINO/PacketQueue.h b/src/interfaces/ARDUINO/PacketQueue.h index 10f422ca82..10024c2bad 100644 --- a/src/interfaces/ARDUINO/PacketQueue.h +++ b/src/interfaces/ARDUINO/PacketQueue.h @@ -55,7 +55,7 @@ class EspNowPacket memcpy(out_mac_address, mac_address, ESP_NOW_MAC_LENGTH); memcpy(out_data, data + ESPNOW_PACKET_HEADER_LENGTH, len); - return len; + return len; } }; @@ -103,7 +103,7 @@ bool PacketQueue::push(const uint8_t *mac_addr, const uint8_t *data, int len) firstSpace = firstSpacePlus1; } } - interrupts(); + interrupts(); return !isFull; } @@ -121,7 +121,7 @@ uint16_t PacketQueue::pop(uint8_t *out_mac_address, uint8_t *out_data, uint16_t length = queue[firstElement].checkAndGet(out_mac_address, out_data, max_length); firstElement = (firstElement + 1) % (PJON_MAX_PACKETS + 1); } - interrupts(); + interrupts(); return length; } diff --git a/src/strategies/ESPNOW/ESPNOW.h b/src/strategies/ESPNOW/ESPNOW.h index 96ace11789..6eee627fa5 100644 --- a/src/strategies/ESPNOW/ESPNOW.h +++ b/src/strategies/ESPNOW/ESPNOW.h @@ -42,8 +42,8 @@ class ESPNOW { bool _espnow_initialised = false; bool _auto_registration = true; uint8_t _channel = 14; - bool _enable_encryption = false; - uint8_t _espnow_pmk[17]; + char _espnow_pmk[17] = + "\xdd\xdb\xdd\x44\x34\xd5\x6a\x0b\x7e\x9f\x4e\x27\xd6\x5b\xa2\x81"; // Remote nodes uint8_t _remote_node_count = 0; @@ -54,7 +54,7 @@ class ESPNOW { bool check_en() { if(!_espnow_initialised) { - if(en.begin(_channel, _enable_encryption, _espnow_pmk)) + if(en.begin(_channel,(uint8_t*)_espnow_pmk)) _espnow_initialised = true; } return _espnow_initialised; @@ -69,7 +69,7 @@ class ESPNOW { void autoregister_sender(const uint8_t *message, uint16_t length) { // Add the last sender to the node table - if(_auto_registration && length > 4) { + if(_auto_registration && length>4) { // First get PJON sender id from incoming packet PJON_Packet_Info packet_info; PJONTools::parse_header(message, packet_info); @@ -112,8 +112,7 @@ class ESPNOW { public: - void enable_encryption(char *espnow_pmk) { - _enable_encryption = true; + void set_pmk(char *espnow_pmk) { memcpy(_espnow_pmk, espnow_pmk, 16); } From 71eed33ebd206a85354d197d838ea55991261094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Mon, 16 Nov 2020 17:17:38 +0100 Subject: [PATCH 11/13] Fixing initialization of wifi channel --- src/interfaces/ARDUINO/ESPNOWHelper.h | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/interfaces/ARDUINO/ESPNOWHelper.h b/src/interfaces/ARDUINO/ESPNOWHelper.h index 31f57207a2..6d194e47b0 100644 --- a/src/interfaces/ARDUINO/ESPNOWHelper.h +++ b/src/interfaces/ARDUINO/ESPNOWHelper.h @@ -109,8 +109,19 @@ class ENHelper { memcpy(_esp_pmk, espnow_pmk, 16); #ifdef ESP8266 - WiFi.mode(ESPNOW_WIFI_MODE); - wifi_set_channel(_channel); + WiFi.persistent(false); + + if (ESPNOW_WIFI_MODE == WIFI_AP) { + WiFi.mode(WIFI_AP); + WiFi.softAP("ESPNOW", nullptr, _channel); + WiFi.softAPdisconnect(false); + } + else { + WiFi.mode(WIFI_STA); + WiFi.begin("ESPNOW", nullptr, _channel); + WiFi.disconnect(false); + } + esp_now_init(); esp_now_set_self_role(ESPNOW_WIFI_ROLE); esp_now_register_send_cb(reinterpret_cast(espnow_send_cb)); From c2945ce74701e55da8c3d186f8c72bc77df5582d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Sat, 27 Aug 2022 15:21:21 +0200 Subject: [PATCH 12/13] Update README.md --- README.md | 73 ++++--------------------------------------------------- 1 file changed, 5 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 666738b44b..4be4b1bb4f 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,7 @@ +#PJON -![PJON](http://www.gioblu.com/PJON/PJON-github-header-tiny.png) -## PJON 12.1 -PJON® (Padded Jittering Operative Network) is an arduino-compatible, multi-master, multi-media network protocol. It proposes a new Open Standard, it is designed as a framework and implements a totally software-defined network protocol stack that can be easily cross-compiled on many MCUs and architectures like ATtiny, ATmega, SAMD, ESP8266, ESP32, STM32, Teensy, Raspberry Pi, Linux, Windows x86, Apple and Android. PJON operates on a wide range of media and protocols like PJDL, PJDLR, PJDLS, Serial, RS485, USB, ASK/FSK, LoRa, UDP, TCP, MQTT and ESPNOW. +This fork of [PJON](http://www.gioblu.com/PJON/PJON-github-header-tiny.png) aimed to add support of ESP-NOW strategy to esp8266 microcontroller. -[![Get PJON bus id](https://img.shields.io/badge/get-PJON%20bus%20id-lightgrey.svg)](http://www.pjon.org/get-bus-id.php) -[![Join the chat at https://gitter.im/gioblu/PJON](https://badges.gitter.im/gioblu/PJON.svg)](https://gitter.im/gioblu/PJON?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -PJON is used in thousands of devices and its community has spread worldwide because of the following 5 key factors: -- **New technology**: [PJON](specification/PJON-protocol-specification-v4.0.md) is an experimental network protocol stack crafted in 10 years of research and experimentation. It was originally developed as an open-source alternative to i2c and 1-Wire but during development its scope and features have been extended to cover use cases where IP is generally applied. PJON has been engineered to have a variable footprint (4.2-8.2 kB program memory) and overhead (5-35 bytes per packet) depending on its configuration. -- **Multi-media support**: PJON operates upon a wide range of protocols like TCP, UDP, MQTT, ESPNOW, USB, Serial, RS485 and LoRa. The PJON network protocol stack specifies and implements also [PJDL](src/strategies/SoftwareBitBang/specification/PJDL-specification-v5.0.md) that operates over a single wire of up to 2000m shared by up to 255 devices, [PJDLR](src/strategies/OverSampling/specification/PJDLR-specification-v3.0.md) that operates with many ASK/FSK/OOK radio modules, and also [PJDLS](src/strategies/AnalogSampling/specification/PJDLS-specification-v2.0.md) that operates wirelessly with light pulses using off-the-shelf LEDs and laser diodes. -- **Increased reliability**: Many protocols massively applied worldwide expose dangerous vulnerabilities, have weak error detection algorithms and are not resilient to interference. PJON is based on years of analysis and study not to make the same mistakes present in most alternatives and provide with a simpler and more efficient solution. -- **High flexibility**: PJON is totally software-defined and it is designed to be easily extensible. it builds out-of-the-box in all supported devices and operates transparently on top of any supported protocol or medium. -- **Low cost**: Without any additional hardware needed to operate, minimal network wiring requirements and direct pin-to-pin or LED-to-LED communication, PJON is extremely energy efficient, cheap to be implemented and maintained. This implementation is kept updated and meticulously tested thanks to the strong commitment of its growing community of end users, testers and developers. - -### Features -- Cross-compilation support with the [interfaces](src/interfaces) system calls abstraction -- Multi-media support with the [strategies](src/strategies) data link layer abstraction -- Modular packet format that includes only the field used (overhead 5-35 bytes) -- Hot-swap support, no need of system reset or shut down when replacing or adding devices -- Flexible local (device id) and shared (bus id) network identification -- Safe error detection done with modern CRC8 and CRC32 polynomials -- Optional inclusion of MAC addresses -- Optional acknowledgement -- Error handling - -### Specifications -- [PJON v4.0](specification/PJON-protocol-specification-v4.0.md) -- [PJDL v5.0](src/strategies/SoftwareBitBang/specification/PJDL-specification-v5.0.md) -- [PJDLR v3.0](src/strategies/OverSampling/specification/PJDLR-specification-v3.0.md) -- [PJDLS v2.0](src/strategies/AnalogSampling/specification/PJDLS-specification-v2.0.md) -- [TSDL v3.0](src/strategies/ThroughSerial/specification/TSDL-specification-v3.0.md) - -### Documentation -- [Addressing](/documentation/addressing.md) -- [Configuration](/documentation/configuration.md) -- [Data reception](/documentation/data-reception.md) -- [Data structures](/documentation/data-structures.md) -- [Data transmission](/documentation/data-transmission.md) -- [Error handling](/documentation/error-handling.md) -- [Routing](/documentation/routing.md) -- [IO setup](/documentation/io-setup.md) - -### Publications -- [PJON protocol handbook](https://www.pjon-technologies.com/collections/frontpage/products/pjon-protocol-hand-book) by Giovanni Blu Mitolo - Distributed by [PJON Technologies srl](https://www.pjon-technologies.com) -- [PJON 12.0 big box](https://www.pjon-technologies.com/collections/frontpage/products/pjon-protocol-12-0-big-box) by Giovanni Blu Mitolo - Distributed by [PJON Technologies srl](https://www.pjon-technologies.com) - -### Academic studies -Researchers are active in many universities worldwide using PJON in different environments. The following list contains all the known published academic studies about PJON: -- [Definition and Application of PJON-PLC for sensor networks](https://repositorio.unican.es/xmlui/bitstream/handle/10902/14012/408952.pdf?sequence=1) by Jorge Gómez Segurola, Ingeniería de Tecnologías de -Telecomunicación - [Universidad de Cantabria](https://web.unican.es/) (ES) -- [Biomimetic electronics](http://c.harl.ie/biomimetic.html) by Charlie Williams with scientific input from researchers Vítor Martins dos Santos, Diana Machado de Sousa and Sabine Vreeburg - Artist in Residency at [Wageningen University](https://www.wur.nl/en.htm) (NL) - -### Contribute -Feel free to send a pull request sharing something you have made that could help, if you want to support this project you can also try to solve an [issue](https://github.com/gioblu/PJON/issues). Thanks to support, expertise, kindness and talent of the following contributors, the protocol's documentation, specification and implementation have been strongly tested, enhanced and verified: - -[Fred Larsen](https://github.com/fredilarsen), [Zbigniew Zasieczny](https://github.com/girgitt), [Matheus Garbelini](https://github.com/Matheus-Garbelini), [sticilface](https://github.com/sticilface), [Felix Barbalet](https://github.com/xlfe), [Oleh Halitskiy](https://github.com/Halytskyi), [fotosettore](https://github.com/fotosettore), [fabpolli](https://github.com/fabpolli), [Adrian Sławiński](https://github.com/4ib3r), [Osman Selçuk Aktepe](https://github.com/osman-aktepe), [Jorgen-VikingGod](https://github.com/Jorgen-VikingGod), [drtrigon](https://github.com/drtrigon), [Endre Karlson](https://github.com/ekarlso), [Wilfried Klaas](https://github.com/willie68), [budaics](https://github.com/budaics), [ibantxo](https://github.com/ibantxo), [gonnavis](https://github.com/gonnavis), [maxidroms83](https://github.com/maxidroms83), [Evgeny Dontsov](https://github.com/dontsovcmc), [zcattacz](https://github.com/zcattacz), [Valerii Koval](https://github.com/valeros), [Ivan Kravets](https://github.com/ivankravets), [Esben Soeltoft](https://github.com/EsbenSoeltoft), [Alex Grishin](https://github.com/240974a), [Andrew Grande](https://github.com/aperepel), [Michael Teeww](https://github.com/MichMich), [Paolo Paolucci](https://github.com/PaoloP74), [per1234](https://github.com/per1234), [Santiago Castro](https://github.com/bryant1410), [pacproduct](https://github.com/pacproduct), [elusive-code](https://github.com/elusive-code), [Emanuele Iannone](https://github.com/eiannone), [Christian Pointner](https://github.com/equinox0815), [Fabian Gärtner](https://github.com/TeeTrizZz), [Mauro Mombelli](https://github.com/MauroMombelli), [Remo Kallio](https://github.com/shacal), [hyndruide](https://github.com/hyndruide), [sigmaeo](https://github.com/sigmaeo), [filogranaf](https://github.com/filogranaf), [Maximiliano Duarte](https://github.com/domonetic), [Viktor Szépe](https://github.com/szepeviktor), [Shachar Limor](), [Pantovich](), [Mauro Zancarlin](), [Franketto](), [jzobac](), [DanRoad](), [fcastrovilli](https://github.com/fcastrovilli), [Andrei Volkau](https://github.com/andrei-volkau), [maniekq](https://github.com/maniekq), [DetAtHome](https://github.com/DetAtHome), [Michael Branson](https://github.com/mxbranson), [chestwood96](https://github.com/chestwood96), [Mattze96](https://github.com/Mattze96), [Steven Bense](https://github.com/justoke), [Jack Anderson](https://github.com/jdaandersj), [callalilychen](https://github.com/callalilychen) and [Julio Aguirre](https://github.com/jcallano). - -### Compliant tools -- [ModuleInterface](https://github.com/fredilarsen/ModuleInterface) - easy config and value sync between IoT modules by Fred Larsen -- [PJON-cython](https://github.com/xlfe/PJON-cython) - cython PJON wrapper by xlfe github user -- [PJON-piper](https://github.com/Girgitt/PJON-piper) - command line wrapper by Zbigniew Zasieczny -- [PJON-python](https://github.com/Girgitt/PJON-python) - python interface by Zbigniew Zasieczny -- [PJON-gRPC](https://github.com/Halytskyi/PJON-gRPC) - gRPC server-client by Oleh Halytskyi - -### License -All the software included in this project is experimental and it is distributed "AS IS" without any warranty, use it at your own risk. [Licensed](https://github.com/gioblu/PJON/blob/master/LICENSE.md) under the Apache License, Version 2.0. PJON® and its brand are registered trademarks, property of Giovanni Blu Mitolo gioscarab@gmail.com - -### Safety warning -When installing or maintaining a PJON network, extreme care must be taken to avoid any danger. If devices are connected to AC power you are exposed to a high chance of being electrocuted if hardware is not installed carefully and properly. If you are not experienced enough ask the support of a skilled technician and consider that many countries prohibit uncertified installations. When a [SoftwareBitBang](/src/strategies/SoftwareBitBang) bus is installed [interference mitigation](https://github.com/gioblu/PJON/wiki/Mitigate-interference) and [protective circuitry](https://github.com/gioblu/PJON/wiki/Protective-circuitry) guidelines must be followed. When working with an [AnalogSampling](/src/strategies/AnalogSampling) LED or laser based setup safety glasses must be worn and transceivers must be operated cautiously to avoid potential eye injuries. Before any practical test or a hardware purchase for a wireless [OverSampling](/src/strategies/OverSampling), [ThroughSerial](/src/strategies/ThroughSerial) or [ThroughLoRa](/src/strategies/ThroughLoRa) radio setup, compliance with government requirements and regulations must be ensured. When connecting a local bus to the internet all devices must be considered potentially compromised, manipulated or remotely actuated against your will. It should be considered a good practice not to connect to the internet systems that may create a damage (fire, flood, data-leak) if hacked. +Originally, I needed this for my project, but then I changed the approach in my project and this was no longer a priority for me. +Realistically, I will not finish this. Sorry. It's a good news that it is possible to make ESP-NOW work on esp8266. If anyone needs +it in the future, this repo and the [associated pull request](https://github.com/gioblu/PJON/pull/361) can be used as a rough guide for a new implementation. From 444c099a0e1223cbfb2b9124f1c5839325073faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0imonek?= Date: Sat, 27 Aug 2022 15:21:29 +0200 Subject: [PATCH 13/13] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4be4b1bb4f..1599c00c0e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -#PJON +# PJON This fork of [PJON](http://www.gioblu.com/PJON/PJON-github-header-tiny.png) aimed to add support of ESP-NOW strategy to esp8266 microcontroller.