Browse Source

ESP-NOW to mesh bridge

pull/2568/head
Christos Themelis 5 months ago
parent
commit
c45a35b210
  1. 49
      boards/esp32-s3-devkitc-1-myboard.json
  2. 6
      elecrow_partitions.csv
  3. 4
      examples/simple_secure_chat/main.cpp
  4. 91
      src/helpers/esp32/ESPNOWRadio.cpp
  5. 31
      variants/elecrow_espnow/platformio.ini
  6. 44
      variants/elecrow_espnow/target.cpp
  7. 16
      variants/elecrow_espnow/target.h
  8. 2
      variants/generic-e22/variant.h
  9. 2
      variants/heltec_v4/platformio.ini
  10. 2
      variants/meshadventurer/variant.h

49
boards/esp32-s3-devkitc-1-myboard.json

@ -0,0 +1,49 @@
{
"build": {
"arduino":{
"ldscript": "esp32s3_out.ld",
"partitions": "elecrow_partitions.csv"
},
"core": "esp32",
"extra_flags": [
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0x303A",
"0x1001"
]
],
"mcu": "esp32s3",
"variant": "esp32s3"
},
"connectivity": [
"wifi"
],
"debug": {
"default_tool": "esp-builtin",
"onboard_tools": [
"esp-builtin"
],
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "Espressif ESP32-S3-DevKitC-1-N8 -ELECROW",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"require_upload_port": true,
"speed": 921600
},
"url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/user-guide-devkitc-1.html",
"vendor": "Espressif"
}

6
elecrow_partitions.csv

@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x300000,
spiffs, data, spiffs, 0x310000,0xE0000,
coredump, data, coredump,0x3F0000,0x10000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x5000
3 otadata data ota 0xe000 0x2000
4 app0 app ota_0 0x10000 0x300000
5 spiffs data spiffs 0x310000 0xE0000
6 coredump data coredump 0x3F0000 0x10000

4
examples/simple_secure_chat/main.cpp

@ -241,8 +241,10 @@ protected:
} }
void onCommandDataRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override { void onCommandDataRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const char *text) override {
MESH_DEBUG_PRINTLN("onCommandDataRecv");
} }
void onSignedMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) override { void onSignedMessageRecv(const ContactInfo& from, mesh::Packet* pkt, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) override {
MESH_DEBUG_PRINTLN("onSignedMessageRecv");
} }
void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) override { void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) override {
@ -255,10 +257,12 @@ protected:
} }
uint8_t onContactRequest(const ContactInfo& contact, uint32_t sender_timestamp, const uint8_t* data, uint8_t len, uint8_t* reply) override { uint8_t onContactRequest(const ContactInfo& contact, uint32_t sender_timestamp, const uint8_t* data, uint8_t len, uint8_t* reply) override {
MESH_DEBUG_PRINTLN("onContactRequest");
return 0; // unknown return 0; // unknown
} }
void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) override { void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) override {
MESH_DEBUG_PRINTLN("onContactResponse");
// not supported // not supported
} }

91
src/helpers/esp32/ESPNOWRadio.cpp

