Firmware for the b-parasite board, but in Rust!
1use bt_hci::cmd::SyncCmd;
2use embassy_futures::join::join;
3use embassy_nrf::{mode, pac, rng};
4use embassy_time::{Duration, Timer};
5use nrf_mpsl::MultiprotocolServiceLayer;
6use nrf_sdc::vendor::ZephyrWriteBdAddr;
7use sachy_bthome::BtHomeAd;
8use sachy_fmt::{info, unwrap};
9use trouble_host::prelude::*;
10
11use crate::{
12 constants::{
13 PARA_ADV_DURATION_SECS, PARA_BLE_TX_POWER, PARA_MAX_ADV_INTERVAL_MS,
14 PARA_MIN_ADV_INTERVAL_MS, PARA_NAME,
15 },
16 state::{ADC_MEASUREMENT, SHTC3_MEASUREMENT, START_MEASUREMENTS},
17};
18
19#[embassy_executor::task]
20pub async fn mpsl_task(mpsl: &'static MultiprotocolServiceLayer<'static>) -> ! {
21 mpsl.run().await
22}
23
24pub fn build_sdc<'d, const N: usize>(
25 p: nrf_sdc::Peripherals<'d>,
26 rng: &'d mut rng::Rng<mode::Async>,
27 mpsl: &'d MultiprotocolServiceLayer,
28 mem: &'d mut nrf_sdc::Mem<N>,
29) -> Result<nrf_sdc::SoftdeviceController<'d>, nrf_sdc::Error> {
30 nrf_sdc::Builder::new()?
31 .support_adv()?
32 .build(p, rng, mpsl, mem)
33}
34
35fn build_addr() -> BdAddr {
36 let ficr = pac::FICR;
37 let high = u64::from(ficr.deviceid(1).read());
38 let addr = high << 32 | u64::from(ficr.deviceid(0).read());
39 let addr = addr | 0x0000_c000_0000_0000;
40 BdAddr::new(unwrap!(addr.to_le_bytes()[..6].try_into()))
41}
42
43#[embassy_executor::task]
44pub async fn run(controller: nrf_sdc::SoftdeviceController<'static>) {
45 let addr = build_addr();
46
47 info!("Our address = {:?}", &addr);
48
49 // Set the bluetooth address
50 unwrap!(ZephyrWriteBdAddr::new(addr).exec(&controller).await);
51
52 let mut resources: HostResources<DefaultPacketPool, 0, 0> = HostResources::new();
53 let stack = trouble_host::new(controller, &mut resources);
54 let Host {
55 mut peripheral,
56 mut runner,
57 ..
58 } = stack.build();
59
60 let _ = join(runner.run(), async {
61 let mut start_measurements = unwrap!(START_MEASUREMENTS.receiver());
62
63 let params: AdvertisementParameters = AdvertisementParameters {
64 interval_min: Duration::from_millis(PARA_MIN_ADV_INTERVAL_MS),
65 interval_max: Duration::from_millis(PARA_MAX_ADV_INTERVAL_MS),
66 tx_power: PARA_BLE_TX_POWER,
67 ..Default::default()
68 };
69
70 loop {
71 start_measurements.changed().await;
72
73 let (adc, shtc3) = join(ADC_MEASUREMENT.wait(), SHTC3_MEASUREMENT.wait()).await;
74
75 let mut ad = BtHomeAd::default();
76
77 let adv_data = ad
78 .add_data(adc.battery)
79 .add_data(shtc3.temperature)
80 .add_data(adc.lux)
81 .add_data(adc.voltage)
82 .add_data(shtc3.humidity)
83 .add_data(adc.moisture)
84 .add_local_name(PARA_NAME)
85 .encode();
86
87 info!("Starting advertising");
88 let advertiser = unwrap!(
89 peripheral
90 .advertise(
91 ¶ms,
92 Advertisement::NonconnectableScannableUndirected {
93 adv_data,
94 scan_data: &[],
95 },
96 )
97 .await
98 );
99 Timer::after_secs(PARA_ADV_DURATION_SECS).await;
100 drop(advertiser);
101 info!("Stopping advertising, sleeping...");
102 }
103 })
104 .await;
105}