mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-30 05:40:39 +00:00
Remove the integrated blocking networking stack (#2488)
* Add simple example using `smoltcp-nal` * Remove the blocking networking stack * Renaming * Fix CI * CHANGELOG.md * Fixes after re-base * Update esp-wifi/MIGRATING-0.10.md Co-authored-by: Dominic Fischer <14130965+Dominaezzz@users.noreply.github.com> * Improve diff in migration guide --------- Co-authored-by: Dominic Fischer <14130965+Dominaezzz@users.noreply.github.com>
This commit is contained in:
parent
6cb5d9643f
commit
5d120f7a70
@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Features `phy-enable-usb` & `dump-packets` have been turned into configuration options `phy_enable_usb` & `dump_packets` (#2446)
|
||||
- Features `ps-min-modem` & `ps-max-modem` have been removed in favour of a runtime config (#2446)
|
||||
|
||||
- The blocking networking stack is removed (#2488)
|
||||
|
||||
## 0.10.1 - 2024-10-10
|
||||
|
||||
|
@ -108,7 +108,7 @@ coex = []
|
||||
sys-logs = ["esp-wifi-sys/sys-logs"]
|
||||
|
||||
## Enable support for `defmt`
|
||||
defmt = ["dep:defmt", "smoltcp?/defmt", "esp-hal/defmt", "bt-hci?/defmt", "esp-wifi-sys/defmt"]
|
||||
defmt = ["dep:defmt", "smoltcp?/defmt", "esp-hal/defmt", "bt-hci?/defmt", "esp-wifi-sys/defmt", "heapless/defmt-03"]
|
||||
|
||||
## Enable support for the `log` crate
|
||||
log = ["dep:log", "esp-hal/log", "esp-wifi-sys/log"]
|
||||
@ -131,32 +131,6 @@ smoltcp = ["dep:smoltcp"]
|
||||
## Provide utilities for smoltcp initialization. Adds smoltcp dependency
|
||||
utils = ["smoltcp"]
|
||||
|
||||
## IPv6 support. Includes utils feature
|
||||
ipv6 = ["wifi", "utils", "smoltcp?/proto-ipv6"]
|
||||
|
||||
## IPv4 support. Includes utils feature
|
||||
ipv4 = ["wifi", "utils", "smoltcp?/proto-ipv4"]
|
||||
|
||||
## TCP socket support. Includes ipv4 feature
|
||||
tcp = ["ipv4", "smoltcp?/socket-tcp"]
|
||||
|
||||
## UDP socket support. Includes ipv4 feature
|
||||
udp = ["ipv4", "smoltcp?/socket-udp"]
|
||||
|
||||
## ICMP socket support. Includes ipv4 feature
|
||||
icmp = ["ipv4", "smoltcp?/socket-icmp"]
|
||||
|
||||
## IGMP (multicast) support. Includes ipv4 featu
|
||||
igmp = ["ipv4", "smoltcp?/proto-igmp"]
|
||||
|
||||
## DNS support. Includes udp feature
|
||||
dns = ["udp", "smoltcp?/proto-dns", "smoltcp?/socket-dns"]
|
||||
|
||||
## DHCPv4 support, both creating sockets and autoconfiguring network settings. Includes utils feature
|
||||
dhcpv4 = ["wifi", "utils", "smoltcp?/proto-dhcpv4", "smoltcp?/socket-dhcpv4"]
|
||||
|
||||
## Convenience to enable "ipv4", "tcp", "udp", "icmp", "igmp", "dns", "dhcpv4"
|
||||
wifi-default = ["ipv4", "tcp", "udp", "icmp", "igmp", "dns", "dhcpv4"]
|
||||
|
||||
# Implement serde Serialize / Deserialize
|
||||
serde = ["dep:serde", "enumset?/serde", "heapless/serde"]
|
||||
|
@ -42,3 +42,37 @@ The cost of this is that we need to rename the various `async` methods on `WifiC
|
||||
- controller.start().await.unwrap();
|
||||
+ controller.start_async().await.unwrap();
|
||||
```
|
||||
|
||||
## The blocking networking stack was removed
|
||||
|
||||
The blocking networking stack is not included anymore. You can use e.g. `smoltcp-nal` or even use `smoltcp` directly.
|
||||
|
||||
For an easy migration path there is https://github.com/bjoernQ/blocking-network-stack.git which is basically the previously included networking stack as it's
|
||||
own crate.
|
||||
|
||||
The `create_network_interface` function doesn't take `&mut SocketSet[..]` anymore.
|
||||
|
||||
```diff
|
||||
+use blocking_network_stack::Stack;
|
||||
use esp_wifi::{
|
||||
- wifi_interface::WifiStack,
|
||||
};
|
||||
|
||||
+ let mut rng = Rng::new(peripherals.RNG);
|
||||
- let init = init(timg0.timer0, rng, peripherals.RADIO_CLK).unwrap();
|
||||
+ let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap();
|
||||
|
||||
let mut wifi = peripherals.WIFI;
|
||||
let mut socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
+ let (iface, device, mut controller) =
|
||||
+ create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap();
|
||||
+
|
||||
- let (iface, device, mut controller, sockets) =
|
||||
- create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap();
|
||||
+ let socket_set = SocketSet::new(&mut socket_set_entries[..]);
|
||||
let now = || time::now().duration_since_epoch().to_millis();
|
||||
- let wifi_stack = WifiStack::new(iface, device, sockets, now);
|
||||
+ let wifi_stack = Stack::new(iface, device, socket_set, now, rng.random());
|
||||
```
|
||||
|
||||
The related features are removed from `esp-wifi`: wifi-default, ipv6, ipv4, tcp, udp, icmp, igmp, dns, dhcpv4
|
||||
|
@ -136,17 +136,6 @@ pub mod tasks;
|
||||
|
||||
pub(crate) mod memory_fence;
|
||||
|
||||
#[cfg(all(feature = "wifi", any(feature = "tcp", feature = "udp")))]
|
||||
pub mod wifi_interface;
|
||||
|
||||
// [esp_hal::time::now()] as a smoltcp [`Instant]`
|
||||
#[cfg(feature = "smoltcp")]
|
||||
fn timestamp() -> smoltcp::time::Instant {
|
||||
smoltcp::time::Instant::from_micros(
|
||||
esp_hal::time::now().duration_since_epoch().to_micros() as i64
|
||||
)
|
||||
}
|
||||
|
||||
// this is just to verify that we use the correct defaults in `build.rs`
|
||||
#[allow(clippy::assertions_on_constants)] // TODO: try assert_eq once it's usable in const context
|
||||
const _: () = {
|
||||
|
@ -829,282 +829,6 @@ impl Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/// IPv4 network configurations.
|
||||
pub mod ipv4 {
|
||||
pub use core::net::Ipv4Addr;
|
||||
use core::{fmt::Display, str::FromStr};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents a subnet mask.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub struct Mask(pub u8);
|
||||
|
||||
impl FromStr for Mask {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
s.parse::<u8>()
|
||||
.map_err(|_| "Invalid subnet mask")
|
||||
.map_or_else(Err, |mask| {
|
||||
if (1..=32).contains(&mask) {
|
||||
Ok(Mask(mask))
|
||||
} else {
|
||||
Err("Mask should be a number between 1 and 32")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Mask {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Ipv4Addr> for Mask {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(ip: Ipv4Addr) -> Result<Self, Self::Error> {
|
||||
let octets = ip.octets();
|
||||
let addr: u32 = ((octets[0] as u32 & 0xff) << 24)
|
||||
| ((octets[1] as u32 & 0xff) << 16)
|
||||
| ((octets[2] as u32 & 0xff) << 8)
|
||||
| (octets[3] as u32 & 0xff);
|
||||
|
||||
if addr.leading_ones() + addr.trailing_zeros() == 32 {
|
||||
Ok(Mask(addr.leading_ones() as u8))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mask> for Ipv4Addr {
|
||||
fn from(mask: Mask) -> Self {
|
||||
let addr: u32 = ((1 << (32 - mask.0)) - 1) ^ 0xffffffffu32;
|
||||
|
||||
let (a, b, c, d) = (
|
||||
((addr >> 24) & 0xff) as u8,
|
||||
((addr >> 16) & 0xff) as u8,
|
||||
((addr >> 8) & 0xff) as u8,
|
||||
(addr & 0xff) as u8,
|
||||
);
|
||||
|
||||
Ipv4Addr::new(a, b, c, d)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a subnet consisting of a gateway and a mask.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub struct Subnet {
|
||||
/// The gateway IP address of the subnet.
|
||||
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
|
||||
pub gateway: Ipv4Addr,
|
||||
/// The subnet mask associated with the subnet.
|
||||
pub mask: Mask,
|
||||
}
|
||||
|
||||
impl Display for Subnet {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{}/{}", self.gateway, self.mask)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Subnet {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut split = s.split('/');
|
||||
if let Some(gateway_str) = split.next() {
|
||||
if let Some(mask_str) = split.next() {
|
||||
if split.next().is_none() {
|
||||
if let Ok(gateway) = gateway_str.parse::<Ipv4Addr>() {
|
||||
return mask_str.parse::<Mask>().map(|mask| Self { gateway, mask });
|
||||
} else {
|
||||
return Err("Invalid IP address format, expected XXX.XXX.XXX.XXX");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err("Expected <gateway-ip-address>/<mask>")
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings for a client in an IPv4 network.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub struct ClientSettings {
|
||||
/// The client's IPv4 address.
|
||||
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
|
||||
pub ip: Ipv4Addr,
|
||||
|
||||
/// The subnet associated with the client's IP address.
|
||||
pub subnet: Subnet,
|
||||
|
||||
/// The primary DNS server for name resolution.
|
||||
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
|
||||
pub dns: Option<Ipv4Addr>,
|
||||
|
||||
/// The secondary DNS server for name resolution.
|
||||
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
|
||||
pub secondary_dns: Option<Ipv4Addr>,
|
||||
}
|
||||
|
||||
impl Default for ClientSettings {
|
||||
fn default() -> ClientSettings {
|
||||
ClientSettings {
|
||||
ip: Ipv4Addr::new(192, 168, 71, 200),
|
||||
subnet: Subnet {
|
||||
gateway: Ipv4Addr::new(192, 168, 71, 1),
|
||||
mask: Mask(24),
|
||||
},
|
||||
dns: Some(Ipv4Addr::new(8, 8, 8, 8)),
|
||||
secondary_dns: Some(Ipv4Addr::new(8, 8, 4, 4)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings for the DHCP client.
|
||||
///
|
||||
/// This struct contains the configuration for a DHCP client, including a
|
||||
/// hostname that can be sent during DHCP negotiations.
|
||||
#[derive(Default, Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub struct DHCPClientSettings {
|
||||
pub hostname: Option<heapless::String<30>>,
|
||||
}
|
||||
|
||||
/// Configuration for the client in an IPv4 network.
|
||||
///
|
||||
/// This enum defines how the client's IP settings are obtained: either
|
||||
/// through DHCP, or as a fixed (static) configuration.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub enum ClientConfiguration {
|
||||
/// Use DHCP to obtain network settings.
|
||||
DHCP(DHCPClientSettings),
|
||||
|
||||
/// Use a fixed configuration for network settings.
|
||||
Fixed(ClientSettings),
|
||||
}
|
||||
|
||||
impl ClientConfiguration {
|
||||
/// Returns a reference to the fixed settings if the client is using a
|
||||
/// static configuration, `None` otherwise.
|
||||
pub fn as_fixed_settings_ref(&self) -> Option<&ClientSettings> {
|
||||
match self {
|
||||
Self::Fixed(client_settings) => Some(client_settings),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the fixed settings, creating a
|
||||
/// default fixed configuration if necessary.
|
||||
pub fn as_fixed_settings_mut(&mut self) -> &mut ClientSettings {
|
||||
match self {
|
||||
Self::Fixed(client_settings) => client_settings,
|
||||
_ => {
|
||||
*self = ClientConfiguration::Fixed(Default::default());
|
||||
self.as_fixed_settings_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ClientConfiguration {
|
||||
fn default() -> ClientConfiguration {
|
||||
ClientConfiguration::DHCP(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Router configuration in an IPv4 network.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub struct RouterConfiguration {
|
||||
/// The subnet the router is responsible for.
|
||||
pub subnet: Subnet,
|
||||
|
||||
/// Indicates whether DHCP is enabled on the router.
|
||||
pub dhcp_enabled: bool,
|
||||
|
||||
/// The primary DNS server for the router.
|
||||
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
|
||||
pub dns: Option<Ipv4Addr>,
|
||||
|
||||
/// The secondary DNS server for the router.
|
||||
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
|
||||
pub secondary_dns: Option<Ipv4Addr>,
|
||||
}
|
||||
|
||||
impl Default for RouterConfiguration {
|
||||
fn default() -> RouterConfiguration {
|
||||
RouterConfiguration {
|
||||
subnet: Subnet {
|
||||
gateway: Ipv4Addr::new(192, 168, 71, 1),
|
||||
mask: Mask(24),
|
||||
},
|
||||
dhcp_enabled: true,
|
||||
dns: Some(Ipv4Addr::new(8, 8, 8, 8)),
|
||||
secondary_dns: Some(Ipv4Addr::new(8, 8, 4, 4)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the network configuration for a device.
|
||||
///
|
||||
/// Holds either a client configuration (for devices connecting to a
|
||||
/// network) or a router configuration (for devices providing a network
|
||||
/// to other clients).
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub enum Configuration {
|
||||
/// Configuration for a device acting as a client in the network.
|
||||
Client(ClientConfiguration),
|
||||
|
||||
/// Configuration for a device acting as a router in the network.
|
||||
Router(RouterConfiguration),
|
||||
}
|
||||
|
||||
impl Default for Configuration {
|
||||
fn default() -> Self {
|
||||
Self::Client(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents IPv4 information for a device.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
pub struct IpInfo {
|
||||
/// The IPv4 address of the device.
|
||||
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
|
||||
pub ip: Ipv4Addr,
|
||||
|
||||
/// The subnet mask associated with the device's IP address.
|
||||
pub subnet: Subnet,
|
||||
|
||||
/// The primary DNS server for the device.
|
||||
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
|
||||
pub dns: Option<Ipv4Addr>,
|
||||
|
||||
/// The secondary DNS server for the device.
|
||||
#[cfg_attr(feature = "defmt", defmt(Debug2Format))]
|
||||
pub secondary_dns: Option<Ipv4Addr>,
|
||||
}
|
||||
}
|
||||
|
||||
trait AuthMethodExt {
|
||||
fn to_raw(&self) -> wifi_auth_mode_t;
|
||||
fn from_raw(raw: wifi_auth_mode_t) -> Self;
|
||||
|
@ -1,83 +1,59 @@
|
||||
//! Convenience utilities for non-async code
|
||||
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
use smoltcp::socket::dhcpv4::Socket as Dhcpv4Socket;
|
||||
use smoltcp::{
|
||||
iface::{Config, Interface, SocketSet, SocketStorage},
|
||||
iface::{Config, Interface},
|
||||
wire::{EthernetAddress, HardwareAddress},
|
||||
};
|
||||
|
||||
use super::{WifiApDevice, WifiController, WifiDevice, WifiDeviceMode, WifiError, WifiStaDevice};
|
||||
use crate::{timestamp, EspWifiController};
|
||||
use crate::EspWifiController;
|
||||
|
||||
fn setup_iface<'a, MODE: WifiDeviceMode>(
|
||||
device: &mut WifiDevice<'_, MODE>,
|
||||
mode: MODE,
|
||||
storage: &'a mut [SocketStorage<'a>],
|
||||
) -> (Interface, SocketSet<'a>) {
|
||||
// [esp_hal::time::now()] as a smoltcp [`Instant]`
|
||||
#[cfg(feature = "smoltcp")]
|
||||
fn timestamp() -> smoltcp::time::Instant {
|
||||
smoltcp::time::Instant::from_micros(
|
||||
esp_hal::time::now().duration_since_epoch().to_micros() as i64
|
||||
)
|
||||
}
|
||||
|
||||
fn setup_iface<MODE: WifiDeviceMode>(device: &mut WifiDevice<'_, MODE>, mode: MODE) -> Interface {
|
||||
let mac = mode.mac_address();
|
||||
let hw_address = HardwareAddress::Ethernet(EthernetAddress::from_bytes(&mac));
|
||||
|
||||
let config = Config::new(hw_address);
|
||||
let iface = Interface::new(config, device, timestamp());
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut socket_set = SocketSet::new(storage);
|
||||
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
if mode.mode().is_sta() {
|
||||
// only add DHCP client in STA mode
|
||||
let dhcp_socket = Dhcpv4Socket::new();
|
||||
socket_set.add(dhcp_socket);
|
||||
}
|
||||
|
||||
(iface, socket_set)
|
||||
iface
|
||||
}
|
||||
|
||||
/// Convenient way to create an `smoltcp` ethernet interface
|
||||
/// You can use the provided macros to create and pass a suitable backing
|
||||
/// storage.
|
||||
pub fn create_network_interface<'a, 'd, MODE: WifiDeviceMode>(
|
||||
pub fn create_network_interface<'d, MODE: WifiDeviceMode>(
|
||||
inited: &'d EspWifiController<'d>,
|
||||
device: impl crate::hal::peripheral::Peripheral<P = crate::hal::peripherals::WIFI> + 'd,
|
||||
mode: MODE,
|
||||
storage: &'a mut [SocketStorage<'a>],
|
||||
) -> Result<
|
||||
(
|
||||
Interface,
|
||||
WifiDevice<'d, MODE>,
|
||||
WifiController<'d>,
|
||||
SocketSet<'a>,
|
||||
),
|
||||
WifiError,
|
||||
> {
|
||||
) -> Result<(Interface, WifiDevice<'d, MODE>, WifiController<'d>), WifiError> {
|
||||
let (mut device, controller) = crate::wifi::new_with_mode(inited, device, mode)?;
|
||||
|
||||
let (iface, socket_set) = setup_iface(&mut device, mode, storage);
|
||||
let iface = setup_iface(&mut device, mode);
|
||||
|
||||
Ok((iface, device, controller, socket_set))
|
||||
Ok((iface, device, controller))
|
||||
}
|
||||
|
||||
pub struct ApStaInterface<'a, 'd> {
|
||||
pub struct ApStaInterface<'d> {
|
||||
pub ap_interface: Interface,
|
||||
pub sta_interface: Interface,
|
||||
pub ap_device: WifiDevice<'d, WifiApDevice>,
|
||||
pub sta_device: WifiDevice<'d, WifiStaDevice>,
|
||||
pub controller: WifiController<'d>,
|
||||
pub ap_socket_set: SocketSet<'a>,
|
||||
pub sta_socket_set: SocketSet<'a>,
|
||||
}
|
||||
|
||||
pub fn create_ap_sta_network_interface<'a, 'd>(
|
||||
pub fn create_ap_sta_network_interface<'d>(
|
||||
inited: &'d EspWifiController<'d>,
|
||||
device: impl crate::hal::peripheral::Peripheral<P = crate::hal::peripherals::WIFI> + 'd,
|
||||
ap_storage: &'a mut [SocketStorage<'a>],
|
||||
sta_storage: &'a mut [SocketStorage<'a>],
|
||||
) -> Result<ApStaInterface<'a, 'd>, WifiError> {
|
||||
) -> Result<ApStaInterface<'d>, WifiError> {
|
||||
let (mut ap_device, mut sta_device, controller) = crate::wifi::new_ap_sta(inited, device)?;
|
||||
|
||||
let (ap_interface, ap_socket_set) = setup_iface(&mut ap_device, WifiApDevice, ap_storage);
|
||||
let (sta_interface, sta_socket_set) = setup_iface(&mut sta_device, WifiStaDevice, sta_storage);
|
||||
let ap_interface = setup_iface(&mut ap_device, WifiApDevice);
|
||||
let sta_interface = setup_iface(&mut sta_device, WifiStaDevice);
|
||||
|
||||
Ok(ApStaInterface {
|
||||
ap_interface,
|
||||
@ -85,7 +61,5 @@ pub fn create_ap_sta_network_interface<'a, 'd>(
|
||||
ap_device,
|
||||
sta_device,
|
||||
controller,
|
||||
ap_socket_set,
|
||||
sta_socket_set,
|
||||
})
|
||||
}
|
||||
|
@ -1,995 +0,0 @@
|
||||
//! Non-async Networking primitives for TCP/UDP communication.
|
||||
|
||||
use core::{borrow::BorrowMut, cell::RefCell, fmt::Display};
|
||||
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
use smoltcp::socket::dhcpv4::Socket as Dhcpv4Socket;
|
||||
#[cfg(feature = "tcp")]
|
||||
use smoltcp::socket::tcp::Socket as TcpSocket;
|
||||
#[cfg(feature = "dns")]
|
||||
use smoltcp::wire::DnsQueryType;
|
||||
#[cfg(feature = "udp")]
|
||||
use smoltcp::wire::IpEndpoint;
|
||||
use smoltcp::{
|
||||
iface::{Interface, SocketHandle, SocketSet},
|
||||
time::Instant,
|
||||
wire::{IpAddress, IpCidr, Ipv4Address},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
timestamp,
|
||||
wifi::{ipv4, WifiDevice, WifiDeviceMode},
|
||||
};
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
const LOCAL_PORT_MIN: u16 = 41000;
|
||||
#[cfg(feature = "tcp")]
|
||||
const LOCAL_PORT_MAX: u16 = 65535;
|
||||
|
||||
/// Non-async TCP/IP network stack
|
||||
///
|
||||
/// Mostly a convenience wrapper for `smoltcp`
|
||||
pub struct WifiStack<'a, MODE: WifiDeviceMode> {
|
||||
device: RefCell<WifiDevice<'a, MODE>>,
|
||||
network_interface: RefCell<Interface>,
|
||||
sockets: RefCell<SocketSet<'a>>,
|
||||
current_millis_fn: fn() -> u64,
|
||||
#[cfg(feature = "tcp")]
|
||||
local_port: RefCell<u16>,
|
||||
pub(crate) network_config: RefCell<ipv4::Configuration>,
|
||||
pub(crate) ip_info: RefCell<Option<ipv4::IpInfo>>,
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
pub(crate) dhcp_socket_handle: RefCell<Option<SocketHandle>>,
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
pub(crate) old_connected: RefCell<bool>,
|
||||
#[cfg(feature = "dns")]
|
||||
dns_socket_handle: RefCell<Option<SocketHandle>>,
|
||||
}
|
||||
|
||||
impl<'a, MODE: WifiDeviceMode> WifiStack<'a, MODE> {
|
||||
/// Creates new `WifiStack` instance.
|
||||
///
|
||||
/// Handles optional DHCP/DNS features and sets up the
|
||||
/// configuration for the network interface.
|
||||
pub fn new(
|
||||
network_interface: Interface,
|
||||
device: WifiDevice<'a, MODE>,
|
||||
#[allow(unused_mut)] mut sockets: SocketSet<'a>,
|
||||
current_millis_fn: fn() -> u64,
|
||||
) -> WifiStack<'a, MODE> {
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
let mut dhcp_socket_handle: Option<SocketHandle> = None;
|
||||
#[cfg(feature = "dns")]
|
||||
let mut dns_socket_handle: Option<SocketHandle> = None;
|
||||
|
||||
#[cfg(any(feature = "dhcpv4", feature = "dns"))]
|
||||
for (handle, socket) in sockets.iter_mut() {
|
||||
match socket {
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
smoltcp::socket::Socket::Dhcpv4(_) => dhcp_socket_handle = Some(handle),
|
||||
#[cfg(feature = "dns")]
|
||||
smoltcp::socket::Socket::Dns(_) => dns_socket_handle = Some(handle),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let this = Self {
|
||||
device: RefCell::new(device),
|
||||
network_interface: RefCell::new(network_interface),
|
||||
network_config: RefCell::new(ipv4::Configuration::Client(
|
||||
ipv4::ClientConfiguration::DHCP(ipv4::DHCPClientSettings {
|
||||
// FIXME: smoltcp currently doesn't have a way of giving a hostname through DHCP
|
||||
hostname: Some(unwrap!("Espressif".try_into().ok())),
|
||||
}),
|
||||
)),
|
||||
ip_info: RefCell::new(None),
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
dhcp_socket_handle: RefCell::new(dhcp_socket_handle),
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
old_connected: RefCell::new(false),
|
||||
sockets: RefCell::new(sockets),
|
||||
current_millis_fn,
|
||||
#[cfg(feature = "tcp")]
|
||||
local_port: RefCell::new(
|
||||
(unsafe { crate::common_adapter::random() }
|
||||
% (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u32) as u16
|
||||
+ LOCAL_PORT_MIN,
|
||||
),
|
||||
#[cfg(feature = "dns")]
|
||||
dns_socket_handle: RefCell::new(dns_socket_handle),
|
||||
};
|
||||
|
||||
this.reset();
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
/// Update the interface configuration
|
||||
pub fn update_iface_configuration(
|
||||
&self,
|
||||
conf: &ipv4::Configuration,
|
||||
) -> Result<(), WifiStackError> {
|
||||
let mac = self.device.borrow().mac_address();
|
||||
let hw_address = smoltcp::wire::HardwareAddress::Ethernet(
|
||||
smoltcp::wire::EthernetAddress::from_bytes(&mac),
|
||||
);
|
||||
self.network_interface
|
||||
.borrow_mut()
|
||||
.set_hardware_addr(hw_address);
|
||||
info!("Set hardware address: {:?}", hw_address);
|
||||
|
||||
self.reset(); // reset IP address
|
||||
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
{
|
||||
let mut dhcp_socket_handle_ref = self.dhcp_socket_handle.borrow_mut();
|
||||
let mut sockets_ref = self.sockets.borrow_mut();
|
||||
|
||||
if let Some(dhcp_handle) = *dhcp_socket_handle_ref {
|
||||
// remove the DHCP client if we use a static IP
|
||||
if matches!(
|
||||
conf,
|
||||
ipv4::Configuration::Client(ipv4::ClientConfiguration::Fixed(_))
|
||||
) {
|
||||
sockets_ref.remove(dhcp_handle);
|
||||
*dhcp_socket_handle_ref = None;
|
||||
}
|
||||
}
|
||||
|
||||
// re-add the DHCP client if we use DHCP and it has been removed before
|
||||
if matches!(
|
||||
conf,
|
||||
ipv4::Configuration::Client(ipv4::ClientConfiguration::DHCP(_))
|
||||
) && dhcp_socket_handle_ref.is_none()
|
||||
{
|
||||
let dhcp_socket = Dhcpv4Socket::new();
|
||||
let dhcp_socket_handle = sockets_ref.add(dhcp_socket);
|
||||
*dhcp_socket_handle_ref = Some(dhcp_socket_handle);
|
||||
}
|
||||
|
||||
if let Some(dhcp_handle) = *dhcp_socket_handle_ref {
|
||||
let dhcp_socket = sockets_ref.get_mut::<Dhcpv4Socket>(dhcp_handle);
|
||||
info!("Reset DHCP client");
|
||||
dhcp_socket.reset();
|
||||
}
|
||||
}
|
||||
|
||||
*self.network_config.borrow_mut() = conf.clone();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reset the stack
|
||||
pub fn reset(&self) {
|
||||
debug!("Reset TCP stack");
|
||||
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
{
|
||||
let dhcp_socket_handle_ref = self.dhcp_socket_handle.borrow_mut();
|
||||
if let Some(dhcp_handle) = *dhcp_socket_handle_ref {
|
||||
self.with_mut(|_, _, sockets| {
|
||||
let dhcp_socket = sockets.get_mut::<Dhcpv4Socket>(dhcp_handle);
|
||||
debug!("Reset DHCP client");
|
||||
dhcp_socket.reset();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.with_mut(|interface, _, _| {
|
||||
interface.routes_mut().remove_default_ipv4_route();
|
||||
interface.update_ip_addrs(|addrs| {
|
||||
addrs.clear();
|
||||
});
|
||||
|
||||
#[cfg(feature = "ipv6")]
|
||||
{
|
||||
unwrap!(interface.routes_mut().add_default_ipv6_route(
|
||||
smoltcp::wire::Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0,)
|
||||
));
|
||||
|
||||
let mut mac = [0u8; 6];
|
||||
match interface.hardware_addr() {
|
||||
smoltcp::wire::HardwareAddress::Ethernet(hw_address) => {
|
||||
mac.copy_from_slice(hw_address.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
let a4 = ((mac[0] ^ 2) as u16) << 8 | mac[1] as u16;
|
||||
let a5 = (mac[2] as u16) << 8 | 0xff;
|
||||
let a6 = 0xfe << 8 | mac[3] as u16;
|
||||
let a7 = (mac[4] as u16) << 8 | mac[5] as u16;
|
||||
|
||||
info!(
|
||||
"IPv6 link-local address fe80::{:x}:{:x}:{:x}:{:x}",
|
||||
a4, a5, a6, a7
|
||||
);
|
||||
|
||||
interface.update_ip_addrs(|addrs| {
|
||||
unwrap!(addrs.push(IpCidr::new(
|
||||
smoltcp::wire::IpAddress::v6(0xfe80, 0, 0, 0, a4, a5, a6, a7),
|
||||
64,
|
||||
)));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Retrieve all current IP addresses
|
||||
pub fn get_ip_addresses(&self, f: impl FnOnce(&[smoltcp::wire::IpCidr])) {
|
||||
self.with_mut(|interface, _, _| f(interface.ip_addrs()))
|
||||
}
|
||||
|
||||
/// Convenience function to poll the DHCP socket.
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
pub fn poll_dhcp(
|
||||
&self,
|
||||
interface: &mut Interface,
|
||||
sockets: &mut SocketSet<'a>,
|
||||
) -> Result<(), WifiStackError> {
|
||||
let dhcp_socket_handle_ref = self.dhcp_socket_handle.borrow_mut();
|
||||
if let Some(dhcp_handle) = *dhcp_socket_handle_ref {
|
||||
let dhcp_socket = sockets.get_mut::<Dhcpv4Socket>(dhcp_handle);
|
||||
|
||||
let connected = matches!(
|
||||
crate::wifi::get_sta_state(),
|
||||
crate::wifi::WifiState::StaConnected
|
||||
);
|
||||
|
||||
if connected && !*self.old_connected.borrow() {
|
||||
dhcp_socket.reset();
|
||||
}
|
||||
|
||||
*self.old_connected.borrow_mut() = connected;
|
||||
|
||||
let event = dhcp_socket.poll();
|
||||
if let Some(event) = event {
|
||||
match event {
|
||||
smoltcp::socket::dhcpv4::Event::Deconfigured => {
|
||||
*self.ip_info.borrow_mut() = None;
|
||||
interface.routes_mut().remove_default_ipv4_route();
|
||||
}
|
||||
smoltcp::socket::dhcpv4::Event::Configured(config) => {
|
||||
let dns = config.dns_servers.first();
|
||||
*self.ip_info.borrow_mut() = Some(ipv4::IpInfo {
|
||||
ip: config.address.address().0.into(),
|
||||
subnet: ipv4::Subnet {
|
||||
gateway: unwrap!(config.router).0.into(),
|
||||
mask: ipv4::Mask(config.address.prefix_len()),
|
||||
},
|
||||
dns: dns.map(|x| x.0.into()),
|
||||
secondary_dns: config.dns_servers.get(1).map(|x| x.0.into()),
|
||||
});
|
||||
|
||||
let address = config.address;
|
||||
interface.borrow_mut().update_ip_addrs(|addrs| {
|
||||
unwrap!(addrs.push(smoltcp::wire::IpCidr::Ipv4(address)));
|
||||
});
|
||||
if let Some(route) = config.router {
|
||||
unwrap!(interface.routes_mut().add_default_ipv4_route(route));
|
||||
}
|
||||
|
||||
#[cfg(feature = "dns")]
|
||||
if let (Some(&dns), Some(dns_handle)) =
|
||||
(dns, *self.dns_socket_handle.borrow())
|
||||
{
|
||||
sockets
|
||||
.get_mut::<smoltcp::socket::dns::Socket>(dns_handle)
|
||||
.update_servers(&[dns.into()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a new [Socket]
|
||||
#[cfg(feature = "tcp")]
|
||||
pub fn get_socket<'s>(
|
||||
&'s self,
|
||||
rx_buffer: &'a mut [u8],
|
||||
tx_buffer: &'a mut [u8],
|
||||
) -> Socket<'s, 'a, MODE>
|
||||
where
|
||||
'a: 's,
|
||||
{
|
||||
let socket = TcpSocket::new(
|
||||
smoltcp::socket::tcp::SocketBuffer::new(rx_buffer),
|
||||
smoltcp::socket::tcp::SocketBuffer::new(tx_buffer),
|
||||
);
|
||||
|
||||
let socket_handle =
|
||||
self.with_mut(|_interface, _device, sockets| sockets.borrow_mut().add(socket));
|
||||
|
||||
Socket {
|
||||
socket_handle,
|
||||
network: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [UdpSocket]
|
||||
#[cfg(feature = "udp")]
|
||||
pub fn get_udp_socket<'s>(
|
||||
&'s self,
|
||||
rx_meta: &'a mut [smoltcp::socket::udp::PacketMetadata],
|
||||
rx_buffer: &'a mut [u8],
|
||||
tx_meta: &'a mut [smoltcp::socket::udp::PacketMetadata],
|
||||
tx_buffer: &'a mut [u8],
|
||||
) -> UdpSocket<'s, 'a, MODE>
|
||||
where
|
||||
'a: 's,
|
||||
{
|
||||
let socket = smoltcp::socket::udp::Socket::new(
|
||||
smoltcp::socket::udp::PacketBuffer::new(rx_meta, rx_buffer),
|
||||
smoltcp::socket::udp::PacketBuffer::new(tx_meta, tx_buffer),
|
||||
);
|
||||
|
||||
let socket_handle =
|
||||
self.with_mut(|_interface, _device, sockets| sockets.borrow_mut().add(socket));
|
||||
|
||||
UdpSocket {
|
||||
socket_handle,
|
||||
network: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if DNS is configured
|
||||
#[cfg(feature = "dns")]
|
||||
pub fn is_dns_configured(&self) -> bool {
|
||||
self.dns_socket_handle.borrow().is_some()
|
||||
}
|
||||
|
||||
/// Configure DNS
|
||||
#[cfg(feature = "dns")]
|
||||
pub fn configure_dns(
|
||||
&'a self,
|
||||
servers: &[IpAddress],
|
||||
query_storage: &'a mut [Option<smoltcp::socket::dns::DnsQuery>],
|
||||
) {
|
||||
if let Some(old_handle) = self.dns_socket_handle.take() {
|
||||
self.with_mut(|_interface, _device, sockets| sockets.remove(old_handle));
|
||||
// the returned socket get dropped and frees a slot for the new one
|
||||
}
|
||||
|
||||
let dns = smoltcp::socket::dns::Socket::new(servers, query_storage);
|
||||
let handle = self.with_mut(|_interface, _device, sockets| sockets.add(dns));
|
||||
self.dns_socket_handle.replace(Some(handle));
|
||||
}
|
||||
|
||||
/// Update the DNS servers
|
||||
#[cfg(feature = "dns")]
|
||||
pub fn update_dns_servers(&self, servers: &[IpAddress]) {
|
||||
if let Some(dns_handle) = *self.dns_socket_handle.borrow_mut() {
|
||||
self.with_mut(|_interface, _device, sockets| {
|
||||
sockets
|
||||
.get_mut::<smoltcp::socket::dns::Socket>(dns_handle)
|
||||
.update_servers(servers);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a DNS query
|
||||
#[cfg(feature = "dns")]
|
||||
pub fn dns_query(
|
||||
&self,
|
||||
name: &str,
|
||||
query_type: DnsQueryType,
|
||||
) -> Result<heapless::Vec<IpAddress, { smoltcp::config::DNS_MAX_RESULT_COUNT }>, WifiStackError>
|
||||
{
|
||||
use smoltcp::socket::dns;
|
||||
|
||||
match query_type {
|
||||
// check if name is already an IP
|
||||
DnsQueryType::A => {
|
||||
if let Ok(ip) = name.parse::<Ipv4Address>() {
|
||||
return Ok([ip.into()].into_iter().collect());
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ipv6")]
|
||||
DnsQueryType::Aaaa => {
|
||||
if let Ok(ip) = name.parse::<smoltcp::wire::Ipv6Address>() {
|
||||
return Ok([ip.into()].into_iter().collect());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let Some(dns_handle) = *self.dns_socket_handle.borrow() else {
|
||||
return Err(WifiStackError::DnsNotConfigured);
|
||||
};
|
||||
|
||||
let query = self.with_mut(|interface, _device, sockets| {
|
||||
sockets
|
||||
.get_mut::<dns::Socket>(dns_handle)
|
||||
.start_query(interface.context(), name, query_type)
|
||||
.map_err(WifiStackError::DnsQueryError)
|
||||
})?;
|
||||
|
||||
loop {
|
||||
self.work();
|
||||
|
||||
let result = self.with_mut(|_interface, _device, sockets| {
|
||||
sockets
|
||||
.get_mut::<dns::Socket>(dns_handle)
|
||||
.get_query_result(query)
|
||||
});
|
||||
|
||||
match result {
|
||||
Ok(addrs) => return Ok(addrs), // query finished
|
||||
Err(dns::GetQueryResultError::Pending) => {} // query not finished
|
||||
Err(_) => return Err(WifiStackError::DnsQueryFailed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Let the stack make progress
|
||||
///
|
||||
/// Make sure to regularly call this function.
|
||||
pub fn work(&self) {
|
||||
loop {
|
||||
let did_work = self.with_mut(|interface, device, sockets| {
|
||||
let network_config = self.network_config.borrow().clone();
|
||||
if let ipv4::Configuration::Client(ipv4::ClientConfiguration::DHCP(_)) =
|
||||
network_config
|
||||
{
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
self.poll_dhcp(interface, sockets).ok();
|
||||
} else if let ipv4::Configuration::Client(ipv4::ClientConfiguration::Fixed(
|
||||
settings,
|
||||
)) = network_config
|
||||
{
|
||||
let addr = Ipv4Address::from_bytes(&settings.ip.octets());
|
||||
if !interface.has_ip_addr(addr) {
|
||||
let gateway = Ipv4Address::from_bytes(&settings.subnet.gateway.octets());
|
||||
interface.routes_mut().add_default_ipv4_route(gateway).ok();
|
||||
interface.update_ip_addrs(|addrs| {
|
||||
unwrap!(addrs.push(IpCidr::new(addr.into(), settings.subnet.mask.0)));
|
||||
});
|
||||
}
|
||||
}
|
||||
interface.poll(
|
||||
Instant::from_millis((self.current_millis_fn)() as i64),
|
||||
device,
|
||||
sockets,
|
||||
)
|
||||
});
|
||||
|
||||
if !did_work {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
fn next_local_port(&self) -> u16 {
|
||||
self.local_port.replace_with(|local_port| {
|
||||
if *local_port == LOCAL_PORT_MAX {
|
||||
LOCAL_PORT_MIN
|
||||
} else {
|
||||
*local_port + 1
|
||||
}
|
||||
});
|
||||
*self.local_port.borrow()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn with<R>(&self, f: impl FnOnce(&Interface, &WifiDevice<MODE>, &SocketSet<'a>) -> R) -> R {
|
||||
f(
|
||||
&self.network_interface.borrow(),
|
||||
&self.device.borrow(),
|
||||
&self.sockets.borrow(),
|
||||
)
|
||||
}
|
||||
|
||||
fn with_mut<R>(
|
||||
&self,
|
||||
f: impl FnOnce(&mut Interface, &mut WifiDevice<MODE>, &mut SocketSet<'a>) -> R,
|
||||
) -> R {
|
||||
f(
|
||||
&mut self.network_interface.borrow_mut(),
|
||||
&mut self.device.borrow_mut(),
|
||||
&mut self.sockets.borrow_mut(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors returned by functions in this module
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum WifiStackError {
|
||||
/// An unknown error occurred, with the associated error code.
|
||||
Unknown(i32),
|
||||
|
||||
/// An error occurred during Wi-Fi stack initialization.
|
||||
InitializationError(crate::InitializationError),
|
||||
|
||||
/// A common Wi-Fi error occured.
|
||||
DeviceError(crate::wifi::WifiError),
|
||||
|
||||
/// Couldn't get the device's IP.
|
||||
MissingIp,
|
||||
|
||||
/// DNS is not configured.
|
||||
#[cfg(feature = "dns")]
|
||||
DnsNotConfigured,
|
||||
|
||||
/// An error occurred when starting a DNS query.
|
||||
#[cfg(feature = "dns")]
|
||||
DnsQueryError(smoltcp::socket::dns::StartQueryError),
|
||||
|
||||
/// Cannot get result from a DNS query.
|
||||
#[cfg(feature = "dns")]
|
||||
DnsQueryFailed,
|
||||
}
|
||||
|
||||
impl Display for WifiStackError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE: WifiDeviceMode> WifiStack<'_, MODE> {
|
||||
/// Retrieves the current interface configuration.
|
||||
pub fn get_iface_configuration(&self) -> Result<ipv4::Configuration, WifiStackError> {
|
||||
Ok(self.network_config.borrow().clone())
|
||||
}
|
||||
|
||||
/// Sets a new interface configuration using the provided IPv4
|
||||
/// configuration.
|
||||
pub fn set_iface_configuration(
|
||||
&mut self,
|
||||
conf: &ipv4::Configuration,
|
||||
) -> Result<(), WifiStackError> {
|
||||
self.update_iface_configuration(conf)
|
||||
}
|
||||
|
||||
/// Checks if the interface is up (has IP information).
|
||||
pub fn is_iface_up(&self) -> bool {
|
||||
self.ip_info.borrow().is_some()
|
||||
}
|
||||
|
||||
/// Retrieves the current IP information (IP address, subnet info).
|
||||
pub fn get_ip_info(&self) -> Result<ipv4::IpInfo, WifiStackError> {
|
||||
self.ip_info.borrow().ok_or(WifiStackError::MissingIp)
|
||||
}
|
||||
}
|
||||
|
||||
/// A TCP socket
|
||||
#[cfg(feature = "tcp")]
|
||||
pub struct Socket<'s, 'n: 's, MODE: WifiDeviceMode> {
|
||||
socket_handle: SocketHandle,
|
||||
network: &'s WifiStack<'n, MODE>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
impl<'s, 'n: 's, MODE: WifiDeviceMode> Socket<'s, 'n, MODE> {
|
||||
/// Connect the socket
|
||||
pub fn open<'i>(&'i mut self, addr: IpAddress, port: u16) -> Result<(), IoError>
|
||||
where
|
||||
's: 'i,
|
||||
{
|
||||
{
|
||||
let res = self.network.with_mut(|interface, _device, sockets| {
|
||||
let sock = sockets.get_mut::<TcpSocket>(self.socket_handle);
|
||||
let cx = interface.context();
|
||||
let remote_endpoint = (addr, port);
|
||||
sock.set_ack_delay(Some(smoltcp::time::Duration::from_millis(100)));
|
||||
sock.connect(cx, remote_endpoint, self.network.next_local_port())
|
||||
});
|
||||
|
||||
res.map_err(IoError::ConnectError)?;
|
||||
}
|
||||
|
||||
loop {
|
||||
let can_send = self.network.with_mut(|_interface, _device, sockets| {
|
||||
let sock = sockets.get_mut::<TcpSocket>(self.socket_handle);
|
||||
sock.can_send()
|
||||
});
|
||||
|
||||
if can_send {
|
||||
break;
|
||||
}
|
||||
|
||||
self.work();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Listen on the given port. This blocks until there is a peer connected
|
||||
pub fn listen<'i>(&'i mut self, port: u16) -> Result<(), IoError>
|
||||
where
|
||||
's: 'i,
|
||||
{
|
||||
{
|
||||
let res = self.network.with_mut(|_interface, _device, sockets| {
|
||||
let sock = sockets.get_mut::<TcpSocket>(self.socket_handle);
|
||||
sock.listen(port)
|
||||
});
|
||||
|
||||
res.map_err(IoError::ListenError)?;
|
||||
}
|
||||
|
||||
loop {
|
||||
let can_send = self.network.with_mut(|_interface, _device, sockets| {
|
||||
let sock = sockets.get_mut::<TcpSocket>(self.socket_handle);
|
||||
sock.can_send()
|
||||
});
|
||||
|
||||
if can_send {
|
||||
break;
|
||||
}
|
||||
|
||||
self.work();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Listen on the given port. This doesn't block
|
||||
pub fn listen_unblocking<'i>(&'i mut self, port: u16) -> Result<(), IoError>
|
||||
where
|
||||
's: 'i,
|
||||
{
|
||||
{
|
||||
let res = self.network.with_mut(|_interface, _device, sockets| {
|
||||
let sock = sockets.get_mut::<TcpSocket>(self.socket_handle);
|
||||
sock.listen(port)
|
||||
});
|
||||
|
||||
res.map_err(IoError::ListenError)?;
|
||||
}
|
||||
|
||||
self.work();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Closes the socket
|
||||
pub fn close(&mut self) {
|
||||
self.network.with_mut(|_interface, _device, sockets| {
|
||||
sockets.get_mut::<TcpSocket>(self.socket_handle).close();
|
||||
});
|
||||
|
||||
self.work();
|
||||
}
|
||||
|
||||
/// Disconnect the socket
|
||||
pub fn disconnect(&mut self) {
|
||||
self.network.with_mut(|_interface, _device, sockets| {
|
||||
sockets.get_mut::<TcpSocket>(self.socket_handle).abort();
|
||||
});
|
||||
|
||||
self.work();
|
||||
}
|
||||
|
||||
/// Checks if the socket is currently open
|
||||
pub fn is_open(&mut self) -> bool {
|
||||
self.network.with_mut(|_interface, _device, sockets| {
|
||||
sockets.get_mut::<TcpSocket>(self.socket_handle).is_open()
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks if the socket is currently connected
|
||||
pub fn is_connected(&mut self) -> bool {
|
||||
self.network.with_mut(|_interface, _device, sockets| {
|
||||
let socket = sockets.get_mut::<TcpSocket>(self.socket_handle);
|
||||
|
||||
socket.may_recv() && socket.may_send()
|
||||
})
|
||||
}
|
||||
|
||||
/// Delegates to [WifiStack::work]
|
||||
pub fn work(&mut self) {
|
||||
self.network.work()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
impl<'s, 'n: 's, MODE: WifiDeviceMode> Drop for Socket<'s, 'n, MODE> {
|
||||
fn drop(&mut self) {
|
||||
self.network
|
||||
.with_mut(|_interface, _device, sockets| sockets.remove(self.socket_handle));
|
||||
}
|
||||
}
|
||||
|
||||
/// IO Errors
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum IoError {
|
||||
SocketClosed,
|
||||
#[cfg(feature = "igmp")]
|
||||
MultiCastError(smoltcp::iface::MulticastError),
|
||||
#[cfg(feature = "tcp")]
|
||||
TcpRecvError,
|
||||
#[cfg(feature = "udp")]
|
||||
UdpRecvError(smoltcp::socket::udp::RecvError),
|
||||
#[cfg(feature = "tcp")]
|
||||
TcpSendError(smoltcp::socket::tcp::SendError),
|
||||
#[cfg(feature = "udp")]
|
||||
UdpSendError(smoltcp::socket::udp::SendError),
|
||||
#[cfg(feature = "tcp")]
|
||||
ConnectError(smoltcp::socket::tcp::ConnectError),
|
||||
#[cfg(feature = "udp")]
|
||||
BindError(smoltcp::socket::udp::BindError),
|
||||
#[cfg(feature = "tcp")]
|
||||
ListenError(smoltcp::socket::tcp::ListenError),
|
||||
}
|
||||
|
||||
impl embedded_io::Error for IoError {
|
||||
fn kind(&self) -> embedded_io::ErrorKind {
|
||||
embedded_io::ErrorKind::Other
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::ErrorType for Socket<'s, 'n, MODE> {
|
||||
type Error = IoError;
|
||||
}
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::Read for Socket<'s, 'n, MODE> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.network.with_mut(|interface, device, sockets| {
|
||||
use smoltcp::socket::tcp::RecvError;
|
||||
|
||||
loop {
|
||||
interface.poll(timestamp(), device, sockets);
|
||||
let socket = sockets.get_mut::<TcpSocket>(self.socket_handle);
|
||||
|
||||
match socket.recv_slice(buf) {
|
||||
Ok(0) => continue, // no data
|
||||
Ok(n) => return Ok(n),
|
||||
Err(RecvError::Finished) => return Err(IoError::SocketClosed), // eof
|
||||
Err(RecvError::InvalidState) => return Err(IoError::TcpRecvError),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::ReadReady for Socket<'s, 'n, MODE> {
|
||||
fn read_ready(&mut self) -> Result<bool, Self::Error> {
|
||||
self.network.with_mut(|interface, device, sockets| {
|
||||
use smoltcp::socket::tcp::RecvError;
|
||||
|
||||
interface.poll(timestamp(), device, sockets);
|
||||
let socket = sockets.get_mut::<TcpSocket>(self.socket_handle);
|
||||
|
||||
match socket.peek(1) {
|
||||
Ok(s) => Ok(!s.is_empty()),
|
||||
Err(RecvError::Finished) => Err(IoError::SocketClosed),
|
||||
Err(RecvError::InvalidState) => Err(IoError::TcpRecvError),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::Write for Socket<'s, 'n, MODE> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
loop {
|
||||
let (may_send, is_open, can_send) =
|
||||
self.network.with_mut(|interface, device, sockets| {
|
||||
interface.poll(
|
||||
Instant::from_millis((self.network.current_millis_fn)() as i64),
|
||||
device,
|
||||
sockets,
|
||||
);
|
||||
|
||||
let socket = sockets.get_mut::<TcpSocket>(self.socket_handle);
|
||||
|
||||
(socket.may_send(), socket.is_open(), socket.can_send())
|
||||
});
|
||||
|
||||
if may_send {
|
||||
break;
|
||||
}
|
||||
|
||||
if !is_open || !can_send {
|
||||
return Err(IoError::SocketClosed);
|
||||
}
|
||||
}
|
||||
|
||||
let mut written = 0;
|
||||
loop {
|
||||
self.flush()?;
|
||||
|
||||
self.network.with_mut(|_interface, _device, sockets| {
|
||||
sockets
|
||||
.get_mut::<TcpSocket>(self.socket_handle)
|
||||
.send_slice(&buf[written..])
|
||||
.map(|len| written += len)
|
||||
.map_err(IoError::TcpSendError)
|
||||
})?;
|
||||
|
||||
if written >= buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(written)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
loop {
|
||||
let res = self.network.with_mut(|interface, device, sockets| {
|
||||
interface.poll(
|
||||
Instant::from_millis((self.network.current_millis_fn)() as i64),
|
||||
device,
|
||||
sockets,
|
||||
)
|
||||
});
|
||||
|
||||
if !res {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tcp")]
|
||||
impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::WriteReady for Socket<'s, 'n, MODE> {
|
||||
fn write_ready(&mut self) -> Result<bool, Self::Error> {
|
||||
let (may_send, is_open, can_send) = self.network.with_mut(|interface, device, sockets| {
|
||||
interface.poll(
|
||||
Instant::from_millis((self.network.current_millis_fn)() as i64),
|
||||
device,
|
||||
sockets,
|
||||
);
|
||||
|
||||
let socket = sockets.get_mut::<TcpSocket>(self.socket_handle);
|
||||
|
||||
(socket.may_send(), socket.is_open(), socket.can_send())
|
||||
});
|
||||
|
||||
if !is_open || !can_send {
|
||||
return Err(IoError::SocketClosed);
|
||||
}
|
||||
|
||||
Ok(may_send)
|
||||
}
|
||||
}
|
||||
|
||||
/// A UDP socket
|
||||
#[cfg(feature = "udp")]
|
||||
pub struct UdpSocket<'s, 'n: 's, MODE: WifiDeviceMode> {
|
||||
socket_handle: SocketHandle,
|
||||
network: &'s WifiStack<'n, MODE>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "udp")]
|
||||
impl<'s, 'n: 's, MODE: WifiDeviceMode> UdpSocket<'s, 'n, MODE> {
|
||||
/// Binds the socket to the given port
|
||||
pub fn bind<'i>(&'i mut self, port: u16) -> Result<(), IoError>
|
||||
where
|
||||
's: 'i,
|
||||
{
|
||||
self.work();
|
||||
|
||||
{
|
||||
let res = self.network.with_mut(|_interface, _device, sockets| {
|
||||
let sock = sockets.get_mut::<smoltcp::socket::udp::Socket>(self.socket_handle);
|
||||
sock.bind(port)
|
||||
});
|
||||
|
||||
if let Err(err) = res {
|
||||
return Err(IoError::BindError(err));
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
let can_send = self.network.with_mut(|_interface, _device, sockets| {
|
||||
let sock = sockets.get_mut::<smoltcp::socket::udp::Socket>(self.socket_handle);
|
||||
sock.can_send()
|
||||
});
|
||||
|
||||
if can_send {
|
||||
break;
|
||||
}
|
||||
|
||||
self.work();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Close the socket
|
||||
pub fn close(&mut self) {
|
||||
self.network.with_mut(|_interface, _device, sockets| {
|
||||
sockets
|
||||
.get_mut::<smoltcp::socket::udp::Socket>(self.socket_handle)
|
||||
.close();
|
||||
});
|
||||
|
||||
self.work();
|
||||
}
|
||||
|
||||
/// Sends data on the socket to the given address
|
||||
pub fn send(&mut self, addr: IpAddress, port: u16, data: &[u8]) -> Result<(), IoError> {
|
||||
loop {
|
||||
self.work();
|
||||
|
||||
let (can_send, packet_capacity, payload_capacity) =
|
||||
self.network.with_mut(|_interface, _device, sockets| {
|
||||
let sock = sockets.get_mut::<smoltcp::socket::udp::Socket>(self.socket_handle);
|
||||
(
|
||||
sock.can_send(),
|
||||
sock.packet_send_capacity(),
|
||||
sock.payload_send_capacity(),
|
||||
)
|
||||
});
|
||||
|
||||
if can_send && packet_capacity > 0 && payload_capacity > data.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.network
|
||||
.with_mut(|_interface, _device, sockets| {
|
||||
let endpoint = (addr, port);
|
||||
let endpoint: IpEndpoint = endpoint.into();
|
||||
|
||||
sockets
|
||||
.get_mut::<smoltcp::socket::udp::Socket>(self.socket_handle)
|
||||
.send_slice(data, endpoint)
|
||||
})
|
||||
.map_err(IoError::UdpSendError)?;
|
||||
|
||||
self.work();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receives a single datagram message on the socket
|
||||
pub fn receive(&mut self, data: &mut [u8]) -> Result<(usize, IpAddress, u16), IoError> {
|
||||
self.work();
|
||||
|
||||
let res = self.network.with_mut(|_interface, _device, sockets| {
|
||||
sockets
|
||||
.get_mut::<smoltcp::socket::udp::Socket>(self.socket_handle)
|
||||
.recv_slice(data)
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok((len, endpoint)) => {
|
||||
let addr = endpoint.endpoint.addr;
|
||||
Ok((len, addr, endpoint.endpoint.port))
|
||||
}
|
||||
Err(e) => Err(IoError::UdpRecvError(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// This function specifies a new multicast group for this socket to join
|
||||
#[cfg(feature = "igmp")]
|
||||
pub fn join_multicast_group(&mut self, addr: IpAddress) -> Result<bool, IoError> {
|
||||
self.work();
|
||||
|
||||
let res = self.network.with_mut(|interface, device, _| {
|
||||
interface.join_multicast_group(
|
||||
device,
|
||||
addr,
|
||||
Instant::from_millis((self.network.current_millis_fn)() as i64),
|
||||
)
|
||||
});
|
||||
|
||||
self.work();
|
||||
|
||||
res.map_err(IoError::MultiCastError)
|
||||
}
|
||||
|
||||
/// Delegates to [WifiStack::work]
|
||||
pub fn work(&mut self) {
|
||||
self.network.work()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "udp")]
|
||||
impl<'s, 'n: 's, MODE: WifiDeviceMode> Drop for UdpSocket<'s, 'n, MODE> {
|
||||
fn drop(&mut self) {
|
||||
self.network
|
||||
.with_mut(|_, _, sockets| sockets.borrow_mut().remove(self.socket_handle));
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ publish = false
|
||||
[dependencies]
|
||||
aligned = { version = "0.4.2", optional = true }
|
||||
bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "a5148d8ae679e021b78f53fd33afb8bb35d0b62e", features = [ "macros", "async"] }
|
||||
blocking-network-stack = { git = "https://github.com/bjoernQ/blocking-network-stack.git", rev = "1c581661d78e0cf0f17b936297179b993fb149d7" }
|
||||
bt-hci = "0.1.1"
|
||||
cfg-if = "1.0.0"
|
||||
critical-section = "1.1.3"
|
||||
@ -42,6 +43,8 @@ nb = "1.1.0"
|
||||
portable-atomic = { version = "1.9.0", default-features = false }
|
||||
sha2 = { version = "0.10.8", default-features = false }
|
||||
smoltcp = { version = "0.11.0", default-features = false, features = [ "medium-ethernet", "socket-raw"] }
|
||||
smoltcp-nal = "0.5.1"
|
||||
embedded-time = "=0.12.1"
|
||||
ssd1306 = "0.8.4"
|
||||
static_cell = { version = "2.1.0", features = ["nightly"] }
|
||||
trouble-host = { git = "https://github.com/embassy-rs/trouble", package = "trouble-host", rev = "4f1114ce58e96fe54f5ed7e726f66e1ad8d9ce54", features = [ "log", "gatt" ] }
|
||||
|
@ -3,7 +3,7 @@
|
||||
//! Periodically transmits a beacon frame.
|
||||
//!
|
||||
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
|
@ -8,12 +8,13 @@
|
||||
//! On Android you might need to choose _Keep Accesspoint_ when it tells you the WiFi has no internet connection, Chrome might not want to load the URL - you can use a shell and try `curl` and `ping`
|
||||
//!
|
||||
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use blocking_network_stack::Stack;
|
||||
use embedded_io::*;
|
||||
use esp_alloc as _;
|
||||
use esp_backtrace as _;
|
||||
@ -32,9 +33,8 @@ use esp_wifi::{
|
||||
Configuration,
|
||||
WifiApDevice,
|
||||
},
|
||||
wifi_interface::WifiStack,
|
||||
};
|
||||
use smoltcp::iface::SocketStorage;
|
||||
use smoltcp::iface::{SocketSet, SocketStorage};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
@ -49,19 +49,18 @@ fn main() -> ! {
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
|
||||
let init = init(
|
||||
timg0.timer0,
|
||||
Rng::new(peripherals.RNG),
|
||||
peripherals.RADIO_CLK,
|
||||
)
|
||||
.unwrap();
|
||||
let mut rng = Rng::new(peripherals.RNG);
|
||||
|
||||
let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap();
|
||||
|
||||
let mut wifi = peripherals.WIFI;
|
||||
let mut socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
let (iface, device, mut controller, sockets) =
|
||||
create_network_interface(&init, &mut wifi, WifiApDevice, &mut socket_set_entries).unwrap();
|
||||
let (iface, device, mut controller) =
|
||||
create_network_interface(&init, &mut wifi, WifiApDevice).unwrap();
|
||||
let now = || time::now().duration_since_epoch().to_millis();
|
||||
let mut wifi_stack = WifiStack::new(iface, device, sockets, now);
|
||||
|
||||
let mut socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
let socket_set = SocketSet::new(&mut socket_set_entries[..]);
|
||||
let mut stack = Stack::new(iface, device, socket_set, now, rng.random());
|
||||
|
||||
let client_config = Configuration::AccessPoint(AccessPointConfiguration {
|
||||
ssid: "esp-wifi".try_into().unwrap(),
|
||||
@ -75,14 +74,16 @@ fn main() -> ! {
|
||||
|
||||
println!("{:?}", controller.get_capabilities());
|
||||
|
||||
wifi_stack
|
||||
.set_iface_configuration(&esp_wifi::wifi::ipv4::Configuration::Client(
|
||||
esp_wifi::wifi::ipv4::ClientConfiguration::Fixed(
|
||||
esp_wifi::wifi::ipv4::ClientSettings {
|
||||
ip: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")),
|
||||
subnet: esp_wifi::wifi::ipv4::Subnet {
|
||||
gateway: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")),
|
||||
mask: esp_wifi::wifi::ipv4::Mask(24),
|
||||
stack
|
||||
.set_iface_configuration(&blocking_network_stack::ipv4::Configuration::Client(
|
||||
blocking_network_stack::ipv4::ClientConfiguration::Fixed(
|
||||
blocking_network_stack::ipv4::ClientSettings {
|
||||
ip: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")),
|
||||
subnet: blocking_network_stack::ipv4::Subnet {
|
||||
gateway: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip(
|
||||
"192.168.2.1",
|
||||
)),
|
||||
mask: blocking_network_stack::ipv4::Mask(24),
|
||||
},
|
||||
dns: None,
|
||||
secondary_dns: None,
|
||||
@ -96,7 +97,7 @@ fn main() -> ! {
|
||||
|
||||
let mut rx_buffer = [0u8; 1536];
|
||||
let mut tx_buffer = [0u8; 1536];
|
||||
let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
|
||||
socket.listen(8080).unwrap();
|
||||
|
||||
|
@ -9,12 +9,13 @@
|
||||
//! On Android you might need to choose _Keep Accesspoint_ when it tells you the WiFi has no internet connection, Chrome might not want to load the URL - you can use a shell and try `curl` and `ping`
|
||||
//!
|
||||
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use blocking_network_stack::Stack;
|
||||
use embedded_io::*;
|
||||
use esp_alloc as _;
|
||||
use esp_backtrace as _;
|
||||
@ -33,10 +34,9 @@ use esp_wifi::{
|
||||
ClientConfiguration,
|
||||
Configuration,
|
||||
},
|
||||
wifi_interface::WifiStack,
|
||||
};
|
||||
use smoltcp::{
|
||||
iface::SocketStorage,
|
||||
iface::{SocketSet, SocketStorage},
|
||||
wire::{IpAddress, Ipv4Address},
|
||||
};
|
||||
|
||||
@ -56,37 +56,29 @@ fn main() -> ! {
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
|
||||
let init = init(
|
||||
timg0.timer0,
|
||||
Rng::new(peripherals.RNG),
|
||||
peripherals.RADIO_CLK,
|
||||
)
|
||||
.unwrap();
|
||||
let mut rng = Rng::new(peripherals.RNG);
|
||||
|
||||
let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap();
|
||||
|
||||
let wifi = peripherals.WIFI;
|
||||
|
||||
let mut ap_socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
let mut sta_socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
|
||||
let ApStaInterface {
|
||||
ap_interface,
|
||||
sta_interface,
|
||||
ap_device,
|
||||
sta_device,
|
||||
mut controller,
|
||||
ap_socket_set,
|
||||
sta_socket_set,
|
||||
} = create_ap_sta_network_interface(
|
||||
&init,
|
||||
wifi,
|
||||
&mut ap_socket_set_entries,
|
||||
&mut sta_socket_set_entries,
|
||||
)
|
||||
.unwrap();
|
||||
} = create_ap_sta_network_interface(&init, wifi).unwrap();
|
||||
|
||||
let now = || time::now().duration_since_epoch().to_millis();
|
||||
let mut wifi_ap_stack = WifiStack::new(ap_interface, ap_device, ap_socket_set, now);
|
||||
let wifi_sta_stack = WifiStack::new(sta_interface, sta_device, sta_socket_set, now);
|
||||
let mut ap_socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
let ap_socket_set = SocketSet::new(&mut ap_socket_set_entries[..]);
|
||||
let mut ap_stack = Stack::new(ap_interface, ap_device, ap_socket_set, now, rng.random());
|
||||
|
||||
let mut sta_socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
let mut sta_socket_set = SocketSet::new(&mut sta_socket_set_entries[..]);
|
||||
sta_socket_set.add(smoltcp::socket::dhcpv4::Socket::new());
|
||||
let sta_stack = Stack::new(sta_interface, sta_device, sta_socket_set, now, rng.random());
|
||||
|
||||
let client_config = Configuration::Mixed(
|
||||
ClientConfiguration {
|
||||
@ -107,14 +99,16 @@ fn main() -> ! {
|
||||
|
||||
println!("{:?}", controller.get_capabilities());
|
||||
|
||||
wifi_ap_stack
|
||||
.set_iface_configuration(&esp_wifi::wifi::ipv4::Configuration::Client(
|
||||
esp_wifi::wifi::ipv4::ClientConfiguration::Fixed(
|
||||
esp_wifi::wifi::ipv4::ClientSettings {
|
||||
ip: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")),
|
||||
subnet: esp_wifi::wifi::ipv4::Subnet {
|
||||
gateway: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")),
|
||||
mask: esp_wifi::wifi::ipv4::Mask(24),
|
||||
ap_stack
|
||||
.set_iface_configuration(&blocking_network_stack::ipv4::Configuration::Client(
|
||||
blocking_network_stack::ipv4::ClientConfiguration::Fixed(
|
||||
blocking_network_stack::ipv4::ClientSettings {
|
||||
ip: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")),
|
||||
subnet: blocking_network_stack::ipv4::Subnet {
|
||||
gateway: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip(
|
||||
"192.168.2.1",
|
||||
)),
|
||||
mask: blocking_network_stack::ipv4::Mask(24),
|
||||
},
|
||||
dns: None,
|
||||
secondary_dns: None,
|
||||
@ -128,10 +122,10 @@ fn main() -> ! {
|
||||
// wait for STA getting an ip address
|
||||
println!("Wait to get an ip address");
|
||||
loop {
|
||||
wifi_sta_stack.work();
|
||||
sta_stack.work();
|
||||
|
||||
if wifi_sta_stack.is_iface_up() {
|
||||
println!("got ip {:?}", wifi_sta_stack.get_ip_info());
|
||||
if sta_stack.is_iface_up() {
|
||||
println!("got ip {:?}", sta_stack.get_ip_info());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -141,11 +135,11 @@ fn main() -> ! {
|
||||
|
||||
let mut rx_buffer = [0u8; 1536];
|
||||
let mut tx_buffer = [0u8; 1536];
|
||||
let mut ap_socket = wifi_ap_stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
let mut ap_socket = ap_stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
|
||||
let mut sta_rx_buffer = [0u8; 1536];
|
||||
let mut sta_tx_buffer = [0u8; 1536];
|
||||
let mut sta_socket = wifi_sta_stack.get_socket(&mut sta_rx_buffer, &mut sta_tx_buffer);
|
||||
let mut sta_socket = sta_stack.get_socket(&mut sta_rx_buffer, &mut sta_tx_buffer);
|
||||
|
||||
ap_socket.listen(8080).unwrap();
|
||||
|
||||
|
@ -8,12 +8,13 @@
|
||||
//! Ensure you have set the IP of your local machine in the `HOST_IP` env variable. E.g `HOST_IP="192.168.0.24"` and also set SSID and PASSWORD env variable before running this example.
|
||||
//!
|
||||
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use blocking_network_stack::Stack;
|
||||
use embedded_io::*;
|
||||
use esp_alloc as _;
|
||||
use esp_backtrace as _;
|
||||
@ -35,11 +36,10 @@ use esp_wifi::{
|
||||
WifiError,
|
||||
WifiStaDevice,
|
||||
},
|
||||
wifi_interface::WifiStack,
|
||||
};
|
||||
use smoltcp::{
|
||||
iface::SocketStorage,
|
||||
wire::{IpAddress, Ipv4Address},
|
||||
iface::{SocketSet, SocketStorage},
|
||||
wire::{DhcpOption, IpAddress, Ipv4Address},
|
||||
};
|
||||
|
||||
const SSID: &str = env!("SSID");
|
||||
@ -69,19 +69,26 @@ fn main() -> ! {
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
|
||||
let init = init(
|
||||
timg0.timer0,
|
||||
Rng::new(peripherals.RNG),
|
||||
peripherals.RADIO_CLK,
|
||||
)
|
||||
.unwrap();
|
||||
let mut rng = Rng::new(peripherals.RNG);
|
||||
|
||||
let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap();
|
||||
|
||||
let mut wifi = peripherals.WIFI;
|
||||
let (iface, device, mut controller) =
|
||||
create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap();
|
||||
|
||||
let mut socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
let (iface, device, mut controller, sockets) =
|
||||
create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap();
|
||||
let mut socket_set = SocketSet::new(&mut socket_set_entries[..]);
|
||||
let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new();
|
||||
// we can set a hostname here (or add other DHCP options)
|
||||
dhcp_socket.set_outgoing_options(&[DhcpOption {
|
||||
kind: 12,
|
||||
data: b"esp-wifi",
|
||||
}]);
|
||||
socket_set.add(dhcp_socket);
|
||||
|
||||
let now = || time::now().duration_since_epoch().to_millis();
|
||||
let wifi_stack = WifiStack::new(iface, device, sockets, now);
|
||||
let stack = Stack::new(iface, device, socket_set, now, rng.random());
|
||||
|
||||
let client_config = Configuration::Client(ClientConfiguration {
|
||||
ssid: SSID.try_into().unwrap(),
|
||||
@ -122,17 +129,17 @@ fn main() -> ! {
|
||||
// wait for getting an ip address
|
||||
println!("Wait to get an ip address");
|
||||
loop {
|
||||
wifi_stack.work();
|
||||
stack.work();
|
||||
|
||||
if wifi_stack.is_iface_up() {
|
||||
println!("got ip {:?}", wifi_stack.get_ip_info());
|
||||
if stack.is_iface_up() {
|
||||
println!("got ip {:?}", stack.get_ip_info());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut rx_buffer = [0u8; RX_BUFFER_SIZE];
|
||||
let mut tx_buffer = [0u8; TX_BUFFER_SIZE];
|
||||
let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
|
||||
let delay = Delay::new();
|
||||
|
||||
@ -149,9 +156,9 @@ fn main() -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
fn test_download<'a>(
|
||||
fn test_download<'a, D: smoltcp::phy::Device>(
|
||||
server_address: Ipv4Address,
|
||||
socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a, WifiStaDevice>,
|
||||
socket: &mut blocking_network_stack::Socket<'a, 'a, D>,
|
||||
) {
|
||||
println!("Testing download...");
|
||||
socket.work();
|
||||
@ -183,9 +190,9 @@ fn test_download<'a>(
|
||||
socket.disconnect();
|
||||
}
|
||||
|
||||
fn test_upload<'a>(
|
||||
fn test_upload<'a, D: smoltcp::phy::Device>(
|
||||
server_address: Ipv4Address,
|
||||
socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a, WifiStaDevice>,
|
||||
socket: &mut blocking_network_stack::Socket<'a, 'a, D>,
|
||||
) {
|
||||
println!("Testing upload...");
|
||||
socket.work();
|
||||
@ -217,9 +224,9 @@ fn test_upload<'a>(
|
||||
socket.disconnect();
|
||||
}
|
||||
|
||||
fn test_upload_download<'a>(
|
||||
fn test_upload_download<'a, D: smoltcp::phy::Device>(
|
||||
server_address: Ipv4Address,
|
||||
socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a, WifiStaDevice>,
|
||||
socket: &mut blocking_network_stack::Socket<'a, 'a, D>,
|
||||
) {
|
||||
println!("Testing upload+download...");
|
||||
socket.work();
|
||||
|
@ -8,7 +8,7 @@
|
||||
//! Note: On ESP32-C2 and ESP32-C3 you need a wifi-heap size of 70000, on ESP32-C6 you need 80000 and a tx_queue_size of 10
|
||||
//!
|
||||
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/ble esp-wifi/coex
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/ble esp-wifi/coex
|
||||
//% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![allow(static_mut_refs)]
|
||||
@ -26,6 +26,7 @@ use bleps::{
|
||||
Ble,
|
||||
HciConnector,
|
||||
};
|
||||
use blocking_network_stack::Stack;
|
||||
use embedded_io::*;
|
||||
use esp_alloc as _;
|
||||
use esp_backtrace as _;
|
||||
@ -40,11 +41,10 @@ use esp_wifi::{
|
||||
ble::controller::BleConnector,
|
||||
init,
|
||||
wifi::{utils::create_network_interface, ClientConfiguration, Configuration, WifiStaDevice},
|
||||
wifi_interface::WifiStack,
|
||||
};
|
||||
use smoltcp::{
|
||||
iface::SocketStorage,
|
||||
wire::{IpAddress, Ipv4Address},
|
||||
iface::{SocketSet, SocketStorage},
|
||||
wire::{DhcpOption, IpAddress, Ipv4Address},
|
||||
};
|
||||
|
||||
const SSID: &str = env!("SSID");
|
||||
@ -81,22 +81,28 @@ fn main() -> ! {
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
|
||||
let init = init(
|
||||
timg0.timer0,
|
||||
Rng::new(peripherals.RNG),
|
||||
peripherals.RADIO_CLK,
|
||||
)
|
||||
.unwrap();
|
||||
let mut rng = Rng::new(peripherals.RNG);
|
||||
|
||||
let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap();
|
||||
|
||||
let mut wifi = peripherals.WIFI;
|
||||
let bluetooth = peripherals.BT;
|
||||
|
||||
let mut socket_set_entries: [SocketStorage; 2] = Default::default();
|
||||
let (iface, device, mut controller, sockets) =
|
||||
create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap();
|
||||
let (iface, device, mut controller) =
|
||||
create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap();
|
||||
|
||||
let mut socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
let mut socket_set = SocketSet::new(&mut socket_set_entries[..]);
|
||||
let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new();
|
||||
// we can set a hostname here (or add other DHCP options)
|
||||
dhcp_socket.set_outgoing_options(&[DhcpOption {
|
||||
kind: 12,
|
||||
data: b"esp-wifi",
|
||||
}]);
|
||||
socket_set.add(dhcp_socket);
|
||||
|
||||
let now = || time::now().duration_since_epoch().to_millis();
|
||||
let wifi_stack = WifiStack::new(iface, device, sockets, now);
|
||||
let stack = Stack::new(iface, device, socket_set, now, rng.random());
|
||||
|
||||
let client_config = Configuration::Client(ClientConfiguration {
|
||||
ssid: SSID.try_into().unwrap(),
|
||||
@ -128,10 +134,10 @@ fn main() -> ! {
|
||||
// wait for getting an ip address
|
||||
println!("Wait to get an ip address");
|
||||
loop {
|
||||
wifi_stack.work();
|
||||
stack.work();
|
||||
|
||||
if wifi_stack.is_iface_up() {
|
||||
println!("got ip {:?}", wifi_stack.get_ip_info());
|
||||
if stack.is_iface_up() {
|
||||
println!("got ip {:?}", stack.get_ip_info());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -161,7 +167,7 @@ fn main() -> ! {
|
||||
|
||||
let mut rx_buffer = [0u8; 128];
|
||||
let mut tx_buffer = [0u8; 128];
|
||||
let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
|
||||
loop {
|
||||
println!("Making HTTP request");
|
||||
|
@ -4,7 +4,7 @@
|
||||
//! Set SSID and PASSWORD env variable before running this example.
|
||||
//!
|
||||
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/log
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/log
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
@ -12,6 +12,7 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use blocking_network_stack::Stack;
|
||||
use esp_alloc as _;
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
@ -32,10 +33,11 @@ use esp_wifi::{
|
||||
WifiError,
|
||||
WifiStaDevice,
|
||||
},
|
||||
wifi_interface::WifiStack,
|
||||
// EspWifiInitFor,
|
||||
};
|
||||
use smoltcp::iface::SocketStorage;
|
||||
use smoltcp::{
|
||||
iface::{SocketSet, SocketStorage},
|
||||
wire::DhcpOption,
|
||||
};
|
||||
|
||||
const SSID: &str = env!("SSID");
|
||||
const PASSWORD: &str = env!("PASSWORD");
|
||||
@ -51,20 +53,27 @@ fn main() -> ! {
|
||||
|
||||
esp_alloc::heap_allocator!(72 * 1024);
|
||||
|
||||
let mut rng = Rng::new(peripherals.RNG);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
let init = init(
|
||||
timg0.timer0,
|
||||
Rng::new(peripherals.RNG),
|
||||
peripherals.RADIO_CLK,
|
||||
)
|
||||
.unwrap();
|
||||
let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap();
|
||||
|
||||
let mut wifi = peripherals.WIFI;
|
||||
let (iface, device, mut controller) =
|
||||
create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap();
|
||||
|
||||
let mut socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
let (iface, device, mut controller, sockets) =
|
||||
create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap();
|
||||
let mut socket_set = SocketSet::new(&mut socket_set_entries[..]);
|
||||
let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new();
|
||||
// we can set a hostname here (or add other DHCP options)
|
||||
dhcp_socket.set_outgoing_options(&[DhcpOption {
|
||||
kind: 12,
|
||||
data: b"esp-wifi",
|
||||
}]);
|
||||
socket_set.add(dhcp_socket);
|
||||
|
||||
let now = || time::now().duration_since_epoch().to_millis();
|
||||
let wifi_stack = WifiStack::new(iface, device, sockets, now);
|
||||
let stack = Stack::new(iface, device, socket_set, now, rng.random());
|
||||
|
||||
let client_config = Configuration::Client(ClientConfiguration {
|
||||
ssid: SSID.try_into().unwrap(),
|
||||
@ -120,10 +129,10 @@ fn main() -> ! {
|
||||
// wait for getting an ip address
|
||||
println!("Wait to get an ip address");
|
||||
loop {
|
||||
wifi_stack.work();
|
||||
stack.work();
|
||||
|
||||
if wifi_stack.is_iface_up() {
|
||||
println!("got ip {:?}", wifi_stack.get_ip_info());
|
||||
if stack.is_iface_up() {
|
||||
println!("got ip {:?}", stack.get_ip_info());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
//! This gets an ip address via DHCP then performs an HTTP get request to some "random" server
|
||||
//!
|
||||
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use blocking_network_stack::Stack;
|
||||
use embedded_io::*;
|
||||
use esp_alloc as _;
|
||||
use esp_backtrace as _;
|
||||
@ -34,11 +35,10 @@ use esp_wifi::{
|
||||
WifiError,
|
||||
WifiStaDevice,
|
||||
},
|
||||
wifi_interface::WifiStack,
|
||||
};
|
||||
use smoltcp::{
|
||||
iface::SocketStorage,
|
||||
wire::{IpAddress, Ipv4Address},
|
||||
iface::{SocketSet, SocketStorage},
|
||||
wire::{DhcpOption, IpAddress, Ipv4Address},
|
||||
};
|
||||
|
||||
const SSID: &str = env!("SSID");
|
||||
@ -57,19 +57,26 @@ fn main() -> ! {
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
|
||||
let init = init(
|
||||
timg0.timer0,
|
||||
Rng::new(peripherals.RNG),
|
||||
peripherals.RADIO_CLK,
|
||||
)
|
||||
.unwrap();
|
||||
let mut rng = Rng::new(peripherals.RNG);
|
||||
|
||||
let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap();
|
||||
|
||||
let mut wifi = peripherals.WIFI;
|
||||
let (iface, device, mut controller) =
|
||||
create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap();
|
||||
|
||||
let mut socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
let (iface, device, mut controller, sockets) =
|
||||
create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap();
|
||||
let mut socket_set = SocketSet::new(&mut socket_set_entries[..]);
|
||||
let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new();
|
||||
// we can set a hostname here (or add other DHCP options)
|
||||
dhcp_socket.set_outgoing_options(&[DhcpOption {
|
||||
kind: 12,
|
||||
data: b"esp-wifi",
|
||||
}]);
|
||||
socket_set.add(dhcp_socket);
|
||||
|
||||
let now = || time::now().duration_since_epoch().to_millis();
|
||||
let wifi_stack = WifiStack::new(iface, device, sockets, now);
|
||||
let stack = Stack::new(iface, device, socket_set, now, rng.random());
|
||||
|
||||
let client_config = Configuration::Client(ClientConfiguration {
|
||||
ssid: SSID.try_into().unwrap(),
|
||||
@ -110,10 +117,10 @@ fn main() -> ! {
|
||||
// wait for getting an ip address
|
||||
println!("Wait to get an ip address");
|
||||
loop {
|
||||
wifi_stack.work();
|
||||
stack.work();
|
||||
|
||||
if wifi_stack.is_iface_up() {
|
||||
println!("got ip {:?}", wifi_stack.get_ip_info());
|
||||
if stack.is_iface_up() {
|
||||
println!("got ip {:?}", stack.get_ip_info());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -122,7 +129,7 @@ fn main() -> ! {
|
||||
|
||||
let mut rx_buffer = [0u8; 1536];
|
||||
let mut tx_buffer = [0u8; 1536];
|
||||
let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
|
||||
loop {
|
||||
println!("Making HTTP request");
|
||||
|
224
examples/src/bin/wifi_dhcp_smoltcp_nal.rs
Normal file
224
examples/src/bin/wifi_dhcp_smoltcp_nal.rs
Normal file
@ -0,0 +1,224 @@
|
||||
//! DHCP Example using [smoltcp-nal](https://crates.io/crates/smoltcp-nal)
|
||||
//!
|
||||
//!
|
||||
//! Set SSID and PASSWORD env variable before running this example.
|
||||
//!
|
||||
//! This gets an ip address via DHCP then performs an HTTP get request to some "random" server
|
||||
//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate.
|
||||
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use esp_alloc as _;
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
prelude::*,
|
||||
rng::Rng,
|
||||
time::{self, Duration},
|
||||
timer::timg::TimerGroup,
|
||||
};
|
||||
use esp_println::{print, println};
|
||||
use esp_wifi::{
|
||||
init,
|
||||
wifi::{
|
||||
utils::create_network_interface,
|
||||
AccessPointInfo,
|
||||
ClientConfiguration,
|
||||
Configuration,
|
||||
WifiError,
|
||||
WifiStaDevice,
|
||||
},
|
||||
};
|
||||
use smoltcp::{
|
||||
iface::{SocketSet, SocketStorage},
|
||||
wire::DhcpOption,
|
||||
};
|
||||
use smoltcp_nal::{
|
||||
embedded_nal::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpClientStack},
|
||||
NetworkStack,
|
||||
};
|
||||
|
||||
const SSID: &str = env!("SSID");
|
||||
const PASSWORD: &str = env!("PASSWORD");
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
esp_println::logger::init_logger_from_env();
|
||||
let peripherals = esp_hal::init({
|
||||
let mut config = esp_hal::Config::default();
|
||||
config.cpu_clock = CpuClock::max();
|
||||
config
|
||||
});
|
||||
|
||||
esp_alloc::heap_allocator!(72 * 1024);
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
|
||||
let init = init(
|
||||
timg0.timer0,
|
||||
Rng::new(peripherals.RNG),
|
||||
peripherals.RADIO_CLK,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut wifi = peripherals.WIFI;
|
||||
let (iface, device, mut controller) =
|
||||
create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap();
|
||||
|
||||
let mut socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
let mut sockets = SocketSet::new(&mut socket_set_entries[..]);
|
||||
|
||||
let mut rx_buffer = [0u8; 128];
|
||||
let mut tx_buffer = [0u8; 128];
|
||||
sockets.add(smoltcp::socket::tcp::Socket::new(
|
||||
smoltcp::socket::tcp::SocketBuffer::new(&mut rx_buffer[..]),
|
||||
smoltcp::socket::tcp::SocketBuffer::new(&mut tx_buffer[..]),
|
||||
));
|
||||
let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new();
|
||||
// we can set a hostname here (or add other DHCP options)
|
||||
dhcp_socket.set_outgoing_options(&[DhcpOption {
|
||||
kind: 12,
|
||||
data: b"esp-wifi",
|
||||
}]);
|
||||
sockets.add(dhcp_socket);
|
||||
let mut network_stack = NetworkStack::new(iface, device, sockets, StackClock);
|
||||
|
||||
let client_config = Configuration::Client(ClientConfiguration {
|
||||
ssid: SSID.try_into().unwrap(),
|
||||
password: PASSWORD.try_into().unwrap(),
|
||||
..Default::default()
|
||||
});
|
||||
let res = controller.set_configuration(&client_config);
|
||||
println!("wifi_set_configuration returned {:?}", res);
|
||||
|
||||
controller.start().unwrap();
|
||||
println!("is wifi started: {:?}", controller.is_started());
|
||||
|
||||
println!("Start Wifi Scan");
|
||||
let res: Result<(heapless::Vec<AccessPointInfo, 10>, usize), WifiError> = controller.scan_n();
|
||||
if let Ok((res, _count)) = res {
|
||||
for ap in res {
|
||||
println!("{:?}", ap);
|
||||
}
|
||||
}
|
||||
|
||||
println!("{:?}", controller.get_capabilities());
|
||||
println!("wifi_connect {:?}", controller.connect());
|
||||
|
||||
// wait to get connected
|
||||
println!("Wait to get connected");
|
||||
loop {
|
||||
match controller.is_connected() {
|
||||
Ok(true) => break,
|
||||
Ok(false) => {}
|
||||
Err(err) => {
|
||||
println!("{:?}", err);
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("{:?}", controller.is_connected());
|
||||
|
||||
// wait for getting an ip address
|
||||
println!("Wait to get an ip address");
|
||||
loop {
|
||||
network_stack.poll().unwrap();
|
||||
|
||||
if let Some(ip) = network_stack.interface().ipv4_addr() {
|
||||
if !ip.is_unspecified() {
|
||||
println!("got ip {:?}", ip);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Start busy loop on main");
|
||||
|
||||
let mut socket = network_stack.socket().unwrap();
|
||||
|
||||
loop {
|
||||
println!("Making HTTP request");
|
||||
while network_stack.poll().unwrap() {}
|
||||
|
||||
with_network_stack(&mut network_stack, |network_stack| {
|
||||
network_stack.connect(
|
||||
&mut socket,
|
||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(142, 250, 185, 115), 80)),
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
println!("connected");
|
||||
|
||||
with_network_stack(&mut network_stack, |network_stack| {
|
||||
network_stack.send(
|
||||
&mut socket,
|
||||
b"GET / HTTP/1.0\r\nHost: www.mobile-j.de\r\n\r\n",
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let deadline = time::now() + Duration::secs(20);
|
||||
let mut buffer = [0u8; 512];
|
||||
while let Ok(len) = with_network_stack(&mut network_stack, |network_stack| {
|
||||
network_stack.receive(&mut socket, &mut buffer)
|
||||
}) {
|
||||
let to_print = unsafe { core::str::from_utf8_unchecked(&buffer[..len]) };
|
||||
print!("{}", to_print);
|
||||
|
||||
if time::now() > deadline {
|
||||
println!("Timeout");
|
||||
break;
|
||||
}
|
||||
|
||||
network_stack.poll().unwrap();
|
||||
}
|
||||
println!();
|
||||
|
||||
network_stack.close_sockets();
|
||||
|
||||
let deadline = time::now() + Duration::secs(5);
|
||||
while time::now() < deadline {
|
||||
network_stack.poll().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn with_network_stack<'a, D: smoltcp::phy::Device, C: embedded_time::Clock<T = u32>, R>(
|
||||
network_stack: &mut smoltcp_nal::NetworkStack<'a, D, C>,
|
||||
mut f: impl FnMut(
|
||||
&mut smoltcp_nal::NetworkStack<'a, D, C>,
|
||||
) -> smoltcp_nal::embedded_nal::nb::Result<R, smoltcp_nal::NetworkError>,
|
||||
) -> smoltcp_nal::embedded_nal::nb::Result<R, smoltcp_nal::NetworkError> {
|
||||
let res = loop {
|
||||
let res = f(network_stack);
|
||||
if let nb::Result::Err(nb::Error::WouldBlock) = res {
|
||||
network_stack.poll().unwrap();
|
||||
continue;
|
||||
}
|
||||
|
||||
break res;
|
||||
};
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
struct StackClock;
|
||||
|
||||
impl embedded_time::Clock for StackClock {
|
||||
type T = u32;
|
||||
|
||||
const SCALING_FACTOR: embedded_time::rate::Fraction =
|
||||
embedded_time::rate::Fraction::new(1, 1_000_000);
|
||||
|
||||
fn try_now(&self) -> Result<embedded_time::Instant<Self>, embedded_time::clock::Error> {
|
||||
Ok(embedded_time::Instant::new(
|
||||
esp_hal::time::now().ticks() as u32
|
||||
))
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
//! Because of the huge task-arena size configured this won't work on ESP32-S2
|
||||
//!
|
||||
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
|
@ -12,7 +12,7 @@
|
||||
//! Because of the huge task-arena size configured this won't work on ESP32-S2
|
||||
//!
|
||||
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
|
@ -10,7 +10,7 @@
|
||||
//! Because of the huge task-arena size configured this won't work on ESP32-S2 and ESP32-C2
|
||||
//!
|
||||
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c3 esp32c6
|
||||
|
||||
#![allow(static_mut_refs)]
|
||||
|
@ -7,7 +7,7 @@
|
||||
//!
|
||||
//! Because of the huge task-arena size configured this won't work on ESP32-S2
|
||||
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
|
@ -4,7 +4,7 @@
|
||||
//!
|
||||
//! Because of the huge task-arena size configured this won't work on ESP32-S2
|
||||
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
|
@ -4,7 +4,7 @@
|
||||
//!
|
||||
//! Because of the huge task-arena size configured this won't work on ESP32-S2
|
||||
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now
|
||||
//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
|
@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! Broadcasts, receives and sends messages via esp-now
|
||||
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
|
@ -3,7 +3,7 @@
|
||||
//! Sniffs for beacon frames.
|
||||
//!
|
||||
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
|
@ -7,12 +7,13 @@
|
||||
//! - responds with some HTML content when connecting to port 8080
|
||||
//!
|
||||
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils
|
||||
//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils
|
||||
//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use blocking_network_stack::Stack;
|
||||
use embedded_io::*;
|
||||
use esp_alloc as _;
|
||||
use esp_backtrace as _;
|
||||
@ -33,9 +34,8 @@ use esp_wifi::{
|
||||
WifiError,
|
||||
WifiStaDevice,
|
||||
},
|
||||
wifi_interface::WifiStack,
|
||||
};
|
||||
use smoltcp::iface::SocketStorage;
|
||||
use smoltcp::iface::{SocketSet, SocketStorage};
|
||||
|
||||
const SSID: &str = env!("SSID");
|
||||
const PASSWORD: &str = env!("PASSWORD");
|
||||
@ -55,20 +55,19 @@ fn main() -> ! {
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
|
||||
let init = init(
|
||||
timg0.timer0,
|
||||
Rng::new(peripherals.RNG),
|
||||
peripherals.RADIO_CLK,
|
||||
)
|
||||
.unwrap();
|
||||
let mut rng = Rng::new(peripherals.RNG);
|
||||
|
||||
let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap();
|
||||
|
||||
let mut wifi = peripherals.WIFI;
|
||||
let (iface, device, mut controller) =
|
||||
create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap();
|
||||
|
||||
let mut socket_set_entries: [SocketStorage; 3] = Default::default();
|
||||
let (iface, device, mut controller, sockets) =
|
||||
create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap();
|
||||
let socket_set = SocketSet::new(&mut socket_set_entries[..]);
|
||||
|
||||
let now = || time::now().duration_since_epoch().to_millis();
|
||||
let mut wifi_stack = WifiStack::new(iface, device, sockets, now);
|
||||
let mut stack = Stack::new(iface, device, socket_set, now, rng.random());
|
||||
|
||||
let client_config = Configuration::Client(ClientConfiguration {
|
||||
ssid: SSID.try_into().unwrap(),
|
||||
@ -108,14 +107,14 @@ fn main() -> ! {
|
||||
|
||||
println!("Setting static IP {}", STATIC_IP);
|
||||
|
||||
wifi_stack
|
||||
.set_iface_configuration(&esp_wifi::wifi::ipv4::Configuration::Client(
|
||||
esp_wifi::wifi::ipv4::ClientConfiguration::Fixed(
|
||||
esp_wifi::wifi::ipv4::ClientSettings {
|
||||
ip: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip(STATIC_IP)),
|
||||
subnet: esp_wifi::wifi::ipv4::Subnet {
|
||||
gateway: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip(GATEWAY_IP)),
|
||||
mask: esp_wifi::wifi::ipv4::Mask(24),
|
||||
stack
|
||||
.set_iface_configuration(&blocking_network_stack::ipv4::Configuration::Client(
|
||||
blocking_network_stack::ipv4::ClientConfiguration::Fixed(
|
||||
blocking_network_stack::ipv4::ClientSettings {
|
||||
ip: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip(STATIC_IP)),
|
||||
subnet: blocking_network_stack::ipv4::Subnet {
|
||||
gateway: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip(GATEWAY_IP)),
|
||||
mask: blocking_network_stack::ipv4::Mask(24),
|
||||
},
|
||||
dns: None,
|
||||
secondary_dns: None,
|
||||
@ -131,7 +130,7 @@ fn main() -> ! {
|
||||
|
||||
let mut rx_buffer = [0u8; 1536];
|
||||
let mut tx_buffer = [0u8; 1536];
|
||||
let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer);
|
||||
|
||||
socket.listen(8080).unwrap();
|
||||
|
||||
|
@ -159,11 +159,11 @@ fn apply_feature_rules(package: &Package, config: &Config) -> Vec<String> {
|
||||
let mut features = vec![];
|
||||
if config.contains("wifi") {
|
||||
features.push("wifi".to_owned());
|
||||
features.push("wifi-default".to_owned());
|
||||
features.push("esp-now".to_owned());
|
||||
features.push("sniffer".to_owned());
|
||||
features.push("utils".to_owned());
|
||||
features.push("embassy-net".to_owned());
|
||||
features.push("smoltcp/proto-ipv4".to_owned());
|
||||
features.push("smoltcp/proto-ipv6".to_owned());
|
||||
}
|
||||
if config.contains("ble") {
|
||||
features.push("ble".to_owned());
|
||||
@ -171,7 +171,6 @@ fn apply_feature_rules(package: &Package, config: &Config) -> Vec<String> {
|
||||
if config.contains("wifi") && config.contains("ble") {
|
||||
features.push("coex".to_owned());
|
||||
}
|
||||
features.push("async".to_owned());
|
||||
features
|
||||
}
|
||||
_ => vec![],
|
||||
|
@ -700,7 +700,7 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> {
|
||||
let mut features = format!("--features={chip},defmt,sys-logs");
|
||||
|
||||
if device.contains("wifi") {
|
||||
features.push_str(",wifi-default,esp-now,sniffer")
|
||||
features.push_str(",esp-now,sniffer")
|
||||
}
|
||||
if device.contains("bt") {
|
||||
features.push_str(",ble")
|
||||
|
Loading…
x
Reference in New Issue
Block a user