Implement ESP-NOW support (#40)

This commit is contained in:
Oleg Kalachev
2026-05-21 10:48:31 +03:00
committed by GitHub
parent bd2b1bd5de
commit fdcc9533b3
11 changed files with 227 additions and 27 deletions
+6 -2
View File
@@ -10,6 +10,7 @@
extern const int MOTOR_REAR_LEFT, MOTOR_REAR_RIGHT, MOTOR_FRONT_RIGHT, MOTOR_FRONT_LEFT;
extern const int RAW, ACRO, STAB, AUTO;
extern const int W_AP, W_STA, W_ESPNOW;
extern float t, dt, loopRate;
extern uint16_t channels[16];
extern float controlTime;
@@ -45,6 +46,7 @@ const char* motd =
"wifi - show Wi-Fi info\n"
"ap <ssid> <password> - setup Wi-Fi access point\n"
"sta <ssid> <password> - setup Wi-Fi client mode\n"
"espnow <mac> [<key>] - setup ESP-NOW peer\n"
"mot - show motor output\n"
"log [dump] - print log header [and data]\n"
"cr - calibrate RC\n"
@@ -143,9 +145,11 @@ void doCommand(String str, bool echo = false) {
} else if (command == "wifi") {
printWiFiInfo();
} else if (command == "ap") {
configWiFi(true, arg0.c_str(), arg1.c_str());
configWiFi(W_AP, arg0.c_str(), arg1.c_str());
} else if (command == "sta") {
configWiFi(false, arg0.c_str(), arg1.c_str());
configWiFi(W_STA, arg0.c_str(), arg1.c_str());
} else if (command == "espnow") {
configWiFi(W_ESPNOW, arg0.c_str(), arg1.c_str());
} else if (command == "mot") {
print("front-right %g front-left %g rear-right %g rear-left %g\n",
motors[MOTOR_FRONT_RIGHT], motors[MOTOR_FRONT_LEFT], motors[MOTOR_REAR_RIGHT], motors[MOTOR_REAR_LEFT]);
+4 -1
View File
@@ -10,7 +10,7 @@ extern int channelZero[16];
extern int channelMax[16];
extern int rollChannel, pitchChannel, throttleChannel, yawChannel, armedChannel, modeChannel;
extern int rcRxPin;
extern int wifiMode, udpLocalPort, udpRemotePort;
extern int wifiMode, wifiLongRange, udpLocalPort, udpRemotePort, espnowChannel;
extern float rcLossTimeout, descendTime;
extern int voltagePin;
extern float voltageScale;
@@ -112,6 +112,9 @@ Parameter parameters[] = {
{"WIFI_MODE", &wifiMode},
{"WIFI_PORT_LOC", &udpLocalPort},
{"WIFI_PORT_REM", &udpRemotePort},
{"WIFI_LONG_RANGE", &wifiLongRange},
// espnow
{"ESPNOW_CHANNEL", &espnowChannel},
// mavlink
{"MAV_SYS_ID", &mavlinkSysId},
{"MAV_RATE_SLOW", &telemetrySlow.rate},
+11
View File
@@ -6,6 +6,7 @@
#pragma once
#include <math.h>
#include <ESP32_NOW_Serial.h>
const float ONE_G = 9.80665;
extern float t;
@@ -46,6 +47,16 @@ void splitString(String& str, String& token0, String& token1, String& token2) {
if (token2.c_str() == NULL) token2 = "";
}
// Simplified ESP-NOW Serial without tx buffering and resends
class ESPNOWSerial : public ESP_NOW_Serial_Class {
public:
using ESP_NOW_Serial_Class::ESP_NOW_Serial_Class;
void onSent(bool success) override {} // disable resends
size_t write(const uint8_t *data, size_t len) override {
return ESP_NOW_Peer::send(data, len); // pure send without buffering
}
};
// Rate limiter
class Rate {
public:
+64 -17
View File
@@ -1,82 +1,129 @@
// Copyright (c) 2023 Oleg Kalachev <okalachev@gmail.com>
// Repository: https://github.com/okalachev/flix
// Wi-Fi communication
// Wi-Fi and ESP-NOW communication
#include <WiFi.h>
#include <WiFiAP.h>
#include <WiFiUdp.h>
#include <MacAddress.h>
#include <ESP32_NOW_Serial.h>
#include "Preferences.h"
#include "util.h"
extern Preferences storage; // use the main preferences storage
const int W_DISABLED = 0, W_AP = 1, W_STA = 2;
const int W_DISABLED = 0, W_AP = 1, W_STA = 2, W_ESPNOW = 3;
int wifiMode = W_AP;
int wifiLongRange = 0;
int udpLocalPort = 14550;
int udpRemotePort = 14550;
IPAddress udpRemoteIP = "255.255.255.255";
WiFiUDP udp;
ESPNOWSerial espnow(NULL, 0, WIFI_IF_AP);
ESPNOWSerial espnowBroadcast(ESP_NOW.BROADCAST_ADDR, 0, WIFI_IF_AP);
int espnowChannel = 6;
void setupWiFi() {
print("Setup Wi-Fi\n");
WiFi.enableLongRange(wifiLongRange);
if (wifiMode == W_AP) {
WiFi.softAP(storage.getString("WIFI_AP_SSID", "flix").c_str(), storage.getString("WIFI_AP_PASS", "flixwifi").c_str());
udp.begin(udpLocalPort);
} else if (wifiMode == W_STA) {
WiFi.begin(storage.getString("WIFI_STA_SSID", "").c_str(), storage.getString("WIFI_STA_PASS", "").c_str());
} else {
return;
udp.begin(udpLocalPort);
} else if (wifiMode == W_ESPNOW) {
WiFi.mode(WIFI_AP);
WiFi.setChannel(espnowChannel);
espnow.addr(MacAddress(storage.getString("ESPNOW_PEER_MAC", "FF:FF:FF:FF:FF:FF").c_str()));
String key = storage.getString("ESPNOW_PEER_KEY", "");
espnow.setKey(key.isEmpty() ? nullptr : (const uint8_t *)key.c_str());
espnow.begin();
espnowBroadcast.begin();
}
WiFi.setSleep(false); // disable power save
udp.begin(udpLocalPort);
}
void sendWiFi(const uint8_t *buf, int len) {
if (espnow) {
espnow.write(buf, len);
static Rate discovery(2);
if (discovery) espnowBroadcast.write((const uint8_t *)"flix", 4); // broadcast message to help finding this device
return;
}
if (WiFi.softAPgetStationNum() == 0 && !WiFi.isConnected()) return;
udp.beginPacket(udpRemoteIP, udpRemotePort);
udp.write(buf, len);
udp.endPacket();
}
int receiveWiFi(uint8_t *buf, int len) {
if (espnow) {
return espnow.read(buf, len);
}
if (WiFi.softAPgetStationNum() == 0 && !WiFi.isConnected()) return 0;
udp.parsePacket();
if (udp.remoteIP()) udpRemoteIP = udp.remoteIP();
return udp.read(buf, len);
}
void printWiFiInfo() {
if (WiFi.getMode() == WIFI_MODE_AP) {
if (espnow) {
print("Mode: ESP-NOW\n");
print("ESP-NOW version: %d\n", ESP_NOW.getVersion());
print("Max packet size: %d\n", ESP_NOW.getMaxDataLen());
print("MAC: %s\n", WiFi.softAPmacAddress().c_str());
print("Peer MAC: %s\n", MacAddress(espnow.addr()).toString().c_str());
print("Encrypted: %d\n", espnow.isEncrypted());
print("Channel: %d\n", espnow.getChannel());
} else if (WiFi.getMode() == WIFI_MODE_AP) {
print("Mode: Access Point (AP)\n");
print("MAC: %s\n", WiFi.softAPmacAddress().c_str());
print("SSID: %s\n", WiFi.softAPSSID().c_str());
print("Password: ***\n");
print("Channel: %d\n", WiFi.channel());
print("Clients: %d\n", WiFi.softAPgetStationNum());
print("IP: %s\n", WiFi.softAPIP().toString().c_str());
print("Remote IP: %s\n", udpRemoteIP.toString().c_str());
} else if (WiFi.getMode() == WIFI_MODE_STA) {
print("Mode: Client (STA)\n");
print("Connected: %d\n", WiFi.isConnected());
print("MAC: %s\n", WiFi.macAddress().c_str());
print("SSID: %s\n", WiFi.SSID().c_str());
print("Password: ***\n");
print("IP: %s\n", WiFi.localIP().toString().c_str());
print("Channel: %d\n", WiFi.channel());
print("RSSI: %d dBm\n", WiFi.RSSI());
print("IP: %s\n", WiFi.localIP().toString().c_str());
print("Remote IP: %s\n", udpRemoteIP.toString().c_str());
} else {
print("Mode: Disabled\n");
return;
}
print("Channel: %d\n", WiFi.channel());
print("Remote IP: %s\n", udpRemoteIP.toString().c_str());
print("MAVLink connected: %d\n", mavlinkConnected);
}
void configWiFi(bool ap, const char *ssid, const char *password) {
if (ap) {
storage.putString("WIFI_AP_SSID", ssid);
storage.putString("WIFI_AP_PASS", password);
void configWiFi(int mode, const char *first, const char *second) {
MacAddress mac;
if (mode == W_AP && strlen(first) > 0 && strlen(second) >= 8) {
storage.putString("WIFI_AP_SSID", first);
storage.putString("WIFI_AP_PASS", second);
} else if (mode == W_STA && strlen(first) > 0 && strlen(second) >= 8) {
storage.putString("WIFI_STA_SSID", first);
storage.putString("WIFI_STA_PASS", second);
} else if (mode == W_ESPNOW && mac.fromString(first)) {
storage.putString("ESPNOW_PEER_MAC", first);
storage.putString("ESPNOW_PEER_KEY", strlen(second) == ESP_NOW_KEY_LEN ? second : "");
} else {
storage.putString("WIFI_STA_SSID", ssid);
storage.putString("WIFI_STA_PASS", password);
print("Invalid configuration\n");
return;
}
print("✓ Reboot to apply new settings\n");
}