mirror of
https://github.com/ferrous-systems/embedded-trainings-2020.git
synced 2025-01-09 07:45:34 +00:00
Merge pull request #33 from ferrous-systems/change-channel
add `change-channel` tool
This commit is contained in:
commit
3f2ba0af36
13 changed files with 2255 additions and 1870 deletions
File diff suppressed because it is too large
Load diff
|
@ -1,12 +1,12 @@
|
|||
// f6c27c0a5af464795d1a64c45fa1b3039f44a62a
|
||||
#![deny(unused_must_use)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::fmt::Write as _;
|
||||
use core::{convert::TryFrom, fmt::Write as _};
|
||||
|
||||
use async_core::unsync::Mutex;
|
||||
use hal::{
|
||||
radio::{self, Channel, Packet},
|
||||
radio::{self, Channel},
|
||||
usbd,
|
||||
};
|
||||
use heapless::{consts, String};
|
||||
|
@ -14,17 +14,67 @@ use panic_abort as _;
|
|||
|
||||
#[no_mangle]
|
||||
fn main() -> ! {
|
||||
let mut stx = usbd::serial();
|
||||
let (mut rtx, mut rrx) = radio::claim(Channel::_20);
|
||||
let mut output = String::::new();
|
||||
let stx = Mutex::new(usbd::serial());
|
||||
let (mut hidout, _) = usbd::hid();
|
||||
let (rtx, mut rrx) = radio::claim(Channel::_20);
|
||||
|
||||
let mut output = String::<consts::U128>::new();
|
||||
|
||||
output.push_str("deviceid=").ok();
|
||||
write!(output, "{:08x}{:08x}", hal::deviceid1(), hal::deviceid0()).ok();
|
||||
write!(output, " channel={} TxPower=+8dBm app=loopback.hex\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;
|
||||
stx.write(output.as_bytes());
|
||||
let rtx = Mutex::new(rtx);
|
||||
|
||||
let t1 = async {
|
||||
let mut output = String::<consts::U128>::new();
|
||||
let mut hidbuf = usbd::Packet::new().await;
|
||||
let zlp = radio::Packet::new().await;
|
||||
|
||||
loop {
|
||||
hidout.recv(&mut hidbuf).await;
|
||||
semidap::info!("HID: {}", *hidbuf);
|
||||
|
||||
let arg = if hidbuf.len() == 1 {
|
||||
// Linux / macOS
|
||||
Some(hidbuf[0])
|
||||
} else if hidbuf.len() == 64 {
|
||||
// Windows (it zero pads the packet)
|
||||
Some(hidbuf[0])
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(arg) = arg {
|
||||
if let Ok(chan) = Channel::try_from(arg) {
|
||||
let mut rtx = rtx.lock().await;
|
||||
rtx.set_channel(chan);
|
||||
// send a zero-length packet to force the radio to listen on the new channel
|
||||
rtx.write(&zlp).await.ok();
|
||||
drop(rtx);
|
||||
|
||||
output.clear();
|
||||
writeln!(output, "now listening on channel {}", chan).ok();
|
||||
stx.lock().await.write(output.as_bytes());
|
||||
} else {
|
||||
stx.lock()
|
||||
.await
|
||||
.write(b"requested channel is out of range (11-26)\n");
|
||||
}
|
||||
} else {
|
||||
stx.lock().await.write(b"invalid HID packet\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let t2 = async {
|
||||
let mut packet = radio::Packet::new().await;
|
||||
stx.lock().await.write(output.as_bytes());
|
||||
|
||||
loop {
|
||||
let crcres = rrx.read(&mut packet).await;
|
||||
|
@ -39,7 +89,7 @@ fn main() -> ! {
|
|||
let mut busy = false;
|
||||
if crcres.is_ok() {
|
||||
packet.reverse();
|
||||
busy = rtx.write(&packet).await.is_err();
|
||||
busy = rtx.lock().await.write(&packet).await.is_err();
|
||||
}
|
||||
|
||||
output.clear();
|
||||
|
@ -64,12 +114,12 @@ fn main() -> ! {
|
|||
|
||||
if busy {
|
||||
output.push_str("didn't reply -- channel was busy\n").ok();
|
||||
stx.write(output.as_bytes());
|
||||
stx.lock().await.write(output.as_bytes());
|
||||
}
|
||||
|
||||
stx.write(output.as_bytes());
|
||||
stx.lock().await.write(output.as_bytes());
|
||||
}
|
||||
};
|
||||
|
||||
executor::run!(task)
|
||||
executor::run!(t1, t2)
|
||||
}
|
||||
|
|
1960
boards/dongle/puzzle.hex
Executable file → Normal file
1960
boards/dongle/puzzle.hex
Executable file → Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,10 @@
|
|||
// f6c27c0a5af464795d1a64c45fa1b3039f44a62a
|
||||
#![deny(unused_must_use)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::fmt::Write as _;
|
||||
use core::{fmt::Write as _, convert::TryFrom};
|
||||
|
||||
use async_core::unsync::Mutex;
|
||||
use hal::{radio::{self, Packet, Channel}, usbd, led};
|
||||
use heapless::{consts, LinearMap, String};
|
||||
use panic_abort as _;
|
||||
|
@ -25,8 +25,9 @@ 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 stx = Mutex::new(usbd::serial());
|
||||
let (mut hidout, _) = usbd::hid();
|
||||
let (rtx, mut rrx) = radio::claim(Channel::_25);
|
||||
let mut output = String::<consts::U128>::new();
|
||||
|
||||
let mut dict = LinearMap::<_, _, consts::U128>::new();
|
||||
|
@ -38,9 +39,52 @@ fn main() -> ! {
|
|||
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 rtx = Mutex::new(rtx);
|
||||
|
||||
let t1 = async {
|
||||
let mut output = String::<consts::U128>::new();
|
||||
let mut hidbuf = usbd::Packet::new().await;
|
||||
let zlp = radio::Packet::new().await;
|
||||
|
||||
loop {
|
||||
hidout.recv(&mut hidbuf).await;
|
||||
semidap::info!("HID: {}", *hidbuf);
|
||||
|
||||
let arg = if hidbuf.len() == 1 {
|
||||
// Linux / macOS
|
||||
Some(hidbuf[0])
|
||||
} else if hidbuf.len() == 64 {
|
||||
// Windows (it zero pads the packet)
|
||||
Some(hidbuf[0])
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(arg) = arg {
|
||||
if let Ok(chan) = Channel::try_from(arg) {
|
||||
let mut rtx = rtx.lock().await;
|
||||
rtx.set_channel(chan);
|
||||
// send a zero-length packet to force the radio to listen on the new channel
|
||||
rtx.write(&zlp).await.ok();
|
||||
drop(rtx);
|
||||
|
||||
output.clear();
|
||||
writeln!(output, "now listening on channel {}", chan).ok();
|
||||
stx.lock().await.write(output.as_bytes());
|
||||
} else {
|
||||
stx.lock()
|
||||
.await
|
||||
.write(b"requested channel is out of range (11-26)\n");
|
||||
}
|
||||
} else {
|
||||
stx.lock().await.write(b"invalid HID packet\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let t2 = async {
|
||||
let mut packet = Packet::new().await;
|
||||
stx.write(output.as_bytes());
|
||||
stx.lock().await.write(output.as_bytes());
|
||||
|
||||
loop {
|
||||
let crcres = rrx.read(&mut packet).await;
|
||||
|
@ -76,7 +120,7 @@ fn main() -> ! {
|
|||
});
|
||||
}
|
||||
|
||||
busy = rtx.write(&packet).await.is_err();
|
||||
busy = rtx.lock().await.write(&packet).await.is_err();
|
||||
}
|
||||
|
||||
output.clear();
|
||||
|
@ -101,12 +145,12 @@ fn main() -> ! {
|
|||
|
||||
if busy {
|
||||
output.push_str("didn't reply -- channel was busy\n").ok();
|
||||
stx.write(output.as_bytes());
|
||||
stx.lock().await.write(output.as_bytes());
|
||||
}
|
||||
|
||||
stx.write(output.as_bytes());
|
||||
stx.lock().await.write(output.as_bytes());
|
||||
}
|
||||
};
|
||||
|
||||
executor::run!(task)
|
||||
executor::run!(t1, t2)
|
||||
}
|
||||
|
|
8
common/pids/Cargo.toml
Normal file
8
common/pids/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
authors = ["Jorge Aparicio <jorge.aparicio@ferrous-systems.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
name = "pids"
|
||||
version = "0.0.0"
|
||||
|
||||
[dependencies]
|
4
common/pids/src/lib.rs
Normal file
4
common/pids/src/lib.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
#![no_std]
|
||||
|
||||
pub const LOOPBACK: u16 = 0x0309;
|
||||
pub const PUZZLE: u16 = 0x0310;
|
|
@ -57,9 +57,33 @@ If you run the `serial-term` application you should see the following output:
|
|||
``` console
|
||||
$ serial-term
|
||||
deviceid=588c06af0877c8f2 channel=20 TxPower=+8dBm app=loopback.hex
|
||||
(..)
|
||||
```
|
||||
|
||||
This line is printed by the `loopback` app on boot. It contains the device ID of the dongle, a 64-bit unique identifier (so everyone will see a different number); the radio channel that the device will use to communicate; and the transmission power of the radio in dBm.
|
||||
|
||||
At this point you should *not* get more output from `serial-term`. If you get "received N bytes" lines in output like this:
|
||||
|
||||
``` console
|
||||
$ serial-term
|
||||
deviceid=588c06af0877c8f2 channel=20 TxPower=+8dBm
|
||||
received 7 bytes (CRC=Ok(0x2459), LQI=0)
|
||||
received 5 bytes (CRC=Ok(0xdad9), LQI=0)
|
||||
received 6 bytes (CRC=Ok(0x72bb), LQI=0)
|
||||
```
|
||||
|
||||
That means the device is observing interference traffic, likely from 2.4 GHz WiFi or Bluetooth. In this scenario you should switch the listening channel to one where you don't observe interference. Use the `tools/change-channel` tool to do this. The tool takes a single argument: the new listening channel which must be in the range 11-26.
|
||||
|
||||
``` console
|
||||
$ change-channel 11
|
||||
requested channel change to channel 11
|
||||
```
|
||||
|
||||
Then you should see new output from `serial-term`:
|
||||
|
||||
``` console
|
||||
deviceid=588c06af0877c8f2 channel=20 TxPower=+8dBm
|
||||
(..)
|
||||
now listening on channel 11
|
||||
```
|
||||
|
||||
Leave the Dongle connected and the `serial-term` application running. Now we'll switch back to the Development Kit.
|
||||
|
|
25
tools/Cargo.lock
generated
25
tools/Cargo.lock
generated
|
@ -122,9 +122,19 @@ version = "0.1.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "change-channel"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"consts",
|
||||
"hidapi",
|
||||
"pids",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "consts"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
|
@ -158,7 +168,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "dk-run"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayref",
|
||||
|
@ -174,7 +184,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "dongle-flash"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ihex",
|
||||
|
@ -516,6 +526,10 @@ dependencies = [
|
|||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pids"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.17"
|
||||
|
@ -768,7 +782,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serial-term"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"consts",
|
||||
|
@ -932,9 +946,10 @@ checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
|||
|
||||
[[package]]
|
||||
name = "usb-list"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"consts",
|
||||
"pids",
|
||||
"rusb",
|
||||
]
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"change-channel",
|
||||
"dk-run",
|
||||
"dongle-flash",
|
||||
"serial-term",
|
||||
|
|
12
tools/change-channel/Cargo.toml
Normal file
12
tools/change-channel/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
authors = ["Jorge Aparicio <jorge.aparicio@ferrous-systems.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
name = "change-channel"
|
||||
version = "0.0.0"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.27"
|
||||
consts = { path = "../../advanced/common/consts" }
|
||||
hidapi = "1.2.2"
|
||||
pids = { path = "../../common/pids" }
|
33
tools/change-channel/src/main.rs
Normal file
33
tools/change-channel/src/main.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use std::env;
|
||||
|
||||
use anyhow::{anyhow, bail, ensure};
|
||||
use hidapi::HidApi;
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
let args = env::args()
|
||||
.skip(1) // skip program name
|
||||
.collect::<Vec<_>>();
|
||||
ensure!(!args.is_empty(), "expected exactly one argument");
|
||||
|
||||
let api = HidApi::new()?;
|
||||
let dev = api
|
||||
.device_list()
|
||||
.filter(|dev| dev.vendor_id() == consts::VID && check_pid(dev.product_id()))
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("device not found"))?
|
||||
.open_device(&api)?;
|
||||
|
||||
let chan = args[0].parse::<u8>()?;
|
||||
if chan < 11 || chan > 26 {
|
||||
bail!("channel is out of range (`11..=26`)")
|
||||
}
|
||||
const REPORT_ID: u8 = 0;
|
||||
dev.write(&[REPORT_ID, chan])?;
|
||||
println!("requested channel change to channel {}", chan);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_pid(pid: u16) -> bool {
|
||||
pid == pids::LOOPBACK || pid == pids::PUZZLE
|
||||
}
|
|
@ -7,4 +7,5 @@ version = "0.0.0"
|
|||
|
||||
[dependencies]
|
||||
consts = { path = "../../advanced/common/consts" }
|
||||
pids = { path = "../../common/pids" }
|
||||
rusb = "0.5.5"
|
||||
|
|
|
@ -6,8 +6,8 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
let suffix = match (desc.vendor_id(), desc.product_id()) {
|
||||
(0x1366, 0x1015) => " <- J-Link on the nRF52840 Development Kit",
|
||||
(0x1915, 0x521f) => " <- nRF52840 Dongle (in bootloader mode)",
|
||||
(0x2020, 0x0309) => " <- nRF52840 Dongle (loopback.hex)",
|
||||
(0x2020, 0x0310) => " <- nRF52840 Dongle (puzzle.hex)",
|
||||
(0x2020, pids::LOOPBACK) => " <- nRF52840 Dongle (loopback.hex)",
|
||||
(0x2020, pids::PUZZLE) => " <- nRF52840 Dongle (puzzle.hex)",
|
||||
(consts::VID, consts::PID) => " <- nRF52840 on the nRF52840 Development Kit",
|
||||
_ => "",
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue