Something went wrong. Try again.
A RPi Pico powered Lightning Detector
Something went wrong. Try again.
4.7 kB · 163 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164#![allow(dead_code)]
use core::{ future::poll_fn, sync::atomic::{AtomicBool, Ordering}, task::Poll,};
use embassy_rp::{ peripherals, rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc},};use embassy_strike_driver::traits::TimeSource;use embassy_sync::{rwlock::RwLock, waitqueue::AtomicWaker};use embassy_time::Instant;use jiff::{ SignedDuration, Timestamp, tz::{Offset, TimeZone},};use sachy_sntp::SntpTimestamp;use static_cell::StaticCell;
use crate::{errors::PicoError, locks::RtcLock};
static WAKER: AtomicWaker = AtomicWaker::new();static ALARM_OCCURRED: AtomicBool = AtomicBool::new(false);
struct RtcInner<'device> { rtc: Rtc<'device, peripherals::RTC>, timestamp: Timestamp, offset: Instant,}
#[derive(Clone, Copy)]pub struct GlobalRtc<'device>(&'device RwLock<RtcLock, RtcInner<'device>>);
impl GlobalRtc<'static> { pub fn init(rtc: Rtc<'static, peripherals::RTC>) -> Self { static RTC: StaticCell<RwLock<RtcLock, RtcInner<'static>>> = StaticCell::new();
Self(&*RTC.init_with(|| { RwLock::new(RtcInner { rtc, timestamp: Default::default(), offset: Instant::MIN, }) })) }}
impl GlobalRtc<'_> { pub async fn is_ready(&self) -> bool { let inner = self.0.read().await;
inner.rtc.is_running() && inner.rtc.alarm_scheduled().is_some() }
async fn get_timestamp(&self) -> Timestamp { let inner = self.0.read().await;
let elapsed = inner.offset.elapsed().as_micros() as i64; inner.timestamp + SignedDuration::from_micros(elapsed) }
pub async fn track_time(&self) -> TimeTracker { TimeTracker { timestamp: self.get_timestamp().await, offset: Instant::now(), } }
pub async fn set_rtc_datetime(&self, timestamp: SntpTimestamp) -> Result<(), PicoError> { let timestamp = timestamp.try_to_unix_timestamp()?; let zoned = timestamp.to_zoned(TimeZone::fixed(Offset::UTC));
let mut inner = self.0.write().await; inner.offset = Instant::now(); inner.timestamp = timestamp; inner.rtc.set_datetime(DateTime { year: zoned.year() as u16, month: zoned.month() as u8, day: zoned.day() as u8, day_of_week: match zoned.weekday() { jiff::civil::Weekday::Monday => DayOfWeek::Monday, jiff::civil::Weekday::Tuesday => DayOfWeek::Tuesday, jiff::civil::Weekday::Wednesday => DayOfWeek::Wednesday, jiff::civil::Weekday::Thursday => DayOfWeek::Thursday, jiff::civil::Weekday::Friday => DayOfWeek::Friday, jiff::civil::Weekday::Saturday => DayOfWeek::Saturday, jiff::civil::Weekday::Sunday => DayOfWeek::Sunday, }, hour: zoned.hour() as u8, minute: zoned.minute() as u8, second: zoned.second() as u8, })?;
// Schedule the next alarm from now in order to reset the RTC // the next day on the same hour, on the hour. inner.rtc.schedule_alarm( DateTimeFilter::default() .hour(zoned.hour() as u8) .minute(0) .second(0), );
Ok(()) }
pub async fn wait_for_reset(&self) { poll_fn(|cx| { if critical_section::with(|_| { let outcome = ALARM_OCCURRED.load(Ordering::SeqCst);
if outcome { ALARM_OCCURRED.store(false, Ordering::SeqCst); } else { WAKER.register(cx.waker()); }
outcome }) { Poll::Ready(()) } else { Poll::Pending } }) .await;
let mut inner = self.0.write().await; inner.rtc.clear_interrupt(); }}
pub struct TimeTracker { timestamp: Timestamp, offset: Instant,}
impl TimeSource for TimeTracker { type Source = GlobalRtc<'static>;
#[inline] fn timestamp(&self) -> i64 { let elapsed = self.offset.elapsed().as_micros() as i64; (self.timestamp + SignedDuration::from_micros(elapsed)).as_microsecond() }
async fn reset_from_source(&mut self, source: Self::Source) { self.timestamp = source.get_timestamp().await; self.offset = Instant::now(); }}
pub struct GlobalRtcHandler;
impl embassy_rp::interrupt::typelevel::Handler<embassy_rp::interrupt::typelevel::RTC_IRQ> for GlobalRtcHandler{ unsafe fn on_interrupt() { ALARM_OCCURRED.store(true, Ordering::SeqCst); WAKER.wake(); }}