Skip to main content

clock_console/
clock_console.rs

1#![allow(missing_docs)]
2//! Console Clock - WiFi-synced time logging to console
3//!
4//! This example demonstrates WiFi connection with auto-provisioning
5//! and logs time sync events to the console.
6
7#![allow(missing_docs)]
8#![cfg(feature = "wifi")]
9#![no_std]
10#![no_main]
11#![allow(clippy::future_not_send, reason = "single-threaded")]
12
13use core::convert::Infallible;
14use defmt::info;
15use defmt_rtt as _;
16use device_envoy::button::PressedTo;
17use device_envoy::clock_sync::{ClockSync, ClockSyncStatic, ONE_SECOND};
18use device_envoy::flash_array::FlashArray;
19use device_envoy::wifi_auto::WifiAuto;
20use device_envoy::wifi_auto::WifiAutoEvent;
21use device_envoy::wifi_auto::fields::{TimezoneField, TimezoneFieldStatic};
22use device_envoy::{Error, Result};
23use embassy_executor::Spawner;
24use panic_probe as _;
25
26#[embassy_executor::main]
27pub async fn main(spawner: Spawner) -> ! {
28    let err = inner_main(spawner).await.unwrap_err();
29    core::panic!("{err}");
30}
31
32async fn inner_main(spawner: Spawner) -> Result<Infallible> {
33    info!("Starting Console Clock with WiFi");
34
35    // Initialize RP2040 peripherals
36    let p = embassy_rp::init(Default::default());
37
38    // Use two blocks of flash storage: Wi-Fi credentials + timezone
39    let [wifi_credentials_flash_block, timezone_flash_block] = FlashArray::<2>::new(p.FLASH)?;
40
41    // Define timezone field for captive portal
42    static TIMEZONE_FIELD_STATIC: TimezoneFieldStatic = TimezoneField::new_static();
43    let timezone_field = TimezoneField::new(&TIMEZONE_FIELD_STATIC, timezone_flash_block);
44
45    // Set up WiFi via captive portal
46    let wifi_auto = WifiAuto::new(
47        p.PIN_23,  // CYW43 power
48        p.PIN_24,  // CYW43 clock
49        p.PIN_25,  // CYW43 chip select
50        p.PIN_29,  // CYW43 data pin
51        p.PIO0,    // CYW43 PIO interface
52        p.DMA_CH0, // CYW43 DMA channel
53        wifi_credentials_flash_block,
54        p.PIN_13, // Reset button pin
55        PressedTo::Ground,
56        "www.picoclock.net",
57        [timezone_field],
58        spawner,
59    )?;
60
61    // Connect to WiFi
62    let (stack, _button) = wifi_auto
63        .connect(|event| async move {
64            match event {
65                WifiAutoEvent::CaptivePortalReady => {
66                    info!("Captive portal ready - connect to WiFi network");
67                }
68                WifiAutoEvent::Connecting {
69                    try_index,
70                    try_count,
71                } => {
72                    info!(
73                        "Connecting to WiFi (attempt {} of {})...",
74                        try_index + 1,
75                        try_count
76                    );
77                }
78                WifiAutoEvent::ConnectionFailed => {
79                    info!("WiFi connection failed!");
80                }
81            }
82            Ok(())
83        })
84        .await?;
85
86    info!("WiFi connected successfully!");
87
88    // Create ClockSync device with timezone from WiFi portal
89    let timezone_offset_minutes = timezone_field
90        .offset_minutes()?
91        .ok_or(Error::MissingCustomWifiAutoField)?;
92    static CLOCK_SYNC_STATIC: ClockSyncStatic = ClockSync::new_static();
93    let clock_sync = ClockSync::new(
94        &CLOCK_SYNC_STATIC,
95        stack,
96        timezone_offset_minutes,
97        Some(ONE_SECOND),
98        spawner,
99    );
100
101    info!("WiFi connected, entering event loop");
102
103    // Main event loop - log time on every tick
104    loop {
105        let tick = clock_sync.wait_for_tick().await;
106        let time_info = tick.local_time;
107        info!(
108            "Current time: {:04}-{:02}-{:02} {:02}:{:02}:{:02}",
109            time_info.year(),
110            u8::from(time_info.month()),
111            time_info.day(),
112            time_info.hour(),
113            time_info.minute(),
114            time_info.second(),
115        );
116    }
117}