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