mirror of
https://github.com/ferrous-systems/embedded-trainings-2020.git
synced 2025-01-04 21:38:53 +00:00
remove outdated code; add puzzle source code
This commit is contained in:
parent
ae9d58aa43
commit
e093f48807
6 changed files with 117 additions and 421 deletions
|
@ -1,54 +0,0 @@
|
|||
[package]
|
||||
authors = ["Jorge Aparicio <jorge.aparicio@ferrous-systems.com>"]
|
||||
edition = "2018"
|
||||
name = "dongle"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
consts = { path = "../../common/consts" }
|
||||
cortex-m-rt = "0.6.12"
|
||||
cortex-m-rtfm = "0.5.1"
|
||||
embedded-hal = "0.2.3"
|
||||
hal = { package = "nrf52840-hal", git = "https://github.com/japaric/nrf-hal", branch = "143+144" }
|
||||
heapless = "0.5.5"
|
||||
panic-halt = "0.2.0"
|
||||
usb-device = "0.2.5"
|
||||
usbd-serial = "0.1.0"
|
||||
|
||||
# optimize code in both profiles
|
||||
[profile.dev]
|
||||
codegen-units = 1
|
||||
debug = 1
|
||||
debug-assertions = true # !
|
||||
incremental = false
|
||||
lto = "fat"
|
||||
opt-level = 'z' # !
|
||||
overflow-checks = false
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
debug = 1
|
||||
debug-assertions = false
|
||||
incremental = false
|
||||
lto = "fat"
|
||||
opt-level = 3
|
||||
overflow-checks = false
|
||||
|
||||
# faster builds from scratch
|
||||
[profile.dev.build-override]
|
||||
codegen-units = 8
|
||||
debug = false
|
||||
debug-assertions = false
|
||||
opt-level = 0
|
||||
overflow-checks = false
|
||||
|
||||
[profile.release.build-override]
|
||||
codegen-units = 8
|
||||
debug = false
|
||||
debug-assertions = false
|
||||
opt-level = 0
|
||||
overflow-checks = false
|
||||
|
||||
[patch.crates-io.usb-device]
|
||||
git = "https://github.com/jonas-schievink/usb-device.git"
|
||||
branch = "inhibit-setaddr-resp"
|
|
@ -16,11 +16,11 @@ use panic_abort as _;
|
|||
fn main() -> ! {
|
||||
let mut stx = usbd::serial();
|
||||
let (mut rtx, mut rrx) = radio::claim(Channel::_20);
|
||||
let mut output = String::<consts::U64>::new();
|
||||
let mut output = String::::new();
|
||||
|
||||
output.push_str("deviceid=").ok();
|
||||
write!(output, "{:08x}{:08x}", hal::deviceid1(), hal::deviceid0()).ok();
|
||||
write!(output, " channel={} TxPower=+8dBm\n", rtx.channel()).ok();
|
||||
write!(output, " channel={} TxPower=+8dBm app=loopback.hex\n", rtx.channel()).ok();
|
||||
|
||||
let task = async {
|
||||
let mut packet = Packet::new().await;
|
||||
|
@ -28,7 +28,8 @@ fn main() -> ! {
|
|||
|
||||
loop {
|
||||
let crcres = rrx.read(&mut packet).await;
|
||||
let lqi = if packet.len() >= 3 {
|
||||
let len = packet.len();
|
||||
let lqi = if len >= 3 {
|
||||
Some(packet.lqi())
|
||||
} else {
|
||||
// packet is too small; LQI is not valid
|
||||
|
@ -37,12 +38,11 @@ fn main() -> ! {
|
|||
|
||||
let mut busy = false;
|
||||
if crcres.is_ok() {
|
||||
// packet.reverse();
|
||||
packet.reverse();
|
||||
busy = rtx.write(&packet).await.is_err();
|
||||
}
|
||||
|
||||
output.clear();
|
||||
let len = packet.len();
|
||||
write!(
|
||||
&mut output,
|
||||
"received {} byte{}",
|
||||
|
|
112
boards/dongle/puzzle.rs
Normal file
112
boards/dongle/puzzle.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
// f6c27c0a5af464795d1a64c45fa1b3039f44a62a
|
||||
#![deny(unused_must_use)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::fmt::Write as _;
|
||||
|
||||
use hal::{radio::{self, Packet, Channel}, usbd, led};
|
||||
use heapless::{consts, LinearMap, String};
|
||||
use panic_abort as _;
|
||||
|
||||
static FROM: &[u8] = &[
|
||||
// <redacted>
|
||||
];
|
||||
|
||||
static TO: &[u8] = &[
|
||||
// <redacted>
|
||||
];
|
||||
|
||||
// store the secret rather than the plaintext -- otherwise `strings $elf` will reveal the answer
|
||||
static SECRET: &[u8] = b"<redacted>";
|
||||
|
||||
#[no_mangle]
|
||||
fn main() -> ! {
|
||||
// so we can visually differentiate this one from `loopback.hex`
|
||||
led::Green.on();
|
||||
|
||||
let mut stx = usbd::serial();
|
||||
let (mut rtx, mut rrx) = radio::claim(Channel::_25);
|
||||
let mut output = String::<consts::U128>::new();
|
||||
|
||||
let mut dict = LinearMap::<_, _, consts::U128>::new();
|
||||
for (&from, &to) in FROM.iter().zip(TO.iter()) {
|
||||
dict.insert(from, to).ok();
|
||||
}
|
||||
|
||||
output.push_str("deviceid=").ok();
|
||||
write!(output, "{:08x}{:08x}", hal::deviceid1(), hal::deviceid0()).ok();
|
||||
write!(output, " channel={} TxPower=+8dBm app=puzzle.hex\n", rtx.channel()).ok();
|
||||
|
||||
let task = async {
|
||||
let mut packet = Packet::new().await;
|
||||
stx.write(output.as_bytes());
|
||||
|
||||
loop {
|
||||
let crcres = rrx.read(&mut packet).await;
|
||||
let len = packet.len();
|
||||
let lqi = if len >= 3 {
|
||||
Some(packet.lqi())
|
||||
} else {
|
||||
// packet is too small; LQI is not valid
|
||||
None
|
||||
};
|
||||
|
||||
let mut busy = false;
|
||||
if crcres.is_ok() {
|
||||
if packet.is_empty() {
|
||||
packet.copy_from_slice(SECRET);
|
||||
} else if packet.len() == 1 {
|
||||
let p = packet[0];
|
||||
let c = dict.get(&p).unwrap_or(&p);
|
||||
packet.copy_from_slice(&[*c]);
|
||||
} else {
|
||||
// encrypt
|
||||
for slot in packet.iter_mut() {
|
||||
if let Some(c) = dict.get(slot) {
|
||||
*slot = *c;
|
||||
}
|
||||
}
|
||||
|
||||
let matches = &packet[..] == SECRET;
|
||||
packet.copy_from_slice(if matches {
|
||||
b"correct"
|
||||
} else {
|
||||
b"incorrect"
|
||||
});
|
||||
}
|
||||
|
||||
busy = rtx.write(&packet).await.is_err();
|
||||
}
|
||||
|
||||
output.clear();
|
||||
write!(
|
||||
&mut output,
|
||||
"received {} byte{}",
|
||||
len,
|
||||
if len == 1 { "" } else { "s" }
|
||||
)
|
||||
.ok();
|
||||
|
||||
let (res, crc) = match crcres {
|
||||
Ok(x) => ("Ok", x),
|
||||
Err(x) => ("Err", x),
|
||||
};
|
||||
|
||||
write!(&mut output, " (CRC={}({:#06x})", res, crc).ok();
|
||||
if let Some(lqi) = lqi {
|
||||
write!(&mut output, ", LQI={}", lqi).ok();
|
||||
}
|
||||
output.push_str(")\n").ok();
|
||||
|
||||
if busy {
|
||||
output.push_str("didn't reply -- channel was busy\n").ok();
|
||||
stx.write(output.as_bytes());
|
||||
}
|
||||
|
||||
stx.write(output.as_bytes());
|
||||
}
|
||||
};
|
||||
|
||||
executor::run!(task)
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
//! `radio` & `usb` apps merged into a single application
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::fmt::Write as _;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use embedded_hal::digital::v2::{OutputPin as _, StatefulOutputPin as _};
|
||||
use hal::{
|
||||
clocks::{Clocks, ExternalOscillator, Internal, LfOscStopped},
|
||||
gpio::{
|
||||
p0::{self, P0_06, P0_12},
|
||||
Level, Output, PushPull,
|
||||
},
|
||||
ieee802154::{Packet, Radio},
|
||||
target::USBD,
|
||||
usbd::Usbd,
|
||||
};
|
||||
use heapless::String;
|
||||
use panic_halt as _; // panic handler
|
||||
use usb_device::{bus::UsbBusAllocator, prelude::*};
|
||||
use usbd_serial::{SerialPort, USB_CLASS_CDC};
|
||||
|
||||
// TODO make channel configurable via a single `const`ant
|
||||
|
||||
#[rtfm::app(device = hal::target, peripherals = true)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
blue: P0_12<Output<PushPull>>,
|
||||
configured: AtomicBool,
|
||||
green: P0_06<Output<PushPull>>,
|
||||
radio: Radio<'static>,
|
||||
serial_port: SerialPort<'static, Usbd<'static>>,
|
||||
usb_dev: UsbDevice<'static, Usbd<'static>>,
|
||||
}
|
||||
|
||||
#[init]
|
||||
fn init(cx: init::Context) -> init::LateResources {
|
||||
static mut EP_BUF: [u8; 1024] = [0; 1024];
|
||||
static mut CLOCKS: Option<Clocks<ExternalOscillator, Internal, LfOscStopped>> = None;
|
||||
static mut USB_BUS: Option<UsbBusAllocator<Usbd<'static>>> = None;
|
||||
|
||||
let periph = cx.device;
|
||||
|
||||
let port = p0::Parts::new(periph.P0);
|
||||
// P0.6: green LED indicates USB is working
|
||||
// P0.12: blue LED is toggled on each new packet
|
||||
let green = port.p0_06.into_push_pull_output(Level::High);
|
||||
let blue = port.p0_12.into_push_pull_output(Level::High);
|
||||
|
||||
let clocks = Clocks::new(periph.CLOCK);
|
||||
// extend lifetime to `'static`
|
||||
let clocks = CLOCKS.get_or_insert(clocks.enable_ext_hfosc());
|
||||
|
||||
let mut radio = Radio::init(periph.RADIO, clocks);
|
||||
// set TX power to its maximum value
|
||||
radio.set_txpower(8);
|
||||
|
||||
// these loops should always terminate
|
||||
// detected USB power? (device is USB powered)
|
||||
while !periph
|
||||
.POWER
|
||||
.usbregstatus
|
||||
.read()
|
||||
.vbusdetect()
|
||||
.is_vbus_present()
|
||||
{}
|
||||
|
||||
// wait until USB 3.3V supply (internal regulator) is stable
|
||||
while !periph
|
||||
.POWER
|
||||
.events_usbpwrrdy
|
||||
.read()
|
||||
.events_usbpwrrdy()
|
||||
.bit_is_clear()
|
||||
{}
|
||||
|
||||
// enable all interrupts
|
||||
periph.USBD.intenset.write(|w| {
|
||||
w.endepin0().set_bit();
|
||||
w.endepin1().set_bit();
|
||||
w.endepin2().set_bit();
|
||||
w.endepout0().set_bit();
|
||||
w.endepout1().set_bit();
|
||||
w.endepout2().set_bit();
|
||||
w.ep0datadone().set_bit();
|
||||
w.ep0setup().set_bit();
|
||||
w.epdata().set_bit();
|
||||
w.usbevent().set_bit();
|
||||
w.usbreset().set_bit();
|
||||
w.sof().set_bit();
|
||||
w
|
||||
});
|
||||
|
||||
let usb_bus = USB_BUS.get_or_insert(Usbd::new_alloc(periph.USBD, EP_BUF, clocks));
|
||||
let serial_port = SerialPort::new(usb_bus);
|
||||
|
||||
let usb_dev = UsbDeviceBuilder::new(usb_bus, UsbVidPid(consts::VID, consts::PID))
|
||||
.device_class(USB_CLASS_CDC)
|
||||
.max_packet_size_0(64) // (makes control transfers 8x faster)
|
||||
.build();
|
||||
|
||||
init::LateResources {
|
||||
blue,
|
||||
green,
|
||||
configured: AtomicBool::new(false),
|
||||
radio,
|
||||
usb_dev,
|
||||
serial_port,
|
||||
}
|
||||
}
|
||||
|
||||
#[idle(resources = [blue, &configured, radio, serial_port])]
|
||||
fn idle(cx: idle::Context) -> ! {
|
||||
let blue = cx.resources.blue;
|
||||
let configured = cx.resources.configured;
|
||||
let mut serial_port = cx.resources.serial_port;
|
||||
let radio = cx.resources.radio;
|
||||
|
||||
// TODO run Radio in parallel to the USBD task
|
||||
let mut packet = Packet::new();
|
||||
let mut buf = String::<heapless::consts::U64>::new();
|
||||
loop {
|
||||
// let res = radio.recv(&mut packet);
|
||||
// let lqi = packet.lqi();
|
||||
// let len = packet.len();
|
||||
// if res.is_ok() {
|
||||
// if blue.is_set_low() == Ok(true) {
|
||||
// blue.set_high().ok();
|
||||
// } else {
|
||||
// blue.set_low().ok();
|
||||
// }
|
||||
|
||||
// // loopback the packet
|
||||
// radio.send(&packet);
|
||||
// }
|
||||
|
||||
let is_configured = configured.load(Ordering::Relaxed);
|
||||
|
||||
if is_configured {
|
||||
blue.set_low().ok();
|
||||
// buf.clear();
|
||||
|
||||
// // TODO switch to a single `uwriteln!`
|
||||
// if res.is_ok() {
|
||||
// buf.push_str("CRC OK").ok()
|
||||
// } else {
|
||||
// buf.push_str("BAD CRC").ok()
|
||||
// };
|
||||
// buf.push_str(" - LQI: ").ok();
|
||||
// write!(buf, "{}", lqi).ok();
|
||||
// buf.push_str(" - payload: ").ok();
|
||||
// write!(buf, "{}", len).ok();
|
||||
// buf.push_str(" bytes\n").ok();
|
||||
|
||||
// serial_port.lock(|port| port.write(buf.as_bytes()).ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[task(binds = USBD, resources = [&configured, green, usb_dev, serial_port])]
|
||||
fn usbd(cx: usbd::Context) {
|
||||
let configured = cx.resources.configured;
|
||||
let green = cx.resources.green;
|
||||
let serial_port = cx.resources.serial_port;
|
||||
let usb_dev = cx.resources.usb_dev;
|
||||
|
||||
usb_dev.poll(&mut [serial_port]);
|
||||
if !configured.load(Ordering::Relaxed) {
|
||||
if usb_dev.state() == UsbDeviceState::Configured {
|
||||
// off
|
||||
green.set_high().ok();
|
||||
configured.store(true, Ordering::Relaxed);
|
||||
let _ = serial_port.write(b"bChannel: 20\nTX power: +8 dBm\n");
|
||||
}
|
||||
} else {
|
||||
// blink until configured
|
||||
if green.is_set_low() == Ok(true) {
|
||||
green.set_high().ok();
|
||||
} else {
|
||||
green.set_low().ok();
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME `hal::Usbd` is not clearing all EVENTS?
|
||||
let usbd: USBD = unsafe { core::mem::transmute(()) };
|
||||
if usbd.events_epdata.read().bits() != 0 && usbd.epdatastatus.read().bits() == 0 {
|
||||
usbd.events_epdata.reset();
|
||||
}
|
||||
if usbd.events_sof.read().bits() != 0 {
|
||||
usbd.events_sof.reset();
|
||||
}
|
||||
// usbd.events_endepout[0].reset();
|
||||
// usbd.events_endepout[1].reset();
|
||||
// usbd.events_endepout[2].reset();
|
||||
// usbd.events_ep0setup.reset();
|
||||
// usbd.events_ep0datadone.reset();
|
||||
// usbd.events_epdata.reset();
|
||||
// usbd.events_usbevent.reset();
|
||||
// usbd.events_usbreset.reset();
|
||||
}
|
||||
};
|
|
@ -1,63 +0,0 @@
|
|||
//! Test program, mainly for debugging purposes
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal::digital::v2::{OutputPin as _, StatefulOutputPin as _};
|
||||
use hal::{
|
||||
clocks::Clocks,
|
||||
gpio::{p0, Level},
|
||||
ieee802154::{Packet, Radio},
|
||||
target as pac,
|
||||
};
|
||||
use panic_halt as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let periph = pac::Peripherals::take().unwrap();
|
||||
|
||||
let clocks = Clocks::new(periph.CLOCK);
|
||||
let clocks = clocks.enable_ext_hfosc();
|
||||
|
||||
let port = p0::Parts::new(periph.P0);
|
||||
|
||||
// NOTE LEDs turn on when the output level is low (0V)
|
||||
let mut green = port.p0_06.into_push_pull_output(Level::High); // LD1
|
||||
|
||||
let mut blue = port.p0_12.into_push_pull_output(Level::High); // LD2 (RGB LED)
|
||||
let mut red = port.p0_08.into_push_pull_output(Level::High); // LD2 (RGB LED)
|
||||
|
||||
let mut radio = Radio::init(periph.RADIO, &clocks);
|
||||
|
||||
// turn on green LED to indicate the radio has been initialized
|
||||
green.set_low().ok();
|
||||
|
||||
// set TX power to its maximum value
|
||||
radio.set_txpower(8);
|
||||
|
||||
let mut packet = Packet::new();
|
||||
loop {
|
||||
let res = radio.recv(&mut packet);
|
||||
|
||||
if res.is_ok() {
|
||||
// CRC check passed
|
||||
// clear visual error state (turn off the red LED)
|
||||
red.set_high().ok();
|
||||
|
||||
// toggle blue LED on each successfully received packet
|
||||
if blue.is_set_low() == Ok(true) {
|
||||
blue.set_high().ok();
|
||||
} else {
|
||||
blue.set_low().ok();
|
||||
}
|
||||
|
||||
// return packet with the contents unchanged
|
||||
radio.send(&packet);
|
||||
} else {
|
||||
// CRC check failed
|
||||
// indicate error using the red LED
|
||||
red.set_low().ok();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
//! Test program used to debug the `serial-term` tool
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal::digital::v2::OutputPin as _;
|
||||
use hal::{
|
||||
clocks::Clocks,
|
||||
gpio::{p0, Level},
|
||||
target as pac,
|
||||
usbd::Usbd,
|
||||
};
|
||||
use panic_halt as _;
|
||||
use usb_device::device::{UsbDeviceBuilder, UsbDeviceState, UsbVidPid};
|
||||
use usbd_serial::{SerialPort, USB_CLASS_CDC};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
static mut EP_BUF: [u8; 1024] = [0; 1024];
|
||||
|
||||
let periph = pac::Peripherals::take().unwrap();
|
||||
|
||||
let clocks = Clocks::new(periph.CLOCK);
|
||||
let clocks = clocks.enable_ext_hfosc();
|
||||
|
||||
let port = p0::Parts::new(periph.P0);
|
||||
|
||||
// NOTE LEDs turn on when the output level is low (0V)
|
||||
let mut green = port.p0_06.into_push_pull_output(Level::High); // LD1
|
||||
let mut blue = port.p0_12.into_push_pull_output(Level::High); // LD2 (RGB LED)
|
||||
let mut red = port.p0_08.into_push_pull_output(Level::High); // LD2 (RGB LED)
|
||||
|
||||
// these loops should always terminate
|
||||
// detected USB power? (device is USB powered)
|
||||
while !periph
|
||||
.POWER
|
||||
.usbregstatus
|
||||
.read()
|
||||
.vbusdetect()
|
||||
.is_vbus_present()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// wait until USB 3.3V supply (internal regulator) is stable
|
||||
while !periph
|
||||
.POWER
|
||||
.events_usbpwrrdy
|
||||
.read()
|
||||
.events_usbpwrrdy()
|
||||
.bit_is_clear()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// turn on green LED to show the program is working
|
||||
green.set_low().ok();
|
||||
|
||||
let usb_bus = Usbd::new_alloc(periph.USBD, EP_BUF, &clocks);
|
||||
let mut serial_port = SerialPort::new(&usb_bus);
|
||||
|
||||
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(consts::VID, consts::PID))
|
||||
.device_class(USB_CLASS_CDC)
|
||||
.max_packet_size_0(64) // (makes control transfers 8x faster)
|
||||
.build();
|
||||
|
||||
let mut buf = [0; 64];
|
||||
let mut once = true;
|
||||
loop {
|
||||
if !usb_dev.poll(&mut [&mut serial_port]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if usb_dev.state() == UsbDeviceState::Configured {
|
||||
// turn on the blue LED to show the device has been configured
|
||||
blue.set_low().ok();
|
||||
|
||||
if once {
|
||||
once = false;
|
||||
|
||||
serial_port.write(b"Hello, world!\n").ok();
|
||||
serial_port.flush().ok();
|
||||
}
|
||||
|
||||
if serial_port.read(&mut buf).is_ok() {
|
||||
// received data from the PC. I'm interpreting this as an error but it may actually
|
||||
// be part of the CDC/ACM protocol? -- we never send data to the device
|
||||
red.set_low().ok();
|
||||
}
|
||||
} else {
|
||||
// otherwise turn it off
|
||||
blue.set_high().ok();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue