Merge pull request #33 from ferrous-systems/change-channel

add `change-channel` tool
This commit is contained in:
Jorge Aparicio 2020-07-14 08:58:16 +00:00 committed by GitHub
commit 3f2ba0af36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 2255 additions and 1870 deletions

File diff suppressed because it is too large Load diff

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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
View 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
View file

@ -0,0 +1,4 @@
#![no_std]
pub const LOOPBACK: u16 = 0x0309;
pub const PUZZLE: u16 = 0x0310;

View file

@ -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
View file

@ -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",
]

View file

@ -1,5 +1,6 @@
[workspace]
members = [
"change-channel",
"dk-run",
"dongle-flash",
"serial-term",

View 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" }

View 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
}

View file

@ -7,4 +7,5 @@ version = "0.0.0"
[dependencies]
consts = { path = "../../advanced/common/consts" }
pids = { path = "../../common/pids" }
rusb = "0.5.5"

View file

@ -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",
_ => "",
};