#![no_std]
#![no_main]
use core::sync::atomic::Ordering;
use embassy_executor::Spawner;
use embassy_futures::join::{join, join3};
use embassy_time::{Duration, Timer};
use esp_csi_rs::csi::CSIDataPacket;
use esp_csi_rs::logging::logging::LogMode;
use esp_csi_rs::{
config::CsiConfig, logging::logging::init_logger, CSINode, CollectionMode, CsiDeliveryMode,
WifiSnifferConfig,
};
use esp_csi_rs::{
get_dropped_packets_rx, log_ln, set_csi_callback, set_csi_delivery_mode,
set_csi_logging_enabled, CSINodeClient, CSINodeHardware,
};
use esp_hal::clock::CpuClock;
use esp_hal::timer::timg::TimerGroup;
use esp_radio::wifi::WifiController;
use portable_atomic::{AtomicI32, AtomicU32, AtomicU64};
use {esp_backtrace as _, esp_println as _};
extern crate alloc;
static WIFI_CONTROLLER: static_cell::StaticCell<WifiController<'static>> =
static_cell::StaticCell::new();
esp_bootloader_esp_idf::esp_app_desc!();
#[allow(
clippy::large_stack_frames,
reason = "it's not unusual to allocate larger buffers etc. in main"
)]
macro_rules! mk_static {
($t:ty,$val:expr) => {{
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
#[deny(unused_attributes)]
let x = STATIC_CELL.uninit().write(($val));
x
}};
}
static CSI_CB_COUNT: AtomicU32 = AtomicU32::new(0);
static LATEST_RSSI: AtomicI32 = AtomicI32::new(0);
static LATEST_TONE_ENERGY: AtomicU64 = AtomicU64::new(0);
static LATEST_TONE_COUNT: AtomicU32 = AtomicU32::new(0);
static CSI_DRAIN_COUNT: AtomicU32 = AtomicU32::new(0);
static LATEST_DRAIN_ENERGY: AtomicU64 = AtomicU64::new(0);
fn on_csi(packet: &CSIDataPacket) {
LATEST_RSSI.store(packet.rssi as i32, Ordering::Relaxed);
CSI_CB_COUNT.fetch_add(1, Ordering::Relaxed);
let mut energy: u64 = 0;
let tones = packet.csi_data.len();
for sample in packet.csi_data.iter() {
energy = energy.wrapping_add((*sample as i32).unsigned_abs() as u64);
}
LATEST_TONE_ENERGY.store(energy, Ordering::Relaxed);
LATEST_TONE_COUNT.store(tones as u32, Ordering::Relaxed);
}
async fn csi_drainer(client: &mut CSINodeClient) {
loop {
let packet = client.next_csi_packet().await;
CSI_DRAIN_COUNT.fetch_add(1, Ordering::Relaxed);
let mut energy: u64 = 0;
for sample in packet.csi_data.iter() {
energy = energy.wrapping_add((*sample as i32).unsigned_abs() as u64);
}
LATEST_DRAIN_ENERGY.store(energy, Ordering::Relaxed);
}
}
async fn stats_task() {
let mut last_cb = CSI_CB_COUNT.load(Ordering::Relaxed);
let mut last_drain = CSI_DRAIN_COUNT.load(Ordering::Relaxed);
loop {
Timer::after_secs(1).await;
let cb = CSI_CB_COUNT.load(Ordering::Relaxed);
let drain = CSI_DRAIN_COUNT.load(Ordering::Relaxed);
let cb_delta = cb.wrapping_sub(last_cb);
let drain_delta = drain.wrapping_sub(last_drain);
last_cb = cb;
last_drain = drain;
log_ln!(
"mode={:?} cb/sec: {}, drain/sec: {}, dropped: {}, RSSI: {} dBm, tones: {}, cb_energy: {}, drain_energy: {}",
esp_csi_rs::csi_delivery_mode(),
cb_delta,
drain_delta,
get_dropped_packets_rx(),
LATEST_RSSI.load(Ordering::Relaxed),
LATEST_TONE_COUNT.load(Ordering::Relaxed),
LATEST_TONE_ENERGY.load(Ordering::Relaxed),
LATEST_DRAIN_ENERGY.load(Ordering::Relaxed),
);
}
}
async fn mode_switcher() {
loop {
log_ln!("--- switching to CsiDeliveryMode::Callback ---");
set_csi_delivery_mode(CsiDeliveryMode::Callback);
Timer::after_secs(10).await;
log_ln!("--- switching to CsiDeliveryMode::Async ---");
set_csi_delivery_mode(CsiDeliveryMode::Async);
Timer::after_secs(10).await;
}
}
#[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);
init_logger(spawner, LogMode::Text);
set_csi_logging_enabled(false);
esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 61440);
let timg0 = TimerGroup::new(peripherals.TIMG0);
let sw_interrupt =
esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0);
log_ln!("Embassy initialized!");
log_ln!("Starting CSI callback test (sniffer mode)");
let config_radio = esp_radio::wifi::ControllerConfig::default();
let (wifi_controller, mut interfaces) =
esp_radio::wifi::new(peripherals.WIFI, config_radio)
.expect("Failed to initialize Wi-Fi controller");
let controller = WIFI_CONTROLLER.init(wifi_controller);
let mut node_handle = CSINodeClient::new();
let csi_hardware = CSINodeHardware::new(&mut interfaces, controller);
let mut node = CSINode::new(
esp_csi_rs::Node::Peripheral(esp_csi_rs::PeripheralOpMode::WifiSniffer(
WifiSnifferConfig::default(),
)),
CollectionMode::Collector,
Some(CsiConfig::default()),
Some(1000),
csi_hardware,
);
node.set_protocol(esp_radio::wifi::Protocol::LR);
node.set_rate(esp_radio::esp_now::WifiPhyRate::RateMcs0Lgi);
set_csi_callback(on_csi);
join(
node.run(),
join3(csi_drainer(&mut node_handle), mode_switcher(), stats_task()),
)
.await;
loop {
log_ln!("Hello world!");
Timer::after(Duration::from_secs(1)).await;
}
}