When it comes to showcasing real-time data on a user-friendly webpage for a broad audience, especially in scenarios involving a high volume of small data packets like IoT sensors, finding the right data transfer method is crucial. While MQTT and HTTP are common choices, limitations arise when dealing with IoT sensors due to their power constraints. In such cases, a simpler and more efficient solution is required – introducing UDP.
Why UDP?
User Datagram Protocol (UDP) offers a straightforward and faster approach to data transfer. Although it lacks built-in flow control, acknowledgments, and guaranteed order of delivery, UDP suits scenarios where reordering is acceptable, and the loss of occasional data points is tolerable.
Sending UDP Datagrams: Elixir and Python
Elixir:
{:ok, socket} = :gen_udp.open(55678)
ts = DateTime.utc_now() |> DateTime.to_unix(:second)
val = get_sensor_data()
:gen_udp.send(state.socket, {127,0,0,1}, 32323, "SensorName";#{val};#{ts}")
Pyhton:
UDP_IP = "127.0.01"
UPD_PORT = "32323"
val = get_sensor_data()
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto("SensorName;" + str(val) + ";" + str(int(native_time.time())), UDP_IP, UPD_PORT))
Receiving UDP Traffic: Elixir and Broadcasting to LiveView
In this scenario, a GenServer is defined. In the init/1
call, the message format is set to binary, and the active
mode is set to true. This configuration prompts gen_udp
to send the complete content of the package as a process message, rather than just a notification. The handle_info
callback is invoked upon receiving a new UDP package.
defmodule Sensor.UDPBridge do
use GenServer
def start_link(port) do
GenServer.start_link(__MODULE__, port)
end
def init(port) do
:gen_udp.open(port, [:binary, active: true])
end
def handle_info({:udp, _socket, _address, _port, data}, socket) do
handle_packet(data, socket)
end
defp handle_packet(data, socket) do
SensorWeb.Endpoint.broadcast("ScaleEvent", "scale1", data)
{:noreply, socket}
end
end
~While receiving messages, consider adding a catch-all match to account for different message formats.~
In Line 17, you can also spot, that we are sending a Broadcast Message with the Topic ScaleEvent
with the Event scale1
and our data from the UDP Package.
defmodule SensorWeb.ScalesLive do
use SensorWeb, :live_view
@dialyzer {:nowarn_function, mount: 3}
alias Contex.{Sparkline}
def mount(_params, _session, socket) do
SensorWeb.Endpoint.subscribe("ScaleEvent")
{:ok, socket |> assign(data: %{})}
end
def handle_info(%{topic: topic, payload: payload}, socket) do
[device, data, _ts] = String.split(payload, ";")
{:noreply, socket |> assign(data: data)}
end
end
By implementing UDP communication in your Phoenix Application, you can efficiently transmit real-time data from IoT sensors to a web interface, enabling multiple users to access the data concurrently. While UDP lacks some of the reliability features of other protocols, its simplicity and speed make it a powerful tool for specific use cases like IoT applications.