Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions arch/esp32/esp32.ini
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ build_flags =
-DLIBPAX_ARDUINO
-DLIBPAX_WIFI
-DLIBPAX_BLE
-DHAS_UDP_MULTICAST=1
thebentern marked this conversation as resolved.
Show resolved Hide resolved
;-DDEBUG_HEAP

lib_deps =
Expand Down
12 changes: 11 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -1278,4 +1288,4 @@ void loop()
mainDelay.delay(delayMsec);
}
}
#endif
#endif
5 changes: 5 additions & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
3 changes: 3 additions & 0 deletions src/mesh/NodeDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())) {
Expand Down
12 changes: 12 additions & 0 deletions src/mesh/Router.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,12 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
abortSendAndNak(encodeResult, p);
return encodeResult; // FIXME - this isn't a valid ErrorCode
}
#if HAS_UDP_MULTICAST
if (udpThread && isFromUs(p) &&
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) {
Expand Down Expand Up @@ -624,6 +630,12 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
// After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet
if ((decoded || p_encrypted->pki_encrypted) && moduleConfig.mqtt.enabled && !isFromUs(p) && mqtt)
mqtt->onSend(*p_encrypted, *p, p->channel);
#endif
#if HAS_UDP_MULTICAST
if ((decoded || p_encrypted->pki_encrypted) && !isFromUs(p) && udpThread &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this logic should be different than for MQTT. The reason for MQTT it is also sending everything it receives is because MQTT is used not only for transport (bridging) but also as client. So a packet directed towards you, which is not rebroadcasted, also ends up on the MQTT server. For UDP I think we should only use onSend() when the Router sends, and thus remove the isFromUs() there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this as well. It makes sense to remove the isFromUs check

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant to remove isFromUs(p) from Router::send() and remove the complete block out of Router::handleReceived() (since packets received via UDP already arrive here). This way it only sends via UDP if it would via LoRa.
However, if you want it to always rebroadcast over UDP (without decrementing the hop limit, before adding yourself to the traceroute, etc.) then we could do it the same as for MQTT. So depends on whether we see this as a normal hop or more a virtual one.

config.network.enabled_protocols & meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST) {
udpThread->onSend(const_cast<meshtastic_MeshPacket *>(p_encrypted));
}
#endif
}

Expand Down
70 changes: 70 additions & 0 deletions src/mesh/udp/UdpMulticastThread.h
Original file line number Diff line number Diff line change
@@ -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: Might consider a heartbeat for discovery or keep alive?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would align this with the store&forward heartbeat mechanism. Could also include a display symbol for the available transports in the long term.

return UDP_MULTICAST_THREAD_INTERVAL_MS;
}

private:
IPAddress udpIpAddress;
AsyncUDP udp;
};
#endif // ARCH_ESP32
8 changes: 7 additions & 1 deletion src/mesh/wifi/WiFiAPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -437,4 +443,4 @@ uint8_t getWifiDisconnectReason()
{
return wifiDisconnectReason;
}
#endif
#endif
Loading