1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
//! # esp-net-easy
//! Copyright (C) 2026 Jorge Andre Castro
//!
//! Abstraction asynchrone `no_std` pour simplifier la configuration WiFi et la
//! mise en route de la pile réseau [Embassy](https://embassy.dev/) sur les puces ESP32.
//!
//! Cette version cible l'écosystème **esp-hal 1.x / esp-radio 0.18 / embassy-net 0.9 /
//! embassy-executor 0.10** (où `esp-rtos` remplace `esp-hal-embassy`, et où
//! `esp-wifi` est remplacé par `esp-radio`).
//!
//! Cette crate encapsule la « tuyauterie » habituellement nécessaire pour démarrer
//! un client WiFi (mode Station) avec une adresse IP statique : configuration de
//! la pile `embassy-net`, allocation statique des ressources requises par Embassy,
//! et lancement des tâches de fond (gestion de la connexion WiFi et exécution de
//! la pile réseau).
//!
//! ## Prérequis applicatifs
//!
//! L'initialisation du runtime Embassy (`esp_rtos::start`) reste à la charge de
//! l'application, car elle nécessite la possession d'un timer matériel qui ne
//! peut pas être abstrait sans imposer un câblage figé.
//!
//! Depuis `esp-radio` 0.18, il n'existe plus de `esp_radio::init()` ni de
//! `esp_radio::Controller` séparés : l'initialisation du contrôleur radio est
//! intégrée directement dans `esp_radio::wifi::new`, qui ne prend en entrée que
//! le périphérique `WIFI` de la HAL. Cette crate prend donc directement ce
//! périphérique en entrée.
//!
//! ## Exemple d'utilisation
//!
//! ```rust,ignore
//! #![no_std]
//! #![no_main]
//!
//! use embassy_executor::Spawner;
//! use esp_hal::clock::CpuClock;
//! use esp_hal::rng::Rng;
//! use esp_hal::timer::timg::TimerGroup;
//! use esp_net_easy::{init_esp_net_easy, WifiEasyConfig};
//!
//! #[esp_rtos::main]
//! async fn main(spawner: Spawner) -> ! {
//! let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
//! let peripherals = esp_hal::init(config);
//!
//! esp_alloc::heap_allocator!(size: 72 * 1024);
//!
//! let timg0 = TimerGroup::new(peripherals.TIMG0);
//! esp_rtos::start(timg0.timer0);
//!
//! let net_config = WifiEasyConfig {
//! ssid: "MonReseauWifi",
//! password: "MotDePasseSecret",
//! static_ip: "192.168.1.50",
//! gateway_ip: "192.168.1.1",
//! dns_server: Some("8.8.8.8"),
//! };
//!
//! let stack = init_esp_net_easy(&spawner, peripherals.WIFI, net_config).await;
//!
//! // `stack` est prête : on peut désormais créer des sockets TCP/UDP.
//! loop {
//! embassy_time::Timer::after(embassy_time::Duration::from_secs(1)).await;
//! }
//! }
//! ```
//!
//! ## Fonctionnement interne
//!
//! 1. Le pilote radio `esp-radio` initialise le contrôleur et l'interface Station
//! à partir du périphérique `WIFI` fourni par l'application
//! (`esp_radio::wifi::new`).
//! 2. L'adresse IP statique, le masque `/24` et la passerelle sont assemblés en
//! une configuration réseau `embassy-net` (avec un serveur DNS optionnel).
//! 3. Une graine aléatoire 64 bits est générée via le RNG matériel de l'ESP32 pour
//! initialiser la pile réseau.
//! 4. Les ressources statiques de la pile (`StackResources`) sont allouées via
//! [`static_cell`], évitant toute gestion manuelle de mémoire statique côté
//! application.
//! 5. Deux tâches Embassy sont lancées en arrière-plan :
//! - `[`net_task`]` fait tourner le `Runner` de la pile réseau.
//! - `[`wifi_connection_task`]`configure le mode Station (SSID/mot de passe) —
//! ce qui démarre implicitement le contrôleur WiFi — puis se connecte au
//! point d'accès et tente une reconnexion automatique en cas de
//! déconnexion.
//! 6. La fonction attend que la configuration réseau soit active
//! (`stack.wait_config_up()`) avant de retourner la [`Stack`] prête à l'emploi.
//!
//! ## Limitations connues
//!
//! - Seul le mode Station (client) avec adressage **IPv4 statique** est supporté ;
//! ni le DHCP, ni le mode Access Point, ni l'IPv6 ne sont gérés pour l'instant.
//! - Le masque de sous-réseau est figé à `/24`.
//! - Les paramètres `ssid`, `password`, `static_ip` et `gateway_ip` doivent être
//! des chaînes statiques (`&'static str`).
//! - En cas de SSID/IP invalides, la fonction `panic!` (`.unwrap()` / `.expect()`),
//! ce qui est jugé acceptable pour une configuration figée au démarrage sur
//! systèmes embarqués.
use FromStr;
use Spawner;
use ;
use ;
use Rng;
use ;
/// Macro publique pour faciliter l'allocation statique requise par Embassy.
///
/// Permet de promouvoir une valeur à une référence `'static` sans `unsafe`,
/// en s'appuyant sur [`static_cell::StaticCell`]. Utilisée en interne par
/// [`init_esp_net_easy`] pour allouer les [`embassy_net::StackResources`], mais
/// reste exportée pour un usage applicatif (par exemple pour d'autres
/// ressources nécessitant une durée de vie `'static`).
/// Tâche Embassy interne qui maintient la pile réseau active en arrière-plan.
///
/// Cette tâche exécute en boucle le [`embassy_net::Runner`] associé à la pile
/// réseau. Elle ne retourne jamais et doit rester active pendant toute la durée
/// de vie de l'application pour que les sockets TCP/UDP fonctionnent.
async
/// Tâche Embassy interne qui configure le mode Station, démarre implicitement
/// le contrôleur WiFi, et gère la connexion ainsi que les reconnexions.
///
/// Cette tâche :
/// - configure le contrôleur en mode Station avec le SSID et le mot de passe
/// fournis via [`WifiController::set_config`] — ce qui démarre (ou redémarre)
/// automatiquement le contrôleur WiFi sous-jacent (`esp_wifi_start`), sans
/// appel `start_async` séparé,
/// - tente de se connecter au point d'accès (`connect_async`),
/// - attend la déconnexion (`wait_for_disconnect_async`) et tente
/// automatiquement une reconnexion.
async
/// Structure de configuration pour l'initialisation du réseau.
///
/// Tous les champs de type chaîne attendent des `&'static str` (généralement des
/// littéraux ou des constantes), car ils sont utilisés pour configurer le matériel
/// au démarrage et n'ont pas besoin d'être réalloués.
///
/// # Champs
///
/// - `ssid` : nom du point d'accès WiFi auquel se connecter.
/// - `password` : mot de passe associé au point d'accès.
/// - `static_ip` : adresse IPv4 statique à attribuer à l'ESP32 (ex. `"192.168.1.50"`).
/// - `gateway_ip` : adresse IPv4 de la passerelle/routeur (ex. `"192.168.1.1"`).
/// - `dns_server` : adresse IPv4 d'un serveur DNS optionnel (ex. `Some("8.8.8.8")`).
/// Si `None`, aucun serveur DNS n'est configuré. Si l'adresse fournie est
/// invalide, elle est silencieusement ignorée.
///
/// # Exemple
///
/// ```rust,ignore
/// let config = WifiEasyConfig {
/// ssid: "MonReseauWifi",
/// password: "MotDePasseSecret",
/// static_ip: "192.168.1.50",
/// gateway_ip: "192.168.1.1",
/// dns_server: Some("8.8.8.8"),
/// };
/// ```
/// Initialise le WiFi de l'ESP32 et démarre la pile réseau Embassy de manière transparente.
///
/// Cette fonction crée le contrôleur et l'interface Station à partir du
/// périphérique `WIFI` fourni, configure une adresse IP statique, alloue les
/// ressources statiques requises, lance les tâches de fond d'Embassy et attend
/// que l'interface réseau soit active.
///
/// # Arguments
/// * `spawner` - Le `Spawner` d'Embassy permettant de lancer les tâches réseau en tâche de fond.
/// * `wifi_peripheral` - Le périphérique matériel `WIFI` de la HAL.
/// * `config` - Les paramètres réseau (SSID, mot de passe, IP statique).
///
/// # Retour
/// Retourne une référence statique sur la `Stack` Embassy, prête pour créer des sockets TCP/UDP.
///
/// # Panics
///
/// Cette fonction panique dans les cas suivants :
/// - `config.static_ip` ou `config.gateway_ip` n'est pas une adresse IPv4 valide
/// (échec de [`Ipv4Address::from_str`]).
/// - L'initialisation de l'interface WiFi `esp-radio` échoue (`esp_radio::wifi::new`).
/// - Le lancement des tâches `[`net_task`]` ou `[`wifi_connection_task`]` via le
/// [`Spawner`] échoue (par exemple si l'arène de tâches Embassy est pleine).
///
/// # Exemple
///
/// ```rust,ignore
/// let stack = init_esp_net_easy(&spawner, peripherals.WIFI, config).await;
/// // `stack` peut maintenant être utilisée pour créer des sockets TCP/UDP.
/// ```
pub async