Add firmware build timestamp support for sensors; update detection workflows and UI accordingly.

This commit is contained in:
Kai Börnert
2026-04-27 16:46:24 +02:00
parent c04109a76c
commit e0b8acd55c
11 changed files with 204 additions and 33 deletions

View File

@@ -8,4 +8,15 @@ fn main() {
std::fs::write(out_dir.join("memory.x"), include_bytes!("memory.x")).unwrap();
println!("cargo:rustc-link-search={}", out_dir.display());
println!("cargo:rerun-if-changed=memory.x");
// Embed firmware build timestamp as minutes since Unix epoch (4 bytes, big-endian).
// Dropping sub-minute precision keeps it in 4 bytes for many years.
let build_seconds = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("System time before UNIX_EPOCH")
.as_secs();
let build_minutes = (build_seconds / 60) as u32;
let bytes = build_minutes.to_be_bytes();
std::fs::write(out_dir.join("build_minutes.bin"), bytes).unwrap();
println!("cargo:rerun-if-changed=build.rs");
}

View File

@@ -3,7 +3,7 @@
extern crate alloc;
use crate::hal::peripherals::CAN1;
use canapi::id::{plant_id, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET};
use canapi::id::{plant_id, FIRMWARE_BUILD_OFFSET, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET};
use canapi::SensorSlot;
use ch32_hal::adc::{Adc, SampleTime, ADC_MAX};
use ch32_hal::{pac};
@@ -47,6 +47,10 @@ static CAN_TX_CH: Channel<CriticalSectionRawMutex, CanFrame, 4> = Channel::new()
static BEACON: AtomicBool = AtomicBool::new(false);
/// Firmware build timestamp in minutes since Unix epoch, embedded at compile time.
const FIRMWARE_BUILD_MINUTES: u32 =
u32::from_be_bytes(*include_bytes!(concat!(env!("OUT_DIR"), "/build_minutes.bin")));
#[embassy_executor::main(entry = "qingke_rt::entry")]
async fn main(spawner: Spawner) {
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
@@ -111,6 +115,7 @@ async fn main(spawner: Spawner) {
}
let moisture_id = plant_id(MOISTURE_DATA_OFFSET, slot, addr as u16);
let identify_id = plant_id(IDENTIFY_CMD_OFFSET, slot, addr as u16);
let firmware_build_id = plant_id(FIRMWARE_BUILD_OFFSET, slot, addr as u16);
let standard_identify_id = StandardId::new(identify_id).unwrap();
//is any floating, or invalid addr (only 1-8 are valid)
@@ -269,8 +274,9 @@ async fn main(spawner: Spawner) {
// filter.get(0).unwrap().set(Id::Standard(standard_identify_id), Default::default());
// can.add_filter(filter);
let standard_moisture_id = StandardId::new(moisture_id).unwrap();
let standard_firmware_build_id = StandardId::new(firmware_build_id).unwrap();
spawner
.spawn(can_task(can,info, warn, standard_identify_id, standard_moisture_id))
.spawn(can_task(can, info, warn, standard_identify_id, standard_moisture_id, standard_firmware_build_id))
.unwrap();
// move Q output, LED, ADC and analog input into worker task
@@ -282,6 +288,7 @@ async fn main(spawner: Spawner) {
ain,
standard_moisture_id,
standard_identify_id,
standard_firmware_build_id,
))
.unwrap();
}
@@ -362,6 +369,7 @@ async fn can_task(
warn: &'static mut Output<'static>,
identify_id: StandardId,
moisture_id: StandardId,
firmware_build_id: StandardId,
) {
// Non-blocking beacon blink timing.
// We keep this inside the CAN task so it can't stall other tasks (like `worker`) with `await`s.
@@ -460,6 +468,7 @@ async fn worker(
mut ain: hal::peripherals::PA1,
moisture_id: StandardId,
identify_id: StandardId,
firmware_build_id: StandardId,
) {
// 555 emulation state: Q initially Low
let mut q_high = false;
@@ -540,6 +549,12 @@ async fn worker(
let moisture = CanFrame::new(moisture_id, &(freq_hz as u32).to_be_bytes()).unwrap();
CAN_TX_CH.send(moisture).await;
// Send firmware build timestamp after each measurement so the controller
// always has up-to-date build info without requiring an identify request.
if let Some(build_frame) = CanFrame::new(firmware_build_id, &FIRMWARE_BUILD_MINUTES.to_be_bytes()) {
CAN_TX_CH.send(build_frame).await;
}
}
}