adda lot of basic webserver back
This commit is contained in:
		@@ -1,6 +1,7 @@
 | 
			
		||||
use crate::hal::Box;
 | 
			
		||||
use alloc::string::String;
 | 
			
		||||
use async_trait::async_trait;
 | 
			
		||||
use core::error::Error;
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
 | 
			
		||||
#[async_trait]
 | 
			
		||||
@@ -17,6 +18,12 @@ pub trait BatteryInteraction {
 | 
			
		||||
    async fn get_battery_state(&mut self) -> Result<BatteryState, BatteryError>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<BatteryError> for anyhow::Error {
 | 
			
		||||
    fn from(err: BatteryError) -> Self {
 | 
			
		||||
        anyhow::anyhow!(err)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize)]
 | 
			
		||||
pub struct BatteryInfo {
 | 
			
		||||
    pub voltage_milli_volt: u16,
 | 
			
		||||
 
 | 
			
		||||
@@ -76,6 +76,7 @@ pub struct Esp<'a> {
 | 
			
		||||
    pub slot: usize,
 | 
			
		||||
    pub next_slot: usize,
 | 
			
		||||
    pub ota_state: OtaImageState,
 | 
			
		||||
    pub slot_addres: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct IpInfo {
 | 
			
		||||
@@ -200,7 +201,7 @@ impl Esp<'_> {
 | 
			
		||||
        let (stack, runner) = embassy_net::new(
 | 
			
		||||
            device,
 | 
			
		||||
            config,
 | 
			
		||||
            mk_static!(StackResources<3>, StackResources::<3>::new()),
 | 
			
		||||
            mk_static!(StackResources<4>, StackResources::<4>::new()),
 | 
			
		||||
            seed,
 | 
			
		||||
        );
 | 
			
		||||
        let stack = mk_static!(Stack, stack);
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,9 @@ use embassy_net::{IpListenEndpoint, Ipv4Cidr, Runner, Stack, StackResources, Sta
 | 
			
		||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
 | 
			
		||||
use embassy_sync::once_lock::OnceLock;
 | 
			
		||||
use embassy_time::{Duration, Timer};
 | 
			
		||||
use esp_bootloader_esp_idf::partitions::DataPartitionSubType;
 | 
			
		||||
use esp_bootloader_esp_idf::partitions::{
 | 
			
		||||
    AppPartitionSubType, DataPartitionSubType, PartitionEntry,
 | 
			
		||||
};
 | 
			
		||||
use esp_hal::clock::CpuClock;
 | 
			
		||||
use esp_hal::gpio::{Input, InputConfig, Io, Pull};
 | 
			
		||||
use esp_hal::timer::systimer::SystemTimer;
 | 
			
		||||
@@ -286,6 +288,23 @@ impl PlantHal {
 | 
			
		||||
        println!("current {:?} - next {:?}", current, current.next());
 | 
			
		||||
        let ota_state = ota.current_ota_state()?;
 | 
			
		||||
 | 
			
		||||
        let current_app = if current.number() == 0 {
 | 
			
		||||
            pt.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App(
 | 
			
		||||
                AppPartitionSubType::Ota0,
 | 
			
		||||
            ))
 | 
			
		||||
        } else {
 | 
			
		||||
            pt.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App(
 | 
			
		||||
                AppPartitionSubType::Ota1,
 | 
			
		||||
            ))
 | 
			
		||||
        };
 | 
			
		||||
        let app_address = match current_app {
 | 
			
		||||
            Result::Ok(part) => match part {
 | 
			
		||||
                None => 0,
 | 
			
		||||
                Some(entry) => entry.offset(),
 | 
			
		||||
            },
 | 
			
		||||
            Err(_) => 0,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let mut esp = Esp {
 | 
			
		||||
            rng,
 | 
			
		||||
            controller: Some(controller),
 | 
			
		||||
@@ -294,6 +313,7 @@ impl PlantHal {
 | 
			
		||||
            mqtt_client: None,
 | 
			
		||||
            storage,
 | 
			
		||||
            slot: current.number(),
 | 
			
		||||
            slot_addres: app_address,
 | 
			
		||||
            next_slot: current.next().number(),
 | 
			
		||||
            ota_state,
 | 
			
		||||
            wall_clock_offset: 0,
 | 
			
		||||
 
 | 
			
		||||
@@ -147,15 +147,14 @@ async fn safe_main(spawner: Spawner) -> anyhow::Result<()> {
 | 
			
		||||
    info!("Startup Rust");
 | 
			
		||||
 | 
			
		||||
    let mut to_config = false;
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
 | 
			
		||||
    let version = get_version();
 | 
			
		||||
    let version = get_version(&mut board).await;
 | 
			
		||||
    info!(
 | 
			
		||||
        "Version using git has {} build on {}",
 | 
			
		||||
        version.git_hash, version.build_time
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let _esp = BOARD_ACCESS.get().await.lock().await.board_hal.get_esp();
 | 
			
		||||
 | 
			
		||||
    //TODO
 | 
			
		||||
 | 
			
		||||
    // TODO
 | 
			
		||||
@@ -179,7 +178,7 @@ async fn safe_main(spawner: Spawner) -> anyhow::Result<()> {
 | 
			
		||||
    //};
 | 
			
		||||
    //log(LogMessage::PartitionState, 0, 0, "", ota_state_string);
 | 
			
		||||
    let _ota_state_string = "unknown";
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
 | 
			
		||||
    board.board_hal.general_fault(false).await;
 | 
			
		||||
    let cur = board
 | 
			
		||||
        .board_hal
 | 
			
		||||
@@ -1117,15 +1116,16 @@ pub fn in_time_range(cur: &DateTime<Tz>, start: u8, end: u8) -> bool {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_version() -> VersionInfo {
 | 
			
		||||
async fn get_version(
 | 
			
		||||
    board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>,
 | 
			
		||||
) -> VersionInfo {
 | 
			
		||||
    let branch = env!("VERGEN_GIT_BRANCH").to_owned();
 | 
			
		||||
    let hash = &env!("VERGEN_GIT_SHA")[0..8];
 | 
			
		||||
 | 
			
		||||
    //TODO
 | 
			
		||||
    //let running_partition = unsafe { esp_ota_get_running_partition() };
 | 
			
		||||
    let address = 0;
 | 
			
		||||
    //let address = unsafe { (*running_partition).address };
 | 
			
		||||
    let partition = if address > 100000 {
 | 
			
		||||
    let board = board.board_hal.get_esp();
 | 
			
		||||
    let ota_slot = board.slot;
 | 
			
		||||
    let address = board.slot_addres;
 | 
			
		||||
    let partition = if ota_slot == 0 {
 | 
			
		||||
        "ota_1 @ "
 | 
			
		||||
    } else {
 | 
			
		||||
        "ota_0 @ "
 | 
			
		||||
 
 | 
			
		||||
@@ -6,22 +6,32 @@ use crate::{
 | 
			
		||||
    hal::PLANT_COUNT,
 | 
			
		||||
    log::LogMessage,
 | 
			
		||||
    plant_state::{MoistureSensorState, PlantState},
 | 
			
		||||
    BOARD_ACCESS,
 | 
			
		||||
    VersionInfo, BOARD_ACCESS,
 | 
			
		||||
};
 | 
			
		||||
use alloc::boxed::Box;
 | 
			
		||||
use alloc::string::String;
 | 
			
		||||
use alloc::string::{String, ToString};
 | 
			
		||||
use alloc::sync::Arc;
 | 
			
		||||
use alloc::vec::Vec;
 | 
			
		||||
use anyhow::{bail, Context};
 | 
			
		||||
use chrono::DateTime;
 | 
			
		||||
use core::fmt::{Debug, Display, Pointer};
 | 
			
		||||
use core::future::Future;
 | 
			
		||||
use core::net::{IpAddr, Ipv4Addr, SocketAddr};
 | 
			
		||||
use core::result::Result::Ok;
 | 
			
		||||
use core::sync::atomic::AtomicBool;
 | 
			
		||||
use edge_http::io::server::{Connection, DefaultServer, Handler, Server};
 | 
			
		||||
use edge_http::io::Error;
 | 
			
		||||
use edge_http::Method;
 | 
			
		||||
use edge_nal::TcpBind;
 | 
			
		||||
use edge_nal_embassy::{Tcp, TcpAccept, TcpBuffers};
 | 
			
		||||
use embassy_executor::Spawner;
 | 
			
		||||
use embassy_net::tcp::TcpSocket;
 | 
			
		||||
use embassy_net::{IpListenEndpoint, Stack};
 | 
			
		||||
use embassy_time::{Duration, Timer};
 | 
			
		||||
use embassy_time::{Duration, Instant, Timer};
 | 
			
		||||
use embedded_io_async::{Read, Write};
 | 
			
		||||
use esp_println::{print, println};
 | 
			
		||||
use esp_wifi::wifi::WifiController;
 | 
			
		||||
use log::info;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Debug)]
 | 
			
		||||
@@ -89,31 +99,7 @@ pub struct NightLampCommand {
 | 
			
		||||
//     anyhow::Ok(None)
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
// fn get_time(
 | 
			
		||||
//     _request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
// ) -> Result<Option<std::string::String>, anyhow::Error> {
 | 
			
		||||
//     let mut board = BOARD_ACCESS.lock().expect("board access");
 | 
			
		||||
//     let native = board
 | 
			
		||||
//         .board_hal
 | 
			
		||||
//         .get_esp()
 | 
			
		||||
//         .time()
 | 
			
		||||
//         .map(|t| t.to_rfc3339())
 | 
			
		||||
//         .unwrap_or("error".to_string());
 | 
			
		||||
//     let rtc = board
 | 
			
		||||
//         .board_hal
 | 
			
		||||
//         .get_rtc_module()
 | 
			
		||||
//         .get_rtc_time()
 | 
			
		||||
//         .map(|t| t.to_rfc3339())
 | 
			
		||||
//         .unwrap_or("error".to_string());
 | 
			
		||||
//
 | 
			
		||||
//     let data = LoadData {
 | 
			
		||||
//         rtc: rtc.as_str(),
 | 
			
		||||
//         native: native.as_str(),
 | 
			
		||||
//     };
 | 
			
		||||
//     let json = serde_json::to_string(&data)?;
 | 
			
		||||
//
 | 
			
		||||
//     anyhow::Ok(Some(json))
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// fn get_timezones(
 | 
			
		||||
//     _request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
@@ -231,25 +217,9 @@ pub struct NightLampCommand {
 | 
			
		||||
//     anyhow::Ok(Some("saved".to_owned()))
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
// fn get_solar_state(
 | 
			
		||||
//     _request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
// ) -> Result<Option<std::string::String>, anyhow::Error> {
 | 
			
		||||
//     let mut board = BOARD_ACCESS.lock().expect("board access");
 | 
			
		||||
//     let state = SolarState {
 | 
			
		||||
//         mppt_voltage: board.board_hal.get_mptt_voltage()?.as_millivolts() as f32,
 | 
			
		||||
//         mppt_current: board.board_hal.get_mptt_current()?.as_milliamperes() as f32,
 | 
			
		||||
//         is_day: board.board_hal.is_day(),
 | 
			
		||||
//     };
 | 
			
		||||
//     anyhow::Ok(Some(serde_json::to_string(&state)?))
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// fn get_battery_state(
 | 
			
		||||
//     _request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
// ) -> Result<Option<std::string::String>, anyhow::Error> {
 | 
			
		||||
//     let mut board = BOARD_ACCESS.lock().expect("board access");
 | 
			
		||||
//     let battery_state = board.board_hal.get_battery_monitor().get_battery_state();
 | 
			
		||||
//     anyhow::Ok(Some(serde_json::to_string(&battery_state)?))
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// fn get_log(
 | 
			
		||||
//     _request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
@@ -264,11 +234,7 @@ pub struct NightLampCommand {
 | 
			
		||||
//     )?)
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
// fn get_version_web(
 | 
			
		||||
//     _request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
// ) -> Result<Option<std::string::String>, anyhow::Error> {
 | 
			
		||||
//     anyhow::Ok(Some(serde_json::to_string(&get_version())?))
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// fn pump_test(
 | 
			
		||||
//     request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
@@ -323,16 +289,7 @@ pub struct NightLampCommand {
 | 
			
		||||
//     anyhow::Ok(Some(ssid_json))
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
// fn list_files(
 | 
			
		||||
//     _request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
// ) -> Result<Option<std::string::String>, anyhow::Error> {
 | 
			
		||||
//     let mut board = BOARD_ACCESS
 | 
			
		||||
//         .lock()
 | 
			
		||||
//         .expect("It should be possible to lock the board for exclusive fs access");
 | 
			
		||||
//     let result = board.board_hal.get_esp().list_files();
 | 
			
		||||
//     let file_list_json = serde_json::to_string(&result)?;
 | 
			
		||||
//     anyhow::Ok(Some(file_list_json))
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// fn ota(
 | 
			
		||||
//     request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
@@ -390,82 +347,175 @@ pub struct NightLampCommand {
 | 
			
		||||
//     }
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::task]
 | 
			
		||||
pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) {
 | 
			
		||||
    let mut rx_buffer = [0; 1536];
 | 
			
		||||
    let mut tx_buffer = [0; 1536];
 | 
			
		||||
    let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
 | 
			
		||||
    socket.set_keep_alive(Some(Duration::from_secs(30)));
 | 
			
		||||
    socket.set_timeout(Some(Duration::from_secs(5)));
 | 
			
		||||
struct HttpHandler;
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        println!("Wait for connection...");
 | 
			
		||||
        let r = socket
 | 
			
		||||
            .accept(IpListenEndpoint {
 | 
			
		||||
                addr: None,
 | 
			
		||||
                port: 8080,
 | 
			
		||||
            })
 | 
			
		||||
            .await;
 | 
			
		||||
        println!("Connected...");
 | 
			
		||||
impl Handler for HttpHandler {
 | 
			
		||||
    type Error<E>
 | 
			
		||||
        = Error<E>
 | 
			
		||||
    where
 | 
			
		||||
        E: Debug;
 | 
			
		||||
 | 
			
		||||
        if let Err(e) = r {
 | 
			
		||||
            println!("connect error: {:?}", e);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
    async fn handle<'a, T, const N: usize>(
 | 
			
		||||
        &self,
 | 
			
		||||
        _task_id: impl Display + Copy,
 | 
			
		||||
        conn: &mut Connection<'a, T, N>,
 | 
			
		||||
    ) -> anyhow::Result<(), Self::Error<T::Error>>
 | 
			
		||||
    where
 | 
			
		||||
        T: Read + Write,
 | 
			
		||||
    {
 | 
			
		||||
        let start = Instant::now();
 | 
			
		||||
        let headers = conn.headers()?;
 | 
			
		||||
 | 
			
		||||
        use embedded_io_async::Write;
 | 
			
		||||
        let method = headers.method;
 | 
			
		||||
        let path = headers.path;
 | 
			
		||||
 | 
			
		||||
        let mut buffer = [0u8; 1024];
 | 
			
		||||
        let mut pos = 0;
 | 
			
		||||
        loop {
 | 
			
		||||
            match socket.read(&mut buffer).await {
 | 
			
		||||
                core::result::Result::Ok(len) => {
 | 
			
		||||
                    if len == 0 {
 | 
			
		||||
                        println!("read EOF");
 | 
			
		||||
                        break;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        let to_print =
 | 
			
		||||
                            unsafe { core::str::from_utf8_unchecked(&buffer[..(pos + len)]) };
 | 
			
		||||
 | 
			
		||||
                        if to_print.contains("\r\n\r\n") {
 | 
			
		||||
                            print!("{}", to_print);
 | 
			
		||||
                            println!();
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        pos += len;
 | 
			
		||||
        let status = match method {
 | 
			
		||||
            Method::Get => match path {
 | 
			
		||||
                "/favicon.ico" => {
 | 
			
		||||
                    conn.initiate_response(200, Some("OK"), &[("Content-Type", "image/x-icon")])
 | 
			
		||||
                        .await?;
 | 
			
		||||
                    conn.write_all(include_bytes!("favicon.ico")).await?;
 | 
			
		||||
                    Some(200)
 | 
			
		||||
                }
 | 
			
		||||
                "/" => {
 | 
			
		||||
                    conn.initiate_response(200, Some("OK"), &[("Content-Type", "text/html")])
 | 
			
		||||
                        .await?;
 | 
			
		||||
                    conn.write_all(include_bytes!("index.html")).await?;
 | 
			
		||||
                    Some(200)
 | 
			
		||||
                }
 | 
			
		||||
                "/bundle.js" => {
 | 
			
		||||
                    conn.initiate_response(200, Some("OK"), &[("Content-Type", "text/javascript")])
 | 
			
		||||
                        .await?;
 | 
			
		||||
                    conn.write_all(include_bytes!("bundle.js")).await?;
 | 
			
		||||
                    Some(200)
 | 
			
		||||
                }
 | 
			
		||||
                &_ => {
 | 
			
		||||
                    let json = match path {
 | 
			
		||||
                        "/version" => Some(get_version_web(conn).await),
 | 
			
		||||
                        "/time" => Some(get_time(conn).await),
 | 
			
		||||
                        "/battery" => Some(get_battery_state(conn).await),
 | 
			
		||||
                        "/solar" => Some(get_solar_state(conn).await),
 | 
			
		||||
                        "/get_config" => Some(get_config(conn).await),
 | 
			
		||||
                        "/files" => Some(list_files(conn).await),
 | 
			
		||||
                        "/log_localization" => Some(get_log_localization_config(conn).await),
 | 
			
		||||
                        _ => None,
 | 
			
		||||
                    };
 | 
			
		||||
                    match json {
 | 
			
		||||
                        None => None,
 | 
			
		||||
                        Some(json) => Some(handle_json(conn, json).await?),
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    println!("read error: {:?}", e);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        let r = socket
 | 
			
		||||
            .write_all(
 | 
			
		||||
                b"HTTP/1.0 200 OK\r\n\r\n\
 | 
			
		||||
            <html>\
 | 
			
		||||
                <body>\
 | 
			
		||||
                    <h1>Hello Rust! Hello esp-wifi!</h1>\
 | 
			
		||||
                </body>\
 | 
			
		||||
            </html>\r\n\
 | 
			
		||||
            ",
 | 
			
		||||
            )
 | 
			
		||||
            .await;
 | 
			
		||||
        if let Err(e) = r {
 | 
			
		||||
            println!("write error: {:?}", e);
 | 
			
		||||
        }
 | 
			
		||||
            },
 | 
			
		||||
            Method::Options | Method::Delete | Method::Head | Method::Post | Method::Put => None,
 | 
			
		||||
            _ => None,
 | 
			
		||||
        };
 | 
			
		||||
        let code = match status {
 | 
			
		||||
            None => {
 | 
			
		||||
                conn.initiate_response(404, Some("Not found"), &[]).await?;
 | 
			
		||||
                404
 | 
			
		||||
            }
 | 
			
		||||
            Some(code) => code,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let r = socket.flush().await;
 | 
			
		||||
        if let Err(e) = r {
 | 
			
		||||
            println!("flush error: {:?}", e);
 | 
			
		||||
        }
 | 
			
		||||
        Timer::after_millis(100).await;
 | 
			
		||||
        socket.close();
 | 
			
		||||
        Timer::after_millis(100).await;
 | 
			
		||||
        socket.abort();
 | 
			
		||||
        conn.complete().await?;
 | 
			
		||||
        let response_time = Instant::now().duration_since(start).as_millis();
 | 
			
		||||
 | 
			
		||||
        info!("\"{method} {path}\" {code} {response_time}ms");
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_log_localization_config<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
    anyhow::Ok(Some(serde_json::to_string(
 | 
			
		||||
        &LogMessage::to_log_localisation_config(),
 | 
			
		||||
    )?))
 | 
			
		||||
}
 | 
			
		||||
async fn list_files<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    let result = board.board_hal.get_esp().list_files().await;
 | 
			
		||||
    let file_list_json = serde_json::to_string(&result)?;
 | 
			
		||||
    anyhow::Ok(Some(file_list_json))
 | 
			
		||||
}
 | 
			
		||||
async fn get_config<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    let json = serde_json::to_string(&board.board_hal.get_config())?;
 | 
			
		||||
    anyhow::Ok(Some(json))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_solar_state<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    let state = SolarState {
 | 
			
		||||
        mppt_voltage: board.board_hal.get_mptt_voltage().await?.as_millivolts() as f32,
 | 
			
		||||
        mppt_current: board.board_hal.get_mptt_current().await?.as_milliamperes() as f32,
 | 
			
		||||
        is_day: board.board_hal.is_day(),
 | 
			
		||||
    };
 | 
			
		||||
    anyhow::Ok(Some(serde_json::to_string(&state)?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_battery_state<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    let battery_state = board
 | 
			
		||||
        .board_hal
 | 
			
		||||
        .get_battery_monitor()
 | 
			
		||||
        .get_battery_state()
 | 
			
		||||
        .await?;
 | 
			
		||||
    anyhow::Ok(Some(serde_json::to_string(&battery_state)?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_version_web<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    anyhow::Ok(Some(serde_json::to_string(&get_version(&mut board).await)?))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn get_time<T, const N: usize>(
 | 
			
		||||
    _request: &mut Connection<'_, T, N>,
 | 
			
		||||
) -> Result<Option<String>, anyhow::Error> {
 | 
			
		||||
    let mut board = BOARD_ACCESS.get().await.lock().await;
 | 
			
		||||
    //TODO do not fail if rtc module is missing
 | 
			
		||||
    let native = board.board_hal.get_esp().time().to_rfc3339();
 | 
			
		||||
    let rtc = board
 | 
			
		||||
        .board_hal
 | 
			
		||||
        .get_rtc_module()
 | 
			
		||||
        .get_rtc_time()
 | 
			
		||||
        .await?
 | 
			
		||||
        .to_rfc3339();
 | 
			
		||||
 | 
			
		||||
    let data = LoadData {
 | 
			
		||||
        rtc: rtc.as_str(),
 | 
			
		||||
        native: native.as_str(),
 | 
			
		||||
    };
 | 
			
		||||
    let json = serde_json::to_string(&data)?;
 | 
			
		||||
 | 
			
		||||
    anyhow::Ok(Some(json))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy_executor::task]
 | 
			
		||||
pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) {
 | 
			
		||||
    let buffer: TcpBuffers<2, 1024, 1024> = TcpBuffers::new();
 | 
			
		||||
    let tcp = Tcp::new(stack, &buffer);
 | 
			
		||||
    let acceptor = tcp
 | 
			
		||||
        .bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8080))
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    let mut server: Server<2, 512, 10> = Server::new();
 | 
			
		||||
    server
 | 
			
		||||
        .run(Some(5000), acceptor, HttpHandler)
 | 
			
		||||
        .await
 | 
			
		||||
        .expect("TODO: panic message");
 | 
			
		||||
    println!("Wait for connection...");
 | 
			
		||||
 | 
			
		||||
    // let server_config = Configuration {
 | 
			
		||||
    //     stack_size: 32768,
 | 
			
		||||
@@ -805,3 +855,61 @@ pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) {
 | 
			
		||||
//     log::info!("Raw data {}", from_utf8(&allvec)?);
 | 
			
		||||
//     Ok(allvec)
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
async fn handle_json<'a, T, const N: usize>(
 | 
			
		||||
    conn: &mut Connection<'a, T, N>,
 | 
			
		||||
    chain: anyhow::Result<Option<String>>,
 | 
			
		||||
) -> anyhow::Result<u32, Error<T::Error>>
 | 
			
		||||
where
 | 
			
		||||
    T: Read + Write,
 | 
			
		||||
    <T as embedded_io_async::ErrorType>::Error: Debug,
 | 
			
		||||
{
 | 
			
		||||
    match chain {
 | 
			
		||||
        Ok(answer) => match answer {
 | 
			
		||||
            Some(json) => {
 | 
			
		||||
                conn.initiate_response(
 | 
			
		||||
                    200,
 | 
			
		||||
                    Some("OK"),
 | 
			
		||||
                    &[
 | 
			
		||||
                        ("Access-Control-Allow-Origin", "*"),
 | 
			
		||||
                        ("Access-Control-Allow-Headers", "*"),
 | 
			
		||||
                        ("Access-Control-Allow-Methods", "*"),
 | 
			
		||||
                        ("Content-Type", "application/json"),
 | 
			
		||||
                    ],
 | 
			
		||||
                )
 | 
			
		||||
                .await?;
 | 
			
		||||
                conn.write_all(json.as_bytes()).await?;
 | 
			
		||||
                Ok(200)
 | 
			
		||||
            }
 | 
			
		||||
            None => {
 | 
			
		||||
                conn.initiate_response(
 | 
			
		||||
                    200,
 | 
			
		||||
                    Some("OK"),
 | 
			
		||||
                    &[
 | 
			
		||||
                        ("Access-Control-Allow-Origin", "*"),
 | 
			
		||||
                        ("Access-Control-Allow-Headers", "*"),
 | 
			
		||||
                        ("Access-Control-Allow-Methods", "*"),
 | 
			
		||||
                    ],
 | 
			
		||||
                )
 | 
			
		||||
                .await?;
 | 
			
		||||
                Ok(200)
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            let error_text = err.to_string();
 | 
			
		||||
            info!("error handling process {}", error_text);
 | 
			
		||||
            conn.initiate_response(
 | 
			
		||||
                500,
 | 
			
		||||
                Some("OK"),
 | 
			
		||||
                &[
 | 
			
		||||
                    ("Access-Control-Allow-Origin", "*"),
 | 
			
		||||
                    ("Access-Control-Allow-Headers", "*"),
 | 
			
		||||
                    ("Access-Control-Allow-Methods", "*"),
 | 
			
		||||
                ],
 | 
			
		||||
            )
 | 
			
		||||
            .await?;
 | 
			
		||||
            conn.write_all(error_text.as_bytes()).await?;
 | 
			
		||||
            Ok(500)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user