Hello world support for UDP broadcasts over the LAN on ESP32 (#5779)

* UDP local area network meshing on ESP32

* Logs

* Comment

* Update UdpMulticastThread.h

* Changes

* Only use router->send
This commit is contained in:
Ben Meadors 2025-01-11 09:01:49 -06:00 committed by GitHub
parent 7e063c1dda
commit d440dbd522
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 102 additions and 2 deletions

View File

@ -37,6 +37,7 @@ build_flags =
-DLIBPAX_ARDUINO
-DLIBPAX_WIFI
-DLIBPAX_BLE
-DHAS_UDP_MULTICAST=1
;-DDEBUG_HEAP
lib_deps =

View File

@ -114,6 +114,11 @@ AccelerometerThread *accelerometerThread = nullptr;
AudioThread *audioThread = nullptr;
#endif
#ifdef HAS_UDP_MULTICAST
#include "mesh/udp/UdpMulticastThread.h"
UdpMulticastThread *udpThread = nullptr;
#endif
#if defined(TCXO_OPTIONAL)
float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE; // if TCXO is optional, put this here so it can be changed further down.
#endif
@ -783,6 +788,11 @@ void setup()
LOG_DEBUG("Start audio thread");
audioThread = new AudioThread();
#endif
#ifdef HAS_UDP_MULTICAST
LOG_DEBUG("Start multicast thread");
udpThread = new UdpMulticastThread();
#endif
service = new MeshService();
service->init();
@ -1278,4 +1288,4 @@ void loop()
mainDelay.delay(delayMsec);
}
}
#endif
#endif

View File

@ -49,6 +49,11 @@ extern Adafruit_DRV2605 drv;
extern AudioThread *audioThread;
#endif
#ifdef HAS_UDP_MULTICAST
#include "mesh/udp/UdpMulticastThread.h"
extern UdpMulticastThread *udpThread;
#endif
// Global Screen singleton.
extern graphics::Screen *screen;

View File

@ -299,6 +299,9 @@ NodeDB::NodeDB()
resetRadioConfig(); // If bogus settings got saved, then fix them
// nodeDB->LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d", config.lora.region, myNodeInfo.my_node_num, numMeshNodes);
// Uncomment below to always enable UDP broadcasts
// config.network.enabled_protocols = meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST;
// If we are setup to broadcast on the default channel, ensure that the telemetry intervals are coerced to the minimum value
// of 30 minutes or more
if (channels.isDefaultChannel(channels.getPrimaryIndex())) {

View File

@ -274,6 +274,11 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
abortSendAndNak(encodeResult, p);
return encodeResult; // FIXME - this isn't a valid ErrorCode
}
#if HAS_UDP_MULTICAST
if (udpThread && config.network.enabled_protocols & meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST) {
udpThread->onSend(const_cast<meshtastic_MeshPacket *>(p));
}
#endif
#if !MESHTASTIC_EXCLUDE_MQTT
// Only publish to MQTT if we're the original transmitter of the packet
if (moduleConfig.mqtt.enabled && isFromUs(p) && mqtt) {

View File

@ -0,0 +1,70 @@
#pragma once
#if HAS_UDP_MULTICAST
#include "configuration.h"
#include "main.h"
#include "mesh/Router.h"
#include <AsyncUDP.h>
#include <WiFi.h>
#define UDP_MULTICAST_DEFAUL_PORT 4403 // Default port for UDP multicast is same as TCP api server
#define UDP_MULTICAST_THREAD_INTERVAL_MS 15000
class UdpMulticastThread : public concurrency::OSThread
{
public:
UdpMulticastThread() : OSThread("UdpMulticast") { udpIpAddress = IPAddress(224, 0, 0, 69); }
void start()
{
if (udp.listenMulticast(udpIpAddress, UDP_MULTICAST_DEFAUL_PORT)) {
LOG_DEBUG("UDP Listening on IP: %s", WiFi.localIP().toString().c_str());
udp.onPacket([this](AsyncUDPPacket packet) { onReceive(packet); });
} else {
LOG_DEBUG("Failed to listen on UDP");
}
}
void onReceive(AsyncUDPPacket packet)
{
size_t packetLength = packet.length();
LOG_DEBUG("UDP broadcast from: %s, len=%u", packet.remoteIP().toString().c_str(), packetLength);
meshtastic_MeshPacket mp;
uint8_t bytes[meshtastic_MeshPacket_size]; // Allocate buffer for the data
size_t packetSize = packet.readBytes(bytes, packet.length());
LOG_DEBUG("Decoding MeshPacket from UDP len=%u", packetSize);
bool isPacketDecoded = pb_decode_from_bytes(bytes, packetLength, &meshtastic_MeshPacket_msg, &mp);
if (isPacketDecoded && router) {
UniquePacketPoolPacket p = packetPool.allocUniqueCopy(mp);
// Unset received SNR/RSSI
p->rx_snr = 0;
p->rx_rssi = 0;
router->enqueueReceivedMessage(p.release());
}
}
bool onSend(const meshtastic_MeshPacket *mp)
{
if (!mp || WiFi.status() != WL_CONNECTED) {
return false;
}
LOG_DEBUG("Broadcasting packet over UDP (id=%u)", mp->id);
uint8_t buffer[meshtastic_MeshPacket_size];
size_t encodedLength = pb_encode_to_bytes(buffer, sizeof(buffer), &meshtastic_MeshPacket_msg, mp);
udp.broadcastTo(buffer, encodedLength, UDP_MULTICAST_DEFAUL_PORT);
return true;
}
protected:
int32_t runOnce() override
{
canSleep = true;
// TODO: Implement nodeinfo broadcast
return UDP_MULTICAST_THREAD_INTERVAL_MS;
}
private:
IPAddress udpIpAddress;
AsyncUDP udp;
};
#endif // ARCH_ESP32

View File

@ -112,6 +112,12 @@ static void onNetworkConnected()
APStartupComplete = true;
}
#if HAS_UDP_MULTICAST
if (udpThread) {
udpThread->start();
}
#endif
// FIXME this is kinda yucky, instead we should just have an observable for 'wifireconnected'
#ifndef MESHTASTIC_EXCLUDE_MQTT
if (mqtt)
@ -437,4 +443,4 @@ uint8_t getWifiDisconnectReason()
{
return wifiDisconnectReason;
}
#endif
#endif