Перейти до змісту

Успадкування часу від KeiraOS

Припустимо, прошивка запускається як .bin з Keira. У Keira вже є правильний час, і тягнути ще один Wi-Fi стек тільки заради дати файла якось не хочеться. Для цього в LilkaOxide є окремий feature keira-rtc.

Як це взагалі працює

MultiBoot записує застосунок в OTA-розділ і робить програмне перезавантаження. RTC ESP32-S3 при цьому продовжує рахувати час, а ESP-IDF залишає основу Unix-часу в регістрах STORE2/STORE3.

KeiraClock читає цей час через esp-hal, перевіряє допустимий діапазон і далі відраховує його від монотонного таймера. Він не викликає функції всередині Keira, не підключає ESP-IDF та нічого не записує в RTC.

Це не заміна синхронізації

Перед запуском потрібен правильний час у Keira: підключіться до Wi-Fi та дочекайтесь синхронізації її годинника. Після повного вимкнення живлення час може бути втрачений. Також важливі налаштування RTC у конкретній збірці Keira/ESP-IDF; передачу часу потрібно перевірити на пристрої.

Вмикаємо feature

До вже обраних features свого проєкту додаємо keira-rtc. Наприклад:

lilkaoxide = { path = "../../sdk-hal", default-features = false, features = [
    "blocking", "RefCellBus", "display_buffer_1024", "alloc", "keira-rtc",
] }

За замовчуванням цього feature немає. Сам він не вмикає alloc, async, radio чи додаткові залежності. alloc у цьому прикладі належить базовій конфігурації прошивки, а не RTC API.

Мінімальний виклик

Після Lilka::init(...):

use lilkaoxide::{hal::rtc_cntl::Rtc, keira_rtc::KeiraClock};

let rtc = Rtc::new(lilka.peripherals.LPWR);
match KeiraClock::inherit(&rtc) {
    Ok(clock) => {
        let seconds = clock.now_unix_seconds().expect("Clock out of range");
        esp_println::println!("UTC Unix time: {}", seconds);
    }
    Err(error) => {
        esp_println::println!("Time unavailable: {}", error);
    }
}

clock можна зберігати й опитувати протягом роботи програми. Після зняття початкового значення він вже не позичає rtc. Для async-прошивки відрізняється ініціалізація Lilka через .await, а саме RTC API залишається таким самим.

Що повертає API

  • KeiraClock::inherit(&rtc) — Result, а не безумовно правильний час.
  • now_unix_micros() — UTC timestamp у мікросекундах.
  • now_unix_seconds() — той самий час у цілих секундах.
  • RtcTimeError::NotSynchronized — початкове значення не потрапило в діапазон 2020–2107.
  • MonotonicWentBackwards та OutOfRange — порушення відліку або вихід за допустимі межі.

UTC і київський час — різні речі

SDK не додає дві чи три години й не тягне базу часових поясів. Для годинника або FAT-дат конвертуйте UTC вже в застосунку. Якщо потрібен часовий пояс, враховуйте не тільки зсув UTC, а й перехід на літній час. Для FAT реалізуйте embedded_sdmmc::TimeSource, який повертає вже потрібну календарну дату.

Перевірка діапазону відсікає незаданий годинник і очевидне сміття. Але вона не доводить, що час сьогодні правильний, або що його виставила саме Keira. Якщо інша ESP-IDF прошивка залишила правдоподібне, але неправильне значення, SDK цього сам не дізнається.

Повний приклад

examples/keira-clock/ показує UTC timestamp на дисплеї або підказує повернутись у Keira й синхронізувати годинник:

#![no_std]
#![no_main]

use core::fmt::Write;
use embedded_graphics::{
    mono_font::{MonoTextStyle, ascii::FONT_6X10},
    pixelcolor::Rgb565,
    prelude::*,
    text::{Baseline, Text},
};
use esp_println::{self as _, println};
use lilkaoxide::{keira_rtc::KeiraClock, prelude::*};

esp_bootloader_esp_idf::esp_app_desc!();
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
    println!("PANIC: {}", info);
    loop {
        core::hint::spin_loop();
    }
}
#[esp_hal::main]
fn main() -> ! {
    esp_alloc::heap_allocator!(size: 16 * 1024);
    let mut config = Configuration::default();
    config.buzzer_config.duty_pct = 0;
    let lilka = Lilka::init(config).expect("Lilka init failed");
    let rtc = esp_hal::rtc_cntl::Rtc::new(lilka.peripherals.LPWR);
    let clock = KeiraClock::inherit(&rtc);
    let mut display = lilka.display;
    let delay = esp_hal::delay::Delay::new();
    loop {
        let mut message: heapless::String<96> = heapless::String::new();
        match &clock {
            Ok(clock) => match clock.now_unix_seconds() {
                Ok(seconds) => {
                    let _ = write!(message, "UTC Unix seconds:\n{}", seconds);
                }
                Err(error) => {
                    let _ = write!(message, "Clock error:\n{}", error);
                }
            },
            Err(_) => {
                let _ = message.push_str(concat!(
                    "Time unavailable.\n",
                    "Sync clock in Keira via Wi-Fi,\n",
                    "then launch this .bin again."
                ));
            }
        }
        display.clear(Rgb565::BLACK).unwrap();
        Text::with_baseline(
            &message,
            Point::new(8, 24),
            MonoTextStyle::new(&FONT_6X10, Rgb565::CYAN),
            Baseline::Top,
        )
        .draw(&mut display)
        .unwrap();
        delay.delay_millis(1000);
    }
}

З цієї теки створіть .bin:

cargo +esp espflash save-image --release --chip esp32s3 keira-clock.bin

Після цього відкрийте його на microSD через Keira. Rustdoc API містить ті самі методи з їх типами та обмеженнями.