[![Crate](https://img.shields.io/crates/v/esp-hosted.svg)](https://crates.io/crates/esp-hosted)
[![Docs](https://docs.rs/esp-hosted/badge.svg)](https://docs.rs/esp-hosted)

# ESP Hosted
For connecting to an [ESP-Hosted-MCU](https://github.com/espressif/esp-hosted-mcu) from a Host MCU with firmware
written in rust.

Compatible with ESP-Hosted-MCU 2.0.6 and ESP IDF 5.4.1 (And likely anything newer), and any host MCU and architecture. 
For details on ESP-HOSTED-MCU's protocol see
[this document](/esp_hosted_protocol.md). For a list of Wi-Fi commands and dat structures available, reference the
[ESP32 IDF API Reference, Wi-Fi section](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html). For BLE commands, reference the [HCI docs](https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html).

This library includes two approaches: A high-level API using data structures from this library, and full access to 
the native protobuf structures (Wi-Fi) and HCI interface (BLE). The native API is easier to work with, but only
implements a portion of functionality. The protobuf API is complete, but more cumbersome.

This library does not use an allocator. This makes integrating it simple, but it uses a significant amount of flash
for static buffers. These are configured in the `build_proto/src/main.rs` script on a field-by-field basis.

It's transport agnostic; compatible with SPI, SDIO, and UART. It does this by allowing the application firmware to pass
a generic `write` function, and reads are performed as functions that act on buffers passed by the firmware.


Example use:
```rust
use esp_hosted::{self, wifi};

fn init(buf: &mut [u8], uart: &mut Uart) {
    // Write could also be SPI, dma etc.
    let mut write = |buf: &[u8]| {
        uart.write(buf).map_err(|e| {
            println!("Uart write error: {:?}", e);
            EspError::Comms
        })
    };

    let heartbeat_cfg = RpcReqConfigHeartbeat {
        enable: true,
        duration: 10,
    };

    esp_hosted::cfg_heartbeat(buf, &mut write, 0, &heartbeat_cfg)?;
    
    // Configure Wi-Fi settings as-required, using functions in the `esp_hosted::wifi` module.
    wifi::start(buf, &mut write, 1).is_err()?;
}
```

In your UART, SPI etc reception handling (e.g. an interrupt handler), you can parse incoming messages from the Esp. 
The `parse_msg` function returns a `MsgParsed` enum of two varieties. One for Wi-Fi, the other for HCI (Bluetooth).

The Wi-Fi message containing the following:
- The payload header. This contains generic data, and you may not need to use it.
- A struct from this library containing the RPC header. This determines if a request, response, or event, and the Rpc ID being  used.
- The rpc payload, as a `&[u8]`
- A struct generated by the `micropb` library, which contains the full RPC data, in a raw, but complete format. 

The HCI (BLE) message contains a plain byte array of the payload received. We may change this 
later to parse it. For now, it's bring-your-own-HCI tools.

The auto-generated structs are rough: They don't include documentation, use numerical values directly vice
enums, and use `i32` for many integer types that are `u8` in practice, and as defined by ESP-IDF.

This example demonstrates how to read messages sent by the ESP asynchronously.

```rust
#[interrupt]
fn USART2() {
    // Configure your I/O hardware here to start and stop transfers etc.
    // ...
    let msg = esp_hosted::parse_msg(buf)?;

    println!("\nHeader: {:?}", msg.header);
    println!("RPC: {:?}", msg.rpc);
    println!("Data buf: {:?}", msg.data_buf);
    
    match msg {
        MsgParsed::Wifi(wifi_msg) => {
            match wifi_msg.msg_id {
                // Example using native parsing and direct payload.
                RpcId::EventHeartbeat => {
                    println!("Heartbeat data: {:?}", rpc.data);
                }
                _ => ()
            }

            // For access to the full set of responses, parsed from the .proto file:
            if let Some(pl) = &wifi_msg.rpc_parsed.payload {
                match pl {
                    Rpc_::Payload::EventHeartbeat(hb) => {
                        println!("Heartbeat data: {:?}", hb.hb_num);
                    }
                    _ => (), // etc
                }
            }
        }
        MsgParsed::Hci(hci) => {}
    }
}
```

To perform specific actions, there are functions like `wifi::get_protocol`,  `wifi::start`, `wifi::get_mode` etc. There
take a `write` fn and `uid` as parameters, and others on a per-message basis. These are set up using structs that
are part of this library.

To access the full functionality supported by ESP-Hosted, create a `RpcP` struct, then 
pass it, and a write fn to the `write_rpc_proto`. Constructing these `RpcP` structs is done IOC the `micropb` lib. Here's
an example, using the same heartbeat config as above. This is more verbose than our high-level API, but is more flexible:

```rust
    use esp_hosted::{RpcP, RpcTypeP, RpcIdP, Rpc_};

    fn init(buf: &mut [u8], uart: &mut Uart) {
        // let write = ... (Same as above)
        
        let mut hb_msg = RpcP::default();
        hb_msg.uid = 0;
        hb_msg.msg_type = RpcTypeP::Req;
        hb_msg.msg_id = RpcIdP::ReqConfigHeartbeat;

        let mut hb_cfg = Rpc_Req_ConfigHeartbeat::default();
        hb_cfg.enable = true;
        hb_cfg.duration = 10;

        hb_msg.payload = Some(Rpc_::Payload::ReqConfigHeartbeat(hb_cfg));

        esp_hosted::write_rpc_proto(buf, &mut write, hb_msg)?;
    }
```


### Example Wi-Fi initialization
You will run steps like this prior to using Wi-Fi functionality in many cases:

```rust
wifi::init(&mut buf, &mut write, 0, &wifi::InitConfig::default())?;

wifi::set_mode(&mut buf, &mut write, 0, WifiMode::Ap)?;

wifi::start(&mut buf, &mut write, 0)?;
```

## Building the proto file
This is not required if installing from crates.io; only applicable if working with the source directly.

The module `proto.rs` is not included directly in the source code; it's built from 
[esp_hosted_rpc.proto](https://github.com/espressif/esp-hosted-mcu/blob/main/common/proto/esp_hosted_rpc.proto) using 
[Micropb](https://github.com/YuhanLiin/micropb)

To build:

- **1**: Install the [protoc](https://grpc.io/docs/protoc-installation/) application, and place its on your system's path.
- **2:** Run the `build_proto` sub application with `cargo run` from its directory. This will place `esp_hosed_proto.rs` in this program's `src` folder.
