244 lines
7.5 KiB
Rust

use std::sync::Arc;
use std::fmt::Debug;
use std::sync::Mutex;
use chrono::DateTime;
use chrono::Utc;
use esp_idf_svc::eventloop::{EspSubscription, EspSystemEventLoop, System};
use esp_idf_svc::hal::modem::Modem;
use esp_idf_svc::hal::prelude::Peripherals;
use esp_idf_svc::netif::IpEvent;
use esp_idf_svc::nvs::{EspDefaultNvsPartition, EspNvsPartition, NvsDefault};
use esp_idf_svc::sntp::EspSntp;
use esp_idf_svc::sys::esp_efuse_mac_get_default;
use esp_idf_svc::wifi::{AuthMethod, ClientConfiguration, Configuration, EspWifi, WifiEvent};
use rgb::Rgb;
use super::Board;
use crate::buffers::BufferedSurfacePool;
use crate::task::FixedSizeScheduler;
use crate::task::Task;
use crate::buffers::Pixbuf;
use crate::mappings::StrideMapping;
use crate::platform::smart_leds_lib::StrideOutput;
use crate::platform::smart_leds_lib::rmt::FastWs2812Esp32Rmt;
use crate::time::Periodically;
pub struct Esp32Board {
output: Option<<Self as Board>::Output>,
sys_loop: EspSystemEventLoop,
modem: Option<Modem>,
surfaces: BufferedSurfacePool
}
impl Board for Esp32Board {
type Output = StrideOutput<[Rgb<u8>; 310], FastWs2812Esp32Rmt<'static>>;
type Surfaces = BufferedSurfacePool;
type Scheduler = FixedSizeScheduler<2>;
fn take() -> Self {
// It is necessary to call this function once. Otherwise some patches to the runtime
// implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
esp_idf_svc::sys::link_patches();
// Bind the log crate to the ESP Logging facilities
esp_idf_svc::log::EspLogger::initialize_default();
let peripherals = Peripherals::take().unwrap();
let sys_loop = EspSystemEventLoop::take().unwrap();
let channel = peripherals.rmt.channel0;
let pins = peripherals.pins;
let mut chip_id: [u8; 8] = [0; 8];
unsafe {
esp_efuse_mac_get_default(&mut chip_id as *mut u8);
}
log::info!("Setting up for chip ID {:?}", chip_id);
let (pixmap, target) = match chip_id {
[72, 202, 67, 89, 145, 204, 0, 0] => {
(StrideMapping::new_panel(), FastWs2812Esp32Rmt::new(channel, pins.gpio5).unwrap())
},
[140, 170, 181, 131, 95, 116, 0, 0] => {
(StrideMapping::new_jar(), FastWs2812Esp32Rmt::new(channel, pins.gpio14).unwrap())
},
_ => {
(StrideMapping::new(), FastWs2812Esp32Rmt::new(channel, pins.gpio5).unwrap())
}
};
const POWER_VOLTS : u32 = 5;
const POWER_MA : u32 = 500;
const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA;
let output = StrideOutput::new(
Pixbuf::new(),
pixmap,
target,
MAX_POWER_MW
);
Esp32Board {
output: Some(output),
modem: Some(peripherals.modem),
sys_loop: sys_loop.clone(),
surfaces: BufferedSurfacePool::new()
}
}
fn output(&mut self) -> Self::Output {
self.output.take().unwrap()
}
fn surfaces(&mut self) -> Self::Surfaces {
self.surfaces.clone()
}
fn system_tasks(&mut self) -> Self::Scheduler {
let nvs = EspDefaultNvsPartition::take().unwrap();
FixedSizeScheduler::new([
Box::new(WifiTask::new(self.modem.take().unwrap(), self.sys_loop.clone(), &nvs)),
Box::new(self.surfaces.clone())
])
}
}
impl Debug for WifiTask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WifiTask").finish()
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
enum WifiState {
Stopped,
Disconnected,
Connecting,
Connected
}
struct WifiTask {
wifi: EspWifi<'static>,
ntp: EspSntp<'static>,
connection_check: Periodically,
state: Arc<Mutex<WifiState>>,
last_state: WifiState,
sys_loop: EspSystemEventLoop,
wifi_sub: Option<EspSubscription<'static, System>>,
ip_sub: Option<EspSubscription<'static, System>>
}
impl WifiTask {
fn new(modem: Modem, sys_loop: EspSystemEventLoop, nvs: &EspNvsPartition<NvsDefault>) -> Self {
let mut wifi = EspWifi::new(
modem,
sys_loop.clone(),
Some(nvs.clone())
).unwrap();
let wifi_config = Configuration::Client(ClientConfiguration {
ssid: "The Frequency".try_into().unwrap(),
bssid: None,
auth_method: AuthMethod::WPA2Personal,
password: "thepasswordkenneth".try_into().unwrap(),
channel: None,
..Default::default()
});
wifi.set_configuration(&wifi_config).unwrap();
WifiTask {
wifi,
ntp: EspSntp::new_default().unwrap(),
connection_check: Periodically::new_every_n_seconds(1),
state: Arc::new(Mutex::new(WifiState::Stopped)),
last_state: WifiState::Stopped,
sys_loop,
wifi_sub: None,
ip_sub: None
}
}
fn connect(&mut self) {
log::info!("Connecting wifi");
self.wifi.start().unwrap();
self.wifi.connect().unwrap();
}
fn disconnect(&mut self) {
log::info!("Disconnecting wifi");
self.wifi.disconnect().unwrap();
self.wifi.stop().unwrap();
}
}
impl Task for WifiTask {
fn start(&mut self) {
log::info!("Starting wifi!");
log::info!("waiting for loop");
let wifi_state = self.state.clone();
self.wifi_sub = Some(self.sys_loop.subscribe::<WifiEvent, _>( move |evt| {
log::warn!("wifi event {:?}", evt);
let next_state = match evt {
WifiEvent::StaStopped => Some(WifiState::Disconnected),
WifiEvent::StaDisconnected => Some(WifiState::Disconnected),
WifiEvent::StaStarted => Some(WifiState::Connecting),
WifiEvent::StaConnected => Some(WifiState::Connecting),
_ => None
};
match next_state {
Some(s) => {
let mut state = wifi_state.lock().unwrap();
*state = s
},
None => ()
}
}).unwrap());
let ip_state = self.state.clone();
self.ip_sub = Some(self.sys_loop.subscribe::<IpEvent, _>(move |evt| {
log::warn!("ip event {:?}", evt);
match evt {
IpEvent::DhcpIpAssigned(_) => {
let mut state = ip_state.lock().unwrap();
*state = WifiState::Connected;
},
_ => ()
}
}).unwrap());
self.connect();
}
fn tick(&mut self ) {
if self.connection_check.tick() {
let cur_state = *self.state.lock().unwrap();
if self.last_state != cur_state {
match cur_state {
WifiState::Connected => log::info!("Wifi connected!"),
WifiState::Connecting => log::info!("Connecting!"),
WifiState::Stopped => log::info!("Stopped!"),
WifiState::Disconnected => log::info!("Disconnected!")
}
log::info!("online: {:?}", cur_state);
self.last_state = cur_state;
}
let now: DateTime<Utc> = std::time::SystemTime::now().into();
log::info!("Current time: {} status={:?}", now.format("%d/%m/%Y %T"), self.ntp.get_sync_status());
}
}
fn stop(&mut self) {
log::info!("Stopping wifi");
self.disconnect();
}
}