refactor setup tasks
This commit is contained in:
parent
016c57bb14
commit
1970d37d51
3 changed files with 138 additions and 86 deletions
142
src/main.rs
142
src/main.rs
|
@ -5,26 +5,36 @@ use chrono::{NaiveTime, Utc};
|
||||||
use chrono_tz::US::Pacific;
|
use chrono_tz::US::Pacific;
|
||||||
use esp_idf_svc::{
|
use esp_idf_svc::{
|
||||||
eventloop::EspSystemEventLoop,
|
eventloop::EspSystemEventLoop,
|
||||||
hal::{delay::FreeRtos, gpio::*, modem::Modem, peripheral::Peripheral, prelude::Peripherals},
|
hal::{
|
||||||
sntp::{EspSntp, SntpConf, SyncMode, SyncStatus},
|
delay::FreeRtos,
|
||||||
wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi},
|
gpio::{self, Gpio7, OutputPin, PinDriver},
|
||||||
|
prelude::Peripherals,
|
||||||
|
},
|
||||||
|
sntp::{EspSntp, SntpConf, SyncMode},
|
||||||
|
wifi::EspWifi,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
|
mod ntp;
|
||||||
|
mod wifi;
|
||||||
|
|
||||||
const SNTP_STATUS_POLL_INTVL_MS: u32 = 2000;
|
|
||||||
/// Delay between executions of the main control loop, in milliseconds.
|
/// Delay between executions of the main control loop, in milliseconds.
|
||||||
const CONTROL_LOOP_INTVL: u32 = 60000;
|
const CONTROL_LOOP_INTVL: u32 = 60000;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
/// Struct holding resources which should remain in scope throughout execution.
|
||||||
// It is necessary to call this function once. Otherwise some patches to
|
struct AppState<P: OutputPin> {
|
||||||
// the runtime implemented by esp-idf-sys might not link properly. Refer
|
ntp: EspSntp<'static>,
|
||||||
// to https://github.com/esp-rs/esp-idf-template/issues/71.
|
switch_driver: PinDriver<'static, P, gpio::Output>,
|
||||||
esp_idf_svc::sys::link_patches();
|
wifi: EspWifi<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
// Bind the log crate to the ESP Logging facilities
|
fn main() -> Result<()> {
|
||||||
esp_idf_svc::log::EspLogger::initialize_default();
|
let AppState::<_> {
|
||||||
|
ntp: _ntp,
|
||||||
|
mut switch_driver,
|
||||||
|
wifi: _wifi,
|
||||||
|
} = *setup()?;
|
||||||
|
|
||||||
let t_on = NaiveTime::parse_from_str(config::T_ON, "%H:%M")?;
|
let t_on = NaiveTime::parse_from_str(config::T_ON, "%H:%M")?;
|
||||||
let t_off = NaiveTime::parse_from_str(config::T_OFF, "%H:%M")?;
|
let t_off = NaiveTime::parse_from_str(config::T_OFF, "%H:%M")?;
|
||||||
|
@ -32,32 +42,6 @@ fn main() -> Result<()> {
|
||||||
bail!("t_on and t_off must have distinct values");
|
bail!("t_on and t_off must have distinct values");
|
||||||
}
|
}
|
||||||
|
|
||||||
let peripherals = Peripherals::take()?;
|
|
||||||
|
|
||||||
let mut switch = PinDriver::output(peripherals.pins.gpio7)?;
|
|
||||||
switch.set_low()?;
|
|
||||||
|
|
||||||
let sysloop = EspSystemEventLoop::take()?;
|
|
||||||
|
|
||||||
// Dropping EspWifi shuts down the connection, so this variable must remain
|
|
||||||
// in scope.
|
|
||||||
let _wifi = init_wifi(peripherals.modem, sysloop);
|
|
||||||
|
|
||||||
// SNTP client will continue running in the background until this value is
|
|
||||||
// dropped.
|
|
||||||
let ntp = EspSntp::new(&SntpConf {
|
|
||||||
servers: [config::SNTP_SERVER],
|
|
||||||
sync_mode: SyncMode::Smooth,
|
|
||||||
..Default::default()
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// get_sync_status() returns "Completed" one time and then reverts to
|
|
||||||
// "Reset" on subsequent calls.
|
|
||||||
while ntp.get_sync_status() != SyncStatus::Completed {
|
|
||||||
info!("Waiting for SNTP to sync...");
|
|
||||||
FreeRtos::delay_ms(SNTP_STATUS_POLL_INTVL_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======== Main Control Loop ======== //
|
// ======== Main Control Loop ======== //
|
||||||
loop {
|
loop {
|
||||||
let now = Utc::now().with_timezone(&Pacific);
|
let now = Utc::now().with_timezone(&Pacific);
|
||||||
|
@ -78,9 +62,9 @@ fn main() -> Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if active {
|
if active {
|
||||||
switch.set_high()?;
|
switch_driver.set_high()?;
|
||||||
} else {
|
} else {
|
||||||
switch.set_low()?;
|
switch_driver.set_low()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: enter low power mode ("light sleep" or "deep sleep") instead
|
// TODO: enter low power mode ("light sleep" or "deep sleep") instead
|
||||||
|
@ -89,54 +73,40 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start WiFi module and connect to access point. Returns an error if either
|
/// Runs initial application startup tasks, including connecting WiFi, syncing
|
||||||
/// of WiFi startup or connection fail.
|
/// system time over SNTP, and setting the initial switch state. May block for
|
||||||
fn init_wifi(
|
/// several seconds, or indefinitely if initial SNTP sync is not successful.
|
||||||
modem: impl Peripheral<P = Modem> + 'static,
|
fn setup() -> Result<Box<AppState<Gpio7>>> {
|
||||||
sysloop: EspSystemEventLoop,
|
// It is necessary to call this function once. Otherwise some patches to
|
||||||
) -> Result<Box<EspWifi<'static>>> {
|
// the runtime implemented by esp-idf-sys might not link properly. Refer
|
||||||
let auth_method = AuthMethod::WPA2Personal;
|
// to https://github.com/esp-rs/esp-idf-template/issues/71.
|
||||||
let mut esp_wifi = EspWifi::new(modem, sysloop.clone(), None)?;
|
esp_idf_svc::sys::link_patches();
|
||||||
let mut wifi = BlockingWifi::wrap(&mut esp_wifi, sysloop)?;
|
|
||||||
wifi.set_configuration(&Configuration::Client(ClientConfiguration::default()))?;
|
|
||||||
|
|
||||||
info!("Starting wifi...");
|
// Bind the log crate to the ESP Logging facilities
|
||||||
wifi.start()?;
|
esp_idf_svc::log::EspLogger::initialize_default();
|
||||||
info!("Scanning...");
|
|
||||||
let ap_infos = wifi.scan()?;
|
let peripherals = Peripherals::take()?;
|
||||||
let ours = ap_infos.into_iter().find(|a| a.ssid == config::WIFI_SSID);
|
let sysloop = EspSystemEventLoop::take()?;
|
||||||
let channel = if let Some(ours) = ours {
|
|
||||||
info!(
|
let mut switch_driver = PinDriver::output(peripherals.pins.gpio7)?;
|
||||||
"Found configured access point {} on channel {}",
|
switch_driver.set_low()?;
|
||||||
config::WIFI_SSID,
|
|
||||||
ours.channel
|
// Dropping EspWifi shuts down the connection, so this variable must remain
|
||||||
);
|
// in scope.
|
||||||
Some(ours.channel)
|
let wifi = crate::wifi::init(peripherals.modem, sysloop)?;
|
||||||
} else {
|
|
||||||
info!(
|
// SNTP client will continue running in the background until this value is
|
||||||
"Configured access point {} not found during scanning, will go with unknown channel",
|
// dropped.
|
||||||
config::WIFI_SSID
|
let ntp = EspSntp::new(&SntpConf {
|
||||||
);
|
servers: [config::SNTP_SERVER],
|
||||||
None
|
sync_mode: SyncMode::Smooth,
|
||||||
};
|
|
||||||
wifi.set_configuration(&Configuration::Client(ClientConfiguration {
|
|
||||||
ssid: config::WIFI_SSID
|
|
||||||
.try_into()
|
|
||||||
.expect("Could not parse the given SSID into WiFi config"),
|
|
||||||
password: config::WIFI_PASS
|
|
||||||
.try_into()
|
|
||||||
.expect("Could not parse the given password into WiFi config"),
|
|
||||||
channel,
|
|
||||||
auth_method,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}))?;
|
})?;
|
||||||
|
crate::ntp::wait_for_sync(&ntp);
|
||||||
|
|
||||||
info!("Connecting wifi...");
|
Ok(Box::new(AppState::<_> {
|
||||||
wifi.connect()?;
|
ntp,
|
||||||
info!("Waiting for DHCP lease...");
|
switch_driver,
|
||||||
wifi.wait_netif_up()?;
|
wifi: *wifi,
|
||||||
let ip_info = wifi.wifi().sta_netif().get_ip_info()?;
|
}))
|
||||||
info!("Wifi DHCP info: {:?}", ip_info);
|
|
||||||
|
|
||||||
Ok(Box::new(esp_wifi))
|
|
||||||
}
|
}
|
||||||
|
|
20
src/ntp.rs
Normal file
20
src/ntp.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use esp_idf_svc::{
|
||||||
|
hal::delay::FreeRtos,
|
||||||
|
sntp::{EspSntp, SyncStatus},
|
||||||
|
};
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
/// Delay between checks of the initial NTP sync check, in milliseconds.
|
||||||
|
const SNTP_STATUS_POLL_INTVL: u32 = 2000;
|
||||||
|
|
||||||
|
/// Blocks until the SNTP client reports successful synchronization.
|
||||||
|
///
|
||||||
|
/// Note that the SNTP client's status flag is reset after it is read. Calling
|
||||||
|
/// this function twice will cause the second invocation to wait for the next
|
||||||
|
/// synchronization event.
|
||||||
|
pub(crate) fn wait_for_sync(client: &EspSntp) {
|
||||||
|
while client.get_sync_status() != SyncStatus::Completed {
|
||||||
|
info!("Waiting for SNTP to sync...");
|
||||||
|
FreeRtos::delay_ms(SNTP_STATUS_POLL_INTVL);
|
||||||
|
}
|
||||||
|
}
|
62
src/wifi.rs
Normal file
62
src/wifi.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use esp_idf_svc::{
|
||||||
|
eventloop::EspSystemEventLoop,
|
||||||
|
hal::{modem::Modem, peripheral::Peripheral},
|
||||||
|
wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi},
|
||||||
|
};
|
||||||
|
use log::{debug, info};
|
||||||
|
|
||||||
|
use crate::config;
|
||||||
|
|
||||||
|
/// Start WiFi module and connect to access point. Returns an error if either
|
||||||
|
/// of WiFi startup or connection fail.
|
||||||
|
pub(crate) fn init(
|
||||||
|
modem: impl Peripheral<P = Modem> + 'static,
|
||||||
|
sysloop: EspSystemEventLoop,
|
||||||
|
) -> Result<Box<EspWifi<'static>>> {
|
||||||
|
let auth_method = AuthMethod::WPA2Personal;
|
||||||
|
let mut esp_wifi = EspWifi::new(modem, sysloop.clone(), None)?;
|
||||||
|
let mut wifi = BlockingWifi::wrap(&mut esp_wifi, sysloop)?;
|
||||||
|
wifi.set_configuration(&Configuration::Client(ClientConfiguration::default()))?;
|
||||||
|
|
||||||
|
info!("Starting wifi...");
|
||||||
|
wifi.start()?;
|
||||||
|
debug!("Scanning...");
|
||||||
|
let ap_infos = wifi.scan()?;
|
||||||
|
let ours = ap_infos.into_iter().find(|a| a.ssid == config::WIFI_SSID);
|
||||||
|
let channel = if let Some(ours) = ours {
|
||||||
|
debug!(
|
||||||
|
"Found configured access point {} on channel {}",
|
||||||
|
config::WIFI_SSID,
|
||||||
|
ours.channel
|
||||||
|
);
|
||||||
|
Some(ours.channel)
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"Configured access point {} not found during scanning, will go with unknown channel",
|
||||||
|
config::WIFI_SSID
|
||||||
|
);
|
||||||
|
None
|
||||||
|
};
|
||||||
|
wifi.set_configuration(&Configuration::Client(ClientConfiguration {
|
||||||
|
ssid: config::WIFI_SSID
|
||||||
|
.try_into()
|
||||||
|
.expect("Could not parse the given SSID into WiFi config"),
|
||||||
|
password: config::WIFI_PASS
|
||||||
|
.try_into()
|
||||||
|
.expect("Could not parse the given password into WiFi config"),
|
||||||
|
channel,
|
||||||
|
auth_method,
|
||||||
|
..Default::default()
|
||||||
|
}))?;
|
||||||
|
|
||||||
|
debug!("Connecting wifi...");
|
||||||
|
wifi.connect()?;
|
||||||
|
debug!("Waiting for DHCP lease...");
|
||||||
|
wifi.wait_netif_up()?;
|
||||||
|
|
||||||
|
let ip_info = wifi.wifi().sta_netif().get_ip_info()?;
|
||||||
|
info!("Wifi DHCP info: {:?}", ip_info);
|
||||||
|
|
||||||
|
Ok(Box::new(esp_wifi))
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue