feat: wifi 连接,网络校时,MQTT。

This commit is contained in:
2022-08-07 11:57:05 +08:00
parent 12f3509304
commit 282e1a01a2
21 changed files with 625 additions and 741 deletions

View File

@@ -1,78 +0,0 @@
use esp_idf_hal::gpio::OutputPin;
use esp_idf_hal::ledc;
use esp_idf_hal::ledc::{config::TimerConfig, Channel, Timer};
use esp_idf_hal::prelude::*;
use esp_idf_sys::EspError;
use std::thread;
use std::time::Duration;
type LedcChannel<P, T, C> = Channel<C, T, Timer<T>, P>;
pub struct Beep<P: OutputPin, T: ledc::HwTimer, C: ledc::HwChannel> {
beat: u8,
ringtone: ringtone::Type,
channel: LedcChannel<P, T, C>,
duty: u32,
}
impl<P: OutputPin, T: ledc::HwTimer, C: ledc::HwChannel> Beep<P, T, C> {
pub fn new(pin: P, timer: T, channel: C) -> Result<Self, EspError> {
let channel = Self::init_channel(pin, timer, channel)?;
let max_duty = channel.get_max_duty();
return Ok(Beep {
channel,
beat: 0,
duty: max_duty * 3 / 4,
ringtone: ringtone::SILENCE,
});
}
fn init_channel(pin: P, timer: T, channel: C) -> Result<LedcChannel<P, T, C>, EspError> {
let config = TimerConfig::default().frequency(2.kHz().into());
let timer = Timer::new(timer, &config)?;
let channel: Channel<C, T, Timer<T>, P> = Channel::new(channel, timer, pin)?;
return Ok(channel);
}
pub fn play(&mut self, rx: &mut std::sync::mpsc::Receiver<ringtone::Type>) {
loop {
let curr_ringtone = rx.try_recv().unwrap_or_else(|_| self.ringtone);
if !curr_ringtone.eq(&mut self.ringtone) {
self.beat = 0;
self.ringtone = curr_ringtone;
}
let curr = curr_ringtone[self.beat as usize];
if curr {
self.channel.set_duty(self.duty).expect("Failed to set duty");
} else {
self.channel.set_duty(0).expect("Failed to set duty");
}
thread::sleep(Duration::from_millis(100));
self.beat += 1;
if self.beat == 16 {
self.beat = 0;
}
}
}
}
pub mod ringtone {
pub type Type = [bool; 16];
pub const ADAPTER_DOWN: Type = [
true, true, true, true, false, false, false, false, false, false, false, false, false,
false, false, false,
];
pub const BATTERY_LOW: Type = [
true, true, false, false, true, true, false, false, true, true, false, false, true, true,
false, false,
];
pub const SILENCE: Type = [false; 16];
pub const SHUTDOWN: Type = [
true, false, true, true, true, true, false, true, true, true, true, true, false, false,
false, false,
];
}

View File

@@ -1,47 +0,0 @@
use embedded_hal::digital::blocking::OutputPin;
use std::thread;
use std::time::Duration;
pub struct Blink<T>
where
T: OutputPin,
{
state: bool,
pin: T,
stopped: bool,
}
impl<T> Blink<T>
where
T: OutputPin,
{
pub fn new(pin: T) -> Blink<T> {
return Blink {
state: false,
pin,
stopped: false,
};
}
pub fn toggle(&mut self) -> Result<(), T::Error> {
self.state = !self.state;
if self.state {
self.pin.set_high()
} else {
self.pin.set_low()
}
}
pub fn play(&mut self) {
loop {
if self.stopped {
break;
}
self.toggle().unwrap();
thread::sleep(Duration::from_millis(50));
self.toggle().unwrap();
thread::sleep(Duration::from_millis(950));
}
}
}

View File

