//! Simple websocket client example. //! //! This example connects to a websocket server that echoes messages, //! sends a message, receives the same message, and closes the connection. //! //! See the comment below as to how to build the example with ESP IDF 5+. pub fn main() { #[cfg(esp_idf_version_major = "4")] example::main().unwrap(); // Note that the ESP IDF websocket client IS available on ESP IDF >= 5 too // It is just that it is now an external component, so to use it, you need // to put the following snippet at the end of the `Cargo.toml` file of your binary crate: // // ```toml // [[package.metadata.esp-idf-sys.extra_components]] // remote_component = { name = "espressif/esp_websocket_client", version = "1.1.0" } // ``` #[cfg(not(esp_idf_version_major = "4"))] panic!("This example only compiles on ESP IDF 4. Check the comments in the example how to compile it on ESP IDF 5+") } #[cfg(esp_idf_version_major = "4")] pub mod example { use core::time::Duration; use esp_idf_svc::eventloop::EspSystemEventLoop; use esp_idf_svc::hal::peripherals::Peripherals; use esp_idf_svc::io::EspIOError; use esp_idf_svc::log::EspLogger; use esp_idf_svc::nvs::EspDefaultNvsPartition; use esp_idf_svc::tls::X509; use esp_idf_svc::wifi::*; use esp_idf_svc::ws::client::{ EspWebSocketClient, EspWebSocketClientConfig, FrameType, WebSocketEvent, WebSocketEventType, }; use log::info; use std::sync::mpsc; const SSID: &str = env!("WIFI_SSID"); const PASSWORD: &str = env!("WIFI_PASS"); const ECHO_SERVER_URI: &str = "wss://echo.websocket.org"; /// The PEM-encoded ISRG Root X1 certificate at the end of the cert chain /// for the websocket server at echo.websocket.org. const SERVER_ROOT_CERT: &[u8] = b" -----BEGIN CERTIFICATE----- MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= -----END CERTIFICATE-----\0"; /// The relevant events for this example as it connects to the server, /// sends a message, receives the same message, and closes the connection. #[derive(Debug, PartialEq)] enum ExampleEvent { Connected, MessageReceived, Closed, } pub fn main() -> anyhow::Result<()> { esp_idf_svc::sys::link_patches(); EspLogger::initialize_default(); // Setup Wifi let peripherals = Peripherals::take()?; let sys_loop = EspSystemEventLoop::take()?; let nvs = EspDefaultNvsPartition::take()?; let mut wifi = BlockingWifi::wrap( EspWifi::new(peripherals.modem, sys_loop.clone(), Some(nvs))?, sys_loop, )?; connect_wifi(&mut wifi)?; // Connect websocket let config = EspWebSocketClientConfig { server_cert: Some(X509::pem_until_nul(SERVER_ROOT_CERT)), ..Default::default() }; let timeout = Duration::from_secs(10); let (tx, rx) = mpsc::channel::(); let mut client = EspWebSocketClient::new(ECHO_SERVER_URI, &config, timeout, move |event| { handle_event(&tx, event) })?; assert_eq!(rx.recv(), Ok(ExampleEvent::Connected)); assert!(client.is_connected()); // Send message and receive it back let message = "Hello, World!"; info!("Websocket send, text: {}", message); client.send(FrameType::Text(false), message.as_bytes())?; assert_eq!(rx.recv(), Ok(ExampleEvent::MessageReceived)); // Close websocket drop(client); assert_eq!(rx.recv(), Ok(ExampleEvent::Closed)); Ok(()) } fn connect_wifi(wifi: &mut BlockingWifi>) -> anyhow::Result<()> { let wifi_configuration: Configuration = Configuration::Client(ClientConfiguration { ssid: SSID.try_into().unwrap(), bssid: None, auth_method: AuthMethod::WPA2Personal, password: PASSWORD.try_into().unwrap(), channel: None, ..Default::default() }); wifi.set_configuration(&wifi_configuration)?; wifi.start()?; info!("Wifi started"); wifi.connect()?; info!("Wifi connected"); wifi.wait_netif_up()?; info!("Wifi netif up"); Ok(()) } fn handle_event(tx: &mpsc::Sender, event: &Result) { if let Ok(event) = event { match event.event_type { WebSocketEventType::BeforeConnect => { info!("Websocket before connect"); } WebSocketEventType::Connected => { info!("Websocket connected"); tx.send(ExampleEvent::Connected).ok(); } WebSocketEventType::Disconnected => { info!("Websocket disconnected"); } WebSocketEventType::Close(reason) => { info!("Websocket close, reason: {reason:?}"); } WebSocketEventType::Closed => { info!("Websocket closed"); tx.send(ExampleEvent::Closed).ok(); } WebSocketEventType::Text(text) => { info!("Websocket recv, text: {text}"); if text == "Hello, World!" { tx.send(ExampleEvent::MessageReceived).ok(); } } WebSocketEventType::Binary(binary) => { info!("Websocket recv, binary: {binary:?}"); } WebSocketEventType::Ping => { info!("Websocket ping"); } WebSocketEventType::Pong => { info!("Websocket pong"); } } } } }