refactor setup tasks
This commit is contained in:
parent
016c57bb14
commit
9c903b0c13
3 changed files with 131 additions and 86 deletions
138
src/main.rs
138
src/main.rs
|
@ -5,26 +5,32 @@ use chrono::{NaiveTime, Utc};
|
|||
use chrono_tz::US::Pacific;
|
||||
use esp_idf_svc::{
|
||||
eventloop::EspSystemEventLoop,
|
||||
hal::{delay::FreeRtos, gpio::*, modem::Modem, peripheral::Peripheral, prelude::Peripherals},
|
||||
sntp::{EspSntp, SntpConf, SyncMode, SyncStatus},
|
||||
wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi},
|
||||
hal::{delay::FreeRtos, gpio::*, prelude::Peripherals},
|
||||
sntp::{EspSntp, SntpConf, SyncMode},
|
||||
wifi::EspWifi,
|
||||
};
|
||||
use log::info;
|
||||
|
||||
mod config;
|
||||
mod ntp;
|
||||
mod wifi;
|
||||
|
||||
const SNTP_STATUS_POLL_INTVL_MS: u32 = 2000;
|
||||
/// Delay between executions of the main control loop, in milliseconds.
|
||||
const CONTROL_LOOP_INTVL: u32 = 60000;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// It is necessary to call this function once. Otherwise some patches to
|
||||
// the runtime implemented by esp-idf-sys might not link properly. Refer
|
||||
// to https://github.com/esp-rs/esp-idf-template/issues/71.
|
||||
esp_idf_svc::sys::link_patches();
|
||||
/// Struct holding resources which should remain in scope throughout execution.
|
||||
struct AppState {
|
||||
ntp: EspSntp<'static>,
|
||||
switch_driver: PinDriver<'static>,
|
||||
wifi: EspWifi<'static>,
|
||||
}
|
||||
|
||||
// Bind the log crate to the ESP Logging facilities
|
||||
esp_idf_svc::log::EspLogger::initialize_default();
|
||||
fn main() -> Result<()> {
|
||||
let AppState {
|
||||
ntp: mut_ntp,
|
||||
mut switch_driver,
|
||||
wifi: _wifi,
|
||||
} = setup()?;
|
||||
|
||||
let t_on = NaiveTime::parse_from_str(config::T_ON, "%H:%M")?;
|
||||
let t_off = NaiveTime::parse_from_str(config::T_OFF, "%H:%M")?;
|
||||
|
@ -32,32 +38,6 @@ fn main() -> Result<()> {
|
|||
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 ======== //
|
||||
loop {
|
||||
let now = Utc::now().with_timezone(&Pacific);
|
||||
|
@ -78,9 +58,9 @@ fn main() -> Result<()> {
|
|||
};
|
||||
|
||||
if active {
|
||||
switch.set_high()?;
|
||||
switch_driver.set_high()?;
|
||||
} else {
|
||||
switch.set_low()?;
|
||||
switch_driver.set_low()?;
|
||||
}
|
||||
|
||||
// TODO: enter low power mode ("light sleep" or "deep sleep") instead
|
||||
|
@ -89,54 +69,40 @@ fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Start WiFi module and connect to access point. Returns an error if either
|
||||
/// of WiFi startup or connection fail.
|
||||
fn init_wifi(
|
||||
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()))?;
|
||||
/// Runs initial application startup tasks, including connecting WiFi, syncing
|
||||
/// system time over SNTP, and setting the initial switch state. May block for
|
||||
/// several seconds, or indefinitely if initial SNTP sync is not successful.
|
||||
fn setup() -> Result<Box<AppState>> {
|
||||
// It is necessary to call this function once. Otherwise some patches to
|
||||
// the runtime implemented by esp-idf-sys might not link properly. Refer
|
||||
// to https://github.com/esp-rs/esp-idf-template/issues/71.
|
||||
esp_idf_svc::sys::link_patches();
|
||||
|
||||
info!("Starting wifi...");
|
||||
wifi.start()?;
|
||||
info!("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 {
|
||||
info!(
|
||||
"Found configured access point {} on channel {}",
|
||||
config::WIFI_SSID,
|
||||
ours.channel
|
||||
);
|
||||
Some(ours.channel)
|
||||
} else {
|
||||
info!(
|
||||
"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,
|
||||
// Bind the log crate to the ESP Logging facilities
|
||||
esp_idf_svc::log::EspLogger::initialize_default();
|
||||
|
||||
let peripherals = Peripherals::take()?;
|
||||
let sysloop = EspSystemEventLoop::take()?;
|
||||
|
||||
let mut switch_driver = PinDriver::output(app_state.peripherals.pins.gpio7)?;
|
||||
switch_driver.set_low()?;
|
||||
|
||||
// Dropping EspWifi shuts down the connection, so this variable must remain
|
||||
// in scope.
|
||||
let wifi = crate::wifi::init(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()
|
||||
}))?;
|
||||
})?;
|
||||
crate::ntp::wait_for_sync(&ntp);
|
||||
|
||||
info!("Connecting wifi...");
|
||||
wifi.connect()?;
|
||||
info!("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))
|
||||
Ok(Box::new(AppState {
|
||||
ntp,
|
||||
switch_driver,
|
||||
wifi,
|
||||
}))
|
||||
}
|
||||
|
|
17
src/ntp.rs
Normal file
17
src/ntp.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use esp_idf_svc::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 ntp.get_sync_status() != SyncStatus::Completed {
|
||||
info!("Waiting for SNTP to sync...");
|
||||
FreeRtos::delay_ms(SNTP_STATUS_POLL_INTVL_MS);
|
||||
}
|
||||
}
|
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