@@ -1,181 +0,0 @@
use anyhow::{Ok, Result, anyhow};
use retry;
use std::{
sync::{
mpsc,
Arc, Mutex,
},
thread,
time::{self, SystemTime},
};
use embedded_hal::digital::blocking::OutputPin;
use log::*;
pub enum DcOutStatus {
On,
Off,
TurningOn(mpsc::Receiver<bool>),
TurningOff(mpsc::Receiver<bool>),
}
impl DcOutStatus {
pub fn is_on(&self) -> bool {
match self {
DcOutStatus::On => true,
_ => false,
}
}
pub fn is_off(&self) -> bool {
match self {
DcOutStatus::Off => true,
_ => false,
}
}
}
pub struct DcOutController<P>
where
P: OutputPin + Send,
{
pin: Arc<Mutex<P>>,
shutdown_tx: Option<mpsc::Sender<bool>>,
status: DcOutStatus,
}
impl<P> DcOutController<P>
where
P: OutputPin + Send,
P: 'static,
P: std::marker::Sync,
{
pub fn new(pin: P) -> Self {
return Self {
status: DcOutStatus::On,
pin: Arc::new(Mutex::new(pin)),
shutdown_tx: None,
};
}
pub fn open(&mut self) -> Result<()> {
if self.status.is_on() {
trace!("DC OUT already on, skipping");
return Ok(());
}
let pin = self.pin.clone();
let mut pin = retry::retry(retry::delay::Fixed::from_millis(100).take(10), || {
pin.lock()
})
.map_err(|_| anyhow::anyhow!("Failed to lock pin"))?;
retry::retry(retry::delay::Fixed::from_millis(100).take(10), || {
pin.set_low()
})
.map_err(|_| anyhow::anyhow!("Failed to lock pin"))?;
self.status = DcOutStatus::On;
info!("DC OUT ON");
Ok(())
}
pub fn shutdown(&mut self) {
if self.shutdown_tx.is_some() {
trace!("Shutdown task already running, skipping...");
return;
}
info!("Shutdown task started...");
let (tx, rx) = mpsc::channel();
self.shutdown_tx = Some(tx);
let pin = Arc::clone(&self.pin);
let (status_tx, status_rx) = mpsc::channel();
self.status = DcOutStatus::TurningOff(status_rx);
thread::spawn(move || {
// Wait for computer shutdown finished
let target_at = SystemTime::now() + time::Duration::from_secs(10);
loop {
if rx.try_recv().is_ok_and(|state| !*state) {
break;
}
if target_at > SystemTime::now() {
thread::sleep(time::Duration::from_millis(1000));
continue;
}
info!("Shutdown timeout, force shutdown...");
let result = || {
retry::retry(retry::delay::Fixed::from_millis(1000).take(10), || {
return pin
.lock()
.map_err(|_| anyhow::anyhow!("Can not lock pin"))?
.set_high()
.map_err(|_| anyhow::anyhow!("Can not shutdown"));
}).map_err(|_| anyhow!("Failed to send shutdown status"))?;
status_tx.send(true).map_err(|_| anyhow!("Failed to send shutdown status"))?;
Ok(())
};
if let Err(e) = result() {
warn!("Failed to shutdown: {}", e);
} else {
info!("Shutdown finished");
}
break;
}
});
}
pub fn stop_shutdown(&mut self) -> Result<()> {
if let Some(tx) = self.shutdown_tx.as_mut() {
info!("Shutdown task stopped...");
if retry::retry(retry::delay::Fixed::from_millis(100).take(5), || {
tx.send(false)
})
.map_err(|_| anyhow::anyhow!("Failed to stop shutdown"))
.is_err() {
warn!("Message to shutdown task was not sent");
} else {
info!("Shutdown task stopped");
}
self.shutdown_tx = None;
};
Ok(())
}
pub fn get_status(&mut self) -> DcOutStatus {
match &self.status {
DcOutStatus::TurningOn(rx) => {
rx.try_recv().map_or((), |state| {
trace!("DcOutStatus::TurningOn({})", state);
if state {
self.status = DcOutStatus::On;
} else {
self.status = DcOutStatus::Off;
}
})
},
DcOutStatus::TurningOff(rx) => {
rx.try_recv().map_or((), |state| {
trace!("DcOutStatus::TurningOff({})", state);
if state {
self.status = DcOutStatus::Off;
} else {
self.status = DcOutStatus::On;
}
})
},
_default => {}
}
match &self.status {
DcOutStatus::On => DcOutStatus::On,
DcOutStatus::Off => DcOutStatus::Off,
_ => DcOutStatus::On,
}
}
}

View File

@@ -1,89 +1,48 @@
#![feature(is_some_with)]
use std::{env, thread::sleep, time::{Duration, UNIX_EPOCH}};
use anyhow::{bail, Ok, Result};
use embedded_svc::mqtt::client::{Publish, QoS};
use esp_idf_sys as _;
use log::{error, info};
use std::{thread, time::Duration, sync::mpsc, env};
use log::*;
use crate::wifi::WiFi;
mod beep;
mod blink;
mod dc_out_controller;
mod manager;
mod screen;
mod message_queue;
mod wifi;
mod time;
use crate::{message_queue::MessageQueue, wifi::Internet, time::Time};
fn main() {
env::set_var("DEFMT_LOG", "trace");
env::set_var("RUST_BACKTRACE", "1");
env::set_var("RUST_LOG", "trace");
env_logger::init();
// Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
// or else some patches to the runtime implemented by esp-idf-sys might not link properly.
esp_idf_sys::link_patches();
println!("Hello, world!");
let peripherals = esp_idf_hal::peripherals::Peripherals::take().unwrap();
let wifi = Internet::new().unwrap();
let blink_pin = peripherals.pins.gpio5;
let beep_pin = peripherals.pins.gpio6;
let ledc_timer0 = peripherals.ledc.timer0;
let ledc_channel0 = peripherals.ledc.channel0;
let dc_out_ctl_pin = peripherals.pins.gpio3;
let i2c0 = peripherals.i2c0;
let sda_pin = peripherals.pins.gpio4;
let scl_pin = peripherals.pins.gpio10;
let mut mq = MessageQueue::new();
mq.init().unwrap();
let adc1 = peripherals.adc1;
let adapter_pin = peripherals.pins.gpio1;
let battery_pin = peripherals.pins.gpio2;
let (tx, mut rx) = mpsc::channel();
info!("Starting");
thread::spawn(move || {
let mut blink =
blink::Blink::new(blink_pin.into_output().expect("Failed to set GPIO5 as output"));
blink.play();
});
thread::spawn(move || {
thread::sleep(Duration::from_millis(5000));
});
thread::spawn(move || {
let mut beep = beep::Beep::new(
beep_pin.into_output().expect("Failed to set GPIO6 as output"),
ledc_timer0,
ledc_channel0,
)
.expect("Failed to create beep");
beep.play(&mut rx);
});
let display = screen::Screen::new(i2c0, sda_pin, scl_pin).expect("Failed to create screen");
let dc_out_ctl = dc_out_controller::DcOutController::new(
dc_out_ctl_pin
.into_output()
.expect("Failed to set GPIO3 as output"),
);
let mut manager = manager::Manager::new(
dc_out_ctl,
display,
adc1,
adapter_pin.into_analog_atten_11db().expect("Failed to set GPIO1 as analog input"),
battery_pin.into_analog_atten_11db().expect("Failed to set GPIO2 as analog input"),
tx,
).expect("Failed to create manager");
let wifi = WiFi::new().expect("Failed to connect wifi");
let mut time = Time::new();
time.sync().unwrap();
loop {
match manager.handling_once() {
Ok(_) => {}
Err(err) => {
error!("Exec manager tick task failed: {}", err);
}
sleep(Duration::from_millis(1000));
if let Some(ref mut mq_client) = mq.client {
let timestamps = time.get_time().as_millis();
info!("timestamps {}", timestamps);
mq_client.publish(
"ups-0.2/heartbeat",
QoS::AtMostOnce,
false,
timestamps.to_string().as_bytes(),
).map_err(|err| {
warn!("publish heartbeat failed {}", err)
}).unwrap();
}
thread::sleep(Duration::from_millis(100));
}
}

View File

@@ -1,147 +0,0 @@
use anyhow::{anyhow, Result};
use embedded_hal::digital::blocking::OutputPin;
use embedded_hal_0_2::adc::OneShot;
use esp_idf_hal::{
adc::{Atten11dB, PoweredAdc, ADC1},
gpio::{Gpio1, Gpio2},
};
use log::*;
use std::{
sync::mpsc,
thread,
time::{Duration, SystemTime},
};
use crate::{beep::ringtone, dc_out_controller::DcOutController, screen::Screen};
type AdapterGpio = Gpio1<Atten11dB<ADC1>>;
type BatteryGpio = Gpio2<Atten11dB<ADC1>>;
pub struct Manager<C>
where
C: OutputPin + Send,
{
dc_out_controller: DcOutController<C>,
screen: Screen,
adc: PoweredAdc<ADC1>,
adapter_pin: AdapterGpio,
battery_pin: BatteryGpio,
tx: mpsc::Sender<ringtone::Type>,
adapter_downed_at: Option<SystemTime>,
}
impl<C> Manager<C>
where
C: OutputPin + Send,
C: 'static,
C: std::marker::Sync,
{
pub fn new(
dc_out_controller: DcOutController<C>,
screen: Screen,
adc1: ADC1,
adapter_pin: AdapterGpio,
battery_pin: BatteryGpio,
tx: mpsc::Sender<ringtone::Type>,
) -> Result<Self> {
let adc = PoweredAdc::new(
adc1,
esp_idf_hal::adc::config::Config::new().calibration(true),
)
.map_err(|err| anyhow!("Can not init Adc: {}", err))?;
return Ok(Manager {
dc_out_controller,
screen,
adc,
adapter_pin,
battery_pin,
tx,
adapter_downed_at: None,
});
}
pub fn get_adapter_voltage(&mut self) -> Result<f32> {
return Ok(self
.adc
.read(&mut self.adapter_pin)
.map_err(|err| anyhow!("Can not read adapter voltage. {:?}", err))?
as f32);
}
pub fn get_battery_voltage(&mut self) -> Result<f32> {
return Ok(self
.adc
.read(&mut self.battery_pin)
.map_err(|err| anyhow!("Can not read battery voltage. {:?}", err))?
as f32);
}
pub fn handling_once(&mut self) -> Result<()> {
let mut adapter = 0.0_f32;
let mut battery = 0.0_f32;
for _ in 0..10 {
adapter += self.get_adapter_voltage()?;
battery += self.get_battery_voltage()?;
thread::sleep(Duration::from_millis(10));
}
adapter /= 10.0_f32;
battery /= 10.0_f32;
if is_adapter_down(adapter) {
if self.dc_out_controller.get_status().is_off() {
self.tx
.send(ringtone::SILENCE)
.map_err(|err| anyhow!("Can not send silence to Beep. {:?}", err))?;
} else if is_battery_down(battery) {
self.tx
.send(ringtone::BATTERY_LOW)
.expect("Can not send message");
} else if is_battery_low(battery) {
self.dc_out_controller.shutdown();
if self.adapter_downed_at.is_none() {
info!("Recording adapter downed at: {:?}", SystemTime::now());
self.adapter_downed_at = Some(SystemTime::now());
} else if self
.adapter_downed_at
.is_some_and(|at| at.elapsed().is_ok_and(|dur| *dur > Duration::from_secs(5)))
{
self.tx
.send(ringtone::SHUTDOWN)
.map_err(|err| anyhow!("Can not send shutdown to Beep. {:?}", err))?;
}
} else {
self.tx
.send(ringtone::ADAPTER_DOWN)
.map_err(|err| anyhow!("Can not send adapter down to Beep. {:?}", err))?;
self.dc_out_controller
.stop_shutdown()
.map_err(|err| anyhow!("Can not stop shutdown. {:?}", err))?;
self.adapter_downed_at = None;
}
} else {
self.dc_out_controller
.stop_shutdown()
.expect("Can not stop shutdown");
self.tx.send(ringtone::SILENCE).map_err(|err| anyhow!("Can not send silence to Beep. {:?}", err))?;
self.adapter_downed_at = None;
self.dc_out_controller.open()?;
}
self.screen
.draw_voltage(adapter, battery)
.map_err(|err| anyhow!("Can not draw voltage. {:?}", err))?;
Ok(())
}
}
fn is_battery_low(battery: f32) -> bool {
battery < 1500.0
}
fn is_battery_down(battery: f32) -> bool {
battery < 500.0
}
fn is_adapter_down(adapter: f32) -> bool {
adapter < 1000.0
}

73
src/message_queue.rs Normal file
View File

@@ -0,0 +1,73 @@
use std::thread;
use anyhow::Result;
use embedded_svc::mqtt::client::{utils::ConnState, Client, Connection, MessageImpl, Publish, QoS};
use esp_idf_svc::mqtt::client::{EspMqttClient, MqttClientConfiguration};
use esp_idf_sys::EspError;
use log::*;
pub struct MessageQueue {
pub client: Option<EspMqttClient<ConnState<MessageImpl, EspError>>>,
}
impl MessageQueue {
pub fn new() -> MessageQueue {
return MessageQueue { client: None };
}
pub fn init(&mut self) -> Result<()> {
let conf = MqttClientConfiguration {
client_id: Some("rust-esp32-std-demo"),
crt_bundle_attach: Some(esp_idf_sys::esp_crt_bundle_attach),
..Default::default()
};
let (mut client, mut connection) =
EspMqttClient::new_with_conn("mqtt://192.168.31.8:1883", &conf)?;
info!("MQTT client started");
thread::spawn(move || {
info!("MQTT Listening for messages");
while let Some(msg) = connection.next() {
match msg {
Err(e) => info!("MQTT Message ERROR: {}", e),
Ok(msg) => info!("MQTT Message: {:?}", msg),
}
}
info!("MQTT connection loop exit");
});
client
.subscribe("esp32-c3-rust-wifi-demo", QoS::AtMostOnce)
.map_err(|err| anyhow::anyhow!("subscribe message from queue failed. {}", err))?;
info!("Subscribed to all topics (esp32-c3-rust-wifi-demo)");
client
.publish(
"esp32-c3-rust-wifi-demo/ping",
QoS::AtMostOnce,
false,
"Hello".as_bytes(),
)
.map_err(|err| anyhow::anyhow!("publish message to queue failed. {}", err))?;
info!("Published a hello message to topic \"esp32-c3-rust-wifi-demo/ping\"");
self.client = Some(client);
anyhow::Ok(())
}
pub fn publish(&mut self, bytes: &[u8]) -> Result<u32> {
self.client
.as_mut()
.unwrap()
.publish("", QoS::AtMostOnce, false, bytes)
.map_err(|err| {
anyhow::anyhow!("publish message to queue was failed!. {}", err)
})
}
}

View File

@@ -1,142 +0,0 @@
use anyhow::{anyhow, Result, Ok};
use embedded_graphics::{
mono_font::{ascii::FONT_10X20, iso_8859_10::FONT_6X10, MonoTextStyle},
pixelcolor::Rgb565,
prelude::{Dimensions, Drawable, Point, Primitive, RgbColor},
primitives::{PrimitiveStyleBuilder, Rectangle},
text::Text,
};
use embedded_hal::{delay::blocking::DelayUs, i2c::blocking::{I2c, Operation}};
use esp_idf_hal::{
delay,
gpio::{self},
i2c::{self, Master, I2C0},
prelude::*,
};
use log::warn;
use ssd1306::{
mode::{BufferedGraphicsMode, DisplayConfig},
prelude::I2CInterface,
size::DisplaySize128x64,
Ssd1306,
};
type Display = Ssd1306<
I2CInterface<Master<I2C0, gpio::Gpio4<gpio::Unknown>, gpio::Gpio10<gpio::Unknown>>>,
DisplaySize128x64,
BufferedGraphicsMode<DisplaySize128x64>,
>;
pub struct Screen {
pub display: Option<Display>,
}
impl Screen {
pub fn new(
i2c: i2c::I2C0,
sda: gpio::Gpio4<gpio::Unknown>,
scl: gpio::Gpio10<gpio::Unknown>,
) -> Result<Self> {
let config = <i2c::config::MasterConfig as Default>::default().baudrate(400.kHz().into());
let mut i2c = i2c::Master::<i2c::I2C0, _, _>::new(i2c, i2c::MasterPins { sda, scl }, config)?;
let mut buff = [0u8; 10];
if let Err(err) = i2c.transaction(0x3C, &mut [Operation::Read(&mut buff)]) {
warn!("Failed to initialize display: {}", err);
warn!("Failed to initialize display: {}", err);
warn!("Failed to initialize display: {}", err);
warn!("Failed to initialize display: {}", err);
warn!("Failed to initialize display: {}", err);
return Ok(Self { display: None });
}
let di = ssd1306::I2CDisplayInterface::new(i2c);
let mut delay = delay::Ets;
delay.delay_ms(10_u32)?;
let mut display = ssd1306::Ssd1306::new(
di,
ssd1306::size::DisplaySize128x64,
ssd1306::rotation::DisplayRotation::Rotate0,
)
.into_buffered_graphics_mode();
display
.init()
.map_err(|err| anyhow!("Can not init display: {:?}", err))?;
let mut instance = Screen { display: Some(display) };
instance.draw_boot()?;
Ok(instance)
}
pub fn draw_boot(&mut self) -> Result<()> {
if self.display.is_none() {
return Ok(());
}
let display = self.display.as_mut().unwrap();
display.clear();
Rectangle::new(
display.bounding_box().top_left,
display.bounding_box().size,
)
.into_styled(
PrimitiveStyleBuilder::new()
.fill_color(Rgb565::BLUE.into())
.stroke_color(Rgb565::YELLOW.into())
.stroke_width(1)
.build(),
)
.draw(display)
.expect("Failed to draw rectangle");
Text::new(
"Ivan's UPS",
Point::new(
12,
(display.bounding_box().size.height - 10) as i32 / 2 + 1,
),
MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE.into()),
)
.draw(display)
.expect("Failed to draw text");
display
.flush()
.map_err(|e| anyhow::anyhow!("Display error: {:?}", e))?;
Ok(())
}
pub fn draw_voltage(&mut self, adapter: f32, battery: f32) -> Result<()> {
if let Some(display) = self.display.as_mut() {
display.clear();
Text::new(
format!("Adp. {:.2} mV", adapter).as_str(),
Point::new(12, 24),
MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE.into()),
)
.draw(display)
.expect("Failed to draw text");
Text::new(
format!("Bat. {:.2} mV", battery).as_str(),
Point::new(12, 36),
MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE.into()),
)
.draw(display)
.expect("Failed to draw text");
display
.flush()
.map_err(|e| anyhow::anyhow!("Display error: {:?}", e))?;
}
Ok(())
}
}

105
src/time.rs Normal file
View File

@@ -0,0 +1,105 @@
use anyhow::bail;
use embedded_svc::sys_time::SystemTime;
use esp_idf_svc::sntp::{self, EspSntp};
use esp_idf_svc::systime::EspSystemTime;
use log::{warn, info};
use sntpc::{Error, NtpContext, NtpResult, NtpTimestampGenerator, NtpUdpSocket, Result};
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
use std::time::{Duration};
pub struct Time {
result: Option<NtpResult>,
sntp: Option<Box<EspSntp>>,
}
impl Time {
pub fn new () -> Time {
return Time {
result: None,
sntp: None
}
}
pub fn sync(&mut self) -> anyhow::Result<()> {
let sntp = sntp::EspSntp::new_default().map_err(|err| {
anyhow::anyhow!("ESP SNTP Failed: {:?}", err)
})?;
self.sntp = Some(Box::new(sntp));
return anyhow::Ok(())
}
pub fn get_time(&mut self) -> Duration {
if let Some(ref mut sntp) = self.sntp {
info!("ESP SNTP sync status {:?}", sntp.get_sync_status());
}
EspSystemTime {}.now()
}
// pub fn sync(&mut self) -> anyhow::Result<()> {
// let mut error = None;
// for _ in 0..10 {
// let socket = UdpSocket::bind("0.0.0.0:0").expect("Unable to crate UDP socket");
// socket
// .set_read_timeout(Some(Duration::from_secs(2)))
// .expect("Unable to set UDP socket read timeout");
// let sock_wrapper = UdpSocketWrapper(socket);
// let ntp_context = NtpContext::new(StdTimestampGen::default());
// let result = sntpc::get_time("pool.ntp.org:123", sock_wrapper, ntp_context);
// match result {
// Ok(res_time) => {
// println!("Got time: {}.{}", res_time.sec(), res_time.sec_fraction());
// self.result = Some(res_time);
// return anyhow::Ok(())
// }
// Err(err) => {
// error = Some(err);
// warn!("fetch dateTime from ntp failed: {:?}", err);
// },
// }
// }
// bail!("fetch dateTime from ntp failed: {:?}", error)
// }
}
#[derive(Copy, Clone, Default)]
struct StdTimestampGen {
duration: Duration,
}
impl NtpTimestampGenerator for StdTimestampGen {
fn init(&mut self) {
self.duration = std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.unwrap();
}
fn timestamp_sec(&self) -> u64 {
self.duration.as_secs()
}
fn timestamp_subsec_micros(&self) -> u32 {
self.duration.subsec_micros()
}
}
#[derive(Debug)]
struct UdpSocketWrapper(UdpSocket);
impl NtpUdpSocket for UdpSocketWrapper {
fn send_to<T: ToSocketAddrs>(&self, buf: &[u8], addr: T) -> Result<usize> {
match self.0.send_to(buf, addr) {
Ok(usize) => Ok(usize),
Err(_) => Err(Error::Network),
}
}
fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> {
match self.0.recv_from(buf) {
Ok((size, addr)) => Ok((size, addr)),
Err(_) => Err(Error::Network),
}
}
}

