use embassy_futures::select::{Either, Either3, select, select3};
use embassy_time::{Duration, Instant, Timer};
use esp_radio::esp_now::{BROADCAST_ADDRESS, EspNow};
use crate::espnow_phy::with_espnow_recv_suspended;
use crate::{
CENTRAL_MAGIC_NUMBER, ControlPacket, EspNowConfig, IOTaskConfig, PERIPHERAL_MAGIC_NUMBER,
PeripheralPacket, STOP_SIGNAL, log_ln, parse_with_magic, serialize_with_magic,
};
#[cfg(feature = "statistics")]
use crate::STATS;
#[cfg(feature = "statistics")]
use portable_atomic::Ordering;
const BEACON_PERIOD: Duration = Duration::from_secs(1);
const RX_BURST_MAX_RX_ONLY: u16 = 64;
const BEACON_BUF_LEN: usize = 16;
pub async fn run_esp_now_fast_collector(
esp_now: &mut EspNow<'static>,
config: &EspNowConfig,
io_tasks: IOTaskConfig,
) {
let peer_mac = config.peer_mac();
let send_magic = peer_mac.is_none();
if config.secondary_channel().is_none() {
with_espnow_recv_suspended(|| {
esp_now.set_channel(config.channel).unwrap();
});
}
log_ln!("esp-now version {}", esp_now.version().unwrap());
#[cfg(feature = "statistics")]
let mut beacon_seq: u32 = 0;
let mut tx_buf = [0u8; BEACON_BUF_LEN];
let mut detected_src: Option<[u8; 6]> = peer_mac;
if detected_src.is_none() {
let mut beacon_at = Instant::now();
loop {
if io_tasks.tx_enabled && Instant::now() >= beacon_at {
let pkt = ControlPacket::new(
true,
#[cfg(feature = "statistics")]
beacon_seq,
);
if let Ok(msg) =
serialize_with_magic(&pkt, CENTRAL_MAGIC_NUMBER, send_magic, &mut tx_buf)
{
let _ = esp_now.send_async(&BROADCAST_ADDRESS, msg).await;
#[cfg(feature = "statistics")]
{
STATS.tx_count.fetch_add(1, Ordering::Relaxed);
beacon_seq = beacon_seq.wrapping_add(1);
}
}
beacon_at = Instant::now() + BEACON_PERIOD;
}
let until_beacon = beacon_at.saturating_duration_since(Instant::now());
match select3(STOP_SIGNAL.wait(), crate::esp_now_pool::receive_async(), Timer::after(until_beacon)).await {
Either3::First(_) => {
log_ln!("STOP signal received, shutting down fast collector...");
STOP_SIGNAL.signal(());
return;
}
Either3::Second(r) => {
let is_source = match peer_mac {
Some(expected) => r.info.src_address == expected,
None => parse_with_magic::<PeripheralPacket>(
r.data(),
PERIPHERAL_MAGIC_NUMBER,
true,
)
.is_some(),
};
if is_source {
detected_src = Some(r.info.src_address);
break;
}
}
Either3::Third(_) => { }
}
}
}
if let Some(src) = detected_src {
log_ln!(
"ESP-NOW fast collector: source {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} detected, beacon stopped, RX-only flood capture",
src[0],
src[1],
src[2],
src[3],
src[4],
src[5],
);
}
loop {
match select(STOP_SIGNAL.wait(), crate::esp_now_pool::receive_async()).await {
Either::First(_) => {
log_ln!("STOP signal received, shutting down fast collector...");
STOP_SIGNAL.signal(());
break;
}
Either::Second(_first) => {
let mut drained: u16 = 0;
while drained < RX_BURST_MAX_RX_ONLY {
if crate::esp_now_pool::receive().is_none() {
break;
}
drained = drained.saturating_add(1);
}
}
}
}
}