device_envoy_esp/wifi_auto/fields.rs
1//! A device abstraction for extra setup fields used by [`WifiAutoEsp`](crate::wifi_auto::WifiAutoEsp).
2//!
3//! See the [`WifiAutoEsp` struct example](crate::wifi_auto::WifiAutoEsp) for the full setup.
4//!
5//! This module provides ready-to-use field types that can be passed to
6//! [`WifiAutoEsp::new`](crate::wifi_auto::WifiAutoEsp::new) for collecting additional
7//! configuration beyond WiFi credentials.
8//!
9//! There are two levels of customization:
10//!
11//! 1. Use built-in helpers like [`TextField`] and [`TimezoneField`].
12//! 2. Define your own field type by implementing [`WifiAutoField`]. See example there.
13//!
14//! # Example
15//!
16//! ```rust,no_run
17//! # #![no_std]
18//! # #![no_main]
19//! use device_envoy_esp::{
20//! Error, Result,
21//! button::{ButtonEsp, PressedTo},
22//! flash_block::FlashBlockEsp,
23//! wifi_auto::{WifiAuto as _, WifiAutoEvent, WifiAutoEsp},
24//! wifi_auto::fields::{TextField, TextFieldStatic, TimezoneField, TimezoneFieldStatic},
25//! };
26//!
27//! async fn example(
28//! spawner: embassy_executor::Spawner,
29//! p: esp_hal::peripherals::Peripherals,
30//! ) -> Result<()> {
31//! let mut button6 = ButtonEsp::new(p.GPIO6, PressedTo::Ground);
32//! let [wifi_flash, website_flash, timezone_flash] = FlashBlockEsp::new_array::<3>(p.FLASH)?;
33//!
34//! static WEBSITE_STATIC: TextFieldStatic<32> = TextField::new_static();
35//! let website_field = TextField::new(
36//! &WEBSITE_STATIC,
37//! website_flash,
38//! "website",
39//! "Website",
40//! "google.com",
41//! );
42//!
43//! static TIMEZONE_STATIC: TimezoneFieldStatic = TimezoneField::new_static();
44//! let timezone_field = TimezoneField::new(&TIMEZONE_STATIC, timezone_flash);
45//!
46//! let wifi_auto = WifiAutoEsp::new(
47//! p.WIFI,
48//! wifi_flash,
49//! "DeviceEnvoySetup",
50//! [website_field, timezone_field],
51//! spawner,
52//! )?;
53//!
54//! let _stack = wifi_auto
55//! .connect(&mut button6, |wifi_auto_event| async move {
56//! match wifi_auto_event {
57//! WifiAutoEvent::CaptivePortalReady => {}
58//! WifiAutoEvent::Connecting { .. } => {}
59//! WifiAutoEvent::ConnectionFailed => {}
60//! }
61//! Ok(())
62//! })
63//! .await?;
64//!
65//! let _website = website_field.text()?.unwrap_or_default();
66//! let _offset_minutes = timezone_field
67//! .offset_minutes()?
68//! .ok_or(Error::MissingCustomWifiAutoField)?;
69//!
70//! Ok(())
71//! }
72//! ```
73
74#![allow(
75 unsafe_code,
76 reason = "unsafe impl Sync is sound: single-threaded Embassy executor, no concurrent access"
77)]
78
79#[cfg(target_os = "none")]
80use crate::flash_block::FlashBlockEsp;
81use crate::Error;
82use device_envoy_core::__impl_wifi_auto_fields;
83use device_envoy_core::wifi_auto::{FormData, HtmlBuffer, WifiAutoField};
84
85__impl_wifi_auto_fields!(
86 flash_block = FlashBlockEsp,
87 error = Error,
88 wifi_auto_field = WifiAutoField,
89 form_data = FormData<'_>,
90 html_buffer = HtmlBuffer,
91 flash_cfg = target_os = "none"
92);
93
94#[cfg(target_os = "none")]
95impl TimezoneField {
96 /// Initialize a timezone field backed by a flash block.
97 ///
98 /// See the [WifiAutoEsp struct example](crate::wifi_auto::WifiAutoEsp) for usage.
99 pub fn new(
100 timezone_field_static: &'static TimezoneFieldStatic,
101 timezone_flash_block: FlashBlockEsp,
102 ) -> &'static Self {
103 Self::new_with_flash(timezone_field_static, timezone_flash_block)
104 }
105}
106
107#[cfg(not(target_os = "none"))]
108impl TimezoneField {
109 /// Initialize a timezone field backed by in-memory state.
110 ///
111 /// See the [WifiAutoEsp struct example](crate::wifi_auto::WifiAutoEsp) for usage.
112 pub fn new(timezone_field_static: &'static TimezoneFieldStatic) -> &'static Self {
113 Self::new_in_memory(timezone_field_static)
114 }
115}
116
117#[cfg(target_os = "none")]
118impl<const N: usize> TextField<N> {
119 /// Initialize a text field backed by a flash block.
120 ///
121 /// See the [WifiAutoEsp struct example](crate::wifi_auto::WifiAutoEsp) for usage.
122 pub fn new(
123 text_field_static: &'static TextFieldStatic<N>,
124 text_flash_block: FlashBlockEsp,
125 field_name: &'static str,
126 label: &'static str,
127 default_value: &'static str,
128 ) -> &'static Self {
129 Self::new_with_flash(
130 text_field_static,
131 text_flash_block,
132 field_name,
133 label,
134 default_value,
135 )
136 }
137}
138
139#[cfg(not(target_os = "none"))]
140impl<const N: usize> TextField<N> {
141 /// Initialize a text field backed by in-memory state.
142 ///
143 /// See the [WifiAutoEsp struct example](crate::wifi_auto::WifiAutoEsp) for usage.
144 pub fn new(
145 text_field_static: &'static TextFieldStatic<N>,
146 field_name: &'static str,
147 label: &'static str,
148 default_value: &'static str,
149 ) -> &'static Self {
150 Self::new_in_memory(text_field_static, field_name, label, default_value)
151 }
152}