embassy-rp
This commit is contained in:
parent
eb63191b08
commit
dbdaee9c6d
1110
Cargo.lock
generated
1110
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
27
Cargo.toml
27
Cargo.toml
@ -8,20 +8,27 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rp235x-hal = { version = "0.2.0", features = [
|
||||
"critical-section-impl",
|
||||
# Embassy
|
||||
embassy-rp = { version = "0.3.1", features = [
|
||||
"rp235xa",
|
||||
"binary-info",
|
||||
"critical-section-impl",
|
||||
"time-driver",
|
||||
] }
|
||||
embassy-executor = { version = "0.7.0", features = [
|
||||
"arch-cortex-m",
|
||||
"executor-thread",
|
||||
"executor-interrupt",
|
||||
"task-arena-size-65536",
|
||||
] }
|
||||
embassy-time = "0.4.0"
|
||||
|
||||
cortex-m-rt = "0.7.5"
|
||||
critical-section = "1.2.0"
|
||||
embedded-hal = "1.0.0"
|
||||
fugit = "0.3.7"
|
||||
scd4x = { git = "https://github.com/twokilohertz/scd4x-rs.git", branch = "conversion-fixes", features = [
|
||||
"scd41",
|
||||
] }
|
||||
rtt-target = "0.6.1"
|
||||
ssd1351 = "0.5.0"
|
||||
embedded-hal-bus = "0.3.0"
|
||||
embedded-graphics = "0.8.1"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = "s"
|
||||
|
||||
# Super-optimised release build, maximum performance, minimal debuggability
|
||||
# Build with cargo build --profile dist
|
||||
|
12
build.rs
12
build.rs
@ -3,16 +3,22 @@ use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Put the linker script somewhere the linker can find it
|
||||
let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// The file `memory.x` is loaded by cortex-m-rt's `link.x` script, which
|
||||
// is what we specify in `.cargo/config.toml` for Arm builds
|
||||
// ARM build
|
||||
|
||||
let memory_x = include_bytes!("memory.x");
|
||||
let mut f = File::create(out.join("memory.x")).unwrap();
|
||||
f.write_all(memory_x).unwrap();
|
||||
println!("cargo:rerun-if-changed=memory.x");
|
||||
|
||||
// RISC-V build
|
||||
|
||||
let rp235x_riscv_x = include_bytes!("rp235x_riscv.x");
|
||||
let mut f = File::create(out.join("rp235x_riscv.x")).unwrap();
|
||||
f.write_all(rp235x_riscv_x).unwrap();
|
||||
println!("cargo:rerun-if-changed=rp235x_riscv.x");
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
||||
|
2
memory.x
2
memory.x
@ -73,5 +73,3 @@ SECTIONS {
|
||||
|
||||
PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
|
||||
PROVIDE(end_to_start = __start_block_addr - __end_block_addr);
|
||||
|
||||
|
||||
|
252
rp235x_riscv.x
Normal file
252
rp235x_riscv.x
Normal file
@ -0,0 +1,252 @@
|
||||
MEMORY {
|
||||
/*
|
||||
* The RP2350 has either external or internal flash.
|
||||
*
|
||||
* 2 MiB is a safe default here, although a Pico 2 has 4 MiB.
|
||||
*/
|
||||
FLASH : ORIGIN = 0x10000000, LENGTH = 2048K
|
||||
/*
|
||||
* RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping.
|
||||
* This is usually good for performance, as it distributes load on
|
||||
* those banks evenly.
|
||||
*/
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 512K
|
||||
/*
|
||||
* RAM banks 8 and 9 use a direct mapping. They can be used to have
|
||||
* memory areas dedicated for some specific job, improving predictability
|
||||
* of access times.
|
||||
* Example: Separate stacks for core0 and core1.
|
||||
*/
|
||||
SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K
|
||||
SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K
|
||||
}
|
||||
|
||||
/* # Developer notes
|
||||
|
||||
- Symbols that start with a double underscore (__) are considered "private"
|
||||
|
||||
- Symbols that start with a single underscore (_) are considered "semi-public"; they can be
|
||||
overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" {
|
||||
static mut _heap_size }`).
|
||||
|
||||
- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a
|
||||
symbol is not dropped if it appears in or near the front of the linker arguments and "it's not
|
||||
needed" by any of the preceding objects (linker arguments)
|
||||
|
||||
- `PROVIDE` is used to provide default values that can be overridden by a user linker script
|
||||
|
||||
- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
|
||||
the LMA of .data are all `32`-byte aligned. These alignments are assumed by the RAM
|
||||
initialization routine. There's also a second benefit: `32`-byte aligned boundaries
|
||||
means that you won't see "Address (..) is out of bounds" in the disassembly produced by `objdump`.
|
||||
*/
|
||||
|
||||
PROVIDE(_stext = ORIGIN(FLASH));
|
||||
PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM));
|
||||
PROVIDE(_max_hart_id = 0);
|
||||
PROVIDE(_hart_stack_size = 2K);
|
||||
PROVIDE(_heap_size = 0);
|
||||
|
||||
PROVIDE(InstructionMisaligned = ExceptionHandler);
|
||||
PROVIDE(InstructionFault = ExceptionHandler);
|
||||
PROVIDE(IllegalInstruction = ExceptionHandler);
|
||||
PROVIDE(Breakpoint = ExceptionHandler);
|
||||
PROVIDE(LoadMisaligned = ExceptionHandler);
|
||||
PROVIDE(LoadFault = ExceptionHandler);
|
||||
PROVIDE(StoreMisaligned = ExceptionHandler);
|
||||
PROVIDE(StoreFault = ExceptionHandler);
|
||||
PROVIDE(UserEnvCall = ExceptionHandler);
|
||||
PROVIDE(SupervisorEnvCall = ExceptionHandler);
|
||||
PROVIDE(MachineEnvCall = ExceptionHandler);
|
||||
PROVIDE(InstructionPageFault = ExceptionHandler);
|
||||
PROVIDE(LoadPageFault = ExceptionHandler);
|
||||
PROVIDE(StorePageFault = ExceptionHandler);
|
||||
|
||||
PROVIDE(SupervisorSoft = DefaultHandler);
|
||||
PROVIDE(MachineSoft = DefaultHandler);
|
||||
PROVIDE(SupervisorTimer = DefaultHandler);
|
||||
PROVIDE(MachineTimer = DefaultHandler);
|
||||
PROVIDE(SupervisorExternal = DefaultHandler);
|
||||
PROVIDE(MachineExternal = DefaultHandler);
|
||||
|
||||
PROVIDE(DefaultHandler = DefaultInterruptHandler);
|
||||
PROVIDE(ExceptionHandler = DefaultExceptionHandler);
|
||||
|
||||
/* # Pre-initialization function */
|
||||
/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function,
|
||||
then the function this points to will be called before the RAM is initialized. */
|
||||
PROVIDE(__pre_init = default_pre_init);
|
||||
|
||||
/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */
|
||||
PROVIDE(_setup_interrupts = default_setup_interrupts);
|
||||
|
||||
/* # Multi-processing hook function
|
||||
fn _mp_hook() -> bool;
|
||||
|
||||
This function is called from all the harts and must return true only for one hart,
|
||||
which will perform memory initialization. For other harts it must return false
|
||||
and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt).
|
||||
*/
|
||||
PROVIDE(_mp_hook = default_mp_hook);
|
||||
|
||||
/* # Start trap function override
|
||||
By default uses the riscv crates default trap handler
|
||||
but by providing the `_start_trap` symbol external crates can override.
|
||||
*/
|
||||
PROVIDE(_start_trap = default_start_trap);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text.dummy (NOLOAD) :
|
||||
{
|
||||
/* This section is intended to make _stext address work */
|
||||
. = ABSOLUTE(_stext);
|
||||
} > FLASH
|
||||
|
||||
.text _stext :
|
||||
{
|
||||
/* Put reset handler first in .text section so it ends up as the entry */
|
||||
/* point of the program. */
|
||||
KEEP(*(.init));
|
||||
KEEP(*(.init.rust));
|
||||
. = ALIGN(4);
|
||||
__start_block_addr = .;
|
||||
KEEP(*(.start_block));
|
||||
KEEP(*(.boot_info));
|
||||
. = ALIGN(4);
|
||||
*(.trap);
|
||||
*(.trap.rust);
|
||||
*(.text.abort);
|
||||
*(.text .text.*);
|
||||
. = ALIGN(4);
|
||||
} > FLASH
|
||||
|
||||
/* ### Picotool 'Binary Info' Entries
|
||||
*
|
||||
* Picotool looks through this block (as we have pointers to it in our
|
||||
* header) to find interesting information.
|
||||
*/
|
||||
.bi_entries : ALIGN(4)
|
||||
{
|
||||
/* We put this in the header */
|
||||
__bi_entries_start = .;
|
||||
/* Here are the entries */
|
||||
KEEP(*(.bi_entries));
|
||||
/* Keep this block a nice round size */
|
||||
. = ALIGN(4);
|
||||
/* We put this in the header */
|
||||
__bi_entries_end = .;
|
||||
} > FLASH
|
||||
|
||||
.rodata : ALIGN(4)
|
||||
{
|
||||
*(.srodata .srodata.*);
|
||||
*(.rodata .rodata.*);
|
||||
|
||||
/* 4-byte align the end (VMA) of this section.
|
||||
This is required by LLD to ensure the LMA of the following .data
|
||||
section will have the correct alignment. */
|
||||
. = ALIGN(4);
|
||||
} > FLASH
|
||||
|
||||
.data : ALIGN(32)
|
||||
{
|
||||
_sidata = LOADADDR(.data);
|
||||
__sidata = LOADADDR(.data);
|
||||
_sdata = .;
|
||||
__sdata = .;
|
||||
/* Must be called __global_pointer$ for linker relaxations to work. */
|
||||
PROVIDE(__global_pointer$ = . + 0x800);
|
||||
*(.sdata .sdata.* .sdata2 .sdata2.*);
|
||||
*(.data .data.*);
|
||||
. = ALIGN(32);
|
||||
_edata = .;
|
||||
__edata = .;
|
||||
} > RAM AT > FLASH
|
||||
|
||||
.bss (NOLOAD) : ALIGN(32)
|
||||
{
|
||||
_sbss = .;
|
||||
*(.sbss .sbss.* .bss .bss.*);
|
||||
. = ALIGN(32);
|
||||
_ebss = .;
|
||||
} > RAM
|
||||
|
||||
.end_block : ALIGN(4)
|
||||
{
|
||||
__end_block_addr = .;
|
||||
KEEP(*(.end_block));
|
||||
} > FLASH
|
||||
|
||||
/* fictitious region that represents the memory available for the heap */
|
||||
.heap (NOLOAD) :
|
||||
{
|
||||
_sheap = .;
|
||||
. += _heap_size;
|
||||
. = ALIGN(4);
|
||||
_eheap = .;
|
||||
} > RAM
|
||||
|
||||
/* fictitious region that represents the memory available for the stack */
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
_estack = .;
|
||||
. = ABSOLUTE(_stack_start);
|
||||
_sstack = .;
|
||||
} > RAM
|
||||
|
||||
/* fake output .got section */
|
||||
/* Dynamic relocations are unsupported. This section is only used to detect
|
||||
relocatable code in the input files and raise an error if relocatable code
|
||||
is found */
|
||||
.got (INFO) :
|
||||
{
|
||||
KEEP(*(.got .got.*));
|
||||
}
|
||||
|
||||
.eh_frame (INFO) : { KEEP(*(.eh_frame)) }
|
||||
.eh_frame_hdr (INFO) : { *(.eh_frame_hdr) }
|
||||
}
|
||||
|
||||
PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
|
||||
PROVIDE(end_to_start = __start_block_addr - __end_block_addr);
|
||||
|
||||
|
||||
/* Do not exceed this mark in the error messages above | */
|
||||
ASSERT(ORIGIN(FLASH) % 4 == 0, "
|
||||
ERROR(riscv-rt): the start of the FLASH must be 4-byte aligned");
|
||||
|
||||
ASSERT(ORIGIN(RAM) % 32 == 0, "
|
||||
ERROR(riscv-rt): the start of the RAM must be 32-byte aligned");
|
||||
|
||||
ASSERT(_stext % 4 == 0, "
|
||||
ERROR(riscv-rt): `_stext` must be 4-byte aligned");
|
||||
|
||||
ASSERT(_sdata % 32 == 0 && _edata % 32 == 0, "
|
||||
BUG(riscv-rt): .data is not 32-byte aligned");
|
||||
|
||||
ASSERT(_sidata % 32 == 0, "
|
||||
BUG(riscv-rt): the LMA of .data is not 32-byte aligned");
|
||||
|
||||
ASSERT(_sbss % 32 == 0 && _ebss % 32 == 0, "
|
||||
BUG(riscv-rt): .bss is not 32-byte aligned");
|
||||
|
||||
ASSERT(_sheap % 4 == 0, "
|
||||
BUG(riscv-rt): start of .heap is not 4-byte aligned");
|
||||
|
||||
ASSERT(_stext + SIZEOF(.text) < ORIGIN(FLASH) + LENGTH(FLASH), "
|
||||
ERROR(riscv-rt): The .text section must be placed inside the FLASH region.
|
||||
Set _stext to an address smaller than 'ORIGIN(FLASH) + LENGTH(FLASH)'");
|
||||
|
||||
ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, "
|
||||
ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts.
|
||||
Consider changing `_max_hart_id` or `_hart_stack_size`.");
|
||||
|
||||
ASSERT(SIZEOF(.got) == 0, "
|
||||
.got section detected in the input files. Dynamic relocations are not
|
||||
supported. If you are linking to C code compiled using the `gcc` crate
|
||||
then modify your build script to compile the C code _without_ the
|
||||
-fPIC flag. See the documentation of the `gcc::Config.fpic` method for
|
||||
details.");
|
||||
|
||||
/* Do not exceed this mark in the error messages above | */
|
@ -1,2 +0,0 @@
|
||||
/// Pico 2 W on-board crystal oscillator frequency (AEL 12.0)
|
||||
pub const XTAL_FREQ_HZ: u32 = 12_000_000u32;
|
203
src/main.rs
203
src/main.rs
@ -1,179 +1,50 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
// RP235x HAL
|
||||
use rp235x_hal as hal;
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
use hal::gpio::FunctionSpi;
|
||||
use hal::spi::Spi;
|
||||
|
||||
// Display
|
||||
use ssd1351::{
|
||||
mode::GraphicsMode,
|
||||
prelude::SPIInterface,
|
||||
properties::{DisplayRotation, DisplaySize},
|
||||
};
|
||||
|
||||
// Sensor
|
||||
// use scd4x::Scd4x;
|
||||
|
||||
use embedded_graphics::{
|
||||
pixelcolor::Rgb565,
|
||||
prelude::{Point, Primitive, RgbColor, Size},
|
||||
primitives::{PrimitiveStyleBuilder, Rectangle},
|
||||
Drawable,
|
||||
};
|
||||
|
||||
use embedded_hal::spi::MODE_0;
|
||||
use fugit::RateExtU32;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
|
||||
mod constants;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::block::ImageDef;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
rtt_init_print!();
|
||||
rprintln!("RTT logging initialised");
|
||||
|
||||
let _peripherals = embassy_rp::init(Default::default());
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
core::arch::asm!("wfi");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
rprintln!("Panicked! {}", info);
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
core::arch::asm!("wfi");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
|
||||
static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||
|
||||
#[hal::entry]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
rprintln!("Logging over RTT initialised");
|
||||
|
||||
let mut peripherals = hal::pac::Peripherals::take().unwrap();
|
||||
|
||||
let mut watchdog = hal::Watchdog::new(peripherals.WATCHDOG);
|
||||
|
||||
let clocks = hal::clocks::init_clocks_and_plls(
|
||||
constants::XTAL_FREQ_HZ,
|
||||
peripherals.XOSC,
|
||||
peripherals.CLOCKS,
|
||||
peripherals.PLL_SYS,
|
||||
peripherals.PLL_USB,
|
||||
&mut peripherals.RESETS,
|
||||
&mut watchdog,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut timer = hal::Timer::new_timer0(peripherals.TIMER0, &mut peripherals.RESETS, &clocks);
|
||||
|
||||
let sio = hal::Sio::new(peripherals.SIO);
|
||||
|
||||
let pins = hal::gpio::Pins::new(
|
||||
peripherals.IO_BANK0,
|
||||
peripherals.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut peripherals.RESETS,
|
||||
);
|
||||
|
||||
rprintln!("Core RP2350 hardware initialisation successful");
|
||||
|
||||
// Display
|
||||
|
||||
let mosi_pin = pins.gpio19.into_function::<FunctionSpi>();
|
||||
let sclk_pin = pins.gpio18.into_function::<FunctionSpi>();
|
||||
let cs_pin = pins.gpio17.into_push_pull_output();
|
||||
let dc_pin = pins.gpio20.into_push_pull_output();
|
||||
let mut rst_pin = pins.gpio21.into_push_pull_output();
|
||||
|
||||
// SPI initialisation
|
||||
let spi_pins = (mosi_pin, sclk_pin);
|
||||
let spi = Spi::<_, _, _, 8>::new(peripherals.SPI0, spi_pins).init(
|
||||
&mut peripherals.RESETS,
|
||||
&clocks.peripheral_clock,
|
||||
16_u32.MHz(),
|
||||
MODE_0,
|
||||
);
|
||||
let spi_device = embedded_hal_bus::spi::ExclusiveDevice::new_no_delay(spi, cs_pin).unwrap();
|
||||
let spi_interface = SPIInterface::new(spi_device, dc_pin);
|
||||
|
||||
let mut display: GraphicsMode<_> = ssd1351::builder::Builder::new()
|
||||
.with_size(DisplaySize::Display128x128)
|
||||
.with_rotation(DisplayRotation::Rotate0)
|
||||
.connect_interface(spi_interface)
|
||||
.into();
|
||||
display.reset(&mut rst_pin, &mut timer).unwrap();
|
||||
display.init().unwrap();
|
||||
|
||||
let rect = Rectangle::new(Point::new(0, 40), Size::new(40, 20)).into_styled(
|
||||
PrimitiveStyleBuilder::new()
|
||||
.fill_color(Rgb565::CYAN)
|
||||
.build(),
|
||||
);
|
||||
rect.draw(&mut display).unwrap();
|
||||
|
||||
// // Initialise SCD41 sensor
|
||||
// let i2c0 = hal::I2C::i2c0(
|
||||
// peripherals.I2C0,
|
||||
// pins.gpio4.reconfigure(), // Pin 6 on Pico 2 (SDA)
|
||||
// pins.gpio5.reconfigure(), // Pin 7 on Pico 2 (SCL)
|
||||
// 400.kHz(),
|
||||
// &mut peripherals.RESETS,
|
||||
// &clocks.peripheral_clock,
|
||||
// );
|
||||
|
||||
// timer.delay_ms(30); // Power-up delay
|
||||
// let mut scd41 = Scd4x::new(i2c0, timer);
|
||||
// scd41.wake_up();
|
||||
|
||||
// match scd41.reinit() {
|
||||
// Ok(_) => rprintln!("Initialised SCD41"),
|
||||
// Err(error) => rprintln!("Failed to initialise SCD41: {:?}", error),
|
||||
// }
|
||||
// timer.delay_ms(30); // Soft reset delay
|
||||
|
||||
// match scd41.serial_number() {
|
||||
// Ok(serial) => rprintln!("SCD41 serial number: {}", serial),
|
||||
// Err(error) => rprintln!("SCD41 did not respond to get_serial_number: {:?}", error),
|
||||
// }
|
||||
|
||||
// match scd41.self_test_is_ok() {
|
||||
// Ok(ok) => {
|
||||
// if ok {
|
||||
// rprintln!("SCD41 reported successful self-test")
|
||||
// } else {
|
||||
// rprintln!("SCD41 reported unsuccessful self-test!")
|
||||
// }
|
||||
// }
|
||||
// Err(_) => rprintln!("SCD41 failed to perform self-test"),
|
||||
// }
|
||||
|
||||
// match scd41.start_periodic_measurement() {
|
||||
// Ok(_) => rprintln!("Configured sensor to measure every 5 seconds"),
|
||||
// Err(error) => rprintln!("SCD41 start_periodic_measurement() failed: {:?}", error),
|
||||
// }
|
||||
|
||||
// loop {
|
||||
// timer.delay_ms(5010);
|
||||
// match scd41.measurement() {
|
||||
// Ok(data) => rprintln!(
|
||||
// "CO2: {}, temperature: {}, humidity: {}",
|
||||
// data.co2,
|
||||
// data.temperature,
|
||||
// data.humidity
|
||||
// ),
|
||||
// Err(error) => rprintln!("SCD41 get_measurement() failed: {:?}", error),
|
||||
// }
|
||||
// }
|
||||
|
||||
loop {
|
||||
hal::arch::wfi();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[panic_handler]
|
||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
rprintln!("Panicked! {}", info);
|
||||
loop {
|
||||
hal::arch::nop()
|
||||
}
|
||||
}
|
||||
|
||||
/// Program metadata for `picotool info`
|
||||
// Program metadata for picotool
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 3] = [
|
||||
hal::binary_info::rp_program_name!(c"Pico Environment Sensor"),
|
||||
hal::binary_info::rp_cargo_version!(),
|
||||
hal::binary_info::rp_program_build_attribute!(),
|
||||
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
|
||||
embassy_rp::binary_info::rp_program_name!(c"Pico Environment Sensor"),
|
||||
embassy_rp::binary_info::rp_program_description!(
|
||||
c"A CO2, temperature & humidity sensing application for the RPi Pico 2 W"
|
||||
),
|
||||
embassy_rp::binary_info::rp_cargo_version!(),
|
||||
embassy_rp::binary_info::rp_program_build_attribute!(),
|
||||
];
|
||||
|
Loading…
x
Reference in New Issue
Block a user