View File

@@ -1,81 +1,77 @@
use std::{sync::Arc, time::Duration};
use anyhow::{
bail,
Result, Ok,
};
use anyhow::{bail, Ok, Result};
use embedded_svc::{ipv4, wifi::*};
use esp_idf_svc::ping::EspPing;
use esp_idf_svc::{
netif::EspNetifStack, nvs::EspDefaultNvs, sysloop::EspSysLoopStack, wifi::EspWifi
netif::EspNetifStack, nvs::EspDefaultNvs, sysloop::EspSysLoopStack, wifi::EspWifi,
};
use embedded_svc::{wifi::*, ipv4};
use log::info;
use embedded_svc::ping::Ping;
pub struct WiFi {
pub struct Internet {
wifi: Box<EspWifi>,
auto_connect: bool,
connected: bool,
}
impl WiFi {
impl Internet {
pub fn new() -> Result<Self> {
let netif_stack = Arc::new(EspNetifStack::new()?);
let sys_loop_stack = Arc::new(EspSysLoopStack::new()?);
let default_nvs = Arc::new(EspDefaultNvs::new()?);
let wifi = Self::wifi(
netif_stack.clone(),
sys_loop_stack.clone(),
default_nvs.clone(),
)?;
let wifi = Box::new(EspWifi::new(netif_stack, sys_loop_stack, default_nvs)?);
Ok(Self { wifi })
let mut instance = Self { wifi, auto_connect: true, connected: false };
instance.connect_ap()?;
Ok(instance)
}
fn wifi(
netif_stack: Arc<EspNetifStack>,
sys_loop_stack: Arc<EspSysLoopStack>,
default_nvs: Arc<EspDefaultNvs>,
) -> Result<Box<EspWifi>> {
fn connect_ap(
&mut self,
) -> Result<()> {
const SSID: &str = "Ivan Li";
const PASSWORD: &str = "ivanli.cc";
let mut wifi = Box::new(EspWifi::new(netif_stack, sys_loop_stack, default_nvs)?);
info!("Wifi created, about to scan");
let ap_infos = wifi.scan()?;
let wifi = self.wifi.as_mut();
// let ap_infos = wifi.scan()?;
info!("Wifi AP Count {}", ap_infos.len());
// info!("Wifi AP Count {}", ap_infos.len());
let ours = ap_infos.into_iter().find(|a| a.ssid == SSID);
// let ours = ap_infos.into_iter().find(|a| a.ssid == SSID);
let channel = if let Some(ours) = ours {
info!(
"Found configured access point {} on channel {}",
SSID, ours.channel
);
Some(ours.channel)
} else {
info!(
"Configured access point {} not found during scanning, will go with unknown channel",
SSID
);
None
};
// let channel = if let Some(ours) = ours {
// info!(
// "Found configured access point {} on channel {}",
// SSID, ours.channel
// );
// Some(ours.channel)
// } else {
// info!(
// "Configured access point {} not found during scanning, will go with unknown channel",
// SSID
// );
// None
// };
wifi.set_configuration(&Configuration::Mixed(
wifi.set_configuration(&Configuration::Client(
ClientConfiguration {
ssid: SSID.into(),
password: PASSWORD.into(),
channel,
..Default::default()
},
AccessPointConfiguration {
ssid: "aptest".into(),
channel: channel.unwrap_or(1),
channel: None,
..Default::default()
},
// AccessPointConfiguration {
// ssid: "aptest".into(),
// channel: channel.unwrap_or(1),
// ..Default::default()
// },
))?;
info!("Wifi configuration set, about to get status");
@@ -85,11 +81,13 @@ impl WiFi {
let status = wifi.get_status();
info!("we have the wifi status");
if let Status(
ClientStatus::Started(ClientConnectionStatus::Connected(ClientIpStatus::Done(
ip_settings,
))),
ApStatus::Started(ApIpStatus::Done),
ApStatus::Stopped
) = status
{
info!("Wifi connected");
@@ -99,22 +97,22 @@ impl WiFi {
bail!("Unexpected Wifi status: {:?}", status);
}
Ok(wifi)
Ok(())
}
fn ping(ip_settings: &ipv4::ClientSettings) -> Result<()> {
info!("About to do some pings for {:?}", ip_settings);
let ping_summary =
EspPing::default().ping(ip_settings.subnet.gateway, &Default::default())?;
if ping_summary.transmitted != ping_summary.received {
bail!(
"Pinging gateway {} resulted in timeouts",
ip_settings.subnet.gateway
);
pub fn ping(ip_settings: &ipv4::ClientSettings) -> Result<()> {
info!("About to do some pings for {:?}", ip_settings);
let ping_summary =
EspPing::default().ping(ip_settings.subnet.gateway, &Default::default())?;
if ping_summary.transmitted != ping_summary.received {
bail!(
"Pinging gateway {} resulted in timeouts",
ip_settings.subnet.gateway
);
}
info!("Pinging done");
Ok(())
}
info!("Pinging done");
Ok(())
}
}
}