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