Compare commits

...

15 Commits

Author SHA1 Message Date
Philipp_EndevourOS
cab2533fab deleted some imports 2025-10-27 15:19:51 +01:00
Philipp_EndevourOS
5f65cc7a73 Merge remote-tracking branch 'origin/main' into feature/newlib 2025-10-27 15:11:04 +01:00
Philipp_EndevourOS
03e6a9036f implemented LED for new lib !esp-hhal-smartled is not released! 2025-10-27 15:08:22 +01:00
16ea1db55f Merge branch 'feature/newlib' 2025-10-27 14:34:31 +01:00
a0ed04a560 moved LED and feedback to new lib 2025-10-27 14:25:26 +01:00
Philipp_EndevourOS
4e988e8f01 changed LED amount to 1 2025-10-27 14:16:36 +01:00
009f6cbb2e changed to new ESP libs WIP 2025-10-27 13:56:13 +01:00
Philipp_EndevourOS
967da9fc30 feedback ack turns LED off after lighting green 2025-10-25 15:04:05 +02:00
00cb7efedb unwraps now give a warning 2025-10-24 13:24:44 +02:00
ebbec7885e added missing persist mapping 2025-10-23 17:55:29 +02:00
7ecd2052d8 handle error in sse event route 2025-10-23 17:37:46 +02:00
96512c8a12 added wifi password set at compiletime 2025-10-23 16:55:16 +02:00
c3eaff03d9 implemented year select for export csv 2025-10-23 16:33:15 +02:00
4bf89626b9 changed defaults in csv generation 2025-10-23 16:32:55 +02:00
7c0c0699b5 added umlaut user to mock data 2025-10-23 16:32:38 +02:00
15 changed files with 759 additions and 673 deletions

View File

@@ -12,3 +12,7 @@ target = "riscv32imac-unknown-none-elf"
[unstable]
build-std = ["alloc", "core"]
[env]
WIFI_PASSWD = "hunter22"
WIFI_SSID = "fwa"

