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
    • Auto-detect coordinates
  • 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

System Overview

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