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:
Björn Quentin 2024-11-12 12:43:01 +01:00 committed by GitHub
parent 6cb5d9643f
commit 5d120f7a70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 472 additions and 1522 deletions

View File

@ -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

View File

@ -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"]

View File

@ -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

View File

@ -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 _: () = {

View File

@ -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;

View File

@ -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,
})
}

View File

@ -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));
}
}

View File

@ -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" ] }

View File

@ -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]

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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");

View File

@ -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;
}
}

View File

@ -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");

View 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
))
}
}

View File

@ -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]

View File

@ -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]

View File

@ -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)]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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();

View File

@ -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![],

View File

@ -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")