924
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,64 +12,44 @@ bench = false
[dependencies]
esp-bootloader-esp-idf = "0.1.0"
embassy-net = { version = "0.7.0", features = [
"dhcpv4",
"medium-ethernet",
"tcp",
"udp",
] }
embedded-hal = "=1.0.0"
embedded-io = "0.6.1"
embedded-io-async = "0.6.1"
esp-alloc = "0.8.0"
esp-hal = { version = "1.0.0-beta.1", features = ["esp32c6", "unstable"] }
smoltcp = { version = "0.12.0", default-features = false, features = [
"medium-ethernet",
"multicast",
"proto-dhcpv4",
"proto-dns",
"proto-ipv4",
"socket-dns",
"socket-icmp",
"socket-raw",
"socket-tcp",
"socket-udp",
] }
# for more networking protocol support see https://crates.io/crates/edge-net
bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "a5148d8ae679e021b78f53fd33afb8bb35d0b62e", features = [
"async",
"macros",
] }
esp-hal = { version = "1.0.0-rc.1", features = ["esp32c6", "unstable"] }
esp-alloc = "0.9.0"
esp-println = { version = "0.16.0", features = ["esp32c6", "log-04"] }
esp-radio = { version = "0.16.0", features = ["esp32c6","esp-alloc", "wifi", "log-04", "smoltcp","unstable"]}
esp-rtos = { version = "0.1.1", features = ["esp32c6", "embassy", "esp-radio", "esp-alloc"] }
critical-section = "1.2.0"
embassy-executor = { version = "0.7.0", features = ["nightly"] }
embassy-time = { version = "0.4.0", features = ["generic-queue-8"] }
esp-hal-embassy = { version = "0.9.0", features = ["esp32c6"] }
esp-wifi = { version = "0.15.0", features = [
"wifi",
"builtin-scheduler",
"esp-alloc",
"esp32c6",
"log-04",
] }
heapless = { version = "0.8.0", default-features = false }
static_cell = { version = "2.1.0", features = ["nightly"] }
esp-println = { version = "0.15.0", features = ["esp32c6", "log-04"] }
log = { version = "0.4" }
static_cell = { version = "2.1.1", features = ["nightly"] }
heapless = { version = "0.8.0", default-features = false }
chrono = { version = "0.4.41", default-features = false }
embedded-hal = "1.0.0"
embedded-io = "0.7.1"
embedded-io-async = "0.7.0"
embassy-executor = { version = "0.9.0", features = [] }
embassy-time = { version = "0.5.0", features = [] }
embassy-futures = { version = "0.1.2", features = ["log"] }
embassy-sync = { version = "0.7.2", features = ["log"] }
embassy-net = { version = "0.7.0", features = [ "dhcpv4", "medium-ethernet", "tcp", "udp" ] }
smoltcp = { version = "0.12.0", default-features = false, features = [ "medium-ethernet", "multicast", "proto-dhcpv4", "proto-dns", "proto-ipv4", "socket-dns", "socket-icmp", "socket-raw", "socket-tcp", "socket-udp" ] }
bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "a5148d8ae679e021b78f53fd33afb8bb35d0b62e", features = [ "async", "macros" ] }
edge-dhcp = { version = "0.6.0", features = ["log"] }
edge-nal = "0.5.0"
edge-nal-embassy = { version = "0.6.0", features = ["log"] }
picoserve = { git = "https://github.com/sammhicks/picoserve.git", rev = "400df53f61137e1bb2883ec610fc191bfe551a3a", features = ["embassy", "log", "json"] }
embassy-sync = { version = "0.7.0", features = ["log"] }
ds3231 = { version = "0.3.0", features = ["async", "temperature_f32"] }
chrono = { version = "0.4.41", default-features = false }
dir-embed = "0.3.0"
esp-hal-smartled = { git = "https://github.com/esp-rs/esp-hal-community.git", package = "esp-hal-smartled", branch = "main", features = ["esp32c6"]}
smart-leds = "0.4.0"
serde = { version = "1.0.219", default-features = false, features = ["derive", "alloc"] }
serde_json = { version = "1.0.143", default-features = false, features = ["alloc"]}
ds3231 = { version = "0.3.0", features = ["async", "temperature_f32"] }
esp-hal-smartled = { git = "https://github.com/esp-rs/esp-hal-community.git", rev = "ab4316534d90e3a12785907f043f6899faee0f20", package = "esp-hal-smartled", features = ["esp32c6"]}
smart-leds = "0.4.0"
embedded-sdmmc = "0.8.0"
embedded-hal-bus = "0.3.0"
serde_json = { version = "1.0.143", default-features = false, features = ["alloc"]}
embassy-futures = { version = "0.1.2", features = ["log"] }
[profile.dev]
# Rust debug is too slow.

View File

