Skip to content

Commit af266a0

Browse files
authored
Add support for inline mode (#4)
Add support for inline settings mode implemented in Crazyradio2 FW >= 5.1. This support is transparent from the outside: if the inline mode can be enabled, it is enabled and used. Otherwise the previous control-transfer settings are used.
1 parent 28747fc commit af266a0

File tree

1 file changed

+132
-34
lines changed

1 file changed

+132
-34
lines changed

src/lib.rs

Lines changed: 132 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ enum UsbCommand {
9393
AckEnable = 0x10,
9494
SetContCarrier = 0x20,
9595
// ScanChannels = 0x21,
96+
SetInlineMode = 0x23,
9697
SetPacketLossSimulation = 0x30,
9798
LaunchBootloader = 0xff,
9899
}
@@ -128,11 +129,13 @@ pub struct Crazyradio {
128129
device_handle: rusb::DeviceHandle<rusb::GlobalContext>,
129130

130131
cache_settings: bool,
132+
inline_mode: bool,
131133

132134
// Settings cache
133135
channel: Channel,
134136
address: [u8; 5],
135137
datarate: Datarate,
138+
ack_enable: bool,
136139
}
137140

138141
impl Crazyradio {
@@ -190,10 +193,13 @@ impl Crazyradio {
190193
device_handle,
191194

192195
cache_settings: true,
196+
inline_mode: false,
193197

194198
channel: Channel::from_number(2).unwrap(),
195199
address: [0xe7; 5],
196200
datarate: Datarate::Dr2M,
201+
202+
ack_enable: true,
197203
};
198204

199205
cr.reset()?;
@@ -220,6 +226,9 @@ impl Crazyradio {
220226
let prev_cache_settings = self.cache_settings;
221227
self.cache_settings = false;
222228

229+
// Try to set inline mode, ignore failure as this is not fatal (old radio FW do not implement it and will just be slower)
230+
_ = self.set_inline_mode(true);
231+
223232
self.set_datarate(Datarate::Dr2M)?;
224233
self.set_channel(Channel::from_number(2).unwrap())?;
225234
self.set_cont_carrier(false)?;
@@ -248,7 +257,7 @@ impl Crazyradio {
248257

249258
/// Set the radio channel.
250259
pub fn set_channel(&mut self, channel: Channel) -> Result<()> {
251-
if !self.cache_settings || self.channel != channel {
260+
if !self.inline_mode && (!self.cache_settings || self.channel != channel) {
252261
self.device_handle.write_control(
253262
0x40,
254263
UsbCommand::SetRadioChannel as u8,
@@ -257,15 +266,16 @@ impl Crazyradio {
257266
&[],
258267
Duration::from_secs(1),
259268
)?;
260-
self.channel = channel;
261269
}
262270

271+
self.channel = channel;
272+
263273
Ok(())
264274
}
265275

266276
/// Set the datarate.
267277
pub fn set_datarate(&mut self, datarate: Datarate) -> Result<()> {
268-
if !self.cache_settings || self.datarate != datarate {
278+
if !self.inline_mode && (!self.cache_settings || self.datarate != datarate) {
269279
self.device_handle.write_control(
270280
0x40,
271281
UsbCommand::SetDataRate as u8,
@@ -274,15 +284,16 @@ impl Crazyradio {
274284
&[],
275285
Duration::from_secs(1),
276286
)?;
277-
self.datarate = datarate;
278287
}
279288

289+
self.datarate = datarate;
290+
280291
Ok(())
281292
}
282293

283294
/// Set the radio address.
284295
pub fn set_address(&mut self, address: &[u8; 5]) -> Result<()> {
285-
if !self.cache_settings || self.address != *address {
296+
if !self.inline_mode && (!self.cache_settings || self.address != *address) {
286297
self.device_handle.write_control(
287298
0x40,
288299
UsbCommand::SetRadioAddress as u8,
@@ -291,6 +302,9 @@ impl Crazyradio {
291302
address,
292303
Duration::from_secs(1),
293304
)?;
305+
}
306+
307+
if self.cache_settings || self.inline_mode {
294308
self.address.copy_from_slice(address);
295309
}
296310

@@ -367,14 +381,19 @@ impl Crazyradio {
367381
///
368382
/// Should be disabled when sending broadcast packets.
369383
pub fn set_ack_enable(&mut self, ack_enable: bool) -> Result<()> {
370-
self.device_handle.write_control(
371-
0x40,
372-
UsbCommand::AckEnable as u8,
373-
ack_enable as u16,
374-
0,
375-
&[],
376-
Duration::from_secs(1),
377-
)?;
384+
if !self.inline_mode && ack_enable != self.ack_enable {
385+
self.device_handle.write_control(
386+
0x40,
387+
UsbCommand::AckEnable as u8,
388+
ack_enable as u16,
389+
0,
390+
&[],
391+
Duration::from_secs(1),
392+
)?;
393+
}
394+
395+
self.ack_enable = ack_enable;
396+
378397
Ok(())
379398
}
380399

@@ -431,6 +450,33 @@ impl Crazyradio {
431450
Ok(())
432451
}
433452

453+
/// Set inline-settings USB protocol mode
454+
///
455+
/// When this mode is enabled, setting channel, datarate, address and
456+
/// ack_enable will become cached operations, and these settings
457+
/// will be sent as header to the data over USB. This increases performance
458+
/// when communicating with more than one PRX.
459+
///
460+
/// This mode, if available, is activated by default when creating the Crazyradio
461+
/// object.
462+
///
463+
/// This mode is only available with Crazyradio 2.0+
464+
pub fn set_inline_mode(&mut self, inline_mode_enable: bool) -> Result<()> {
465+
let setting = inline_mode_enable.then_some(1).unwrap_or(0);
466+
467+
self.device_handle.write_control(
468+
0x40,
469+
UsbCommand::SetInlineMode as u8,
470+
setting,
471+
0,
472+
&[],
473+
Duration::from_secs(1),
474+
)?;
475+
self.inline_mode = inline_mode_enable;
476+
477+
Ok(())
478+
}
479+
434480
/// Set packet loss simulation.
435481
///
436482
pub fn set_packet_loss_simulation(&mut self, packet_loss_percent: u8, ack_loss_percent: u8) -> Result<()> {
@@ -465,28 +511,34 @@ impl Crazyradio {
465511
/// be truncated. The length of the ack payload is returned
466512
/// in Ack::length.
467513
pub fn send_packet(&mut self, data: &[u8], ack_data: &mut [u8]) -> Result<Ack> {
468-
self.device_handle
469-
.write_bulk(0x01, data, Duration::from_secs(1))?;
470-
let mut received_data = [0u8; 33];
471-
let received =
472-
self.device_handle
473-
.read_bulk(0x81, &mut received_data, Duration::from_secs(1))?;
474514

475-
if ack_data.len() <= 32 {
476-
ack_data.copy_from_slice(&received_data[1..ack_data.len() + 1]);
515+
if self.inline_mode {
516+
self.send_inline(data, Some(ack_data))
477517
} else {
478-
ack_data
479-
.split_at_mut(32)
480-
.0
481-
.copy_from_slice(&received_data[1..33]);
482-
}
518+
self.device_handle
519+
.write_bulk(0x01, data, Duration::from_secs(1))?;
520+
let mut received_data = [0u8; 33];
521+
let received =
522+
self.device_handle
523+
.read_bulk(0x81, &mut received_data, Duration::from_secs(1))?;
524+
525+
if ack_data.len() <= 32 {
526+
ack_data.copy_from_slice(&received_data[1..ack_data.len() + 1]);
527+
} else {
528+
ack_data
529+
.split_at_mut(32)
530+
.0
531+
.copy_from_slice(&received_data[1..33]);
532+
}
483533

484-
Ok(Ack {
485-
received: received_data[0] & 0x01 != 0,
486-
power_detector: received_data[0] & 0x02 != 0,
487-
retry: ((received_data[0] & 0xf0) >> 4) as usize,
488-
length: received - 1,
489-
})
534+
Ok(Ack {
535+
received: received_data[0] & 0x01 != 0,
536+
power_detector: received_data[0] & 0x02 != 0,
537+
retry: ((received_data[0] & 0xf0) >> 4) as usize,
538+
length: received - 1,
539+
})
540+
}
541+
490542
}
491543

492544
/// Send a data packet without caring for Ack (for broadcast communication).
@@ -495,11 +547,57 @@ impl Crazyradio {
495547
///
496548
/// * `data`: Up to 32 bytes of data to be send.
497549
pub fn send_packet_no_ack(&mut self, data: &[u8]) -> Result<()> {
498-
self.device_handle
499-
.write_bulk(0x01, data, Duration::from_secs(1))?;
550+
if self.inline_mode {
551+
self.send_inline(data, None)?;
552+
} else {
553+
self.device_handle
554+
.write_bulk(0x01, data, Duration::from_secs(1))?;
555+
}
500556

501557
Ok(())
502558
}
559+
560+
fn send_inline(&mut self, data: &[u8], ack_data: Option<&mut [u8]>) -> Result<Ack> {
561+
const OUT_HEADER_LENGTH: usize=8;
562+
const IN_HEADER_LENGTH: usize = 2;
563+
564+
const OUT_FIELD2_ACK_ENABLE: u8 = 0x10;
565+
566+
const IN_HEADER_ACK_RECEIVED: u8 = 0x01;
567+
const IN_HEADER_POWER_DETECTOR: u8 = 0x02;
568+
const _IN_HEADER_INVALID_SETTING: u8 = 0x04;
569+
const IN_HEADER_RETRY_MASK: u8 = 0xf0;
570+
const IN_HEADER_RETRY_SHIFT: u8 = 4;
571+
572+
// Assemble out command
573+
let mut command = vec![];
574+
command.push((OUT_HEADER_LENGTH + data.len()) as u8);
575+
let mut field2 = self.datarate as u8;
576+
if self.ack_enable {
577+
field2 |= OUT_FIELD2_ACK_ENABLE;
578+
}
579+
command.push(field2);
580+
command.push(self.channel.into());
581+
command.extend_from_slice(&self.address);
582+
command.extend_from_slice(&data);
583+
584+
let mut answer = [0u8; 64];
585+
self.device_handle.write_bulk(0x01, &command, Duration::from_secs(1))?;
586+
self.device_handle.read_bulk(0x81, &mut answer, Duration::from_secs(1))?;
587+
588+
// Decode answer
589+
let payload_length = (answer[0] as usize) - 2;
590+
if let Some(ack_data) = ack_data {
591+
ack_data[0..payload_length].copy_from_slice(&answer[IN_HEADER_LENGTH..(IN_HEADER_LENGTH+payload_length)]);
592+
}
593+
594+
Ok(Ack {
595+
received: answer[1] & IN_HEADER_ACK_RECEIVED != 0,
596+
power_detector: answer[1] & IN_HEADER_POWER_DETECTOR != 0,
597+
retry: ((answer[1] & IN_HEADER_RETRY_MASK) >> IN_HEADER_RETRY_SHIFT) as usize,
598+
length: payload_length,
599+
})
600+
}
503601
}
504602

505603
/// # Async implementations

0 commit comments

Comments
 (0)