Skip to content

Commit

Permalink
Merge pull request openwsn-berkeley#297 from malishav/296-add-standal…
Browse files Browse the repository at this point in the history
…one-nrf52840-example

Add standalone nRF52840-DK example over raw BLE radio
  • Loading branch information
geonnave authored Jul 17, 2024
2 parents 26eab7d + c2e14e7 commit ad340ee
Show file tree
Hide file tree
Showing 9 changed files with 556 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"crypto/lakers-crypto-cryptocell310-sys",
"examples/coap",
"examples/lakers-no_std",
"examples/lakers-nrf52840",
"lakers-c",
"lakers-python",
]
Expand Down
9 changes: 9 additions & 0 deletions examples/lakers-nrf52840/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
runner = "probe-rs run --chip nRF52840_xxAA"

[build]
target = "thumbv7em-none-eabi"

[env]
DEFMT_LOG = "trace"
43 changes: 43 additions & 0 deletions examples/lakers-nrf52840/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[package]
name = "lakers-nrf52840"
edition = "2021"
version.workspace = true
repository.workspace = true
license.workspace = true
keywords.workspace = true
categories.workspace = true

[dependencies]

# embassy deps
embassy-executor = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-time = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt", "defmt-timestamp-uptime"] }
embassy-nrf = { git = "https://github.com/embassy-rs/embassy", branch = "main", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }

# lakers
lakers = { package = "lakers", path = "../../lib", default-features = false }
lakers-crypto = { path = "../../crypto", default-features = false }

# misc
hexlit = "0.5.3"

# defmt
defmt = "0.3"
defmt-rtt = "0.4"

# depend on an allocator
embedded-alloc = "0.5.0"

# arm cortex m3 deps
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
panic-probe = { version = "0.3", features = ["print-defmt"] }

[features]
default = [ "crypto-psa", "ead-none" ]
crypto-cryptocell310 = [ "lakers-crypto/cryptocell310" ]
crypto-psa = [ "lakers-crypto/psa-baremetal" ]
ead-none = [ ]

[profile.release]
debug = 2
23 changes: 23 additions & 0 deletions examples/lakers-nrf52840/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Introduction

This folder contains a bare metal (no_std) example of EDHOC Initiator and EDHOC Responder for nRF52840-DK.
The Initiator and Responder communicate over a raw BLE radio.

The example is configured to be ran on nRF52840-DK board.

## Prerequisites

- install probe-rs
- `rustup target add thumbv7m-none-eabi`

## How to use

This folder's `.cargo/config.toml` configures the target (`thumbv7m-none-eabi`) and the probe-rs runner so the things should just work:

cargo run --bin initiator
cargo run --bin responder

You may want to prefix the commands above with e.g. PROBE_RS_PROBE=1366:1051:001050288491 in order to specify which board you want to connect to.
You can get the name of your probes by running:

probe-rs list
35 changes: 35 additions & 0 deletions examples/lakers-nrf52840/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());

// By default, Cargo will re-run a build script whenever
// any file in the project changes. By specifying `memory.x`
// here, we ensure the build script is only re-run when
// `memory.x` is changed.
println!("cargo:rerun-if-changed=memory.x");

println!("cargo:rustc-link-arg-bins=--nmagic");
println!("cargo:rustc-link-arg-bins=-Tlink.x");
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
}
12 changes: 12 additions & 0 deletions examples/lakers-nrf52840/memory.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
RAM : ORIGIN = 0x20000000, LENGTH = 256K

/* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */
/*
FLASH : ORIGIN = 0x00027000, LENGTH = 868K
RAM : ORIGIN = 0x20020000, LENGTH = 128K
*/
}
178 changes: 178 additions & 0 deletions examples/lakers-nrf52840/src/bin/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
use embassy_nrf::radio::ble::Radio;
use embassy_nrf::saadc::Time;
use embassy_nrf::{peripherals, radio};
use embassy_time::Duration;
use embassy_time::TimeoutError;
use embassy_time::WithTimeout;
use hexlit::hex;

use defmt::info;

pub const MAX_PDU: usize = 258;
pub const FREQ: u32 = 2408;
pub const ADV_ADDRESS: u32 = 0x12345678;
pub const ADV_CRC_INIT: u32 = 0xffff;
pub const CRC_POLY: u32 = 0x00065b;

