esp-idf-svc Rust Documentation

repository·master·Indexed 19 days ago

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

Safe Rust wrappers for the ESP-IDF (Espressif IoT Development Framework) services, providing high-level, standard-library-based development for ESP32 microcontrollers. It implements embedded-svc traits and supports timers, event loop, WiFi, Ethernet, HTTP, MQTT, WebSockets, NVS, OTA, and NimBLE BLE host services. The crate supports both blocking and async modes and re-exports esp-idf-hal and esp-idf-sys.

Tokens
29.1K
Snippets
105
Records
135
Agent score
67%

What's inside esp-idf-svc

  1. Overview of esp-idf-svc

    master

    The esp-idf-svc crate provides safe Rust wrappers for the services available in the ESP-IDF SDK. It is designed for developers working with the ESP-IDF framework (which uses the standard library std) rather than bare-metal no_std environments.

    Key features include:

    • Service Support: Covers timers, event loop, WiFi, Ethernet, HTTP client & server, MQTT, WebSockets (WS), NVS, OTA, and more.
    • Trait Implementation: Implements traits from the embedded-svc crate for ecosystem compatibility.
    • Execution Modes: Supports both blocking and async modes for services where async is feasible.
    • Unified Dependency: Re-exports esp-idf-hal (as esp_idf_svc::hal) and esp-idf-sys (as esp_idf_svc::sys), allowing you to use a single dependency for most ESP-IDF-based Rust development.
  2. Choose between esp-idf-svc and esp-hal

    master

    When deciding on a driver/service approach for ESP32, consider the following:

    • Use esp-idf-svc if: You want to use the ESP-IDF SDK, require std support, and want safe wrappers for high-level services like WiFi, MQTT, and HTTP.
    • Use esp-hal if: You want an officially supported Espressif HAL, require no_std (bare-metal) operation, and prefer an async-only programming model.

    Note that esp-idf-svc is a community effort and may lag behind the latest stable ESP-IDF versions.

  3. Flash and monitor examples using cargo-espflash

    master

    You can build, flash, and monitor examples from this repository using cargo-espflash. To run a specific example (e.g., wifi) on a specific MCU (e.g., esp32c3), use the following command structure.

    Note: You must swap the MCU environment variable and the --target flag to match your specific hardware.

    $ MCU=esp32c3 cargo espflash flash --target riscv32imc-esp-espidf --example wifi --monitor
  4. Understand GATT Database elements

    master

    When discovering services or attributes, the library provides several element types to represent the GATT database structure:

    • ServiceElement: Represents a service (UUID, primary/secondary status, and handle range).
    • IncludeServiceElement: Represents a service included in another service.
    • CharacteristicElement: Represents a characteristic (UUID, handle, and properties like Read/Write/Notify).
    • DescriptorElement: Represents a characteristic descriptor.
    • DbElement: A generic element that can be parsed into a DbElementAttrType to determine if it is a PrimaryService, SecondaryService, Characteristic, Descriptor, or IncludedService.
  5. Configure MQTT 5.0 Connection Properties

    master

    If using MQTT 5.0 (MqttProtocolVersion::V5), you can configure specific connection properties via Mqtt5ConnectionPropertyConfig. These must be provided in the MqttClientConfiguration.mqtt5_connection_property field.

    Available properties:

    • session_expiry_interval: Seconds until the session expires.
    • will_delay_interval: Seconds to delay the Will message.
    • receive_maximum: Max concurrent inbound QoS > 0 PUBLISHes.
    • maximum_packet_size: Max packet size in bytes.
    • topic_alias_maximum: Max topic alias the broker may use.
    • request_response_info: Whether to request response information.
    • request_problem_info: Whether to request problem information (defaults to true).
    • message_expiry_interval: Seconds until the Will message expires.
    • payload_format_indicator: Whether the payload is UTF-8 (true) or bytes (false).
    #[cfg(esp_idf_mqtt_protocol_5)]
    let mqtt5_props = Mqtt5ConnectionPropertyConfig {
        session_expiry_interval: Some(3600),
        receive_maximum: Some(10),
        ..Default::default()
    };
    
    let conf = MqttClientConfiguration {
        protocol_version: Some(MqttProtocolVersion::V5),
        #[cfg(esp_idf_mqtt_protocol_5)]
        mqtt5_connection_property: Some(mqtt5_props),
        ..Default::default()
    };
  6. How EspMqttClient and EspMqttConnection work together

    master

    The MQTT implementation separates the Client (used to send commands like publish or subscribe) from the Connection (used to receive incoming events).

    1. The Client (EspMqttClient or EspAsyncMqttClient): This is the handle you use to interact with the broker. It implements Client, Publish, and Enqueue traits.
    2. The Connection (EspMqttConnection or EspAsyncMqttConnection): This is a stream of events. You call .next() on the connection to retrieve the next EspMqttEvent.

    This separation allows you to move the Client to one task (e.g., a command sender) and the Connection to another task (e.g., an event processor) without complex synchronization.

  7. How ThreadDriver modes (Host vs RCP) work

    master

    The ThreadDriver provides a safe wrapper over the ESP IDF Thread C driver, operating at Layer 2 (Data Link). It supports two primary modes:

    1. Host Mode: The driver operates as a host. If the chip lacks a native Thread radio, it must connect via SPI or UART to another chip running the Thread stack in RCP mode.
    2. RCP (Radio Co-Processor) Mode: The driver operates as a co-processor to a host. This is supported on MCUs with a Thread radio (e.g., ESP32-C2, ESP32-C6) and requires a connection via UART or SPI to the host.

    Note: For most networking (IP layer) use cases, EspThread is preferred. ThreadDriver is intended for users implementing custom network stacks (like smoltcp) directly on top of the Thread radio.

    pub struct ThreadDriver<'d, T> where T: Mode { ... }
  8. How NimBLE GATT client operations and events work

    master

    The NimBLE GATT client operates on an "initiate-now, complete-later" model. When you call a GATT client operation (like read or discover_services), the operation is started immediately, and the result is delivered asynchronously via a callback.

    All GATT client callbacks are routed through a single shared hook registered via BleDriver::gattc_subscribe. These events are correlated by the ConnHandle (connection handle), as GATT serializes one transaction per connection.

    Additionally, received notifications or indications ([GattcEvent::Notify]) are demultiplexed from the connection's GAP callback and delivered through this same gattc_subscribe hook.

  9. How FollowRedirectsPolicy works

    master

    The FollowRedirectsPolicy enum determines the client's behavior when encountering HTTP redirect status codes:

    • FollowNone: No redirects are followed.
    • FollowGetHead: Only GET and HEAD requests will follow redirects.
    • FollowAll: All request methods will follow redirects.
    #[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
    pub enum FollowRedirectsPolicy {
        FollowNone,
        #[default]
        FollowGetHead,
        FollowAll,
    }
  10. Manage BLE GATT Server events with GattsEvent

    master

    The GattsEvent enum represents all possible events emitted by the BLE GATT server. You can use this enum to react to client interactions (like Read or Write requests), connection changes (PeerConnected, PeerDisconnected), or service lifecycle changes (ServiceCreated, ServiceStarted).

    Common event variants include:

    • Read: A client is requesting to read an attribute.
    • Write: A client is writing data to an attribute.
    • PeerConnected: A new BLE connection has been established.
    • PeerDisconnected: A connection has been lost.
    • Mtu: The MTU size has changed.
    • CharacteristicAdded / DescriptorAdded: Part of the service definition process.
    match event {
        GattsEvent::Read { conn_id, handle, offset, .. } => {
            // Handle read request
        }
        GattsEvent::Write { value, handle, .. } => {
            // Handle write request
        }
        _ => {}
    }
  11. How NimBLE L2CAP Connection-Oriented Channels (CoC) work

    master

    L2CAP CoC provides a credit-based data pipe that runs parallel to GATT over a GAP connection. It is exposed via the BleDriver API.

    Key Concepts:

    • Channels: A channel is opened either by listening on a Protocol Service Multiplexer (PSM) using l2cap_create_server or by connecting to a peer's PSM using l2cap_connect.
    • Event Handling: All channel operations (connection lifecycle, received data, and flow control) are delivered through a single subscription hook via l2cap_subscribe.
    • Credit-Based Flow Control: Flow control is manual.
      • After handling a L2capEvent::Received, you must replenish the peer's credits by calling l2cap_recv_ready.
      • If l2cap_send returns SendOutcome::Stalled, the peer has run out of credits. Transmission will automatically resume when you receive a L2capEvent::TxUnstalled event.
    • L2capChan Handle: An opaque handle to an open channel. It is valid from L2capEvent::Connected (or Accept) until the matching L2capEvent::Disconnected. Using it after disconnection results in undefined behavior.
    // Example of the flow control pattern:
    match event {
        L2capEvent::Received { chan, data, .. } => {
            // 1. Process data
            // 2. Replenish credits so peer can send more
            driver.l2cap_recv_ready(chan, mtu)?;
        }
        L2capEvent::TxUnstalled { chan, .. } => {
            // 3. Resume sending if previously stalled
        }
        _ => {}
    }