mirror of
https://github.com/Djeeberjr/fw-anwesenheit.git
synced 2026-04-30 18:49:09 +00:00
Compare commits
4 Commits
4a9ff47dcc
...
0f5ca88ae4
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f5ca88ae4 | |||
| 9dd2f88cbc | |||
| aa91d69f0b | |||
| b13ae76bc5 |
@@ -2,7 +2,7 @@ use embassy_time::{Duration, Timer};
|
||||
use esp_hal::{Async, uart::Uart};
|
||||
use log::{debug, info};
|
||||
|
||||
use crate::{TallyPublisher, store::TallyID};
|
||||
use crate::TallyPublisher;
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn rfid_reader_task(mut uart_device: Uart<'static, Async>, chan: TallyPublisher) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use chrono::{TimeZone, Utc};
|
||||
use ds3231::{
|
||||
Config, DS3231, DS3231Error, InterruptControl, Oscillator, SquareWaveFrequency,
|
||||
TimeRepresentation,
|
||||
@@ -9,7 +10,6 @@ use esp_hal::{
|
||||
use log::{debug, error, info};
|
||||
|
||||
use crate::{FEEDBACK_STATE, drivers, feedback, store::Date};
|
||||
use chrono::{TimeZone, Utc};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/build_time.rs"));
|
||||
|
||||
@@ -70,7 +70,6 @@ impl RTCClock {
|
||||
|
||||
buffer
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn unix_to_ymd_string(timestamp: u64) -> (u16, u8, u8) {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use embassy_time::{Delay, Duration, Timer};
|
||||
use esp_hal::{delay, gpio::Output, peripherals, rmt::ConstChannelAccess};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use esp_hal::{peripherals, rmt::ConstChannelAccess};
|
||||
use esp_hal_smartled::SmartLedsAdapterAsync;
|
||||
use init::hardware;
|
||||
use log::{debug, error, info};
|
||||
use log::debug;
|
||||
use smart_leds::SmartLedsWriteAsync;
|
||||
use smart_leds::colors::{BLACK, GREEN, RED, YELLOW};
|
||||
use smart_leds::{brightness, colors::BLUE};
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
use core::cell::RefCell;
|
||||
|
||||
use bleps::att::Att;
|
||||
use critical_section::Mutex;
|
||||
use ds3231::InterruptControl;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_net::Stack;
|
||||
|
||||
use embassy_time::{Duration, Timer};
|
||||
use esp_hal::gpio::{Input, InputConfig};
|
||||
use esp_hal::Blocking;
|
||||
use esp_hal::gpio::Input;
|
||||
use esp_hal::i2c::master::Config;
|
||||
use esp_hal::peripherals::{
|
||||
GPIO0, GPIO1, GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, GPIO23, I2C0, RMT, SPI2,
|
||||
@@ -15,8 +12,6 @@ use esp_hal::peripherals::{
|
||||
};
|
||||
use esp_hal::rmt::{ConstChannelAccess, Rmt};
|
||||
use esp_hal::spi::master::{Config as Spi_config, Spi};
|
||||
|
||||
use esp_hal::Blocking;
|
||||
use esp_hal::time::Rate;
|
||||
use esp_hal::timer::timg::TimerGroup;
|
||||
use esp_hal::{
|
||||
@@ -28,16 +23,12 @@ use esp_hal::{
|
||||
uart::Uart,
|
||||
};
|
||||
use esp_hal_smartled::{SmartLedsAdapterAsync, buffer_size_async};
|
||||
use esp_println::dbg;
|
||||
use esp_println::logger::init_logger;
|
||||
use log::{debug, error, info};
|
||||
use log::{debug, error};
|
||||
|
||||
use crate::FEEDBACK_STATE;
|
||||
use crate::init::network;
|
||||
use crate::init::sd_card::{setup_sdcard, SDCardPersistence};
|
||||
use crate::init::sd_card::{SDCardPersistence, setup_sdcard};
|
||||
use crate::init::wifi;
|
||||
use crate::store::AttendanceDay;
|
||||
use crate::store::persistence::Persistence;
|
||||
|
||||
/*************************************************
|
||||
* GPIO Pinout Xiao Esp32c6
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use core::{net::Ipv4Addr, str::FromStr};
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_net::{Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4};
|
||||
use embassy_time::{Duration, Timer};
|
||||
|
||||
13
src/main.rs
13
src/main.rs
@@ -10,7 +10,7 @@ use embassy_sync::{
|
||||
blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex},
|
||||
mutex::Mutex,
|
||||
pubsub::{
|
||||
PubSubChannel, Publisher,
|
||||
PubSubChannel, Publisher, Subscriber,
|
||||
WaitResult::{Lagged, Message},
|
||||
},
|
||||
signal::Signal,
|
||||
@@ -21,14 +21,14 @@ use esp_hal::{gpio::InputConfig, peripherals};
|
||||
use log::{debug, info};
|
||||
use static_cell::make_static;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use crate::{
|
||||
init::sd_card::SDCardPersistence,
|
||||
store::{Date, IDStore, TallyID},
|
||||
webserver::start_webserver,
|
||||
};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod drivers;
|
||||
mod feedback;
|
||||
mod init;
|
||||
@@ -39,6 +39,7 @@ static FEEDBACK_STATE: Signal<CriticalSectionRawMutex, feedback::FeedbackState>
|
||||
|
||||
type TallyChannel = PubSubChannel<NoopRawMutex, TallyID, 8, 2, 1>;
|
||||
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]
|
||||
@@ -54,12 +55,12 @@ async fn main(mut spawner: Spawner) {
|
||||
let shared_store = Rc::new(Mutex::new(store));
|
||||
|
||||
let chan: &'static mut TallyChannel = make_static!(PubSubChannel::new());
|
||||
let publisher = chan.publisher().unwrap();
|
||||
let mut sub = chan.subscriber().unwrap();
|
||||
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());
|
||||
start_webserver(&mut spawner, stack, shared_store.clone(), chan);
|
||||
|
||||
/****************************** Spawning tasks ***********************************/
|
||||
debug!("spawing NFC reader task...");
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use super::TallyID;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::String;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::TallyID;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Name {
|
||||
pub first: String,
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use crate::drivers::rtc;
|
||||
use crate::drivers::rtc::RTCClock;
|
||||
use crate::store::persistence::Persistence;
|
||||
use alloc::vec::Vec;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::Date;
|
||||
use super::IDMapping;
|
||||
use super::TallyID;
|
||||
use alloc::vec::Vec;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use crate::store::persistence::Persistence;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct AttendanceDay {
|
||||
@@ -75,7 +73,6 @@ impl<T: Persistence> IDStore<T> {
|
||||
/// Add a new id for the current day
|
||||
/// Returns false if ID is already present at the current day.
|
||||
pub async fn add_id(&mut self, id: TallyID, current_date: Date) -> bool {
|
||||
|
||||
if self.current_day.date == current_date {
|
||||
let changed = self.current_day.add_id(id);
|
||||
if changed {
|
||||
|
||||
@@ -1,9 +1,48 @@
|
||||
mod id_mapping;
|
||||
pub mod persistence;
|
||||
mod id_store;
|
||||
use heapless::String;
|
||||
|
||||
pub use id_mapping::{IDMapping, Name};
|
||||
pub use id_store::{IDStore,AttendanceDay};
|
||||
|
||||
mod id_mapping;
|
||||
pub mod persistence;
|
||||
mod id_store;
|
||||
|
||||
pub type TallyID = [u8; 6];
|
||||
pub type Date = [u8; 10];
|
||||
|
||||
pub fn hex_string_to_tally_id(s: &str) -> Option<TallyID> {
|
||||
let bytes = s.as_bytes();
|
||||
if bytes.len() != 12 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut out: TallyID = [0;6];
|
||||
for i in 0..6 {
|
||||
let hi = hex_val(bytes[2 * i])?;
|
||||
let lo = hex_val(bytes[2 * i + 1])?;
|
||||
out[i] = (hi << 4) | lo;
|
||||
}
|
||||
Some(out)
|
||||
}
|
||||
|
||||
pub fn tally_id_to_hex_string(bytes: TallyID) -> Option<String<12>> {
|
||||
const HEX_CHARS: &[u8; 16] = b"0123456789abcdef";
|
||||
let mut s: String<12> = String::new();
|
||||
|
||||
for &b in &bytes {
|
||||
s.push(HEX_CHARS[(b >> 4) as usize] as char).ok()?;
|
||||
s.push(HEX_CHARS[(b & 0x0F) as usize] as char).ok()?;
|
||||
}
|
||||
|
||||
Some(s)
|
||||
}
|
||||
|
||||
fn hex_val(b: u8) -> Option<u8> {
|
||||
match b {
|
||||
b'0'..=b'9' => Some(b - b'0'),
|
||||
b'a'..=b'f' => Some(b - b'a' + 10),
|
||||
b'A'..=b'F' => Some(b - b'A' + 10),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ use picoserve::{
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
store::{Name, TallyID},
|
||||
webserver::app::AppState,
|
||||
store::{Name, hex_string_to_tally_id},
|
||||
webserver::{app::AppState, sse::IDEvents},
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -16,30 +16,6 @@ pub struct NewMapping {
|
||||
name: Name,
|
||||
}
|
||||
|
||||
pub fn hex_string_to_tally_id(s: &str) -> Option<TallyID> {
|
||||
let bytes = s.as_bytes();
|
||||
if bytes.len() != 24 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut out = [0u8; 12];
|
||||
for i in 0..12 {
|
||||
let hi = hex_val(bytes[2 * i])?;
|
||||
let lo = hex_val(bytes[2 * i + 1])?;
|
||||
out[i] = (hi << 4) | lo;
|
||||
}
|
||||
Some(out)
|
||||
}
|
||||
|
||||
fn hex_val(b: u8) -> Option<u8> {
|
||||
match b {
|
||||
b'0'..=b'9' => Some(b - b'0'),
|
||||
b'a'..=b'f' => Some(b - b'a' + 10),
|
||||
b'A'..=b'F' => Some(b - b'A' + 10),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* #[get("/api/idevent")]
|
||||
* #[get("/api/csv")]
|
||||
@@ -64,3 +40,7 @@ pub async fn add_mapping(
|
||||
let tally_id = hex_string_to_tally_id(&data.id).unwrap();
|
||||
store.mapping.add_mapping(tally_id, data.name);
|
||||
}
|
||||
|
||||
pub async fn get_idevent(State(state): State<AppState>) -> impl IntoResponse {
|
||||
response::EventStream(IDEvents(state.chan.subscriber().unwrap()))
|
||||
}
|
||||
|
||||
@@ -3,15 +3,17 @@ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
|
||||
use picoserve::{AppWithStateBuilder, routing::get};
|
||||
|
||||
use crate::{
|
||||
TallyChannel, UsedStore,
|
||||
webserver::{
|
||||
api::{add_mapping, get_mapping},
|
||||
api::{add_mapping, get_idevent, get_mapping},
|
||||
assets::Assets,
|
||||
}, UsedStore,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub store: Rc<Mutex<CriticalSectionRawMutex, UsedStore>>,
|
||||
pub chan: &'static TallyChannel,
|
||||
}
|
||||
|
||||
pub struct AppProps;
|
||||
@@ -23,9 +25,6 @@ impl AppWithStateBuilder for AppProps {
|
||||
fn build_app(self) -> picoserve::Router<Self::PathRouter, AppState> {
|
||||
picoserve::Router::from_service(Assets)
|
||||
.route("/api/mapping", get(get_mapping).post(add_mapping))
|
||||
// .route(
|
||||
// "/api/idevent",
|
||||
// get(move || response::EventStream(Events(self.chan))),
|
||||
// )
|
||||
.route("/api/idevent", get(get_idevent))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ use picoserve::{AppRouter, AppWithStateBuilder};
|
||||
use static_cell::make_static;
|
||||
|
||||
use crate::{
|
||||
UsedStore,
|
||||
TallyChannel, UsedStore,
|
||||
webserver::app::{AppProps, AppState},
|
||||
};
|
||||
|
||||
mod assets;
|
||||
// mod sse;
|
||||
mod api;
|
||||
mod app;
|
||||
mod assets;
|
||||
mod sse;
|
||||
|
||||
pub const WEB_TAKS_SIZE: usize = 3; // Up this number if request start fail with Timeouts.
|
||||
|
||||
@@ -22,10 +22,11 @@ pub fn start_webserver(
|
||||
spawner: &mut Spawner,
|
||||
stack: Stack<'static>,
|
||||
store: Rc<Mutex<CriticalSectionRawMutex, UsedStore>>,
|
||||
chan: &'static TallyChannel,
|
||||
) {
|
||||
let app = make_static!(AppProps.build_app());
|
||||
|
||||
let state = make_static!(AppState { store });
|
||||
let state = make_static!(AppState { store, chan });
|
||||
|
||||
let config = make_static!(picoserve::Config::new(picoserve::Timeouts {
|
||||
start_read_request: Some(Duration::from_secs(5)),
|
||||
|
||||
@@ -2,9 +2,11 @@ use embassy_time::{Duration, Timer};
|
||||
use log::warn;
|
||||
use picoserve::response;
|
||||
|
||||
pub struct Events(pub TallySubscriber);
|
||||
use crate::{TallySubscriber, store::tally_id_to_hex_string};
|
||||
|
||||
impl response::sse::EventSource for Events {
|
||||
pub struct IDEvents(pub TallySubscriber);
|
||||
|
||||
impl response::sse::EventSource for IDEvents {
|
||||
async fn write_events<W: picoserve::io::Write>(
|
||||
mut self,
|
||||
mut writer: response::sse::EventWriter<W>,
|
||||
@@ -16,7 +18,9 @@ impl response::sse::EventSource for Events {
|
||||
match sel.await {
|
||||
embassy_futures::select::Either::First(msg) => match msg {
|
||||
embassy_sync::pubsub::WaitResult::Message(id) => {
|
||||
writer.write_event("msg", id.to_string().as_str()).await?
|
||||
writer
|
||||
.write_event("msg", tally_id_to_hex_string(id).unwrap().as_str())
|
||||
.await?
|
||||
}
|
||||
embassy_sync::pubsub::WaitResult::Lagged(_) => {
|
||||
warn!("SSE subscriber got lagged");
|
||||
|
||||
Reference in New Issue
Block a user