lora_things_plus/
main.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Board file for SparkFun LoRa Thing Plus - expLoRaBLE
6//!
7//! - <https://www.sparkfun.com/products/17506>
8//!
9//! A Semtech SX1262 is connected as a SPI slave to IOM3
10//! See <https://www.northernmechatronics.com/_files/ugd/3c68cb_764598422c704ed1b32400b047fc7651.pdf>
11//! and <https://www.northernmechatronics.com/nm180100> for details
12//!
13//! See <https://github.com/NorthernMechatronics/nmsdk/blob/master/bsp/nm180100evb/bsp_pins.src>
14//! and <https://cdn.sparkfun.com/assets/4/4/f/7/e/expLoRaBLE_Thing_Plus_schematic.pdf>
15//! for details on the pin break outs
16//!
17//! IOM0: Qwiic I2C
18//! IOM1: Not connected
19//! IOM2: Broken out SPI
20//! IOM3: Semtech SX1262
21//!     Apollo 3 Pin Number | Apollo 3 Name | SX1262 Pin Number | SX1262 Name | SX1262 Description
22//!                      H6 |       GPIO 36 |                19 |  NSS        | SPI slave select
23//!                      J6 |       GPIO 38 |                17 |  MOSI       | SPI slave input
24//!                      J5 |       GPIO 43 |                16 |  MISO       | SPI slave output
25//!                      H5 |       GPIO 42 |                18 |  SCK        | SPI clock input
26//!                      J8 |       GPIO 39 |                14 |  BUSY       | Radio busy indicator
27//!                      J9 |       GPIO 40 |                13 |  DIO1       | Multipurpose digital I/O
28//!                      H9 |       GPIO 47 |                6  |  DIO3       | Multipurpose digital I/O
29//!                      J7 |       GPIO 44 |                15 |  NRESET     | Radio reset signal, active low
30//! IOM4: Not connected
31//! IOM5: Pins used by UART0
32
33#![no_std]
34#![no_main]
35#![deny(missing_docs)]
36#![feature(custom_test_frameworks)]
37#![test_runner(test_runner)]
38#![reexport_test_harness_main = "test_main"]
39
40use apollo3::chip::Apollo3DefaultPeripherals;
41use capsules_core::virtualizers::virtual_alarm::MuxAlarm;
42use capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm;
43use components::bme280::Bme280Component;
44use components::ccs811::Ccs811Component;
45use kernel::capabilities;
46use kernel::component::Component;
47use kernel::hil::flash::HasClient;
48use kernel::hil::hasher::Hasher;
49use kernel::hil::i2c::I2CMaster;
50use kernel::hil::led::LedHigh;
51use kernel::hil::spi::SpiMaster;
52use kernel::hil::time::Counter;
53use kernel::platform::{KernelResources, SyscallDriverLookup};
54use kernel::process::ProcessArray;
55use kernel::scheduler::round_robin::RoundRobinSched;
56use kernel::{create_capability, debug, static_init};
57
58#[cfg(feature = "atecc508a")]
59use {
60    capsules_core::virtualizers::virtual_i2c::MuxI2C,
61    components::atecc508a::Atecc508aComponent,
62    kernel::hil::entropy::Entropy32,
63    kernel::hil::gpio::{Configure, Output},
64    kernel::hil::rng::Rng,
65};
66
67#[cfg(any(feature = "chirp_i2c_moisture", feature = "dfrobot_i2c_rainfall"))]
68use capsules_core::virtualizers::virtual_i2c::MuxI2C;
69
70/// Support routines for debugging I/O.
71pub mod io;
72
73#[cfg(test)]
74mod tests;
75
76// Number of concurrent processes this platform supports.
77const NUM_PROCS: usize = 4;
78
79/// Static variables used by io.rs.
80static mut PROCESSES: Option<&'static ProcessArray<NUM_PROCS>> = None;
81
82// Static reference to chip for panic dumps.
83static mut CHIP: Option<&'static apollo3::chip::Apollo3<Apollo3DefaultPeripherals>> = None;
84// Static reference to process printer for panic dumps.
85static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
86    None;
87
88// How should the kernel respond when a process faults.
89const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
90    capsules_system::process_policies::PanicFaultPolicy {};
91
92// Test access to the peripherals
93static mut PERIPHERALS: Option<&'static Apollo3DefaultPeripherals> = None;
94// Test access to board
95#[cfg(test)]
96static mut BOARD: Option<&'static kernel::Kernel> = None;
97// Test access to platform
98#[cfg(test)]
99static mut PLATFORM: Option<&'static LoRaThingsPlus> = None;
100// Test access to main loop capability
101#[cfg(test)]
102static mut MAIN_CAP: Option<&dyn kernel::capabilities::MainLoopCapability> = None;
103// Test access to alarm
104static mut ALARM: Option<&'static MuxAlarm<'static, apollo3::stimer::STimer<'static>>> = None;
105// Test access to sensors
106static mut BME280: Option<
107    &'static capsules_extra::bme280::Bme280<
108        'static,
109        capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
110    >,
111> = None;
112static mut CCS811: Option<&'static capsules_extra::ccs811::Ccs811<'static>> = None;
113#[cfg(feature = "atecc508a")]
114static mut ATECC508A: Option<&'static capsules_extra::atecc508a::Atecc508a<'static>> = None;
115
116kernel::stack_size! {0x1000}
117
118const LORA_SPI_DRIVER_NUM: usize = capsules_core::driver::NUM::LoRaPhySPI as usize;
119const LORA_GPIO_DRIVER_NUM: usize = capsules_core::driver::NUM::LoRaPhyGPIO as usize;
120
121type ChirpI2cMoistureType = components::chirp_i2c_moisture::ChirpI2cMoistureComponentType<
122    capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
123>;
124type DFRobotRainFallType = components::dfrobot_rainfall_sensor::DFRobotRainFallSensorComponentType<
125    capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm<
126        'static,
127        apollo3::stimer::STimer<'static>,
128    >,
129    capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
130>;
131type BME280Sensor = components::bme280::Bme280ComponentType<
132    capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
133>;
134
135type TemperatureDriver = components::temperature::TemperatureComponentType<BME280Sensor>;
136type HumidityDriver = components::humidity::HumidityComponentType<BME280Sensor>;
137
138#[cfg(feature = "atecc508a")]
139type Verifier = capsules_extra::atecc508a::Atecc508a<'static>;
140#[cfg(feature = "atecc508a")]
141type SignatureVerifyInMemoryKeys =
142    components::signature_verify_in_memory_keys::SignatureVerifyInMemoryKeysComponentType<
143        Verifier,
144        1,
145        64,
146        32,
147        64,
148    >;
149
150/// A structure representing this platform that holds references to all
151/// capsules for this platform.
152struct LoRaThingsPlus {
153    alarm: &'static capsules_core::alarm::AlarmDriver<
154        'static,
155        VirtualMuxAlarm<'static, apollo3::stimer::STimer<'static>>,
156    >,
157    led: &'static capsules_core::led::LedDriver<
158        'static,
159        LedHigh<'static, apollo3::gpio::GpioPin<'static>>,
160        1,
161    >,
162    gpio: &'static capsules_core::gpio::GPIO<'static, apollo3::gpio::GpioPin<'static>>,
163    console: &'static capsules_core::console::Console<'static>,
164    i2c_master:
165        &'static capsules_core::i2c_master::I2CMasterDriver<'static, apollo3::iom::Iom<'static>>,
166    external_spi_controller: &'static capsules_core::spi_controller::Spi<
167        'static,
168        capsules_core::virtualizers::virtual_spi::VirtualSpiMasterDevice<
169            'static,
170            apollo3::iom::Iom<'static>,
171        >,
172    >,
173    sx1262_spi_controller: &'static capsules_core::spi_controller::Spi<
174        'static,
175        capsules_core::virtualizers::virtual_spi::VirtualSpiMasterDevice<
176            'static,
177            apollo3::iom::Iom<'static>,
178        >,
179    >,
180    sx1262_gpio: &'static capsules_core::gpio::GPIO<'static, apollo3::gpio::GpioPin<'static>>,
181    temperature: &'static TemperatureDriver,
182    humidity: &'static HumidityDriver,
183    air_quality: &'static capsules_extra::air_quality::AirQualitySensor<'static>,
184    moisture: Option<&'static components::moisture::MoistureComponentType<ChirpI2cMoistureType>>,
185    rainfall: Option<&'static components::rainfall::RainFallComponentType<DFRobotRainFallType>>,
186    rng: Option<
187        &'static capsules_core::rng::RngDriver<
188            'static,
189            capsules_core::rng::Entropy32ToRandom<
190                'static,
191                capsules_extra::atecc508a::Atecc508a<'static>,
192            >,
193        >,
194    >,
195    scheduler: &'static RoundRobinSched<'static>,
196    systick: cortexm4::systick::SysTick,
197    kv_driver: &'static capsules_extra::kv_driver::KVStoreDriver<
198        'static,
199        capsules_extra::virtualizers::virtual_kv::VirtualKVPermissions<
200            'static,
201            capsules_extra::kv_store_permissions::KVStorePermissions<
202                'static,
203                capsules_extra::tickv_kv_store::TicKVKVStore<
204                    'static,
205                    capsules_extra::tickv::TicKVSystem<
206                        'static,
207                        capsules_core::virtualizers::virtual_flash::FlashUser<
208                            'static,
209                            apollo3::flashctrl::FlashCtrl<'static>,
210                        >,
211                        capsules_extra::sip_hash::SipHasher24<'static>,
212                        { apollo3::flashctrl::PAGE_SIZE },
213                    >,
214                    [u8; 8],
215                >,
216            >,
217        >,
218    >,
219}
220
221#[cfg(feature = "atecc508a")]
222fn atecc508a_wakeup() {
223    let peripherals = (unsafe { PERIPHERALS }).unwrap();
224
225    peripherals.gpio_port[6].make_output();
226    peripherals.gpio_port[6].clear();
227
228    // The ATECC508A requires the SDA line to be low for at least 60us
229    // to wake up.
230    for _i in 0..700 {
231        cortexm4::support::nop();
232    }
233
234    // Enable SDA and SCL for I2C (exposed via Qwiic)
235    let _ = &peripherals
236        .gpio_port
237        .enable_i2c(&peripherals.gpio_port[6], &peripherals.gpio_port[5]);
238}
239
240#[cfg(feature = "atecc508a")]
241unsafe fn setup_atecc508a(
242    board_kernel: &'static kernel::Kernel,
243    memory_allocation_cap: &dyn capabilities::MemoryAllocationCapability,
244    mux_i2c: &'static MuxI2C<'static, apollo3::iom::Iom<'static>>,
245) -> &'static capsules_core::rng::RngDriver<
246    'static,
247    capsules_core::rng::Entropy32ToRandom<'static, capsules_extra::atecc508a::Atecc508a<'static>>,
248> {
249    let atecc508a = Atecc508aComponent::new(mux_i2c, 0x60, atecc508a_wakeup).finalize(
250        components::atecc508a_component_static!(apollo3::iom::Iom<'static>),
251    );
252    ATECC508A = Some(atecc508a);
253
254    // Convert hardware RNG to the Random interface.
255    let entropy_to_random = static_init!(
256        capsules_core::rng::Entropy32ToRandom<
257            'static,
258            capsules_extra::atecc508a::Atecc508a<'static>,
259        >,
260        capsules_core::rng::Entropy32ToRandom::new(atecc508a)
261    );
262    atecc508a.set_client(entropy_to_random);
263    // Setup RNG for userspace
264    let rng_local = static_init!(
265        capsules_core::rng::RngDriver<
266            'static,
267            capsules_core::rng::Entropy32ToRandom<
268                'static,
269                capsules_extra::atecc508a::Atecc508a<'static>,
270            >,
271        >,
272        capsules_core::rng::RngDriver::new(
273            entropy_to_random,
274            board_kernel.create_grant(capsules_core::rng::DRIVER_NUM, memory_allocation_cap)
275        )
276    );
277    entropy_to_random.set_client(rng_local);
278
279    rng_local
280}
281
282#[cfg(feature = "chirp_i2c_moisture")]
283unsafe fn setup_chirp_i2c_moisture(
284    board_kernel: &'static kernel::Kernel,
285    _memory_allocation_cap: &dyn capabilities::MemoryAllocationCapability,
286    mux_i2c: &'static MuxI2C<'static, apollo3::iom::Iom<'static>>,
287) -> &'static components::moisture::MoistureComponentType<ChirpI2cMoistureType> {
288    let chirp_moisture =
289        components::chirp_i2c_moisture::ChirpI2cMoistureComponent::new(mux_i2c, 0x20).finalize(
290            components::chirp_i2c_moisture_component_static!(apollo3::iom::Iom<'static>),
291        );
292
293    let moisture = components::moisture::MoistureComponent::new(
294        board_kernel,
295        capsules_extra::moisture::DRIVER_NUM,
296        chirp_moisture,
297    )
298    .finalize(components::moisture_component_static!(ChirpI2cMoistureType));
299
300    moisture
301}
302
303#[cfg(feature = "dfrobot_i2c_rainfall")]
304unsafe fn setup_dfrobot_i2c_rainfall(
305    board_kernel: &'static kernel::Kernel,
306    _memory_allocation_cap: &dyn capabilities::MemoryAllocationCapability,
307    mux_i2c: &'static MuxI2C<'static, apollo3::iom::Iom<'static>>,
308    mux_alarm: &'static MuxAlarm<'static, apollo3::stimer::STimer<'static>>,
309) -> &'static components::rainfall::RainFallComponentType<DFRobotRainFallType> {
310    let dfrobot_rainfall =
311        components::dfrobot_rainfall_sensor::DFRobotRainFallSensorComponent::new(
312            mux_i2c, 0x1D, mux_alarm,
313        )
314        .finalize(components::dfrobot_rainfall_sensor_component_static!(
315            apollo3::stimer::STimer<'static>,
316            apollo3::iom::Iom<'static>
317        ));
318
319    let rainfall = components::rainfall::RainFallComponent::new(
320        board_kernel,
321        capsules_extra::rainfall::DRIVER_NUM,
322        dfrobot_rainfall,
323    )
324    .finalize(components::rainfall_component_static!(DFRobotRainFallType));
325
326    rainfall
327}
328
329/// Mapping of integer syscalls to objects that implement syscalls.
330impl SyscallDriverLookup for LoRaThingsPlus {
331    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
332    where
333        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
334    {
335        match driver_num {
336            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
337            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
338            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
339            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
340            capsules_core::i2c_master::DRIVER_NUM => f(Some(self.i2c_master)),
341            capsules_core::spi_controller::DRIVER_NUM => f(Some(self.external_spi_controller)),
342            LORA_SPI_DRIVER_NUM => f(Some(self.sx1262_spi_controller)),
343            LORA_GPIO_DRIVER_NUM => f(Some(self.sx1262_gpio)),
344            capsules_extra::temperature::DRIVER_NUM => f(Some(self.temperature)),
345            capsules_extra::humidity::DRIVER_NUM => f(Some(self.humidity)),
346            capsules_extra::air_quality::DRIVER_NUM => f(Some(self.air_quality)),
347            capsules_extra::kv_driver::DRIVER_NUM => f(Some(self.kv_driver)),
348            capsules_core::rng::DRIVER_NUM => {
349                if let Some(rng) = self.rng {
350                    f(Some(rng))
351                } else {
352                    f(None)
353                }
354            }
355            capsules_extra::moisture::DRIVER_NUM => {
356                if let Some(moisture) = self.moisture {
357                    f(Some(moisture))
358                } else {
359                    f(None)
360                }
361            }
362            capsules_extra::rainfall::DRIVER_NUM => {
363                if let Some(rainfall) = self.rainfall {
364                    f(Some(rainfall))
365                } else {
366                    f(None)
367                }
368            }
369            _ => f(None),
370        }
371    }
372}
373
374impl KernelResources<apollo3::chip::Apollo3<Apollo3DefaultPeripherals>> for LoRaThingsPlus {
375    type SyscallDriverLookup = Self;
376    type SyscallFilter = ();
377    type ProcessFault = ();
378    type Scheduler = RoundRobinSched<'static>;
379    type SchedulerTimer = cortexm4::systick::SysTick;
380    type WatchDog = ();
381    type ContextSwitchCallback = ();
382
383    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
384        self
385    }
386    fn syscall_filter(&self) -> &Self::SyscallFilter {
387        &()
388    }
389    fn process_fault(&self) -> &Self::ProcessFault {
390        &()
391    }
392    fn scheduler(&self) -> &Self::Scheduler {
393        self.scheduler
394    }
395    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
396        &self.systick
397    }
398    fn watchdog(&self) -> &Self::WatchDog {
399        &()
400    }
401    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
402        &()
403    }
404}
405
406// Ensure that `setup()` is never inlined
407// This helps reduce the stack frame, see https://github.com/tock/tock/issues/3518
408#[inline(never)]
409unsafe fn setup() -> (
410    &'static kernel::Kernel,
411    &'static LoRaThingsPlus,
412    &'static apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
413) {
414    let peripherals = static_init!(Apollo3DefaultPeripherals, Apollo3DefaultPeripherals::new());
415    PERIPHERALS = Some(peripherals);
416
417    // No need to statically allocate mcu/pwr/clk_ctrl because they are only used in main!
418    let mcu_ctrl = apollo3::mcuctrl::McuCtrl::new();
419    let pwr_ctrl = apollo3::pwrctrl::PwrCtrl::new();
420    let clkgen = apollo3::clkgen::ClkGen::new();
421
422    clkgen.set_clock_frequency(apollo3::clkgen::ClockFrequency::Freq48MHz);
423
424    // initialize capabilities
425    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
426
427    // Create an array to hold process references.
428    let processes = components::process_array::ProcessArrayComponent::new()
429        .finalize(components::process_array_component_static!(NUM_PROCS));
430    PROCESSES = Some(processes);
431
432    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
433
434    // Power up components
435    pwr_ctrl.enable_uart0();
436    pwr_ctrl.enable_iom0();
437    pwr_ctrl.enable_iom2();
438    pwr_ctrl.enable_iom3();
439
440    peripherals.init();
441
442    // Enable PinCfg
443    peripherals
444        .gpio_port
445        .enable_uart(&peripherals.gpio_port[48], &peripherals.gpio_port[49]);
446    // Enable Main SPI
447    peripherals.gpio_port.enable_spi(
448        &peripherals.gpio_port[27],
449        &peripherals.gpio_port[28],
450        &peripherals.gpio_port[25],
451    );
452    // Enable SPI for SX1262
453    peripherals.gpio_port.enable_spi(
454        &peripherals.gpio_port[42],
455        &peripherals.gpio_port[38],
456        &peripherals.gpio_port[43],
457    );
458    // Enable the radio pins
459    peripherals.gpio_port.enable_sx1262_radio_pins();
460
461    // Configure kernel debug gpios as early as possible
462    kernel::debug::assign_gpios(Some(&peripherals.gpio_port[26]), None, None);
463
464    // Create a shared UART channel for the console and for kernel debug.
465    let uart_mux = components::console::UartMuxComponent::new(&peripherals.uart0, 115200)
466        .finalize(components::uart_mux_component_static!());
467
468    // Setup the console.
469    let console = components::console::ConsoleComponent::new(
470        board_kernel,
471        capsules_core::console::DRIVER_NUM,
472        uart_mux,
473    )
474    .finalize(components::console_component_static!());
475    // Create the debugger object that handles calls to `debug!()`.
476    components::debug_writer::DebugWriterComponent::new(
477        uart_mux,
478        create_capability!(capabilities::SetDebugWriterCapability),
479    )
480    .finalize(components::debug_writer_component_static!());
481
482    // LEDs
483    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
484        LedHigh<'static, apollo3::gpio::GpioPin>,
485        LedHigh::new(&peripherals.gpio_port[19]),
486    ));
487
488    // GPIOs
489    // Details are at: https://github.com/NorthernMechatronics/nmsdk/blob/master/bsp/nm180100evb/bsp_pins.src
490    let gpio = components::gpio::GpioComponent::new(
491        board_kernel,
492        capsules_core::gpio::DRIVER_NUM,
493        components::gpio_component_helper!(
494            apollo3::gpio::GpioPin,
495            0 => &peripherals.gpio_port[13],  // A0
496            1 => &peripherals.gpio_port[12],  // A1
497            2 => &peripherals.gpio_port[32],  // A2
498            3 => &peripherals.gpio_port[35],  // A3
499            4 => &peripherals.gpio_port[34],  // A4
500        ),
501    )
502    .finalize(components::gpio_component_static!(apollo3::gpio::GpioPin));
503
504    // Create a shared virtualisation mux layer on top of a single hardware
505    // alarm.
506    let _ = peripherals.stimer.start();
507    let mux_alarm = components::alarm::AlarmMuxComponent::new(&peripherals.stimer).finalize(
508        components::alarm_mux_component_static!(apollo3::stimer::STimer),
509    );
510    let alarm = components::alarm::AlarmDriverComponent::new(
511        board_kernel,
512        capsules_core::alarm::DRIVER_NUM,
513        mux_alarm,
514    )
515    .finalize(components::alarm_component_static!(apollo3::stimer::STimer));
516    ALARM = Some(mux_alarm);
517
518    // Create a process printer for panic.
519    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
520        .finalize(components::process_printer_text_component_static!());
521    PROCESS_PRINTER = Some(process_printer);
522
523    // Enable SDA and SCL for I2C (exposed via Qwiic)
524    peripherals
525        .gpio_port
526        .enable_i2c(&peripherals.gpio_port[6], &peripherals.gpio_port[5]);
527
528    // Init the I2C device attached via Qwiic
529    let i2c_master_buffer = static_init!(
530        [u8; capsules_core::i2c_master::BUFFER_LENGTH],
531        [0; capsules_core::i2c_master::BUFFER_LENGTH]
532    );
533    let i2c_master = static_init!(
534        capsules_core::i2c_master::I2CMasterDriver<'static, apollo3::iom::Iom<'static>>,
535        capsules_core::i2c_master::I2CMasterDriver::new(
536            &peripherals.iom0,
537            i2c_master_buffer,
538            board_kernel.create_grant(
539                capsules_core::i2c_master::DRIVER_NUM,
540                &memory_allocation_cap
541            )
542        )
543    );
544
545    peripherals.iom0.set_master_client(i2c_master);
546    peripherals.iom0.enable();
547
548    let mux_i2c = components::i2c::I2CMuxComponent::new(&peripherals.iom0, None).finalize(
549        components::i2c_mux_component_static!(apollo3::iom::Iom<'static>),
550    );
551
552    let bme280 = Bme280Component::new(mux_i2c, 0x77).finalize(
553        components::bme280_component_static!(apollo3::iom::Iom<'static>),
554    );
555    let temperature = components::temperature::TemperatureComponent::new(
556        board_kernel,
557        capsules_extra::temperature::DRIVER_NUM,
558        bme280,
559    )
560    .finalize(components::temperature_component_static!(BME280Sensor));
561    let humidity = components::humidity::HumidityComponent::new(
562        board_kernel,
563        capsules_extra::humidity::DRIVER_NUM,
564        bme280,
565    )
566    .finalize(components::humidity_component_static!(BME280Sensor));
567    BME280 = Some(bme280);
568
569    let ccs811 = Ccs811Component::new(mux_i2c, 0x5B).finalize(
570        components::ccs811_component_static!(apollo3::iom::Iom<'static>),
571    );
572    let air_quality = components::air_quality::AirQualityComponent::new(
573        board_kernel,
574        capsules_extra::temperature::DRIVER_NUM,
575        ccs811,
576    )
577    .finalize(components::air_quality_component_static!());
578    CCS811 = Some(ccs811);
579
580    #[cfg(feature = "chirp_i2c_moisture")]
581    let moisture = Some(setup_chirp_i2c_moisture(
582        board_kernel,
583        &memory_allocation_cap,
584        mux_i2c,
585    ));
586    #[cfg(not(feature = "chirp_i2c_moisture"))]
587    let moisture = None;
588
589    #[cfg(feature = "dfrobot_i2c_rainfall")]
590    let rainfall = Some(setup_dfrobot_i2c_rainfall(
591        board_kernel,
592        &memory_allocation_cap,
593        mux_i2c,
594        mux_alarm,
595    ));
596    #[cfg(not(feature = "dfrobot_i2c_rainfall"))]
597    let rainfall = None;
598
599    #[cfg(feature = "atecc508a")]
600    let rng = Some(setup_atecc508a(
601        board_kernel,
602        &memory_allocation_cap,
603        mux_i2c,
604    ));
605    #[cfg(not(feature = "atecc508a"))]
606    let rng = None;
607
608    // Init the broken out SPI controller
609    let external_mux_spi = components::spi::SpiMuxComponent::new(&peripherals.iom2).finalize(
610        components::spi_mux_component_static!(apollo3::iom::Iom<'static>),
611    );
612
613    let external_spi_controller = components::spi::SpiSyscallComponent::new(
614        board_kernel,
615        external_mux_spi,
616        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
617            &peripherals.gpio_port[11], // A5
618        ),
619        capsules_core::spi_controller::DRIVER_NUM,
620    )
621    .finalize(components::spi_syscall_component_static!(
622        apollo3::iom::Iom<'static>
623    ));
624
625    // Init the internal SX1262 SPI controller
626    let sx1262_mux_spi = components::spi::SpiMuxComponent::new(&peripherals.iom3).finalize(
627        components::spi_mux_component_static!(apollo3::iom::Iom<'static>),
628    );
629
630    let sx1262_spi_controller = components::spi::SpiSyscallComponent::new(
631        board_kernel,
632        sx1262_mux_spi,
633        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
634            &peripherals.gpio_port[36], // H6 - SX1262 Slave Select
635        ),
636        LORA_SPI_DRIVER_NUM,
637    )
638    .finalize(components::spi_syscall_component_static!(
639        apollo3::iom::Iom<'static>
640    ));
641    peripherals
642        .iom3
643        .specify_chip_select(kernel::hil::spi::cs::IntoChipSelect::<
644            _,
645            kernel::hil::spi::cs::ActiveLow,
646        >::into_cs(
647            &peripherals.gpio_port[36], // H6 - SX1262 Slave Select
648        ))
649        .unwrap();
650
651    let sx1262_gpio = components::gpio::GpioComponent::new(
652        board_kernel,
653        LORA_GPIO_DRIVER_NUM,
654        components::gpio_component_helper!(
655            apollo3::gpio::GpioPin,
656            0 => &peripherals.gpio_port[36], // H6 - SX1262 Slave Select
657            1 => &peripherals.gpio_port[39], // J8 - SX1262 Radio Busy Indicator
658            2 => &peripherals.gpio_port[40], // J9 - SX1262 Multipurpose digital I/O (DIO1)
659            3 => &peripherals.gpio_port[47], // H9 - SX1262 Multipurpose digital I/O (DIO3)
660            4 => &peripherals.gpio_port[44], // J7 - SX1262 Reset
661        ),
662    )
663    .finalize(components::gpio_component_static!(apollo3::gpio::GpioPin));
664
665    // Setup BLE
666    mcu_ctrl.disable_ble();
667
668    // Flash
669    let flash_ctrl_read_buf = static_init!(
670        [u8; apollo3::flashctrl::PAGE_SIZE],
671        [0; apollo3::flashctrl::PAGE_SIZE]
672    );
673    let page_buffer = static_init!(
674        apollo3::flashctrl::Apollo3Page,
675        apollo3::flashctrl::Apollo3Page::default()
676    );
677
678    let mux_flash = components::flash::FlashMuxComponent::new(&peripherals.flash_ctrl).finalize(
679        components::flash_mux_component_static!(apollo3::flashctrl::FlashCtrl),
680    );
681
682    // SipHash
683    let sip_hash = static_init!(
684        capsules_extra::sip_hash::SipHasher24,
685        capsules_extra::sip_hash::SipHasher24::new()
686    );
687    kernel::deferred_call::DeferredCallClient::register(sip_hash);
688
689    // TicKV
690    let tickv = components::tickv::TicKVComponent::new(
691        sip_hash,
692        mux_flash, // Flash controller
693        core::ptr::addr_of!(_skv_data) as usize / apollo3::flashctrl::PAGE_SIZE, // Region offset (Last 0x28000 bytes of flash)
694        // Region Size, the final page doens't work correctly
695        core::ptr::addr_of!(_lkv_data) as usize - apollo3::flashctrl::PAGE_SIZE,
696        flash_ctrl_read_buf, // Buffer used internally in TicKV
697        page_buffer,         // Buffer used with the flash controller
698    )
699    .finalize(components::tickv_component_static!(
700        apollo3::flashctrl::FlashCtrl,
701        capsules_extra::sip_hash::SipHasher24,
702        { apollo3::flashctrl::PAGE_SIZE }
703    ));
704    HasClient::set_client(&peripherals.flash_ctrl, mux_flash);
705    sip_hash.set_client(tickv);
706
707    let kv_store = components::kv::TicKVKVStoreComponent::new(tickv).finalize(
708        components::tickv_kv_store_component_static!(
709            capsules_extra::tickv::TicKVSystem<
710                capsules_core::virtualizers::virtual_flash::FlashUser<
711                    apollo3::flashctrl::FlashCtrl,
712                >,
713                capsules_extra::sip_hash::SipHasher24<'static>,
714                { apollo3::flashctrl::PAGE_SIZE },
715            >,
716            capsules_extra::tickv::TicKVKeyType,
717        ),
718    );
719
720    let kv_store_permissions = components::kv::KVStorePermissionsComponent::new(kv_store).finalize(
721        components::kv_store_permissions_component_static!(
722            capsules_extra::tickv_kv_store::TicKVKVStore<
723                capsules_extra::tickv::TicKVSystem<
724                    capsules_core::virtualizers::virtual_flash::FlashUser<
725                        apollo3::flashctrl::FlashCtrl,
726                    >,
727                    capsules_extra::sip_hash::SipHasher24<'static>,
728                    { apollo3::flashctrl::PAGE_SIZE },
729                >,
730                capsules_extra::tickv::TicKVKeyType,
731            >
732        ),
733    );
734
735    let mux_kv = components::kv::KVPermissionsMuxComponent::new(kv_store_permissions).finalize(
736        components::kv_permissions_mux_component_static!(
737            capsules_extra::kv_store_permissions::KVStorePermissions<
738                capsules_extra::tickv_kv_store::TicKVKVStore<
739                    capsules_extra::tickv::TicKVSystem<
740                        capsules_core::virtualizers::virtual_flash::FlashUser<
741                            apollo3::flashctrl::FlashCtrl,
742                        >,
743                        capsules_extra::sip_hash::SipHasher24<'static>,
744                        { apollo3::flashctrl::PAGE_SIZE },
745                    >,
746                    capsules_extra::tickv::TicKVKeyType,
747                >,
748            >
749        ),
750    );
751
752    let virtual_kv_driver = components::kv::VirtualKVPermissionsComponent::new(mux_kv).finalize(
753        components::virtual_kv_permissions_component_static!(
754            capsules_extra::kv_store_permissions::KVStorePermissions<
755                capsules_extra::tickv_kv_store::TicKVKVStore<
756                    capsules_extra::tickv::TicKVSystem<
757                        capsules_core::virtualizers::virtual_flash::FlashUser<
758                            apollo3::flashctrl::FlashCtrl,
759                        >,
760                        capsules_extra::sip_hash::SipHasher24<'static>,
761                        { apollo3::flashctrl::PAGE_SIZE },
762                    >,
763                    capsules_extra::tickv::TicKVKeyType,
764                >,
765            >
766        ),
767    );
768
769    let kv_driver = components::kv::KVDriverComponent::new(
770        virtual_kv_driver,
771        board_kernel,
772        capsules_extra::kv_driver::DRIVER_NUM,
773    )
774    .finalize(components::kv_driver_component_static!(
775        capsules_extra::virtualizers::virtual_kv::VirtualKVPermissions<
776            capsules_extra::kv_store_permissions::KVStorePermissions<
777                capsules_extra::tickv_kv_store::TicKVKVStore<
778                    capsules_extra::tickv::TicKVSystem<
779                        capsules_core::virtualizers::virtual_flash::FlashUser<
780                            apollo3::flashctrl::FlashCtrl,
781                        >,
782                        capsules_extra::sip_hash::SipHasher24<'static>,
783                        { apollo3::flashctrl::PAGE_SIZE },
784                    >,
785                    capsules_extra::tickv::TicKVKeyType,
786                >,
787            >,
788        >
789    ));
790
791    mcu_ctrl.print_chip_revision();
792
793    debug!("Initialization complete. Entering main loop");
794
795    // These symbols are defined in the linker script.
796    extern "C" {
797        /// Beginning of the ROM region containing app images.
798        static _sapps: u8;
799        /// End of the ROM region containing app images.
800        static _eapps: u8;
801        /// Beginning of the RAM region for app memory.
802        static mut _sappmem: u8;
803        /// End of the RAM region for app memory.
804        static _eappmem: u8;
805        /// Beginning of the RAM region containing K/V data.
806        static _skv_data: u8;
807        /// Length of the RAM region containing K/V data.
808        static _lkv_data: u8;
809    }
810
811    let scheduler = components::sched::round_robin::RoundRobinComponent::new(processes)
812        .finalize(components::round_robin_component_static!(NUM_PROCS));
813
814    let systick = cortexm4::systick::SysTick::new_with_calibration(48_000_000);
815
816    let artemis_nano = static_init!(
817        LoRaThingsPlus,
818        LoRaThingsPlus {
819            alarm,
820            led,
821            gpio,
822            console,
823            i2c_master,
824            external_spi_controller,
825            sx1262_spi_controller,
826            sx1262_gpio,
827            temperature,
828            humidity,
829            air_quality,
830            moisture,
831            rainfall,
832            rng,
833            scheduler,
834            systick,
835            kv_driver,
836        }
837    );
838
839    let chip = static_init!(
840        apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
841        apollo3::chip::Apollo3::new(peripherals)
842    );
843    CHIP = Some(chip);
844
845    let checking_policy;
846    #[cfg(feature = "atecc508a")]
847    {
848        // Create the software-based SHA engine.
849        // We could use the ATECC508a for SHA, but writing the entire
850        // application to the device to compute a digtest ends up being
851        // pretty slow and the ATECC508a doesn't support the DigestVerify trait
852        let sha = components::sha::ShaSoftware256Component::new()
853            .finalize(components::sha_software_256_component_static!());
854
855        // These are the generated test keys used below, please do not use them
856        // for anything important!!!!
857        //
858        // These keys are not leaked, they are only used for this test case.
859        //
860        // -----BEGIN PRIVATE KEY-----
861        // MIGHAgEBMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWClhguWHtAK85Kqc
862        // /BucDBQMGQw6R2PEQkyISHkn5xWhRANCAAQUFMTFoNL9oFpGmg6Cp351hQMq9hol
863        // KpEdQfjP1nYF1jxqz52YjPpFHvudkK/fFsik5Rd0AevNkQqjBdWEqmpW
864        // -----END PRIVATE KEY-----
865        //
866        // -----BEGIN PUBLIC KEY-----
867        // MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFBTExaDS/aBaRpoOgqd+dYUDKvYa
868        // JSqRHUH4z9Z2BdY8as+dmIz6RR77nZCv3xbIpOUXdAHrzZEKowXVhKpqVg==
869        // -----END PUBLIC KEY-----
870        let public_key = static_init!(
871            [u8; 64],
872            [
873                0x14, 0x14, 0xc4, 0xc5, 0xa0, 0xd2, 0xfd, 0xa0, 0x5a, 0x46, 0x9a, 0x0e, 0x82, 0xa7,
874                0x7e, 0x75, 0x85, 0x03, 0x2a, 0xf6, 0x1a, 0x25, 0x2a, 0x91, 0x1d, 0x41, 0xf8, 0xcf,
875                0xd6, 0x76, 0x05, 0xd6, 0x3c, 0x6a, 0xcf, 0x9d, 0x98, 0x8c, 0xfa, 0x45, 0x1e, 0xfb,
876                0x9d, 0x90, 0xaf, 0xdf, 0x16, 0xc8, 0xa4, 0xe5, 0x17, 0x74, 0x01, 0xeb, 0xcd, 0x91,
877                0x0a, 0xa3, 0x05, 0xd5, 0x84, 0xaa, 0x6a, 0x56
878            ]
879        );
880        let verifying_keys = kernel::static_init!([&'static mut [u8; 64]; 1], [public_key]);
881
882        // Setup the in-memory key selector.
883        let verifier_multiple_keys =
884            components::signature_verify_in_memory_keys::SignatureVerifyInMemoryKeysComponent::new(
885                ATECC508A.unwrap(),
886                verifying_keys,
887            )
888            .finalize(
889                components::signature_verify_in_memory_keys_component_static!(
890                    Verifier, 1, 64, 32, 64,
891                ),
892            );
893
894        checking_policy = components::appid::checker_signature::AppCheckerSignatureComponent::new(
895            sha,
896            verifier_multiple_keys,
897            tock_tbf::types::TbfFooterV2CredentialsType::EcdsaNistP256,
898        )
899        .finalize(components::app_checker_signature_component_static!(
900            SignatureVerifyInMemoryKeys,
901            capsules_extra::sha256::Sha256Software<'static>,
902            32,
903            64,
904        ));
905    };
906    #[cfg(not(feature = "atecc508a"))]
907    {
908        checking_policy = components::appid::checker_null::AppCheckerNullComponent::new()
909            .finalize(components::app_checker_null_component_static!());
910    }
911
912    // Create the AppID assigner.
913    let assigner = components::appid::assigner_name::AppIdAssignerNamesComponent::new()
914        .finalize(components::appid_assigner_names_component_static!());
915
916    // Create the process checking machine.
917    let checker = components::appid::checker::ProcessCheckerMachineComponent::new(checking_policy)
918        .finalize(components::process_checker_machine_component_static!());
919
920    let storage_permissions_policy =
921        components::storage_permissions::tbf_header::StoragePermissionsTbfHeaderComponent::new()
922            .finalize(
923                components::storage_permissions_tbf_header_component_static!(
924                    apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
925                    kernel::process::ProcessStandardDebugFull,
926                ),
927            );
928
929    let app_flash = core::slice::from_raw_parts(
930        core::ptr::addr_of!(_sapps),
931        core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
932    );
933    let app_memory = core::slice::from_raw_parts_mut(
934        core::ptr::addr_of_mut!(_sappmem),
935        core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
936    );
937
938    // Create and start the asynchronous process loader.
939    let _loader = components::loader::sequential::ProcessLoaderSequentialComponent::new(
940        checker,
941        board_kernel,
942        chip,
943        &FAULT_RESPONSE,
944        assigner,
945        storage_permissions_policy,
946        app_flash,
947        app_memory,
948    )
949    .finalize(components::process_loader_sequential_component_static!(
950        apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
951        kernel::process::ProcessStandardDebugFull,
952        NUM_PROCS,
953    ));
954
955    (board_kernel, artemis_nano, chip)
956}
957
958/// Main function.
959///
960/// This function is called from the arch crate after some very basic RISC-V
961/// setup and RAM initialization.
962#[no_mangle]
963pub unsafe fn main() {
964    apollo3::init();
965
966    #[cfg(test)]
967    test_main();
968
969    #[cfg(not(test))]
970    {
971        let (board_kernel, sf_lora_thing_plus_board, chip) = setup();
972
973        let main_loop_cap = create_capability!(capabilities::MainLoopCapability);
974
975        board_kernel.kernel_loop(
976            sf_lora_thing_plus_board,
977            chip,
978            None::<&kernel::ipc::IPC<{ NUM_PROCS as u8 }>>,
979            &main_loop_cap,
980        );
981    }
982}
983
984#[cfg(test)]
985use kernel::platform::watchdog::WatchDog;
986
987#[cfg(test)]
988fn test_runner(tests: &[&dyn Fn()]) {
989    unsafe {
990        let (board_kernel, sf_lora_thing_plus_board, _chip) = setup();
991
992        BOARD = Some(board_kernel);
993        PLATFORM = Some(&sf_lora_thing_plus_board);
994        MAIN_CAP = Some(&create_capability!(capabilities::MainLoopCapability));
995
996        PLATFORM.map(|p| {
997            p.watchdog().setup();
998        });
999
1000        for test in tests {
1001            test();
1002        }
1003    }
1004
1005    loop {}
1006}