pub const CRED_I: &[u8] = &hex!("A2027734322D35302D33312D46462D45462D33372D33322D333908A101A5010202412B2001215820AC75E9ECE3E50BFC8ED60399889522405C47BF16DF96660A41298CB4307F7EB62258206E5DE611388A4B8A8211334AC7D37ECB52A387D257E6DB3C2A93DF21FF3AFFC8");
pub const I: &[u8] = &hex!("fb13adeb6518cee5f88417660841142e830a81fe334380a953406a1305e8706b");
pub const R: &[u8] = &hex!("72cc4761dbd4c78f758931aa589d348d1ef874a7e303ede2f140dcf3e6aa4aac");
pub const CRED_R: &[u8] = &hex!("A2026008A101A5010202410A2001215820BBC34960526EA4D32E940CAD2A234148DDC21791A12AFBCBAC93622046DD44F02258204519E257236B2A0CE2023F0931F1F386CA7AFDA64FCDE0108C224C51EABF6072");

#[derive(Debug)]
pub enum PacketError {
SliceTooLong,
SliceTooShort,
ParsingError,
TimeoutError,
RadioError,
}
pub struct Packet {
pub len: usize, // total length that gets transmitted over the air, equals length of pdu + 1, for pdu_header
pub pdu_header: Option<u8>, // 1-byte application-level header, used for filtering the packets
pub pdu: [u8; MAX_PDU], // application-level payload
}

impl Default for Packet {
fn default() -> Self {
Packet {
len: 0,
pdu_header: None,
pdu: [0u8; MAX_PDU],
}
}
}

impl Packet {
pub fn new() -> Self {
Packet {
len: 0,
pdu_header: None,
pdu: [0u8; MAX_PDU],
}
}

pub fn new_from_slice(slice: &[u8], header: Option<u8>) -> Result<Self, PacketError> {
let mut buffer = Self::new();
if buffer.fill_with_slice(slice, header).is_ok() {
Ok(buffer)
} else {
Err(PacketError::SliceTooLong)
}
}

pub fn fill_with_slice(&mut self, slice: &[u8], header: Option<u8>) -> Result<(), PacketError> {
if slice.len() <= self.pdu.len() {
self.len = slice.len();
self.pdu_header = header;
self.pdu[..self.len].copy_from_slice(slice);
Ok(())
} else {
Err(PacketError::SliceTooLong)
}
}

pub fn as_bytes(&mut self) -> &[u8] {
let mut offset = 0;
let mut len: usize = 0;

if let Some(header) = self.pdu_header {
offset = 3;
len = self.len + 1;
} else {
offset = 2;
len = self.len;
}
self.pdu.copy_within(..self.len, offset);
self.pdu[0] = 0x00;
self.pdu[1] = len as u8;

if let Some(header) = self.pdu_header {
self.pdu[2] = header;
}
&self.pdu[..len]
}
}

impl TryInto<Packet> for &[u8] {
type Error = ();

fn try_into(self) -> Result<Packet, Self::Error> {
let mut packet: Packet = Default::default();

if self.len() > 1 {
packet.len = self[1] as usize;
packet.pdu[..packet.len].copy_from_slice(&self[2..2 + packet.len]);
Ok(packet)
} else {
Err(())
}
}
}

impl From<TimeoutError> for PacketError {
fn from(error: TimeoutError) -> Self {
PacketError::TimeoutError
}
}

impl From<embassy_nrf::radio::Error> for PacketError {
fn from(error: embassy_nrf::radio::Error) -> Self {
match error {
_ => PacketError::RadioError,
}
}
}

pub async fn receive_and_filter(
radio: &mut Radio<'static, embassy_nrf::peripherals::RADIO>,
header: Option<u8>,
) -> Result<Packet, PacketError> {
let mut buffer: [u8; MAX_PDU] = [0x00u8; MAX_PDU];
loop {
radio.receive(&mut buffer).await?;
if let Ok(pckt) = <&[u8] as TryInto<Packet>>::try_into(&(buffer[..])) {
if let Some(header) = header {
if pckt.pdu[0] == header {
return Ok(pckt);
} else {
continue;
}
} else {
// header is None
return Ok(pckt);
}
} else {
continue;
}
}
}

pub async fn transmit_and_wait_response(
radio: &mut Radio<'static, embassy_nrf::peripherals::RADIO>,
mut packet: Packet,
filter: Option<u8>,
) -> Result<Packet, PacketError> {
let mut rcvd_packet: Packet = Default::default();
let mut buffer: [u8; MAX_PDU] = [0x00u8; MAX_PDU];

radio.transmit(packet.as_bytes()).await?;
let resp = receive_and_filter(radio, filter).await?;

Ok(resp)
}

pub async fn transmit_without_response(
radio: &mut Radio<'static, embassy_nrf::peripherals::RADIO>,
mut packet: Packet,
) -> Result<(), PacketError> {
radio.transmit(packet.as_bytes()).await?;
Ok(())
}

use core::ffi::{c_char, c_void};
#[no_mangle]
pub extern "C" fn strstr(cs: *const c_char, ct: *const c_char) -> *mut c_char {
panic!("strstr handler!");
core::ptr::null_mut()
}
Loading

0 comments on commit ad340ee

Please sign in to comment.