retro-clock
by Grzegorz Krasoń
Development Process
- Experience ESP-IDF
- Multi-threaded implementation
- Distributed application
- User experience
- Security considerations
- Everything in Rust!
ESP32-S3 - WEMOS S3 Mini
- 2x LX7, FPU, 80-240 MHz
- RISC-V co-processor
- 4 MB Flash
- 512 KB SRAM
- 2.4 GHz Wi-Fi
- Bluetooth LE
- GPIO with PWM
- ADC (20 channels)
- RTC
- USB/JTAG
- SPI, I2C, I2S, UART...
Desired Features
-
World Time
- Auto-detect timezone
- Synchronize to NTP server
-
Weather Forecast
-
Current Weather
- Configurable through web UI
Requirements
- Display aligned to round seconds
- Controlled by a single button
- Busy & error indicators
- Buzzer imitating clicking sound
- Adaptive illumination
- AP mode for initial configuration
- Self-hosted web UI
- Resotres WiFi connection
- Energy-safe mode when on battery
Hardware Overview
Prototype
Data Flow
Dropping Channel
pub fn send(&self, obj: T) -> anyhow::Result<()> {
self.send_or_drop(obj)
}
fn send_or_drop(&self, obj: T) -> anyhow::Result<()> {
match self.tx.try_send(obj) {
Ok(()) => Ok(()),
Err(mpsc::TrySendError::Full(_obj)) => {
Ok(()) // drop the message
}
Err(mpsc::TrySendError::Disconnected(_)) => Err(
anyhow::anyhow!(
"Message sent to disconnected channel {}",
self.name
)
),
}
}
Blocking Channel
fn send_or_log_and_block(&self, obj: T) -> anyhow::Result<()> {
match self.tx.try_send(obj) {
Ok(()) => Ok(()),
Err(mpsc::TrySendError::Full(obj)) => {
let msg = format!(
"Channel reached its capacity: {}",
self.name
);
log::error!("{}", msg);
match self.tx.send(obj) {
Ok(()) => Ok(()),
Err(e) => Err(anyhow::anyhow!(
"Failed to send message to channel {}: {}",
self.name, e)),
}
}
Err(mpsc::TrySendError::Disconnected(_)) => Err(
anyhow::anyhow!(
"Message sent to disconnected channel {}",
self.name)),
}
}
Blocking Channel (for debugging)
pub fn send(&self, obj: T) -> anyhow::Result<()> {
#[cfg(not(feature = "panic_on_tx_overflow"))]
self.send_or_log_and_block(obj)?;
#[cfg(feature = "panic_on_tx_overflow")]
self.send_or_panic(obj)?;
Ok(())
}
fn send_or_panic(&self, obj: T) -> anyhow::Result<()> {
match self.tx.try_send(obj) {
Ok(()) => Ok(()),
Err(mpsc::TrySendError::Full(_obj)) => {
panic!(
"Channel reached its capacity: {}",
self.name)}
Err(mpsc::TrySendError::Disconnected(_)) => Err(
anyhow::anyhow!(
"Message sent to disconnected channel {}",
self.name)),
}
}
Platform Simulator