mirror of
https://github.com/Djeeberjr/fw-anwesenheit.git
synced 2026-04-30 18:49:09 +00:00
Compare commits
15 Commits
967da9fc30
...
f22eb91b67
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f22eb91b67 | ||
|
|
a04400a3a0 | ||
|
|
6f7561a295 | ||
|
|
279e2f7454 | ||
|
|
610840311f | ||
| 56d2dbfa7c | |||
| 35f12a4c45 | |||
| 7346fb42bd | |||
| c0bf8399a3 | |||
| 9852534dc6 | |||
| d63e9e964d | |||
| 16ea1db55f | |||
| a0ed04a560 | |||
|
|
4e988e8f01 | ||
| 009f6cbb2e |
925
Cargo.lock
generated
925
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
75
Cargo.toml
75
Cargo.toml
@@ -12,64 +12,45 @@ bench = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
esp-bootloader-esp-idf = "0.1.0"
|
esp-bootloader-esp-idf = "0.1.0"
|
||||||
embassy-net = { version = "0.7.0", features = [
|
esp-hal = { version = "1.0.0-rc.1", features = ["esp32c6", "unstable"] }
|
||||||
"dhcpv4",
|
esp-alloc = "0.9.0"
|
||||||
"medium-ethernet",
|
esp-println = { version = "0.16.0", features = ["esp32c6", "log-04"] }
|
||||||
"tcp",
|
esp-radio = { version = "0.16.0", features = ["esp32c6","esp-alloc", "wifi", "log-04", "smoltcp","unstable"]}
|
||||||
"udp",
|
esp-rtos = { version = "0.1.1", features = ["esp32c6", "embassy", "esp-radio", "esp-alloc"] }
|
||||||
] }
|
|
||||||
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",
|
|
||||||
] }
|
|
||||||
critical-section = "1.2.0"
|
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" }
|
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-dhcp = { version = "0.6.0", features = ["log"] }
|
||||||
edge-nal = "0.5.0"
|
edge-nal = "0.5.0"
|
||||||
edge-nal-embassy = { version = "0.6.0", features = ["log"] }
|
edge-nal-embassy = { version = "0.6.0", features = ["log"] }
|
||||||
|
|
||||||
picoserve = { git = "https://github.com/sammhicks/picoserve.git", rev = "400df53f61137e1bb2883ec610fc191bfe551a3a", features = ["embassy", "log", "json"] }
|
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"
|
dir-embed = "0.3.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", package = "esp-hal-smartled", branch = "main", features = ["esp32c6"]}
|
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"
|
smart-leds = "0.4.0"
|
||||||
serde = { version = "1.0.219", default-features = false, features = ["derive", "alloc"] }
|
|
||||||
embedded-sdmmc = "0.8.0"
|
embedded-sdmmc = "0.8.0"
|
||||||
embedded-hal-bus = "0.3.0"
|
embedded-hal-bus = "0.3.0"
|
||||||
serde_json = { version = "1.0.143", default-features = false, features = ["alloc"]}
|
thiserror = { version = "2.0.17", default-features = false }
|
||||||
embassy-futures = { version = "0.1.2", features = ["log"] }
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
# Rust debug is too slow.
|
# Rust debug is too slow.
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
pub mod nfc_reader;
|
pub mod nfc_reader;
|
||||||
pub mod rtc;
|
pub mod rtc;
|
||||||
pub mod buzzer;
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use log::debug;
|
|
||||||
use std::time::Duration;
|
|
||||||
use tokio::time::sleep;
|
|
||||||
|
|
||||||
use crate::hardware::{Buzzer, Hotspot, StatusLed};
|
|
||||||
|
|
||||||
pub struct MockBuzzer {}
|
|
||||||
|
|
||||||
impl Buzzer for MockBuzzer {
|
|
||||||
async fn modulated_tone(&mut self, frequency_hz: f64, duration: Duration) -> Result<()> {
|
|
||||||
debug!("MockBuzzer: modulte tone: {frequency_hz} Hz");
|
|
||||||
sleep(duration).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MockLed {}
|
|
||||||
|
|
||||||
impl StatusLed for MockLed {
|
|
||||||
fn turn_off(&mut self) -> Result<()> {
|
|
||||||
debug!("Turn mock LED off");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn turn_on(&mut self, color: rgb::RGB8) -> Result<()> {
|
|
||||||
debug!("Turn mock LED on to: {color}");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MockHotspot {}
|
|
||||||
|
|
||||||
impl Hotspot for MockHotspot {
|
|
||||||
async fn enable_hotspot(&self) -> Result<()> {
|
|
||||||
debug!("Mockhotspot: Enable hotspot");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn disable_hotspot(&self) -> Result<()> {
|
|
||||||
debug!("Mockhotspot: Disable hotspot");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
use ds3231::{
|
use ds3231::{
|
||||||
Config, DS3231, DS3231Error, InterruptControl, Oscillator, SquareWaveFrequency,
|
Config, DS3231, InterruptControl, Oscillator, SquareWaveFrequency, TimeRepresentation,
|
||||||
TimeRepresentation,
|
|
||||||
};
|
};
|
||||||
use esp_hal::{
|
use esp_hal::{
|
||||||
Async,
|
Async,
|
||||||
@@ -15,10 +14,6 @@ include!(concat!(env!("OUT_DIR"), "/build_time.rs"));
|
|||||||
|
|
||||||
const RTC_ADDRESS: u8 = 0x68;
|
const RTC_ADDRESS: u8 = 0x68;
|
||||||
|
|
||||||
const SECS_PER_DAY: u64 = 86_400;
|
|
||||||
const UNIX_OFFSET_DAYS: u64 = 719_163; // Days from 0000-03-01 to 1970-01-01
|
|
||||||
const UTC_PLUS_ONE: u64 = 3600;
|
|
||||||
|
|
||||||
pub struct RTCClock {
|
pub struct RTCClock {
|
||||||
dev: DS3231<I2c<'static, Async>>,
|
dev: DS3231<I2c<'static, Async>>,
|
||||||
}
|
}
|
||||||
@@ -34,10 +29,7 @@ impl RTCClock {
|
|||||||
|
|
||||||
pub async fn get_time(&mut self) -> u64 {
|
pub async fn get_time(&mut self) -> u64 {
|
||||||
match self.dev.datetime().await {
|
match self.dev.datetime().await {
|
||||||
Ok(datetime) => {
|
Ok(datetime) => datetime.and_utc().timestamp() as u64,
|
||||||
let utc_time = datetime.and_utc().timestamp() as u64;
|
|
||||||
utc_time
|
|
||||||
}
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
FEEDBACK_STATE.signal(feedback::FeedbackState::Error);
|
FEEDBACK_STATE.signal(feedback::FeedbackState::Error);
|
||||||
error!("Failed to read RTC datetime: {:?}", e);
|
error!("Failed to read RTC datetime: {:?}", e);
|
||||||
@@ -47,33 +39,6 @@ impl RTCClock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unix_to_ymd_string(timestamp: u64) -> (u16, u8, u8) {
|
|
||||||
// Apply UTC+1 offset
|
|
||||||
let ts = timestamp + UTC_PLUS_ONE;
|
|
||||||
|
|
||||||
// Convert to total days since UNIX epoch
|
|
||||||
let days_since_epoch = ts / SECS_PER_DAY;
|
|
||||||
|
|
||||||
// Convert to proleptic Gregorian date
|
|
||||||
civil_from_days(days_since_epoch as i64 + UNIX_OFFSET_DAYS as i64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function returns (year, month, day).
|
|
||||||
// Based on the algorithm by Howard Hinnant.
|
|
||||||
fn civil_from_days(z: i64) -> (u16, u8, u8) {
|
|
||||||
let mut z = z;
|
|
||||||
z -= 60; // shift epoch for algorithm
|
|
||||||
let era = (z >= 0).then_some(z).unwrap_or(z - 146096) / 146097;
|
|
||||||
let doe = z - era * 146097; // [0, 146096]
|
|
||||||
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // [0, 399]
|
|
||||||
let y = yoe + era * 400;
|
|
||||||
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365]
|
|
||||||
let mp = (5 * doy + 2) / 153; // [0, 11]
|
|
||||||
let d = doy - (153 * mp + 2) / 5 + 1; // [1, 31]
|
|
||||||
let m = mp + (if mp < 10 { 3 } else { -9 }); // [1, 12]
|
|
||||||
((y + (m <= 2) as i64) as u16, m as u8, d as u8)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn rtc_config(i2c: I2c<'static, Async>) -> DS3231<I2c<'static, Async>> {
|
pub async fn rtc_config(i2c: I2c<'static, Async>) -> DS3231<I2c<'static, Async>> {
|
||||||
let mut rtc: DS3231<I2c<'static, Async>> = DS3231::new(i2c, RTC_ADDRESS);
|
let mut rtc: DS3231<I2c<'static, Async>> = DS3231::new(i2c, RTC_ADDRESS);
|
||||||
let naive_dt = Utc
|
let naive_dt = Utc
|
||||||
@@ -99,11 +64,13 @@ pub async fn rtc_config(i2c: I2c<'static, Async>) -> DS3231<I2c<'static, Async>>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rtc.datetime().await.unwrap() < naive_dt {
|
||||||
rtc.set_datetime(&naive_dt).await.unwrap_or_else(|e| {
|
rtc.set_datetime(&naive_dt).await.unwrap_or_else(|e| {
|
||||||
FEEDBACK_STATE.signal(feedback::FeedbackState::Error);
|
FEEDBACK_STATE.signal(feedback::FeedbackState::Error);
|
||||||
error!("Failed to set RTC datetime: {:?}", e);
|
error!("Failed to set RTC datetime: {:?}", e);
|
||||||
});
|
});
|
||||||
info!("RTC datetime set to: {}", naive_dt);
|
info!("RTC datetime set to: {}", naive_dt);
|
||||||
|
}
|
||||||
|
|
||||||
match rtc.status().await {
|
match rtc.status().await {
|
||||||
Ok(mut status) => {
|
Ok(mut status) => {
|
||||||
@@ -116,13 +83,5 @@ pub async fn rtc_config(i2c: I2c<'static, Async>) -> DS3231<I2c<'static, Async>>
|
|||||||
}
|
}
|
||||||
Err(e) => info!("Failed to read status: {:?}", e),
|
Err(e) => info!("Failed to read status: {:?}", e),
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc
|
rtc
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_rtc_time<'a>(
|
|
||||||
rtc: &'a mut DS3231<I2c<'static, Async>>,
|
|
||||||
) -> Result<u64, DS3231Error<esp_hal::i2c::master::Error>> {
|
|
||||||
let timestamp_result = rtc.datetime().await?;
|
|
||||||
Ok(timestamp_result.and_utc().timestamp() as u64)
|
|
||||||
}
|
|
||||||
|
|||||||
116
src/feedback.rs
116
src/feedback.rs
@@ -1,11 +1,12 @@
|
|||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use esp_hal::{peripherals, rmt::ConstChannelAccess};
|
use esp_hal::gpio::Output;
|
||||||
use esp_hal_smartled::SmartLedsAdapterAsync;
|
use esp_hal_smartled::SmartLedsAdapterAsync;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use smart_leds::SmartLedsWriteAsync;
|
use smart_leds::SmartLedsWriteAsync;
|
||||||
use smart_leds::colors::{BLACK, GREEN, RED, YELLOW};
|
use smart_leds::colors::{BLACK, GREEN, RED, YELLOW};
|
||||||
use smart_leds::{brightness, colors::BLUE};
|
use smart_leds::{brightness, colors::BLUE};
|
||||||
|
|
||||||
|
use crate::init::hardware;
|
||||||
use crate::{FEEDBACK_STATE, init};
|
use crate::{FEEDBACK_STATE, init};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
@@ -24,14 +25,10 @@ const LED_LEVEL: u8 = 255;
|
|||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
pub async fn feedback_task(
|
pub async fn feedback_task(
|
||||||
mut led: SmartLedsAdapterAsync<
|
mut led: SmartLedsAdapterAsync<'static, { hardware::LED_BUFFER_SIZE }>,
|
||||||
ConstChannelAccess<esp_hal::rmt::Tx, 0>,
|
mut buzzer: Output<'static>,
|
||||||
{ init::hardware::LED_BUFFER_SIZE },
|
|
||||||
>,
|
|
||||||
buzzer: peripherals::GPIO21<'static>,
|
|
||||||
) {
|
) {
|
||||||
debug!("Starting feedback task");
|
debug!("Starting feedback task");
|
||||||
let mut buzzer = init::hardware::setup_buzzer(buzzer);
|
|
||||||
loop {
|
loop {
|
||||||
let feedback_state = FEEDBACK_STATE.wait().await;
|
let feedback_state = FEEDBACK_STATE.wait().await;
|
||||||
match feedback_state {
|
match feedback_state {
|
||||||
@@ -107,6 +104,8 @@ pub async fn feedback_task(
|
|||||||
buzzer.set_high();
|
buzzer.set_high();
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after(Duration::from_millis(100)).await;
|
||||||
buzzer.set_low();
|
buzzer.set_low();
|
||||||
|
|
||||||
|
Timer::after(Duration::from_secs(2)).await;
|
||||||
led.write(brightness(
|
led.write(brightness(
|
||||||
[BLACK; init::hardware::NUM_LEDS].into_iter(),
|
[BLACK; init::hardware::NUM_LEDS].into_iter(),
|
||||||
LED_LEVEL,
|
LED_LEVEL,
|
||||||
@@ -134,106 +133,3 @@ pub async fn feedback_task(
|
|||||||
debug!("Feedback state: {:?}", feedback_state);
|
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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
use core::cell::RefCell;
|
|
||||||
use critical_section::Mutex;
|
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_net::Stack;
|
use embassy_net::Stack;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use esp_hal::Blocking;
|
use esp_hal::Blocking;
|
||||||
use esp_hal::delay::Delay;
|
use esp_hal::delay::Delay;
|
||||||
use esp_hal::gpio::Input;
|
use esp_hal::gpio::AnyPin;
|
||||||
use esp_hal::i2c::master::Config;
|
use esp_hal::i2c::master::Config;
|
||||||
use esp_hal::peripherals::{
|
use esp_hal::peripherals::{
|
||||||
GPIO0, GPIO1, GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, GPIO23, I2C0, RMT, SPI2,
|
GPIO1, GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, GPIO23, I2C0, RMT, SPI2, UART1,
|
||||||
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::spi::master::{Config as Spi_config, Spi};
|
||||||
use esp_hal::system::software_reset;
|
use esp_hal::system::software_reset;
|
||||||
use esp_hal::time::Rate;
|
use esp_hal::time::Rate;
|
||||||
@@ -21,12 +18,15 @@ use esp_hal::{
|
|||||||
clock::CpuClock,
|
clock::CpuClock,
|
||||||
gpio::{Output, OutputConfig},
|
gpio::{Output, OutputConfig},
|
||||||
i2c::master::I2c,
|
i2c::master::I2c,
|
||||||
timer::systimer::SystemTimer,
|
|
||||||
uart::Uart,
|
uart::Uart,
|
||||||
};
|
};
|
||||||
use esp_hal_smartled::{SmartLedsAdapterAsync, buffer_size_async};
|
use esp_hal_smartled::{SmartLedsAdapterAsync, buffer_size_async};
|
||||||
use esp_println::logger::init_logger;
|
use esp_println::logger::init_logger;
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
|
use smart_leds::SmartLedsWriteAsync;
|
||||||
|
use smart_leds::brightness;
|
||||||
|
use smart_leds::colors::RED;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::init::network;
|
use crate::init::network;
|
||||||
use crate::init::sd_card::{SDCardPersistence, setup_sdcard};
|
use crate::init::sd_card::{SDCardPersistence, setup_sdcard};
|
||||||
@@ -49,10 +49,8 @@ use crate::init::wifi;
|
|||||||
*
|
*
|
||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
pub const NUM_LEDS: usize = 66;
|
pub const NUM_LEDS: usize = 1;
|
||||||
pub const LED_BUFFER_SIZE: usize = NUM_LEDS * 25;
|
pub const LED_BUFFER_SIZE: usize = buffer_size_async(NUM_LEDS);
|
||||||
|
|
||||||
static SD_DET: Mutex<RefCell<Option<Input>>> = Mutex::new(RefCell::new(None));
|
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
@@ -64,40 +62,67 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
|
|||||||
|
|
||||||
esp_bootloader_esp_idf::esp_app_desc!();
|
esp_bootloader_esp_idf::esp_app_desc!();
|
||||||
|
|
||||||
pub async fn hardware_init(
|
#[derive(Error, Debug)]
|
||||||
spawner: &mut Spawner,
|
pub enum HardwareInitError {
|
||||||
) -> (
|
#[error("Failed to etup UART")]
|
||||||
Uart<'static, Async>,
|
Uart(#[from] esp_hal::uart::ConfigError),
|
||||||
Stack<'static>,
|
|
||||||
I2c<'static, Async>,
|
#[error("Failed to setup I2C")]
|
||||||
SmartLedsAdapterAsync<ConstChannelAccess<esp_hal::rmt::Tx, 0>, LED_BUFFER_SIZE>,
|
I2C(#[from] esp_hal::i2c::master::ConfigError),
|
||||||
GPIO21<'static>,
|
|
||||||
GPIO0<'static>,
|
#[error("Failed to setup SPI")]
|
||||||
SDCardPersistence,
|
Spi(#[from] esp_hal::spi::master::ConfigError),
|
||||||
) {
|
|
||||||
|
#[error("Failed to setuo LED")]
|
||||||
|
Led(#[from] esp_hal::rmt::Error),
|
||||||
|
|
||||||
|
#[error("Failed to setup wifi")]
|
||||||
|
Wifi(#[from] wifi::WifiError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AppHardware {
|
||||||
|
pub uart: Uart<'static, Async>,
|
||||||
|
pub network_stack: Stack<'static>,
|
||||||
|
pub i2c: I2c<'static, Async>,
|
||||||
|
pub buzzer: Output<'static>,
|
||||||
|
pub sd_present: AnyPin<'static>,
|
||||||
|
pub led: SmartLedsAdapterAsync<'static, LED_BUFFER_SIZE>,
|
||||||
|
pub sdcard: SDCardPersistence,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppHardware {
|
||||||
|
pub async fn init(spawner: Spawner) -> Result<Self, HardwareInitError> {
|
||||||
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
|
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
|
||||||
let peripherals = esp_hal::init(config);
|
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);
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||||
esp_hal_embassy::init(timer0.alarm0);
|
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);
|
init_logger(log::LevelFilter::Debug);
|
||||||
|
|
||||||
let timer1 = TimerGroup::new(peripherals.TIMG0);
|
let mut led = setup_led(peripherals.RMT, peripherals.GPIO1)?;
|
||||||
let mut rng = esp_hal::rng::Rng::new(peripherals.RNG);
|
let _ = led.write(brightness(
|
||||||
|
[RED; NUM_LEDS].into_iter(),
|
||||||
|
255,
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let rng = esp_hal::rng::Rng::new();
|
||||||
let network_seed = (rng.random() as u64) << 32 | rng.random() as u64;
|
let network_seed = (rng.random() as u64) << 32 | rng.random() as u64;
|
||||||
|
|
||||||
wifi::set_antenna_mode(peripherals.GPIO3, peripherals.GPIO14).await;
|
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);
|
let network_stack = network::setup_network(network_seed, interfaces.ap, spawner);
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(1)).await;
|
Timer::after(Duration::from_millis(1)).await;
|
||||||
|
|
||||||
let uart_device = setup_uart(peripherals.UART1, peripherals.GPIO16, peripherals.GPIO17);
|
let uart_device = setup_uart(peripherals.UART1, peripherals.GPIO16, peripherals.GPIO17)?;
|
||||||
|
|
||||||
let i2c_device = setup_i2c(peripherals.I2C0, peripherals.GPIO22, peripherals.GPIO23);
|
let i2c_device = setup_i2c(peripherals.I2C0, peripherals.GPIO22, peripherals.GPIO23)?;
|
||||||
|
|
||||||
let sd_det_gpio = peripherals.GPIO0;
|
let sd_det_gpio = peripherals.GPIO0;
|
||||||
|
|
||||||
@@ -106,7 +131,7 @@ pub async fn hardware_init(
|
|||||||
peripherals.GPIO19,
|
peripherals.GPIO19,
|
||||||
peripherals.GPIO20,
|
peripherals.GPIO20,
|
||||||
peripherals.GPIO18,
|
peripherals.GPIO18,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
let sd_cs_pin = Output::new(
|
let sd_cs_pin = Output::new(
|
||||||
peripherals.GPIO2,
|
peripherals.GPIO2,
|
||||||
@@ -117,55 +142,44 @@ pub async fn hardware_init(
|
|||||||
let vol_mgr = setup_sdcard(spi_bus, sd_cs_pin);
|
let vol_mgr = setup_sdcard(spi_bus, sd_cs_pin);
|
||||||
|
|
||||||
let buzzer_gpio = peripherals.GPIO21;
|
let buzzer_gpio = peripherals.GPIO21;
|
||||||
|
let buzzer = setup_buzzer(buzzer_gpio);
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
|
||||||
let led = setup_led(peripherals.RMT, peripherals.GPIO1);
|
|
||||||
|
|
||||||
debug!("hardware init done");
|
debug!("hardware init done");
|
||||||
|
|
||||||
(
|
Ok(Self {
|
||||||
uart_device,
|
uart: uart_device,
|
||||||
stack,
|
network_stack,
|
||||||
i2c_device,
|
i2c: i2c_device,
|
||||||
|
buzzer,
|
||||||
|
sd_present: sd_det_gpio.into(),
|
||||||
led,
|
led,
|
||||||
buzzer_gpio,
|
sdcard: vol_mgr,
|
||||||
sd_det_gpio,
|
})
|
||||||
vol_mgr,
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_uart(
|
fn setup_uart(
|
||||||
uart1: UART1<'static>,
|
uart1: UART1<'static>,
|
||||||
uart_tx: GPIO16<'static>,
|
uart_tx: GPIO16<'static>,
|
||||||
uart_rx: GPIO17<'static>,
|
uart_rx: GPIO17<'static>,
|
||||||
) -> Uart<'static, Async> {
|
) -> Result<Uart<'static, Async>, esp_hal::uart::ConfigError> {
|
||||||
let uard_device = Uart::new(uart1, esp_hal::uart::Config::default().with_baudrate(9600));
|
let uart_device = Uart::new(uart1, esp_hal::uart::Config::default().with_baudrate(9600))?;
|
||||||
|
Ok(uart_device.with_rx(uart_rx).with_tx(uart_tx).into_async())
|
||||||
match uard_device {
|
|
||||||
Ok(block) => block.with_rx(uart_rx).with_tx(uart_tx).into_async(),
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to initialize UART: {e}");
|
|
||||||
panic!(); //TODO panic!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_i2c(
|
fn setup_i2c(
|
||||||
i2c0: I2C0<'static>,
|
i2c0: I2C0<'static>,
|
||||||
sda: GPIO22<'static>,
|
sda: GPIO22<'static>,
|
||||||
scl: GPIO23<'static>,
|
scl: GPIO23<'static>,
|
||||||
) -> I2c<'static, Async> {
|
) -> Result<I2c<'static, Async>, esp_hal::i2c::master::ConfigError> {
|
||||||
debug!("init I2C");
|
debug!("init I2C");
|
||||||
let config = Config::default().with_frequency(Rate::from_khz(400));
|
let config = Config::default().with_frequency(Rate::from_khz(400));
|
||||||
let i2c = match I2c::new(i2c0, config) {
|
|
||||||
Ok(i2c) => i2c.with_sda(sda).with_scl(scl).into_async(),
|
let i2c = I2c::new(i2c0, config)?;
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to initialize I2C: {:?}", e);
|
Ok(i2c.with_sda(sda).with_scl(scl).into_async())
|
||||||
panic!(); //TODO panic!
|
|
||||||
}
|
|
||||||
};
|
|
||||||
i2c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_spi(
|
fn setup_spi(
|
||||||
@@ -173,39 +187,34 @@ fn setup_spi(
|
|||||||
sck: GPIO19<'static>,
|
sck: GPIO19<'static>,
|
||||||
miso: GPIO20<'static>,
|
miso: GPIO20<'static>,
|
||||||
mosi: GPIO18<'static>,
|
mosi: GPIO18<'static>,
|
||||||
) -> Spi<'static, Blocking> {
|
) -> Result<Spi<'static, Blocking>, esp_hal::spi::master::ConfigError> {
|
||||||
let spi = match Spi::new(spi2, Spi_config::default()) {
|
let spi = Spi::new(spi2, Spi_config::default())?;
|
||||||
Ok(spi) => spi.with_sck(sck).with_miso(miso).with_mosi(mosi),
|
Ok(spi.with_sck(sck).with_miso(miso).with_mosi(mosi))
|
||||||
Err(e) => panic!("Failed to initialize SPI: {:?}", e),
|
|
||||||
};
|
|
||||||
spi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_buzzer(buzzer_gpio: GPIO21<'static>) -> Output<'static> {
|
pub fn setup_buzzer(buzzer_gpio: GPIO21<'static>) -> Output<'static> {
|
||||||
let config = esp_hal::gpio::OutputConfig::default()
|
let config = esp_hal::gpio::OutputConfig::default()
|
||||||
.with_drive_strength(esp_hal::gpio::DriveStrength::_40mA);
|
.with_drive_strength(esp_hal::gpio::DriveStrength::_40mA);
|
||||||
let buzzer = Output::new(buzzer_gpio, esp_hal::gpio::Level::Low, config);
|
|
||||||
|
|
||||||
buzzer
|
Output::new(buzzer_gpio, esp_hal::gpio::Level::Low, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_led(
|
fn setup_led<'a>(
|
||||||
rmt: RMT<'static>,
|
rmt: RMT<'a>,
|
||||||
led_gpio: GPIO1<'static>,
|
led_gpio: GPIO1<'a>,
|
||||||
) -> SmartLedsAdapterAsync<ConstChannelAccess<esp_hal::rmt::Tx, 0>, LED_BUFFER_SIZE> {
|
) -> Result<esp_hal_smartled::SmartLedsAdapterAsync<'a, LED_BUFFER_SIZE>, esp_hal::rmt::Error> {
|
||||||
debug!("setup led");
|
|
||||||
let rmt: Rmt<'_, esp_hal::Async> = {
|
let rmt: Rmt<'_, esp_hal::Async> = {
|
||||||
let frequency: Rate = Rate::from_mhz(80);
|
let frequency: Rate = Rate::from_mhz(80);
|
||||||
Rmt::new(rmt, frequency)
|
Rmt::new(rmt, frequency)
|
||||||
}
|
}?
|
||||||
.expect("Failed to initialize RMT")
|
|
||||||
.into_async();
|
.into_async();
|
||||||
|
|
||||||
let rmt_channel = rmt.channel0;
|
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> =
|
Ok(SmartLedsAdapterAsync::new(
|
||||||
SmartLedsAdapterAsync::new(rmt_channel, led_gpio, rmt_buffer);
|
rmt_channel,
|
||||||
|
led_gpio,
|
||||||
led
|
rmt_buffer,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,41 @@
|
|||||||
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 static_cell::make_static;
|
|
||||||
|
|
||||||
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> {
|
|
||||||
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 {
|
|
||||||
address: Ipv4Cidr::new(gw_ip_addr, 24),
|
|
||||||
gateway: Some(gw_ip_addr),
|
|
||||||
dns_servers: Default::default(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let (stack, runner) = embassy_net::new(
|
|
||||||
wifi,
|
|
||||||
config,
|
|
||||||
make_static!(StackResources::<NETWORK_STACK_SIZE>::new()),
|
|
||||||
seed,
|
|
||||||
);
|
|
||||||
|
|
||||||
spawner.must_spawn(net_task(runner));
|
|
||||||
spawner.must_spawn(run_dhcp(stack, gw_ip_addr_str));
|
|
||||||
|
|
||||||
stack
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
async fn run_dhcp(stack: Stack<'static>, gw_ip_addr: &'static str) {
|
|
||||||
use core::net::{Ipv4Addr, SocketAddrV4};
|
use core::net::{Ipv4Addr, SocketAddrV4};
|
||||||
|
|
||||||
use edge_dhcp::{
|
use edge_dhcp::{
|
||||||
io::{self, DEFAULT_SERVER_PORT},
|
io::{self, DEFAULT_SERVER_PORT},
|
||||||
server::{Server, ServerOptions},
|
server::{Server, ServerOptions},
|
||||||
};
|
};
|
||||||
use edge_nal::UdpBind;
|
use edge_nal::UdpBind;
|
||||||
use edge_nal_embassy::{Udp, UdpBuffers};
|
use edge_nal_embassy::{Udp, UdpBuffers};
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_net::{Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4};
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
use esp_radio::wifi::WifiDevice;
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
let ip = Ipv4Addr::from_str(gw_ip_addr).expect("dhcp task failed to parse gw ip");
|
use crate::webserver::WEB_TAKS_SIZE;
|
||||||
|
|
||||||
|
pub const NETWORK_STACK_SIZE: usize = WEB_TAKS_SIZE + 2; // + 2 for other network taks. Breaks without
|
||||||
|
pub const GW_IP: Ipv4Addr = Ipv4Addr::new(192, 168, 2, 1);
|
||||||
|
|
||||||
|
pub fn setup_network<'a>(seed: u64, wifi: WifiDevice<'static>, spawner: Spawner) -> Stack<'a> {
|
||||||
|
let config = embassy_net::Config::ipv4_static(StaticConfigV4 {
|
||||||
|
address: Ipv4Cidr::new(GW_IP, 24),
|
||||||
|
gateway: Some(GW_IP),
|
||||||
|
dns_servers: Default::default(),
|
||||||
|
});
|
||||||
|
|
||||||
|
static NETWORK_STACK: StaticCell<StackResources<NETWORK_STACK_SIZE>> = StaticCell::new();
|
||||||
|
let nw_stack = NETWORK_STACK.init(StackResources::new());
|
||||||
|
|
||||||
|
let (stack, runner) = embassy_net::new(wifi, config, nw_stack, seed);
|
||||||
|
|
||||||
|
spawner.must_spawn(net_task(runner));
|
||||||
|
spawner.must_spawn(run_dhcp(stack));
|
||||||
|
|
||||||
|
stack
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn run_dhcp(stack: Stack<'static>) {
|
||||||
let mut buf = [0u8; 1500];
|
let mut buf = [0u8; 1500];
|
||||||
|
|
||||||
let mut gw_buf = [Ipv4Addr::UNSPECIFIED];
|
let mut gw_buf = [Ipv4Addr::UNSPECIFIED];
|
||||||
@@ -57,12 +48,12 @@ async fn run_dhcp(stack: Stack<'static>, gw_ip_addr: &'static str) {
|
|||||||
DEFAULT_SERVER_PORT,
|
DEFAULT_SERVER_PORT,
|
||||||
)))
|
)))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.expect("Failed to bind socket for DHCP server");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
_ = io::server::run(
|
_ = io::server::run(
|
||||||
&mut Server::<_, 64>::new_with_et(ip),
|
&mut Server::<_, 64>::new_with_et(GW_IP),
|
||||||
&ServerOptions::new(ip, Some(&mut gw_buf)),
|
&ServerOptions::new(GW_IP, Some(&mut gw_buf)),
|
||||||
&mut bound_socket,
|
&mut bound_socket,
|
||||||
&mut buf,
|
&mut buf,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ use embassy_executor::Spawner;
|
|||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use esp_hal::gpio::{Output, OutputConfig};
|
use esp_hal::gpio::{Output, OutputConfig};
|
||||||
use esp_hal::peripherals::{GPIO3, GPIO14, WIFI};
|
use esp_hal::peripherals::{GPIO3, GPIO14, WIFI};
|
||||||
use esp_wifi::wifi::{
|
use esp_radio::Controller;
|
||||||
AccessPointConfiguration, Configuration, WifiController, WifiEvent, WifiState,
|
use esp_radio::wifi::{
|
||||||
|
AccessPointConfig, Interfaces, ModeConfig, WifiApState, WifiController, WifiEvent,
|
||||||
};
|
};
|
||||||
use esp_wifi::{EspWifiRngSource, EspWifiTimerSource, wifi::Interfaces};
|
use log::debug;
|
||||||
use static_cell::make_static;
|
use static_cell::StaticCell;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
pub async fn set_antenna_mode(gpio3: GPIO3<'static>, gpio14: GPIO14<'static>) {
|
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());
|
let mut rf_switch = Output::new(gpio3, esp_hal::gpio::Level::Low, OutputConfig::default());
|
||||||
@@ -20,41 +22,56 @@ pub async fn set_antenna_mode(gpio3: GPIO3<'static>, gpio14: GPIO14<'static>) {
|
|||||||
antenna_mode.set_low();
|
antenna_mode.set_low();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_wifi<'d: 'static>(
|
#[derive(Error, Debug)]
|
||||||
timer: impl EspWifiTimerSource + 'd,
|
pub enum WifiError {
|
||||||
rng: impl EspWifiRngSource + 'd,
|
#[error("Failed to init radio")]
|
||||||
wifi: WIFI<'static>,
|
Init(#[from] esp_radio::InitializationError),
|
||||||
spawner: &mut Spawner,
|
|
||||||
) -> Interfaces<'d> {
|
|
||||||
let esp_wifi_ctrl = make_static!(esp_wifi::init(timer, rng).unwrap());
|
|
||||||
|
|
||||||
let (controller, interfaces) = esp_wifi::wifi::new(esp_wifi_ctrl, wifi).unwrap();
|
#[error("Failed to init wifi")]
|
||||||
|
Wifi(#[from] esp_radio::wifi::WifiError),
|
||||||
|
|
||||||
spawner.must_spawn(connection(controller));
|
#[error("Failed to spawn wifi task")]
|
||||||
|
Spawn(#[from] embassy_executor::SpawnError),
|
||||||
interfaces
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setup_wifi<'d: 'static>(
|
||||||
|
wifi: WIFI<'static>,
|
||||||
|
spawner: Spawner,
|
||||||
|
) -> Result<Interfaces<'d>, WifiError> {
|
||||||
|
static ESP_WIFI_CTRL: StaticCell<Controller<'static>> = StaticCell::new();
|
||||||
|
|
||||||
|
let esp_wifi_ctrl = ESP_WIFI_CTRL.init(esp_radio::init()?);
|
||||||
|
|
||||||
|
let config = esp_radio::wifi::Config::default();
|
||||||
|
let (controller, interfaces) = esp_radio::wifi::new(esp_wifi_ctrl, wifi, config)?;
|
||||||
|
|
||||||
|
spawner.spawn(connection(controller))?;
|
||||||
|
|
||||||
|
Ok(interfaces)
|
||||||
|
}
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn connection(mut controller: WifiController<'static>) {
|
async fn connection(mut controller: WifiController<'static>) {
|
||||||
|
debug!("start connection task");
|
||||||
|
debug!("Device capabilities: {:?}", controller.capabilities());
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match esp_wifi::wifi::wifi_state() {
|
if esp_radio::wifi::ap_state() == WifiApState::Started {
|
||||||
WifiState::ApStarted => {
|
|
||||||
// wait until we're no longer connected
|
// wait until we're no longer connected
|
||||||
controller.wait_for_event(WifiEvent::ApStop).await;
|
controller.wait_for_event(WifiEvent::ApStop).await;
|
||||||
Timer::after(Duration::from_millis(5000)).await
|
Timer::after(Duration::from_millis(5000)).await
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
if !matches!(controller.is_started(), Ok(true)) {
|
if !matches!(controller.is_started(), Ok(true)) {
|
||||||
let client_config = Configuration::AccessPoint(AccessPointConfiguration {
|
let client_config = ModeConfig::AccessPoint(
|
||||||
ssid: env!("WIFI_SSID").try_into().unwrap(),
|
AccessPointConfig::default()
|
||||||
password: env!("WIFI_PASSWD").try_into().unwrap(),
|
.with_ssid(env!("WIFI_SSID").try_into().unwrap())
|
||||||
auth_method: esp_wifi::wifi::AuthMethod::WPA2Personal,
|
.with_password(env!("WIFI_PASSWD").try_into().unwrap())
|
||||||
..Default::default()
|
.with_auth_method(esp_radio::wifi::AuthMethod::Wpa2Personal),
|
||||||
});
|
);
|
||||||
controller.set_configuration(&client_config).unwrap();
|
controller.set_config(&client_config).unwrap();
|
||||||
|
debug!("Starting wifi");
|
||||||
controller.start_async().await.unwrap();
|
controller.start_async().await.unwrap();
|
||||||
|
debug!("Wifi started!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/main.rs
45
src/main.rs
@@ -2,7 +2,6 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
#![feature(impl_trait_in_assoc_type)]
|
#![feature(impl_trait_in_assoc_type)]
|
||||||
|
|
||||||
#![warn(clippy::unwrap_used)]
|
#![warn(clippy::unwrap_used)]
|
||||||
|
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
@@ -18,15 +17,15 @@ use embassy_sync::{
|
|||||||
signal::Signal,
|
signal::Signal,
|
||||||
};
|
};
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use esp_hal::gpio::Input;
|
use esp_hal::gpio::InputConfig;
|
||||||
use esp_hal::{gpio::InputConfig, peripherals};
|
use esp_hal::gpio::{AnyPin, Input};
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use static_cell::make_static;
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
init::sd_card::SDCardPersistence,
|
init::{hardware::AppHardware, sd_card::SDCardPersistence},
|
||||||
store::{IDStore, day::Day, tally_id::TallyID},
|
store::{IDStore, day::Day, tally_id::TallyID},
|
||||||
webserver::start_webserver,
|
webserver::start_webserver,
|
||||||
};
|
};
|
||||||
@@ -44,38 +43,48 @@ type TallyPublisher = Publisher<'static, NoopRawMutex, TallyID, 8, 2, 1>;
|
|||||||
type TallySubscriber = Subscriber<'static, NoopRawMutex, TallyID, 8, 2, 1>;
|
type TallySubscriber = Subscriber<'static, NoopRawMutex, TallyID, 8, 2, 1>;
|
||||||
type UsedStore = IDStore<SDCardPersistence>;
|
type UsedStore = IDStore<SDCardPersistence>;
|
||||||
|
|
||||||
#[esp_hal_embassy::main]
|
static CHAN: StaticCell<TallyChannel> = StaticCell::new();
|
||||||
async fn main(mut spawner: Spawner) {
|
|
||||||
let (uart_device, stack, i2c, led, buzzer_gpio, sd_det_gpio, persistence_layer) =
|
#[esp_rtos::main]
|
||||||
init::hardware::hardware_init(&mut spawner).await;
|
async fn main(spawner: Spawner) -> ! {
|
||||||
|
let app_hardware = AppHardware::init(spawner).await.unwrap();
|
||||||
|
|
||||||
info!("Starting up...");
|
info!("Starting up...");
|
||||||
|
|
||||||
let mut rtc = drivers::rtc::RTCClock::new(i2c).await;
|
let mut rtc = drivers::rtc::RTCClock::new(app_hardware.i2c).await;
|
||||||
|
|
||||||
let store: UsedStore = IDStore::new_from_storage(persistence_layer).await;
|
let current_day: Day = rtc.get_time().await.into();
|
||||||
|
let store: UsedStore = IDStore::new_from_storage(app_hardware.sdcard, current_day).await;
|
||||||
let shared_store = Rc::new(Mutex::new(store));
|
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 publisher: TallyPublisher = chan.publisher().unwrap();
|
||||||
let mut sub: TallySubscriber = chan.subscriber().unwrap();
|
let mut sub: TallySubscriber = chan.subscriber().unwrap();
|
||||||
|
|
||||||
wait_for_stack_up(stack).await;
|
wait_for_stack_up(app_hardware.network_stack).await;
|
||||||
|
|
||||||
start_webserver(&mut spawner, stack, shared_store.clone(), chan);
|
start_webserver(
|
||||||
|
spawner,
|
||||||
|
app_hardware.network_stack,
|
||||||
|
shared_store.clone(),
|
||||||
|
chan,
|
||||||
|
);
|
||||||
|
|
||||||
/****************************** Spawning tasks ***********************************/
|
/****************************** Spawning tasks ***********************************/
|
||||||
debug!("spawing NFC reader task...");
|
debug!("spawing NFC reader task...");
|
||||||
spawner.must_spawn(drivers::nfc_reader::rfid_reader_task(
|
spawner.must_spawn(drivers::nfc_reader::rfid_reader_task(
|
||||||
uart_device,
|
app_hardware.uart,
|
||||||
publisher,
|
publisher,
|
||||||
));
|
));
|
||||||
|
|
||||||
debug!("spawing feedback task..");
|
debug!("spawing feedback task..");
|
||||||
spawner.must_spawn(feedback::feedback_task(led, buzzer_gpio));
|
spawner.must_spawn(feedback::feedback_task(
|
||||||
|
app_hardware.led,
|
||||||
|
app_hardware.buzzer,
|
||||||
|
));
|
||||||
|
|
||||||
debug!("spawn sd detect task");
|
debug!("spawn sd detect task");
|
||||||
spawner.must_spawn(sd_detect_task(sd_det_gpio));
|
spawner.must_spawn(sd_detect_task(app_hardware.sd_present));
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
debug!("everything spawned");
|
debug!("everything spawned");
|
||||||
@@ -100,7 +109,7 @@ async fn main(mut spawner: Spawner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn sd_detect_task(sd_det_gpio: peripherals::GPIO0<'static>) {
|
async fn sd_detect_task(sd_det_gpio: AnyPin<'static>) {
|
||||||
let mut sd_det = Input::new(sd_det_gpio, InputConfig::default());
|
let mut sd_det = Input::new(sd_det_gpio, InputConfig::default());
|
||||||
sd_det.wait_for(esp_hal::gpio::Event::AnyEdge).await;
|
sd_det.wait_for(esp_hal::gpio::Event::AnyEdge).await;
|
||||||
|
|
||||||
|
|||||||
@@ -40,14 +40,12 @@ pub struct IDStore<T: Persistence> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Persistence> IDStore<T> {
|
impl<T: Persistence> IDStore<T> {
|
||||||
pub async fn new_from_storage(mut persistence_layer: T) -> Self {
|
pub async fn new_from_storage(mut persistence_layer: T, current_date: Day) -> Self {
|
||||||
let mapping = match persistence_layer.load_mapping().await {
|
let mapping = match persistence_layer.load_mapping().await {
|
||||||
Some(map) => map,
|
Some(map) => map,
|
||||||
None => IDMapping::new(),
|
None => IDMapping::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_date: Day = Day::new(1);
|
|
||||||
|
|
||||||
let day = persistence_layer
|
let day = persistence_layer
|
||||||
.load_day(current_date)
|
.load_day(current_date)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use esp_println::dbg;
|
|
||||||
use log::error;
|
use log::error;
|
||||||
use picoserve::{
|
use picoserve::{
|
||||||
extract::{Json, Query, State},
|
extract::{Json, Query, State},
|
||||||
@@ -7,7 +6,7 @@ use picoserve::{
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
store::{self, Name, day::Day, tally_id::TallyID},
|
store::{Name, day::Day, tally_id::TallyID},
|
||||||
webserver::{app::AppState, sse::IDEvents},
|
webserver::{app::AppState, sse::IDEvents},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ impl<State, CurrentPathParameters>
|
|||||||
);
|
);
|
||||||
|
|
||||||
response_writer
|
response_writer
|
||||||
.write_response(request.body_connection.finalize().await.unwrap(), response)
|
.write_response(request.body_connection.finalize().await?, response)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@@ -68,10 +68,7 @@ impl Content for StaticAsset {
|
|||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_content<W: embedded_io_async::Write>(
|
async fn write_content<W: edge_nal::io::Write>(self, mut writer: W) -> Result<(), W::Error> {
|
||||||
self,
|
|
||||||
mut writer: W,
|
|
||||||
) -> Result<(), W::Error> {
|
|
||||||
writer.write_all(self.0).await
|
writer.write_all(self.0).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ mod app;
|
|||||||
mod assets;
|
mod assets;
|
||||||
mod sse;
|
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(
|
pub fn start_webserver(
|
||||||
spawner: &mut Spawner,
|
spawner: Spawner,
|
||||||
stack: Stack<'static>,
|
stack: Stack<'static>,
|
||||||
store: Rc<Mutex<CriticalSectionRawMutex, UsedStore>>,
|
store: Rc<Mutex<CriticalSectionRawMutex, UsedStore>>,
|
||||||
chan: &'static TallyChannel,
|
chan: &'static TallyChannel,
|
||||||
|
|||||||
14
web/package-lock.json
generated
14
web/package-lock.json
generated
@@ -19,7 +19,7 @@
|
|||||||
"svelte": "^5.28.1",
|
"svelte": "^5.28.1",
|
||||||
"svelte-check": "^4.1.6",
|
"svelte-check": "^4.1.6",
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"vite": "^6.3.5"
|
"vite": "^6.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
@@ -771,6 +771,7 @@
|
|||||||
"integrity": "sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==",
|
"integrity": "sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sveltejs/vite-plugin-svelte-inspector": "^4.0.1",
|
"@sveltejs/vite-plugin-svelte-inspector": "^4.0.1",
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
@@ -1100,6 +1101,7 @@
|
|||||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -2211,6 +2213,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -2577,6 +2580,7 @@
|
|||||||
"integrity": "sha512-QIYtKnJGkubWXtNkrUBKVCvyo9gjcccdbnvXfwsGNhvbeNNdQjRDTa/BiQcJ2kWXbXPQbWKyT7CUu53KIj1rfw==",
|
"integrity": "sha512-QIYtKnJGkubWXtNkrUBKVCvyo9gjcccdbnvXfwsGNhvbeNNdQjRDTa/BiQcJ2kWXbXPQbWKyT7CUu53KIj1rfw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ampproject/remapping": "^2.3.0",
|
"@ampproject/remapping": "^2.3.0",
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||||
@@ -2700,6 +2704,7 @@
|
|||||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -2729,10 +2734,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.3.5",
|
"version": "6.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
|
||||||
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.4.4",
|
"fdir": "^6.4.4",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"svelte": "^5.28.1",
|
"svelte": "^5.28.1",
|
||||||
"svelte-check": "^4.1.6",
|
"svelte-check": "^4.1.6",
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"vite": "^6.3.5",
|
"vite": "^6.4.1",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
"express": "^5.1.0"
|
"express": "^5.1.0"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user