feat(power): enable wifi on demand
This commit is contained in:
parent
fec9d8b00a
commit
ab4305a2d3
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -815,11 +815,13 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"const-decoder",
|
||||
"embassy-executor",
|
||||
"embassy-futures",
|
||||
"embassy-net",
|
||||
"embassy-sync 0.5.0 (git+https://github.com/embassy-rs/embassy.git?rev=4b4777)",
|
||||
"embassy-time",
|
||||
"embedded-io-async",
|
||||
"embedded-tls",
|
||||
"enumset",
|
||||
"esp-alloc",
|
||||
"esp-backtrace",
|
||||
"esp-hal",
|
||||
|
@ -13,11 +13,13 @@ bench = false
|
||||
[dependencies]
|
||||
const-decoder = "0.3.0"
|
||||
embassy-executor = { version = "0.5.0", features = ["nightly", "integrated-timers", "arch-riscv32", "executor-thread"] }
|
||||
embassy-futures = "0.1.1"
|
||||
embassy-net = { version = "0.4.0", features = ["dhcpv4", "dhcpv4-hostname", "dns", "medium-ip", "proto-ipv4", "proto-ipv6", "tcp", "udp"] }
|
||||
embassy-sync = { git = "https://github.com/embassy-rs/embassy.git", rev = "4b4777" }
|
||||
embassy-time = { version = "0.3.0" }
|
||||
embedded-io-async = "0.6.1"
|
||||
embedded-tls = { git = "https://github.com/drogue-iot/embedded-tls.git", rev = "f788e02", default-features = false, features = ["embedded-io-adapters"] }
|
||||
enumset = "1.1.3"
|
||||
esp-alloc = "0.3.0"
|
||||
esp-backtrace = { version = "0.11.0", features = ["esp32c3", "exception-handler", "panic-handler", "println"] }
|
||||
esp-hal = { version = "0.16.1", features = ["embassy", "embassy-time-timg0", "esp32c3"] }
|
||||
@ -40,6 +42,8 @@ opt-level = 3
|
||||
|
||||
[features]
|
||||
tls = []
|
||||
tls-sni = ["tls"]
|
||||
real-world = []
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s"
|
||||
|
139
src/main.rs
139
src/main.rs
@ -16,11 +16,14 @@ use alloc::boxed::Box;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::{String, ToString};
|
||||
use embassy_executor::Executor;
|
||||
use embassy_futures::block_on;
|
||||
use embassy_futures::select::{self, select, select3, Either, Either3};
|
||||
use embassy_net::{Config, DhcpConfig, Stack, StackResources};
|
||||
use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embassy_sync::once_lock::OnceLock;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use enumset::enum_set;
|
||||
use esp_alloc::EspHeap;
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::adc::{AdcPin, Attenuation, ADC};
|
||||
@ -50,6 +53,8 @@ static ALLOCATOR: EspHeap = EspHeap::empty();
|
||||
const SSID: Option<&str> = option_env!("WIFI_SSID");
|
||||
const PASSWORD: Option<&str> = option_env!("WIFI_PASSWORD");
|
||||
|
||||
pub const WAKEUP_SCALER: u32 = if cfg!(feature = "real-word") { 20 } else { 1 };
|
||||
|
||||
pub type NetworkStack = &'static Stack<WifiDevice<'static, WifiStaDevice>>;
|
||||
|
||||
fn init_heap() {
|
||||
@ -164,7 +169,7 @@ async fn moisture(
|
||||
.await
|
||||
.insert(Cow::from("moisture"), moisture.to_string());
|
||||
}
|
||||
Timer::after(Duration::from_secs(10) - warmup).await;
|
||||
Timer::after((Duration::from_secs(10) - warmup) * WAKEUP_SCALER).await;
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,14 +195,14 @@ async fn battery_monitor(
|
||||
.await
|
||||
.insert(Cow::from("vbat"), v_bat.to_string());
|
||||
}
|
||||
Timer::after(Duration::from_secs(30)).await;
|
||||
Timer::after(Duration::from_secs(30) * WAKEUP_SCALER).await;
|
||||
}
|
||||
}
|
||||
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
init_logger(log::LevelFilter::Info);
|
||||
init_logger(log::LevelFilter::Debug);
|
||||
init_heap();
|
||||
|
||||
let peripherals = Peripherals::take();
|
||||
@ -272,40 +277,135 @@ fn main() -> ! {
|
||||
make_static!(Mutex::new(ADC::<ADC1>::new(peripherals.ADC1, adc1_config)));
|
||||
|
||||
let executor = EXECUTOR.init(Executor::new());
|
||||
|
||||
let wifi_reqs: &'static _ =
|
||||
make_static!(embassy_sync::channel::Channel::<NoopRawMutex, WifiRequest, 3>::new());
|
||||
let wifi_state: &'static _ = make_static!(embassy_sync::channel::Channel::<
|
||||
NoopRawMutex,
|
||||
WifiRequestState,
|
||||
3,
|
||||
>::new());
|
||||
let wifi_handle = make_static!(WifiHandle {
|
||||
wifi_state: wifi_state.receiver(),
|
||||
wifi_request: wifi_reqs.sender(),
|
||||
});
|
||||
executor.run(move |spawner| {
|
||||
spawner.spawn(publish_data(
|
||||
&stack,
|
||||
seed,
|
||||
Duration::from_secs(60) * WAKEUP_SCALER,
|
||||
wifi_handle.clone(),
|
||||
));
|
||||
if let (Some(ssid), Some(psk)) = (SSID.as_ref(), PASSWORD.as_ref()) {
|
||||
spawner.spawn(ip_task(&stack, wifi_state.sender())).unwrap();
|
||||
spawner
|
||||
.spawn(wifi_connection(controller, ssid, psk))
|
||||
.spawn(wifi_connection(
|
||||
controller,
|
||||
ssid,
|
||||
psk,
|
||||
wifi_reqs.receiver(),
|
||||
wifi_state.sender(),
|
||||
))
|
||||
.unwrap();
|
||||
spawner.spawn(net_task(&stack)).unwrap();
|
||||
spawner.spawn(ip_task(&stack)).unwrap();
|
||||
}
|
||||
// spawner.spawn(blink(led)).unwrap();
|
||||
spawner
|
||||
.spawn(moisture(pin, adc1, moisture_sensor_suppy_pin.into()))
|
||||
.unwrap();
|
||||
spawner.spawn(battery_monitor(vbat_in, adc1)).unwrap();
|
||||
spawner.spawn(publish_data(&stack, seed, Duration::from_secs(60)));
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum WifiRequestState {
|
||||
Connected,
|
||||
Configured,
|
||||
Disconnected,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum WifiRequest {
|
||||
Connect,
|
||||
Disconnect,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WifiHandle<'a> {
|
||||
wifi_state: embassy_sync::channel::Receiver<'a, NoopRawMutex, WifiRequestState, 3>,
|
||||
wifi_request: embassy_sync::channel::Sender<'a, NoopRawMutex, WifiRequest, 3>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WifiScope<'a, 'b> {
|
||||
handle: &'a WifiHandle<'b>,
|
||||
}
|
||||
|
||||
impl Drop for WifiScope<'_, '_> {
|
||||
fn drop(&mut self) {
|
||||
block_on(self.handle.wifi_request.send(WifiRequest::Disconnect));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WifiHandle<'a> {
|
||||
async fn request(&self, request: WifiRequest, desired_state: WifiRequestState) {
|
||||
self.wifi_request.send(request).await;
|
||||
while self.wifi_state.receive().await != desired_state {}
|
||||
}
|
||||
|
||||
pub async fn configured<'b>(&'b self) -> WifiScope<'b, 'a>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
self.request(WifiRequest::Connect, WifiRequestState::Configured)
|
||||
.await;
|
||||
WifiScope { handle: &self }
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn wifi_connection(
|
||||
mut controller: WifiController<'static>,
|
||||
ssid: &'static str,
|
||||
psk: &'static str,
|
||||
wifi_requests: embassy_sync::channel::Receiver<'static, NoopRawMutex, WifiRequest, 3>,
|
||||
wifi_state: embassy_sync::channel::Sender<'static, NoopRawMutex, WifiRequestState, 3>,
|
||||
) {
|
||||
trace!("start wifi connection task");
|
||||
let mut need_connection: u16 = 0;
|
||||
debug!("Device capabilities: {:?}", controller.get_capabilities());
|
||||
loop {
|
||||
match esp_wifi::wifi::get_wifi_state() {
|
||||
WifiState::StaConnected => {
|
||||
// wait until we're no longer connected
|
||||
controller.wait_for_event(WifiEvent::StaDisconnected).await;
|
||||
Timer::after(Duration::from_millis(5000)).await
|
||||
debug!("wifi need: {need_connection}");
|
||||
match select(
|
||||
controller.wait_for_all_events(
|
||||
enum_set!(WifiEvent::StaDisconnected | WifiEvent::StaConnected),
|
||||
true,
|
||||
),
|
||||
wifi_requests.receive(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Either::First(_) => {
|
||||
if controller.is_connected().unwrap() {
|
||||
wifi_state.send(WifiRequestState::Connected).await;
|
||||
}
|
||||
_ => {}
|
||||
if !controller.is_connected().unwrap() {
|
||||
wifi_state.send(WifiRequestState::Disconnected).await;
|
||||
}
|
||||
Timer::after(Duration::from_millis(5000)).await;
|
||||
}
|
||||
Either::Second(req) => match req {
|
||||
WifiRequest::Connect => need_connection += 1,
|
||||
WifiRequest::Disconnect => {
|
||||
need_connection -= 1;
|
||||
if need_connection == 0 {
|
||||
controller.disconnect().await;
|
||||
wifi_state.send(WifiRequestState::Disconnected).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
debug!("wifi need: {need_connection}");
|
||||
if need_connection > 0 {
|
||||
if !matches!(controller.is_started(), Ok(true)) {
|
||||
let client_config = Configuration::Client(ClientConfiguration {
|
||||
ssid: ssid.try_into().unwrap(),
|
||||
@ -320,7 +420,10 @@ async fn wifi_connection(
|
||||
trace!("About to connect...");
|
||||
|
||||
match controller.connect().await {
|
||||
Ok(_) => info!("Wifi connected!"),
|
||||
Ok(_) => {
|
||||
info!("Wifi connected!");
|
||||
wifi_state.send(WifiRequestState::Connected).await;
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to connect to wifi: {e:?}");
|
||||
Timer::after(Duration::from_millis(5000)).await
|
||||
@ -328,11 +431,13 @@ async fn wifi_connection(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub static HAS_IP_ADDRESS: OnceLock<()> = OnceLock::new();
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn ip_task(stack: NetworkStack) {
|
||||
async fn ip_task(
|
||||
stack: NetworkStack,
|
||||
wifi_state: embassy_sync::channel::Sender<'static, NoopRawMutex, WifiRequestState, 3>,
|
||||
) {
|
||||
loop {
|
||||
if stack.is_link_up() {
|
||||
break;
|
||||
@ -342,8 +447,8 @@ async fn ip_task(stack: NetworkStack) {
|
||||
debug!("Waiting to get IP address...");
|
||||
loop {
|
||||
if let Some(config) = stack.config_v4() {
|
||||
wifi_state.send(WifiRequestState::Configured).await;
|
||||
info!("Got IP: {}", config.address);
|
||||
HAS_IP_ADDRESS.get_or_init(|| ());
|
||||
break;
|
||||
}
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
|
19
src/mqtt.rs
19
src/mqtt.rs
@ -4,7 +4,8 @@ use embassy_net::tcp::TcpSocket;
|
||||
use embassy_net::{dns::Error as DnsError, tcp::ConnectError};
|
||||
use embassy_time::{with_timeout, Duration, Instant, TimeoutError, Timer};
|
||||
use embedded_tls::{
|
||||
Aes128GcmSha256, NoVerify, TlsConfig, TlsConnection, TlsContext, TlsError, UnsecureProvider, TLS_RECORD_OVERHEAD,
|
||||
Aes128GcmSha256, NoVerify, TlsConfig, TlsConnection, TlsContext, TlsError, UnsecureProvider,
|
||||
TLS_RECORD_OVERHEAD,
|
||||
};
|
||||
use esp_backtrace as _;
|
||||
use log::{debug, error, info};
|
||||
@ -15,7 +16,7 @@ use rust_mqtt::client::client_config::ClientConfig;
|
||||
use rust_mqtt::packet::v5::reason_codes::ReasonCode;
|
||||
use rust_mqtt::utils::rng_generator::CountingRng;
|
||||
|
||||
use crate::{NetworkStack, DATA, HAS_IP_ADDRESS};
|
||||
use crate::{NetworkStack, WifiHandle, DATA};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SendError {
|
||||
@ -87,8 +88,7 @@ pub async fn send_message(
|
||||
let begin = Instant::now();
|
||||
let dns_resp = stack
|
||||
.dns_query(MQTT_SERVER_HOSTNAME, embassy_net::dns::DnsQueryType::A)
|
||||
.await
|
||||
.map_err(SendError::Dns)?;
|
||||
.await?;
|
||||
let after_dns = Instant::now();
|
||||
const TCP_BUFLEN: usize = 2048 << 2;
|
||||
let mut rx_buffer = [0; TCP_BUFLEN];
|
||||
@ -201,11 +201,17 @@ pub async fn send_message(
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn publish_data(stack: NetworkStack, rng_seed: [u8; 32], interval: Duration) {
|
||||
pub async fn publish_data(
|
||||
stack: NetworkStack,
|
||||
rng_seed: [u8; 32],
|
||||
interval: Duration,
|
||||
wifi: WifiHandle<'static>,
|
||||
) {
|
||||
let mut rng = StdRng::from_seed(rng_seed);
|
||||
HAS_IP_ADDRESS.get().await;
|
||||
loop {
|
||||
{
|
||||
let wifi_guard = wifi.configured().await;
|
||||
Timer::after(Duration::from_millis(10)).await;
|
||||
let mut data = DATA.lock().await;
|
||||
let res = send_message(
|
||||
stack,
|
||||
@ -217,6 +223,7 @@ pub async fn publish_data(stack: NetworkStack, rng_seed: [u8; 32], interval: Dur
|
||||
if let Err(e) = res {
|
||||
error!("Oh no: {e:?}");
|
||||
};
|
||||
drop(wifi_guard);
|
||||
data.clear();
|
||||
}
|
||||
Timer::after(interval).await;
|
||||
|
Loading…
x
Reference in New Issue
Block a user