Use IP networking
Open DNS and TCP connections through NetworkHandle. Never use embassy-net or any vendor type directly: code written
against the standard traits is portable, testable on the host and independent of the stack. Background:
Networking.
Write portable code
Make your layer generic over embedded_nal_async::{Dns, TcpConnect} and embedded_io_async::{Read, Write}:
/// Sends `request` to `host:port` and reads the first chunk of the answer.
///
/// Generic over the standard traits: it works with `NetworkHandle`, with any
/// other `embedded-nal-async` stack, and it is what a TLS or HTTP layer would
/// build on.
async fn exchange<N>(
net: &N,
host: &str,
port: u16,
request: &[u8],
answer: &mut [u8],
) -> Result<usize, NetError>
where
N: Dns<Error = NetError> + TcpConnect<Error = NetError>,
{
let address = net.get_host_by_name(host, AddrType::IPv4).await?;
let mut connection = net.connect(SocketAddr::new(address, port)).await?;
connection.write_all(request).await?;
connection.flush().await?;
connection.read(answer).await
}
Handle the typed NetError:
NetworkUnavailable: Wi-Fi is not connected (or DHCP incomplete). Wait forWifiStatusConnected(seeWifiHandle::status_receiver) and retry; this is not a server problem.DnsFailed/ConnectFailed: the network is up but the name or endpoint failed; apply your own retry policy.NoSocketAvailable: all TCP slots of the product are in use; the call does not wait. Close a connection first.ConnectionReset/Timeout: the connection is gone; reconnect.
Always drop connections you no longer need: the slot is released on drop. Bound waits with your own timeout
(embassy_time::with_timeout); dropping a pending connect is safe and frees its slot
(cancellation).
Test on the host
let ip = rohi_net::testing::FakeIp::<2, 128>::new(); // feature `testing`
ip.add_host("uplink.example", server);
ip.set_ready(true);
let net = NetworkHandle::new(&ip);
FakeIp scripts DNS answers, refused connections, readiness changes, slot exhaustion and held operations and lets you
play the peer. See crates/rohi-net/src/ip/tests.rs and tests/network_example.rs.
On the device
The firmware creates the handle when it starts Wi-Fi (altruist_urban::net::start returns
(EspWifi, NetRunner, NetworkHandle<'static, Ip>)) and hands it to the component that needs it. Capacity (TCP_CONNECTIONS,
buffer sizes) is a product decision in firmwares/altruist-urban/src/resources.rs
(Resource limits). Remember there is no TLS yet: do not send secrets
in clear text.