@ -3,12 +3,27 @@
#include <WiFi.h> #include <WiFi.h>
#include <esp_wifi.h> #include <esp_wifi.h>
static constexpr uint16_t BRIDGE_PACKET_MAGIC = 0xC03E;
static const size_t MAX_ESPNOW_PACKET_SIZE = 250;
/**
* @brief Common field sizes used by bridge implementations
*
* These constants define the size of common packet fields used across bridges.
* BRIDGE_MAGIC_SIZE is used by all bridges for packet identification.
* BRIDGE_LENGTH_SIZE is used by bridges that need explicit length fields (like RS232).
* BRIDGE_CHECKSUM_SIZE is used by all bridges for Fletcher-16 checksums.
*/
static constexpr uint16_t BRIDGE_MAGIC_SIZE = sizeof(BRIDGE_PACKET_MAGIC);
static constexpr uint16_t BRIDGE_LENGTH_SIZE = sizeof(uint16_t);
static constexpr uint16_t BRIDGE_CHECKSUM_SIZE = sizeof(uint16_t);
static uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; static uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
static esp_now_peer_info_t peerInfo; static esp_now_peer_info_t peerInfo;
static volatile bool is_send_complete = false; static volatile bool is_send_complete = false;
static esp_err_t last_send_result; static esp_err_t last_send_result;
static uint8_t rx_buf[256]; static uint8_t rx_buf[256];
static uint8_t last_rx_len = 0; static uint8_t last_rx_len = 0;
static char bridge_secret[16];
// callback when data is sent // callback when data is sent
static void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { static void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
@ -16,15 +31,56 @@ static void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
ESPNOW_DEBUG_PRINTLN("Send Status: %d", (int)status); ESPNOW_DEBUG_PRINTLN("Send Status: %d", (int)status);
} }
static void xorCrypt(uint8_t *data, size_t len) {
size_t keyLen = strlen(bridge_secret);
for (size_t i = 0; i < len; i++) {
data[i] ^= bridge_secret[i % keyLen];
}
}
static uint16_t fletcher16(const uint8_t *data, size_t len) {
uint8_t sum1 = 0, sum2 = 0;
for (size_t i = 0; i < len; i++) {
sum1 = (sum1 + data[i]) % 255;
sum2 = (sum2 + sum1) % 255;
}
return (sum2 << 8) | sum1;
}
static bool validateChecksum(const uint8_t *data, size_t len, uint16_t received_checksum) {
uint16_t calculated_checksum = fletcher16(data, len);
return received_checksum == calculated_checksum;
}
static void OnDataRecv(const uint8_t *mac, const uint8_t *data, int len) { static void OnDataRecv(const uint8_t *mac, const uint8_t *data, int len) {
ESPNOW_DEBUG_PRINTLN("Recv: len = %d", len); ESPNOW_DEBUG_PRINTLN("Recv: len = %d", len);
memcpy(rx_buf, data, len);
last_rx_len = len; if (len < 4) return;
uint16_t magic = (data[0] << 8) | data[1];
if (magic != BRIDGE_PACKET_MAGIC) return;
uint8_t decrypted[MAX_ESPNOW_PACKET_SIZE];
int encLen = len - 2; // everything except magic
memcpy(decrypted, data + 2, encLen);
xorCrypt(decrypted, encLen);
uint16_t rx_cksum = (decrypted[0] << 8) | decrypted[1];
uint8_t* meshPayload = decrypted + 2;
int meshLen = encLen - 2;
if (!validateChecksum(meshPayload, meshLen, rx_cksum)) return;
memcpy(rx_buf, meshPayload, meshLen);
last_rx_len = meshLen;
} }
void ESPNOWRadio::init() { void ESPNOWRadio::init() {
// Set device as a Wi-Fi Station // Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
strncpy(bridge_secret, "LVSITANOS", sizeof(bridge_secret));
// Long Range mode // Long Range mode
esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR); esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_LR);
@ -72,7 +128,36 @@ uint32_t ESPNOWRadio::intID() {
bool ESPNOWRadio::startSendRaw(const uint8_t* bytes, int len) { bool ESPNOWRadio::startSendRaw(const uint8_t* bytes, int len) {
// Send message via ESP-NOW // Send message via ESP-NOW
is_send_complete = false; is_send_complete = false;
esp_err_t result = esp_now_send(broadcastAddress, bytes, len); if (!bytes || len <= 0) {
Serial.println("Invalid raw mesh packet");
return false;
}
if (len > (MAX_ESPNOW_PACKET_SIZE - 4)) {
Serial.println("Mesh packet too large for ESP-NOW");
return false;
}
uint8_t out[MAX_ESPNOW_PACKET_SIZE];
// 1) magic
out[0] = (BRIDGE_PACKET_MAGIC >> 8) & 0xFF;
out[1] = BRIDGE_PACKET_MAGIC & 0xFF;
// 2) checksum (πάνω στο raw mesh packet)
uint16_t cksum = fletcher16(bytes, len);
out[2] = (cksum >> 8) & 0xFF;
out[3] = cksum & 0xFF;
// 3) payload = raw mesh packet
memcpy(out + 4, bytes, len);
// 4) encrypt checksum + payload
xorCrypt(out + 2, len + 2);
// 5) send via ESP-NOW
esp_err_t result = esp_now_send(broadcastAddress, out, len + 4);
if (result == ESP_OK) { if (result == ESP_OK) {
n_sent++; n_sent++;
ESPNOW_DEBUG_PRINTLN("Send success"); ESPNOW_DEBUG_PRINTLN("Send success");

31
variants/elecrow_espnow/platformio.ini

@ -0,0 +1,31 @@
; ----------- Elecrow Panels ESP32-S3 ------------
; No LoRa module - Only ESP-NOW
; Works with repeaters with esp_now
[Elecrow_ESPNOW]
extends = esp32_base
board = esp32-s3-devkitc-1-myboard
build_flags =
${esp32_base.build_flags}
-I variants/Elecrow_espnow
-D PIN_BOARD_SDA=-1
-D PIN_BOARD_SCL=-1
-D PIN_USER_BTN=0
; -D ESPNOW_DEBUG_LOGGING=1
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${esp32_base.build_src_filter}
+<helpers/esp32/ESPNOWRadio.cpp>
+<../variants/Elecrow_espnow>
[env:Elecrow_ESPNOW_terminal_chat]
extends = Elecrow_ESPNOW
build_flags =
${Elecrow_ESPNOW.build_flags}
-D MAX_CONTACTS=350
-D MAX_GROUP_CHANNELS=8
-D MESH_DEBUG=1
build_src_filter = ${Elecrow_ESPNOW.build_src_filter}
+<../examples/simple_secure_chat/main.cpp>
lib_deps =
${Elecrow_ESPNOW.lib_deps}
densaugeo/base64 @ ~1.4.0

44
variants/elecrow_espnow/target.cpp

@ -0,0 +1,44 @@
#include <Arduino.h>
#include "target.h"
#include <helpers/ArduinoHelpers.h>
ESP32Board board;
ESPNOWRadio radio_driver;
ESP32RTCClock rtc_clock;
SensorManager sensors;
bool radio_init() {
rtc_clock.begin();
radio_driver.init();
return true; // success
}
uint32_t radio_get_rng_seed() {
return millis() + radio_driver.intID(); // TODO: where to get some entropy?
}
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) {
// no-op
}
void radio_set_tx_power(uint8_t dbm) {
radio_driver.setTxPower(dbm);
}
// NOTE: as we are using the WiFi radio, the ESP_IDF will have enabled hardware RNG:
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html
class ESP_RNG : public mesh::RNG {
public:
void random(uint8_t* dest, size_t sz) override {
esp_fill_random(dest, sz);
}
};
mesh::LocalIdentity radio_new_identity() {
ESP_RNG rng;
return mesh::LocalIdentity(&rng); // create new random identity
}

16
variants/elecrow_espnow/target.h

@ -0,0 +1,16 @@
#pragma once
#include <helpers/ESP32Board.h>
#include <helpers/esp32/ESPNOWRadio.h>
#include <helpers/SensorManager.h>
extern ESP32Board board;
extern ESPNOWRadio radio_driver;
extern ESP32RTCClock rtc_clock;
extern SensorManager sensors;
bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();

2
variants/generic-e22/variant.h

@ -19,7 +19,7 @@
// Radio // Radio
#define USE_SX1262 // E22-900M30S uses SX1262 #define USE_SX1262 // E22-900M30S uses SX1262
#define USE_SX1268 // E22-400M30S uses SX1268 #define USE_SX1268 // E22-400M30S uses SX1268
#define SX126X_MAX_POWER 22 // Outputting 22dBm from SX1262 results in ~30dBm E22-900M30S output (module only uses last stage of the YP2233W PA) #define SX126X_MAX_POWER 27 // Outputting 22dBm from SX1262 results in ~30dBm E22-900M30S output (module only uses last stage of the YP2233W PA)
#define SX126X_DIO3_TCXO_VOLTAGE 1.8 // E22 series TCXO reference voltage is 1.8V #define SX126X_DIO3_TCXO_VOLTAGE 1.8 // E22 series TCXO reference voltage is 1.8V
#define SX126X_CS 18 // EBYTE module's NSS pin #define SX126X_CS 18 // EBYTE module's NSS pin

2
variants/heltec_v4/platformio.ini

@ -26,7 +26,7 @@ build_flags =
-D PIN_VEXT_EN=36 -D PIN_VEXT_EN=36
-D PIN_VEXT_EN_ACTIVE=HIGH -D PIN_VEXT_EN_ACTIVE=HIGH
-D LORA_TX_POWER=10 ;If it is configured as 10 here, the final output will be 22 dbm. -D LORA_TX_POWER=10 ;If it is configured as 10 here, the final output will be 22 dbm.
-D MAX_LORA_TX_POWER=22 ; Max SX1262 output -D MAX_LORA_TX_POWER=27 ; Max SX1262 output
-D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO2_AS_RF_SWITCH=true
-D SX126X_DIO3_TCXO_VOLTAGE=1.8 -D SX126X_DIO3_TCXO_VOLTAGE=1.8
-D SX126X_CURRENT_LIMIT=140 -D SX126X_CURRENT_LIMIT=140

2
variants/meshadventurer/variant.h

@ -19,7 +19,7 @@
// Radio // Radio
#define USE_SX1262 // E22-900M30S uses SX1262 #define USE_SX1262 // E22-900M30S uses SX1262
#define USE_SX1268 // E22-400M30S uses SX1268 #define USE_SX1268 // E22-400M30S uses SX1268
#define SX126X_MAX_POWER 22 // Outputting 22dBm from SX1262 results in ~30dBm E22-900M30S output (module only uses last stage of the YP2233W PA) #define SX126X_MAX_POWER 27 // Outputting 22dBm from SX1262 results in ~30dBm E22-900M30S output (module only uses last stage of the YP2233W PA)
#define SX126X_DIO3_TCXO_VOLTAGE 1.8 // E22 series TCXO reference voltage is 1.8V #define SX126X_DIO3_TCXO_VOLTAGE 1.8 // E22 series TCXO reference voltage is 1.8V
#define SX126X_CS 18 // EBYTE module's NSS pin #define SX126X_CS 18 // EBYTE module's NSS pin

Loading…
Cancel
Save