embedded-svc Rust Traits

repository·master·Indexed 41 days ago

https://github.com/esp-rs/embedded-svc

A collection of Rust traits providing portable abstractions for embedded services such as Wi-Fi, networking, and HTTP. Version 0.29.0 includes synchronous and asynchronous Sender/Receiver traits for inter-task messaging, a comprehensive HTTP client and server API with middleware support, and IPv4 subnet and mask parsing.

Tokens
9.9K
Snippets
32
Records
53
Agent score
50%

What's inside embedded-svc

  1. Overview of embedded-svc

    master

    The embedded-svc crate provides a set of Rust traits for common embedded services, including Wi-Fi, networking, HTTP servers (httpd), and logging.

    Its primary purpose is to serve as a common abstraction layer. While the current ecosystem primarily uses these traits via the esp-idf-svc implementation for ESP32 devices, the APIs are designed to be portable, allowing different hardware platforms to implement the same traits to create a unified embedded ecosystem.

  2. Handle HTTP requests with Request and Response

    master

    The HTTP server API uses Request<C> and Response<C> types to manage the lifecycle of an HTTP transaction.

    1. Request Phase: A Request is created by wrapping a Connection. You can inspect the method(), uri(), and header()s. To read the body, implement the Read trait or use read().
    2. Transition: To move from a request to a response, call into_response(), into_status_response(), or into_ok_response(). This initiates the HTTP response phase on the underlying connection.
    3. Response Phase: Once transitioned, you use the Response type to write() data and flush() the buffer to the client.

    Note: The asynch module provides an asynchronous version of these types where into_response, read, write, and flush are async methods.

    // Synchronous example sketch
    let mut request = Request::wrap(connection);
    if request.method() == Method::Get {
        let mut response = request.into_ok_response()?;
        response.write(b"Hello, world!")?;
        response.flush()?;
    }
  3. Switch between DHCP and Fixed IP configurations

    master

    The ClientConfiguration enum allows you to manage whether a client uses DHCP or a fixed IP.

    • ClientConfiguration::DHCP(DHCPClientSettings): Uses DHCP. DHCPClientSettings allows an optional hostname (up to 30 characters).
    • ClientConfiguration::Fixed(ClientSettings): Uses a static IP.

    Use as_fixed_settings_ref() to get an optional reference to fixed settings, or as_fixed_settings_mut() to get a mutable reference. Note that calling as_fixed_settings_mut() on a DHCP configuration will automatically convert the configuration to Fixed using Default::default() values.

    let mut config = ClientConfiguration::DHCP(DHCPClientSettings::default());
    
    // This converts config from DHCP to Fixed automatically
    let settings = config.as_fixed_settings_mut();
    settings.ip = Ipv4Addr::new(10, 0, 0, 5);
  4. Process incoming MQTT messages with EventPayload

    master

    The EventPayload enum represents the different types of events that can occur during an MQTT session. When a message is received, the Received variant provides the following data:

    • id: The MessageId of the message.
    • topic: An Option<&str> containing the topic name.
    • data: The raw &[u8] payload.
    • details: A Details enum indicating if the message is complete or part of a chunked transfer.

    Other variants include:

    • BeforeConnect / Connected(bool)
    • Disconnected
    • Subscribed(MessageId) / Unsubscribed(MessageId)
    • Published(MessageId)
    • Deleted(MessageId)
    • Error(&'a E)
    // Accessing received data
    if let EventPayload::Received { id, topic, data, details } = payload {
        let topic_str = topic.unwrap_or("");
        // Process data...
    }
  5. Configure IPv4 Client settings

    master

    Use ClientSettings to define a static (fixed) IP configuration. It includes the IP address, a Subnet (gateway and mask), and optional DNS servers.

    Fields:

    • ip: Ipv4Addr
    • subnet: Subnet
    • dns: Option<Ipv4Addr>
    • secondary_dns: Option<Ipv4Addr>
    let settings = ClientSettings {
        ip: Ipv4Addr::new(192, 168, 1, 50),
        subnet: Subnet {
            gateway: Ipv4Addr::new(192, 168, 1, 1),
            mask: Mask(24),
        },
        dns: Some(Ipv4Addr::new(8, 8, 8, 8)),
        secondary_dns: None,
    };
  6. Configure Station (Client) mode

    master

    To connect an ESP device to an existing Wi-Fi network, use the ClientConfiguration struct within a Configuration::Client variant.

    Required/Key fields:

    • ssid: The target network name (max 32 chars).
    • auth_method: The security protocol (e.g., AuthMethod::WPA2Personal).
    • password: The network password (max 64 chars).
    • scan_method: Determines how the device searches for the AP (CompleteScan or FastScan).
    • pmf_cfg: Configures Protected Management Frames (PmfConfiguration).
    let client_conf = ClientConfiguration {
        ssid: "MyNetwork".try_into().unwrap(),
        auth_method: AuthMethod::WPA2Personal,
        password: "secret_password".try_into().unwrap(),
        ..Default::default()
    };
    
    wifi.set_configuration(&Configuration::Client(client_conf))?;
    wifi.connect()?;
  7. Publish messages with the Publish trait

    master

    The Publish trait allows sending messages to an MQTT topic. This is available for both synchronous and asynchronous clients.

    Methods:

    • publish(topic: &str, qos: QoS, retain: bool, payload: &[u8]): Publishes a payload to the specified topic.
      • retain: If true, the broker will store the message as the last known good value for the topic.
      • Returns a MessageId on success.
    // Synchronous
    client.publish("home/light", QoS::AtMostOnce, true, b"on")?;
    
    // Asynchronous
    client.publish("home/light", QoS::AtMostOnce, true, b"on").await?;
  8. Manage MQTT subscriptions with the Client trait

    master

    The Client trait provides methods to manage topic subscriptions. You can use it in both synchronous and asynchronous contexts (via the asynch module).

    Methods:

    • subscribe(topic: &str, qos: QoS): Subscribes to a specific topic with the requested QoS. Returns a MessageId on success.
    • unsubscribe(topic: &str): Unsubscribes from a topic. Returns a MessageId on success.
    // Synchronous usage
    client.subscribe("sensors/temp", QoS::AtLeastOnce)?;
    client.unsubscribe("sensors/temp")?;
    
    // Asynchronous usage (from asynch module)
    client.subscribe("sensors/temp", QoS::AtLeastOnce).await?;
  9. Make asynchronous HTTP requests with asynch::Client

    master

    The asynch::Client<C> provides an async/await interface for HTTP operations. The workflow is identical to the synchronous client, but all network-bound methods are async.

    Workflow:

    1. Wrap a connection using asynch::Client::wrap(connection).
    2. Await a request method (e.g., client.get(uri).await) to get a Request<&mut C>.
    3. Await request.write(buf).await to send data.
    4. Await request.submit().await to get a Response<C>.
    5. Await response.read(buf).await to read the response body.
    // Example asynchronous flow
    use embedded_svc::http::asynch::Client;
    
    let mut client = Client::wrap(connection);
    let mut request = client.get("http://example.com").await?;
    let response = request.submit().await?;
    
    let mut buf = [0u8; 1024];
    let n = response.read(&mut buf).await?;
  10. Access HTTP Headers via the Headers trait

    master

    The Headers trait allows you to retrieve header values from an HTTP request or response. It provides convenience methods for common headers:

    • header(&str) -> Option<&str>: Get a header by its exact name.
    • content_type() -> Option<&str>: Get the Content-Type header.
    • content_len() -> Option<u64>: Get the Content-Length header parsed as a u64.
    • content_encoding() -> Option<&str>: Get the Content-Encoding header.
    • transfer_encoding() -> Option<&str>: Get the Transfer-Encoding header.
    • host() -> Option<&str>: Get the Host header.
    • connection() -> Option<&str>: Get the Connection header.
    • cache_control() -> Option<&str>: Get the Cache-Control header.
    • upgrade() -> Option<&str>: Get the Upgrade header.
  11. Implement an HTTP Handler

    master

    To process incoming connections, implement the Handler<C> trait. The handle method receives a mutable reference to the Connection.

    For simple logic, you can use FnHandler, which allows you to wrap a closure that takes a Request<&mut C> and returns a Result.

    // Using FnHandler for a simple closure-based handler
    let handler = FnHandler::new(|req: Request<&mut C>| {
        if req.uri() == "/" {
            // logic to initiate response
        }
        Ok(())
    });
  12. Use the synchronous WebSocket Sender and Receiver traits

    master

    For synchronous WebSocket communication, implement or use the following traits:

    Receiver

    To receive data, call recv. You must provide a mutable buffer frame_data_buf where the received data will be stored.

    • fn recv(&mut self, frame_data_buf: &mut [u8]) -> Result<(FrameType, usize), Self::Error>
    • Returns a tuple containing the FrameType and the number of bytes (usize) actually written to the buffer.

    Sender

    To send data, call send with the desired frame type and the data slice.

    • fn send(&mut self, frame_type: FrameType, frame_data: &[u8]) -> Result<(), Self::Error>
    // Conceptual synchronous usage
    // receiver.recv(&mut buffer)? -> (frame_type, length)
    // sender.send(frame_type, &buffer[..length])?