initial webserver stub running
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use crate::config::{NetworkConfig, PlantControllerConfig};
|
||||
use crate::hal::PLANT_COUNT;
|
||||
use crate::hal::{GW_IP_ADDR_ENV, PLANT_COUNT};
|
||||
use crate::log::{log, LogMessage};
|
||||
use crate::STAY_ALIVE;
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
@@ -8,12 +8,21 @@ use serde::Serialize;
|
||||
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use core::marker::PhantomData;
|
||||
use core::net::IpAddr;
|
||||
use core::net::{IpAddr, Ipv4Addr};
|
||||
use core::str::FromStr;
|
||||
use embassy_time::Instant;
|
||||
use embassy_executor::{SendSpawner, Spawner};
|
||||
use embassy_net::tcp::TcpSocket;
|
||||
use embassy_net::{IpListenEndpoint, Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4};
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
use esp_bootloader_esp_idf::ota::OtaImageState;
|
||||
use esp_hal::gpio::Input;
|
||||
use esp_hal::rng::Rng;
|
||||
use esp_println::{print, println};
|
||||
use esp_storage::FlashStorage;
|
||||
use esp_wifi::wifi::{
|
||||
AccessPointConfiguration, Configuration, Interfaces, WifiController, WifiDevice, WifiEvent,
|
||||
WifiState,
|
||||
};
|
||||
|
||||
#[link_section = ".rtc.data"]
|
||||
static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT];
|
||||
@@ -51,12 +60,17 @@ pub struct MqttClient<'a> {
|
||||
base_topic: heapless::String<64>,
|
||||
}
|
||||
pub struct Esp<'a> {
|
||||
pub boot_button: Input<'a>,
|
||||
pub rng: Rng,
|
||||
//first starter (ap or sta will take these)
|
||||
pub interfaces: Option<Interfaces<'static>>,
|
||||
pub controller: Option<WifiController<'static>>,
|
||||
|
||||
//only filled, if a useable mqtt client with working roundtrip could be established
|
||||
pub(crate) mqtt_client: Option<MqttClient<'a>>,
|
||||
pub(crate) dummy: PhantomData<&'a ()>,
|
||||
|
||||
pub boot_button: Input<'a>,
|
||||
pub(crate) wall_clock_offset: u64,
|
||||
//pub(crate) wifi_driver: EspWifi<'a>,
|
||||
//pub(crate) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
|
||||
|
||||
pub storage: FlashStorage,
|
||||
pub slot: usize,
|
||||
pub next_slot: usize,
|
||||
@@ -71,6 +85,17 @@ pub struct IpInfo {
|
||||
|
||||
struct AccessPointInfo {}
|
||||
|
||||
macro_rules! mk_static {
|
||||
($t:ty,$val:expr) => {{
|
||||
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
|
||||
#[deny(unused_attributes)]
|
||||
let x = STATIC_CELL.uninit().write(($val));
|
||||
x
|
||||
}};
|
||||
}
|
||||
|
||||
static WIFI_CONTROLLER: static_cell::StaticCell<WifiController> = static_cell::StaticCell::new();
|
||||
|
||||
impl Esp<'_> {
|
||||
const SPIFFS_PARTITION_NAME: &'static str = "storage";
|
||||
const CONFIG_FILE: &'static str = "/spiffs/config.cfg";
|
||||
@@ -150,24 +175,58 @@ impl Esp<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn wifi_ap(&mut self) -> anyhow::Result<()> {
|
||||
pub(crate) async fn wifi_ap(&mut self) -> anyhow::Result<Stack> {
|
||||
let _ssid = match self.load_config() {
|
||||
Ok(config) => config.network.ap_ssid.clone(),
|
||||
Err(_) => heapless::String::from_str("PlantCtrl Emergency Mode").unwrap(),
|
||||
};
|
||||
|
||||
todo!("todo");
|
||||
//
|
||||
// let apconfig = AccessPointConfiguration {
|
||||
// ssid,
|
||||
// auth_method: AuthMethod::None,
|
||||
// ssid_hidden: false,
|
||||
// ..Default::default()
|
||||
// };
|
||||
// self.wifi_driver
|
||||
// .set_configuration(&Configuration::AccessPoint(apconfig))?;
|
||||
// self.wifi_driver.start()?;
|
||||
// anyhow::Ok(())
|
||||
let spawner = Spawner::for_current_executor().await;
|
||||
|
||||
let device = self.interfaces.take().unwrap().ap;
|
||||
let gw_ip_addr_str = GW_IP_ADDR_ENV.unwrap_or("192.168.2.1");
|
||||
let gw_ip_addr = Ipv4Addr::from_str(gw_ip_addr_str).expect("failed to parse gateway ip");
|
||||
|
||||
let config = embassy_net::Config::ipv4_static(StaticConfigV4 {
|
||||
address: Ipv4Cidr::new(gw_ip_addr, 24),
|
||||
gateway: Some(gw_ip_addr),
|
||||
dns_servers: Default::default(),
|
||||
});
|
||||
|
||||
let seed = (self.rng.random() as u64) << 32 | self.rng.random() as u64;
|
||||
|
||||
// Init network stack
|
||||
let (stack, runner) = embassy_net::new(
|
||||
device,
|
||||
config,
|
||||
mk_static!(StackResources<3>, StackResources::<3>::new()),
|
||||
seed,
|
||||
);
|
||||
let stack = mk_static!(Stack, stack);
|
||||
|
||||
let controller = self.controller.take().unwrap();
|
||||
spawner.spawn(connection(controller)).ok();
|
||||
spawner.spawn(net_task(runner)).ok();
|
||||
spawner.spawn(run_dhcp(stack, gw_ip_addr_str)).ok();
|
||||
|
||||
loop {
|
||||
if stack.is_link_up() {
|
||||
break;
|
||||
}
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
}
|
||||
println!(
|
||||
"Connect to the AP `esp-wifi` and point your browser to http://{gw_ip_addr_str}:8080/"
|
||||
);
|
||||
println!("DHCP is enabled so there's no need to configure a static IP, just in case:");
|
||||
while !stack.is_config_up() {
|
||||
Timer::after(Duration::from_millis(100)).await
|
||||
}
|
||||
stack
|
||||
.config_v4()
|
||||
.inspect(|c| println!("ipv4 config: {c:?}"));
|
||||
|
||||
anyhow::Ok(stack.clone())
|
||||
}
|
||||
|
||||
pub(crate) async fn wifi(&mut self, network_config: &NetworkConfig) -> anyhow::Result<IpInfo> {
|
||||
@@ -638,3 +697,74 @@ impl Esp<'_> {
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn run_dhcp(stack: &'static Stack<'static>, gw_ip_addr: &'static str) {
|
||||
use core::net::{Ipv4Addr, SocketAddrV4};
|
||||
|
||||
use edge_dhcp::{
|
||||
io::{self, DEFAULT_SERVER_PORT},
|
||||
server::{Server, ServerOptions},
|
||||
};
|
||||
use edge_nal::UdpBind;
|
||||
use edge_nal_embassy::{Udp, UdpBuffers};
|
||||
|
||||
let ip = Ipv4Addr::from_str(gw_ip_addr).expect("dhcp task failed to parse gw ip");
|
||||
|
||||
let mut buf = [0u8; 1500];
|
||||
|
||||
let mut gw_buf = [Ipv4Addr::UNSPECIFIED];
|
||||
|
||||
let buffers = UdpBuffers::<3, 1024, 1024, 10>::new();
|
||||
let unbound_socket = Udp::new(*stack, &buffers);
|
||||
let mut bound_socket = unbound_socket
|
||||
.bind(core::net::SocketAddr::V4(SocketAddrV4::new(
|
||||
Ipv4Addr::UNSPECIFIED,
|
||||
DEFAULT_SERVER_PORT,
|
||||
)))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
_ = io::server::run(
|
||||
&mut Server::<_, 64>::new_with_et(ip),
|
||||
&ServerOptions::new(ip, Some(&mut gw_buf)),
|
||||
&mut bound_socket,
|
||||
&mut buf,
|
||||
)
|
||||
.await
|
||||
.inspect_err(|e| log::warn!("DHCP server error: {e:?}"));
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn connection(mut controller: WifiController<'static>) {
|
||||
println!("start connection task");
|
||||
println!("Device capabilities: {:?}", controller.capabilities());
|
||||
loop {
|
||||
match esp_wifi::wifi::wifi_state() {
|
||||
WifiState::ApStarted => {
|
||||
// wait until we're no longer connected
|
||||
controller.wait_for_event(WifiEvent::ApStop).await;
|
||||
Timer::after(Duration::from_millis(5000)).await
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if !matches!(controller.is_started(), core::result::Result::Ok(true)) {
|
||||
let client_config = Configuration::AccessPoint(AccessPointConfiguration {
|
||||
ssid: "esp-wifi".try_into().unwrap(),
|
||||
..Default::default()
|
||||
});
|
||||
controller.set_configuration(&client_config).unwrap();
|
||||
println!("Starting wifi");
|
||||
controller.start_async().await.unwrap();
|
||||
println!("Wifi started!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) {
|
||||
runner.run().await
|
||||
}
|
||||
|
Reference in New Issue
Block a user