@@ -1,11 +1,13 @@
use embassy_time::{Duration, Timer};
use esp_hal::{peripherals, rmt::ConstChannelAccess};
use esp_hal_smartled::SmartLedsAdapterAsync;
use esp_hal::rmt::Rmt;
use esp_hal::peripherals;
use esp_hal_smartled::{SmartLedsAdapterAsync, buffer_size_async};
use log::debug;
use smart_leds::SmartLedsWriteAsync;
use smart_leds::colors::{BLACK, GREEN, RED, YELLOW};
use smart_leds::{brightness, colors::BLUE};
use crate::init::hardware;
use crate::{FEEDBACK_STATE, init};
#[derive(Copy, Clone, Debug)]
@@ -24,14 +26,18 @@ const LED_LEVEL: u8 = 255;
#[embassy_executor::task]
pub async fn feedback_task(
mut led: SmartLedsAdapterAsync<
ConstChannelAccess<esp_hal::rmt::Tx, 0>,
{ init::hardware::LED_BUFFER_SIZE },
>,
buzzer: peripherals::GPIO21<'static>,
rmt: Rmt<'static, esp_hal::Async>,
led_gpio: peripherals::GPIO1<'static>,
buzzer_gpio: peripherals::GPIO21<'static>,
) {
debug!("Starting feedback task");
let mut buzzer = init::hardware::setup_buzzer(buzzer);
let rmt_channel = rmt.channel0;
let rmt_buffer = [esp_hal::rmt::PulseCode::default(); buffer_size_async(hardware::NUM_LEDS)];
let mut led = SmartLedsAdapterAsync::new(rmt_channel, led_gpio, rmt_buffer);
let mut buzzer = init::hardware::setup_buzzer(buzzer_gpio);
loop {
let feedback_state = FEEDBACK_STATE.wait().await;
match feedback_state {
@@ -46,6 +52,12 @@ pub async fn feedback_task(
Timer::after(Duration::from_millis(100)).await;
buzzer.set_low();
Timer::after(Duration::from_millis(50)).await;
led.write(brightness(
[BLACK; init::hardware::NUM_LEDS].into_iter(),
LED_LEVEL,
))
.await
.unwrap();
}
FeedbackState::Nack => {
led.write(brightness(
@@ -118,7 +130,7 @@ pub async fn feedback_task(
}
FeedbackState::Idle => {
led.write(brightness(
[GREEN; init::hardware::NUM_LEDS].into_iter(),
[BLACK; init::hardware::NUM_LEDS].into_iter(),
LED_LEVEL,
))
.await
@@ -128,106 +140,3 @@ pub async fn feedback_task(
debug!("Feedback state: {:?}", feedback_state);
}
}
// async fn beep_ack() {
// buzzer.set_high();
// buzzer.set_low();
// //Timer::after(Duration::from_millis(100)).await;
// }
/* pub async fn failure(&mut self) {
let buzzer_handle = Self::beep_nak(&mut self.buzzer);
let led_handle = Self::flash_led_for_duration(&mut self.led, RED, LED_BLINK_DURATION);
let (buzzer_result, _) = join!(buzzer_handle, led_handle);
buzzer_result.unwrap_or_else(|err| { error!("Failed to buzz: {err}");
});
let _ = self.led_to_status();
}
pub async fn activate_error_state(&mut self) -> Result<()> {
self.led.turn_on(RED)?;
Self::beep_nak(&mut self.buzzer).await?;
Ok(())
}
pub async fn startup(&mut self){
self.device_status = DeviceStatus::Ready;
let led_handle = Self::flash_led_for_duration(&mut self.led, GREEN, Duration::from_secs(1));
let buzzer_handle = Self::beep_startup(&mut self.buzzer);
let (buzzer_result, led_result) = join!(buzzer_handle, led_handle);
buzzer_result.unwrap_or_else(|err| {
error!("Failed to buzz: {err}");
});
led_result.unwrap_or_else(|err| {
error!("Failed to blink led: {err}");
});
let _ = self.led_to_status();
}
async fn flash_led_for_duration(led: &mut L, color: RGB8, duration: Duration) -> Result<()> {
led.turn_on(color)?;
sleep(duration).await;
led.turn_off()?;
Ok(())
}
async fn beep_ack(buzzer: &mut B) -> Result<()> {
buzzer
.modulated_tone(1200.0, Duration::from_millis(100))
.await?;
sleep(Duration::from_millis(10)).await;
buzzer
.modulated_tone(2000.0, Duration::from_millis(50))
.await?;
Ok(())
}
async fn beep_nak(buzzer: &mut B) -> Result<()> {
buzzer
.modulated_tone(600.0, Duration::from_millis(150))
.await?;
sleep(Duration::from_millis(100)).await;
buzzer
.modulated_tone(600.0, Duration::from_millis(150))
.await?;
Ok(())
}
async fn beep_startup(buzzer: &mut B) -> Result<()> {
buzzer
.modulated_tone(523.0, Duration::from_millis(150))
.await?;
buzzer
.modulated_tone(659.0, Duration::from_millis(150))
.await?;
buzzer
.modulated_tone(784.0, Duration::from_millis(150))
.await?;
buzzer
.modulated_tone(1046.0, Duration::from_millis(200))
.await?;
sleep(Duration::from_millis(100)).await;
buzzer
.modulated_tone(784.0, Duration::from_millis(100))
.await?;
buzzer
.modulated_tone(880.0, Duration::from_millis(200))
.await?;
Ok(())
}
*/

View File

@@ -11,7 +11,7 @@ use esp_hal::peripherals::{
GPIO0, GPIO1, GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, GPIO23, I2C0, RMT, SPI2,
UART1,
};
use esp_hal::rmt::{ConstChannelAccess, Rmt};
use esp_hal::rmt::Rmt;
use esp_hal::spi::master::{Config as Spi_config, Spi};
use esp_hal::system::software_reset;
use esp_hal::time::Rate;
@@ -49,8 +49,7 @@ use crate::init::wifi;
*
*************************************************/
pub const NUM_LEDS: usize = 66;
pub const LED_BUFFER_SIZE: usize = NUM_LEDS * 25;
pub const NUM_LEDS: usize = 1;
static SD_DET: Mutex<RefCell<Option<Input>>> = Mutex::new(RefCell::new(None));
@@ -65,32 +64,35 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
esp_bootloader_esp_idf::esp_app_desc!();
pub async fn hardware_init(
spawner: &mut Spawner,
spawner: Spawner,
) -> (
Uart<'static, Async>,
Stack<'static>,
I2c<'static, Async>,
SmartLedsAdapterAsync<ConstChannelAccess<esp_hal::rmt::Tx, 0>, LED_BUFFER_SIZE>,
Rmt<'static, esp_hal::Async>,
GPIO1<'static>,
GPIO21<'static>,
GPIO0<'static>,
SmartLedsAdapterAsync<'static, LED_BUFFER_SIZE>,
SDCardPersistence,
) {
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
esp_alloc::heap_allocator!(size: 72 * 1024);
esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 65536);
let timer0 = SystemTimer::new(peripherals.SYSTIMER);
esp_hal_embassy::init(timer0.alarm0);
let timg0 = TimerGroup::new(peripherals.TIMG0);
let sw_interrupt =
esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0);
init_logger(log::LevelFilter::Debug);
let timer1 = TimerGroup::new(peripherals.TIMG0);
let mut rng = esp_hal::rng::Rng::new(peripherals.RNG);
let rng = esp_hal::rng::Rng::new();
let network_seed = (rng.random() as u64) << 32 | rng.random() as u64;
wifi::set_antenna_mode(peripherals.GPIO3, peripherals.GPIO14).await;
let interfaces = wifi::setup_wifi(timer1.timer0, rng, peripherals.WIFI, spawner);
let interfaces = wifi::setup_wifi(peripherals.WIFI, spawner);
let stack = network::setup_network(network_seed, interfaces.ap, spawner);
Timer::after(Duration::from_millis(1)).await;
@@ -101,6 +103,10 @@ pub async fn hardware_init(
let sd_det_gpio = peripherals.GPIO0;
let rmt: Rmt<'_, esp_hal::Async> = {let frequency: Rate = Rate::from_mhz(80);
Rmt::new(peripherals.RMT, frequency)} .expect("Failed to initialize RMT")
.into_async();
let spi_bus = setup_spi(
peripherals.SPI2,
peripherals.GPIO19,
@@ -116,21 +122,24 @@ pub async fn hardware_init(
let vol_mgr = setup_sdcard(spi_bus, sd_cs_pin);
let led_gpio = peripherals.GPIO1;
let buzzer_gpio = peripherals.GPIO21;
Timer::after(Duration::from_millis(500)).await;
let led = setup_led(peripherals.RMT, peripherals.GPIO1);
Timer::after(Duration::from_millis(500)).await;
debug!("hardware init done");
(
uart_device,
stack,
i2c_device,
led,
rmt,
led_gpio,
buzzer_gpio,
sd_det_gpio,
led,
vol_mgr,
)
}
@@ -189,11 +198,10 @@ pub fn setup_buzzer(buzzer_gpio: GPIO21<'static>) -> Output<'static> {
buzzer
}
fn setup_led(
rmt: RMT<'static>,
led_gpio: GPIO1<'static>,
) -> SmartLedsAdapterAsync<ConstChannelAccess<esp_hal::rmt::Tx, 0>, LED_BUFFER_SIZE> {
debug!("setup led");
fn setup_led<'a>(
rmt: RMT<'a>,
led_gpio: GPIO1<'a>,
) -> esp_hal_smartled::SmartLedsAdapterAsync<'a, LED_BUFFER_SIZE> {
let rmt: Rmt<'_, esp_hal::Async> = {
let frequency: Rate = Rate::from_mhz(80);
Rmt::new(rmt, frequency)
@@ -202,10 +210,7 @@ fn setup_led(
.into_async();
let rmt_channel = rmt.channel0;
let rmt_buffer = [0_u32; buffer_size_async(NUM_LEDS)];
let rmt_buffer = [esp_hal::rmt::PulseCode::default(); LED_BUFFER_SIZE];
let led: SmartLedsAdapterAsync<_, LED_BUFFER_SIZE> =
SmartLedsAdapterAsync::new(rmt_channel, led_gpio, rmt_buffer);
led
SmartLedsAdapterAsync::new(rmt_channel, led_gpio, rmt_buffer)
}

View File

@@ -2,7 +2,7 @@ use core::{net::Ipv4Addr, str::FromStr};
use embassy_executor::Spawner;
use embassy_net::{Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4};
use embassy_time::{Duration, Timer};
use esp_wifi::wifi::WifiDevice;
use esp_radio::wifi::WifiDevice;
use static_cell::make_static;
use crate::webserver::WEB_TAKS_SIZE;
@@ -10,7 +10,7 @@ use crate::webserver::WEB_TAKS_SIZE;
pub const NETWORK_STACK_SIZE: usize = WEB_TAKS_SIZE + 2; // + 2 for other network taks. Breaks
// without
pub fn setup_network<'a>(seed: u64, wifi: WifiDevice<'static>, spawner: &mut Spawner) -> Stack<'a> {
pub fn setup_network<'a>(seed: u64, wifi: WifiDevice<'static>, spawner: Spawner) -> Stack<'a> {
let gw_ip_addr_str = "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 {
@@ -19,12 +19,10 @@ pub fn setup_network<'a>(seed: u64, wifi: WifiDevice<'static>, spawner: &mut Spa
dns_servers: Default::default(),
});
let (stack, runner) = embassy_net::new(
wifi,
config,
make_static!(StackResources::<NETWORK_STACK_SIZE>::new()),
seed,
);
let nw_stack: &'static mut StackResources<NETWORK_STACK_SIZE> =
make_static!(StackResources::<NETWORK_STACK_SIZE>::new());
let (stack, runner) = embassy_net::new(wifi, config, nw_stack, seed);
spawner.must_spawn(net_task(runner));
spawner.must_spawn(run_dhcp(stack, gw_ip_addr_str));

View File

@@ -2,9 +2,14 @@ use embassy_executor::Spawner;
use embassy_time::{Duration, Timer};
use esp_hal::gpio::{Output, OutputConfig};
use esp_hal::peripherals::{GPIO3, GPIO14, WIFI};
use esp_wifi::wifi::{AccessPointConfiguration, Configuration, WifiController, WifiEvent, WifiState};
use esp_wifi::{EspWifiRngSource, EspWifiTimerSource, wifi::Interfaces};
use static_cell::make_static;
use esp_radio::Controller;
use esp_radio::wifi::{
AccessPointConfig, Interfaces, ModeConfig, WifiApState, WifiController, WifiEvent,
};
use log::debug;
use static_cell::StaticCell;
static ESP_WIFI_CTRL: StaticCell<Controller<'static>> = StaticCell::new();
pub async fn set_antenna_mode(gpio3: GPIO3<'static>, gpio14: GPIO14<'static>) {
let mut rf_switch = Output::new(gpio3, esp_hal::gpio::Level::Low, OutputConfig::default());
@@ -18,26 +23,23 @@ pub async fn set_antenna_mode(gpio3: GPIO3<'static>, gpio14: GPIO14<'static>) {
antenna_mode.set_low();
}
pub fn setup_wifi<'d: 'static>(
timer: impl EspWifiTimerSource + 'd,
rng: impl EspWifiRngSource + 'd,
wifi: WIFI<'static>,
spawner: &mut Spawner,
) -> Interfaces<'d> {
let esp_wifi_ctrl = make_static!(esp_wifi::init(timer, rng).unwrap());
pub fn setup_wifi<'d: 'static>(wifi: WIFI<'static>, spawner: Spawner) -> Interfaces<'d> {
let esp_wifi_ctrl = ESP_WIFI_CTRL.init(esp_radio::init().unwrap());
let (controller, interfaces) = esp_wifi::wifi::new(esp_wifi_ctrl, wifi).unwrap();
let config = esp_radio::wifi::Config::default();
let (controller, interfaces) = esp_radio::wifi::new(esp_wifi_ctrl, wifi, config).unwrap();
spawner.must_spawn(connection(controller));
interfaces
}
#[embassy_executor::task]
async fn connection(mut controller: WifiController<'static>) {
debug!("start connection task");
debug!("Device capabilities: {:?}", controller.capabilities());
loop {
match esp_wifi::wifi::wifi_state() {
WifiState::ApStarted => {
match esp_radio::wifi::ap_state() {
WifiApState::Started => {
// wait until we're no longer connected
controller.wait_for_event(WifiEvent::ApStop).await;
Timer::after(Duration::from_millis(5000)).await
@@ -45,12 +47,16 @@ async fn connection(mut controller: WifiController<'static>) {
_ => {}
}
if !matches!(controller.is_started(), Ok(true)) {
let client_config = Configuration::AccessPoint(AccessPointConfiguration {
ssid: "esp-wifi".try_into().unwrap(),
..Default::default()
});
controller.set_configuration(&client_config).unwrap();
let client_config = ModeConfig::AccessPoint(
AccessPointConfig::default()
.with_ssid(env!("WIFI_SSID").try_into().unwrap())
.with_password(env!("WIFI_PASSWD").try_into().unwrap())
.with_auth_method(esp_radio::wifi::AuthMethod::Wpa2Personal),
);
controller.set_config(&client_config).unwrap();
debug!("Starting wifi");
controller.start_async().await.unwrap();
debug!("Wifi started!");
}
}
}

View File

@@ -2,6 +2,7 @@
#![no_main]
#![feature(type_alias_impl_trait)]
#![feature(impl_trait_in_assoc_type)]
#![warn(clippy::unwrap_used)]
use alloc::rc::Rc;
use embassy_executor::Spawner;
@@ -19,7 +20,7 @@ use embassy_time::{Duration, Timer};
use esp_hal::gpio::Input;
use esp_hal::{gpio::InputConfig, peripherals};
use log::{debug, info};
use static_cell::make_static;
use static_cell::StaticCell;
extern crate alloc;
@@ -42,10 +43,12 @@ type TallyPublisher = Publisher<'static, NoopRawMutex, TallyID, 8, 2, 1>;
type TallySubscriber = Subscriber<'static, NoopRawMutex, TallyID, 8, 2, 1>;
type UsedStore = IDStore<SDCardPersistence>;
#[esp_hal_embassy::main]
async fn main(mut spawner: Spawner) {
let (uart_device, stack, i2c, led, buzzer_gpio, sd_det_gpio, persistence_layer) =
init::hardware::hardware_init(&mut spawner).await;
static CHAN: StaticCell<TallyChannel> = StaticCell::new();
#[esp_rtos::main]
async fn main(spawner: Spawner) -> ! {
let (uart_device, stack, i2c, rmt, led_gpio, buzzer_gpio, sd_det_gpio, persistence_layer) =
init::hardware::hardware_init(spawner).await;
info!("Starting up...");
@@ -54,13 +57,13 @@ async fn main(mut spawner: Spawner) {
let store: UsedStore = IDStore::new_from_storage(persistence_layer).await;
let shared_store = Rc::new(Mutex::new(store));
let chan: &'static mut TallyChannel = make_static!(PubSubChannel::new());
let chan: &'static mut TallyChannel = CHAN.init(PubSubChannel::new());
let publisher: TallyPublisher = chan.publisher().unwrap();
let mut sub: TallySubscriber = chan.subscriber().unwrap();
wait_for_stack_up(stack).await;
start_webserver(&mut spawner, stack, shared_store.clone(), chan);
start_webserver(spawner, stack, shared_store.clone(), chan);
/****************************** Spawning tasks ***********************************/
debug!("spawing NFC reader task...");
@@ -70,7 +73,7 @@ async fn main(mut spawner: Spawner) {
));
debug!("spawing feedback task..");
spawner.must_spawn(feedback::feedback_task(led, buzzer_gpio));
spawner.must_spawn(feedback::feedback_task(rmt, led_gpio, buzzer_gpio));
debug!("spawn sd detect task");
spawner.must_spawn(sd_detect_task(sd_det_gpio));

View File

@@ -66,7 +66,7 @@ impl<T: Persistence> IDStore<T> {
.await
}
async fn persist_mapping(&mut self) {
pub async fn persist_mapping(&mut self) {
self.persistence_layer.save_mapping(&self.mapping).await
}

View File

@@ -1,4 +1,4 @@
use esp_println::dbg;
use log::error;
use picoserve::{
extract::{Json, Query, State},
response::{self, IntoResponse},
@@ -6,7 +6,7 @@ use picoserve::{
use serde::Deserialize;
use crate::{
store::{self, Name, day::Day, tally_id::TallyID},
store::{Name, day::Day, tally_id::TallyID},
webserver::{app::AppState, sse::IDEvents},
};
@@ -41,11 +41,23 @@ pub async fn add_mapping(
) -> impl IntoResponse {
let mut store = state.store.lock().await;
store.mapping.add_mapping(data.id, data.name);
store.persist_mapping().await;
}
// SSE /api/idevent
pub async fn get_idevent(State(state): State<AppState>) -> impl IntoResponse {
response::EventStream(IDEvents(state.chan.subscriber().unwrap()))
pub async fn get_idevent(
State(state): State<AppState>,
) -> Result<impl IntoResponse, impl IntoResponse> {
match state.chan.subscriber() {
Ok(chan) => Ok(response::EventStream(IDEvents(chan))),
Err(e) => {
error!("Failed to create SSE: {:?}", e);
Err((
response::StatusCode::INTERNAL_SERVER_ERROR,
"Internal server error",
))
}
}
}
// GET /api/days
@@ -68,7 +80,6 @@ pub async fn get_day(
State(state): State<AppState>,
Query(QueryDay { timestamp, day }): Query<QueryDay>,
) -> Result<impl IntoResponse, impl IntoResponse> {
let parsed_day = timestamp
.map(Day::new_from_timestamp)
.or_else(|| day.map(Day::new))

View File

@@ -38,7 +38,7 @@ impl<State, CurrentPathParameters>
);
response_writer
.write_response(request.body_connection.finalize().await.unwrap(), response)
.write_response(request.body_connection.finalize().await?, response)
.await
}
None => {
@@ -68,10 +68,7 @@ impl Content for StaticAsset {
self.0.len()
}
async fn write_content<W: embedded_io_async::Write>(
self,
mut writer: W,
) -> Result<(), W::Error> {
async fn write_content<W: edge_nal::io::Write>(self, mut writer: W) -> Result<(), W::Error> {
writer.write_all(self.0).await
}
}

View File

@@ -16,10 +16,10 @@ mod app;
mod assets;
mod sse;
pub const WEB_TAKS_SIZE: usize = 3; // Up this number if request start fail with Timeouts.
pub const WEB_TAKS_SIZE: usize = 5; // Up this number if request start fail with Timeouts.
pub fn start_webserver(
spawner: &mut Spawner,
spawner: Spawner,
stack: Stack<'static>,
store: Rc<Mutex<CriticalSectionRawMutex, UsedStore>>,
chan: &'static TallyChannel,

View File

@@ -55,6 +55,13 @@
"first": "Juniper",
"last": "Voss"
}
],
[
"A0B1DB0D133B",
{
"first": "Öäü",
"last": "ßẞ"
}
]
],
"days": [
@@ -62,7 +69,8 @@
"date": 20372,
"ids": [
"123456789ABC",
"A1B2C3D4E5F6"
"A1B2C3D4E5F6",
"A0B1DB0D133B"
]
},
{
@@ -77,7 +85,8 @@
"ids": [
"654321FEDCBA",
"DEADBEEFCAFE",
"BADA55C0FFEE"
"BADA55C0FFEE",
"A0B1DB0D133B"
]
},
{

View File

@@ -1,32 +1,81 @@
<script lang="ts">
import Modal from "./Modal.svelte";
let { onSubmitted }: { onSubmitted?: (from: Date, to: Date) => void } = $props();
let { onSubmitted }: { onSubmitted?: (from: Date, to: Date) => void } =
$props();
let modal: Modal;
let fromDate: string | undefined = $state();
let toDate: string | undefined = $state();
let selectedYear: number = $state(new Date().getFullYear());
let selectedTab = $state(0);
export function open() {
modal.open();
}
function generateYears() {
const currentYear = new Date().getFullYear();
const startingYear = 2020;
return Array.from(
new Array(currentYear + 1 - startingYear),
(_, i) => i + startingYear,
);
}
function onsubmit(e: SubmitEvent) {
let from: Date;
let to: Date;
switch (selectedTab) {
case 0:
if (!fromDate || !toDate) {
e.preventDefault();
return;
}
let from = new Date(fromDate);
let to = new Date(toDate);
from = new Date(fromDate);
to = new Date(toDate);
break;
case 1:
from = new Date(selectedYear, 0);
to = new Date(selectedYear + 1, 0);
break;
default:
console.error("Invalid tab");
return;
}
onSubmitted?.(from, to);
}
</script>
<Modal bind:this={modal}>
<div class="flex">
<button
onclick={() => {
selectedTab = 0;
}}
class="tab {selectedTab === 0 ? 'tab-active' : ''}"
>
Datum
</button>
<button
onclick={() => {
selectedTab = 1;
}}
class="tab {selectedTab === 1 ? 'tab-active' : ''}"
>
Jahr
</button>
</div>
<form method="dialog" {onsubmit} class="flex flex-col">
{#if selectedTab === 0}
<div>
<label class="form-row">
<span>Von:</span>
<input type="date" class="form-input" bind:value={fromDate} />
@@ -36,6 +85,21 @@
<span>Bis:</span>
<input type="date" class="form-input" bind:value={toDate} />
</label>
</div>
{/if}
{#if selectedTab === 1}
<div>
<label class="form-row">
<span>Kalendar Jahr:</span>
<select class="form-input" bind:value={selectedYear}>
{#each generateYears() as year}
<option value={year}>{year}</option>
{/each}
</select>
</label>
</div>
{/if}
<div class="flex justify-end mt-3">
<button
@@ -60,11 +124,19 @@
<style scoped>
@reference "../app.css";
.tab {
@apply px-4 py-2 rounded-t-lg bg-indigo-600 hover:bg-indigo-700 font-medium border-b-2 border-transparent cursor-pointer transition-colors duration-200;
}
.tab-active {
@apply px-4 py-2 bg-indigo-500 font-semibold border-b-2 border-blue-600 shadow-sm cursor-pointer;
}
.form-row {
@apply flex justify-between;
@apply flex justify-between my-1;
}
.form-input {
@apply ml-10;
@apply ml-20;
}
</style>

View File

@@ -11,10 +11,10 @@ type InputRows = RowObject[];
export function generateCSVString(input: InputRows, opts: CSVOptions = {}): string {
const {
delimiter = ",",
delimiter = ";",
headerOrder,
eol = "\r\n",
includeBOM = false,
includeBOM = true,
nullString = "",
} = opts;