Skip to main content

pamoja_radios/sx126x/
command.rs

1//! The SX126x command set: opcodes and the bytes each command sends.
2//!
3//! A command is an opcode followed by its parameters, multi-byte values most significant
4//! byte first, framed by NSS in a single SPI transaction, as chapter 10 of the SX1261/2
5//! datasheet describes. [`Command`] holds the bytes of one command without allocating,
6//! and the builders here fill it from typed parameters. A command that answers comes as
7//! a [`Query`]: the bytes to send, then how many bytes to read back while NSS stays low.
8//! WriteRegister and WriteBuffer send a header built here, then their data, in the same
9//! transaction.
10
11use super::config::{
12    FallbackMode, LoraModulation, LoraPacket, PaConfig, PacketType, RampTime, RegulatorMode,
13    StandbyMode, TcxoVoltage,
14};
15use super::irq::Irq;
16
17/// The opcodes of Tables 11-1 to 11-5.
18pub mod opcode {
19    /// SetSleep.
20    pub const SET_SLEEP: u8 = 0x84;
21    /// SetStandby.
22    pub const SET_STANDBY: u8 = 0x80;
23    /// SetFs.
24    pub const SET_FS: u8 = 0xC1;
25    /// SetTx.
26    pub const SET_TX: u8 = 0x83;
27    /// SetRx.
28    pub const SET_RX: u8 = 0x82;
29    /// StopTimerOnPreamble.
30    pub const STOP_TIMER_ON_PREAMBLE: u8 = 0x9F;
31    /// SetRxDutyCycle.
32    pub const SET_RX_DUTY_CYCLE: u8 = 0x94;
33    /// SetCad.
34    pub const SET_CAD: u8 = 0xC5;
35    /// SetTxContinuousWave.
36    pub const SET_TX_CONTINUOUS_WAVE: u8 = 0xD1;
37    /// SetTxInfinitePreamble.
38    pub const SET_TX_INFINITE_PREAMBLE: u8 = 0xD2;
39    /// SetRegulatorMode.
40    pub const SET_REGULATOR_MODE: u8 = 0x96;
41    /// Calibrate.
42    pub const CALIBRATE: u8 = 0x89;
43    /// CalibrateImage.
44    pub const CALIBRATE_IMAGE: u8 = 0x98;
45    /// SetPaConfig.
46    pub const SET_PA_CONFIG: u8 = 0x95;
47    /// SetRxTxFallbackMode.
48    pub const SET_RX_TX_FALLBACK_MODE: u8 = 0x93;
49    /// WriteRegister.
50    pub const WRITE_REGISTER: u8 = 0x0D;
51    /// ReadRegister.
52    pub const READ_REGISTER: u8 = 0x1D;
53    /// WriteBuffer.
54    pub const WRITE_BUFFER: u8 = 0x0E;
55    /// ReadBuffer.
56    pub const READ_BUFFER: u8 = 0x1E;
57    /// SetDioIrqParams.
58    pub const SET_DIO_IRQ_PARAMS: u8 = 0x08;
59    /// GetIrqStatus.
60    pub const GET_IRQ_STATUS: u8 = 0x12;
61    /// ClearIrqStatus.
62    pub const CLEAR_IRQ_STATUS: u8 = 0x02;
63    /// SetDIO2AsRfSwitchCtrl.
64    pub const SET_DIO2_AS_RF_SWITCH_CTRL: u8 = 0x9D;
65    /// SetDIO3AsTcxoCtrl.
66    pub const SET_DIO3_AS_TCXO_CTRL: u8 = 0x97;
67    /// SetRfFrequency.
68    pub const SET_RF_FREQUENCY: u8 = 0x86;
69    /// SetPacketType.
70    pub const SET_PACKET_TYPE: u8 = 0x8A;
71    /// GetPacketType.
72    pub const GET_PACKET_TYPE: u8 = 0x11;
73    /// SetTxParams.
74    pub const SET_TX_PARAMS: u8 = 0x8E;
75    /// SetModulationParams.
76    pub const SET_MODULATION_PARAMS: u8 = 0x8B;
77    /// SetPacketParams.
78    pub const SET_PACKET_PARAMS: u8 = 0x8C;
79    /// SetCadParams.
80    pub const SET_CAD_PARAMS: u8 = 0x88;
81    /// SetBufferBaseAddress.
82    pub const SET_BUFFER_BASE_ADDRESS: u8 = 0x8F;
83    /// SetLoRaSymbNumTimeout.
84    pub const SET_LORA_SYMB_NUM_TIMEOUT: u8 = 0xA0;
85    /// GetStatus.
86    pub const GET_STATUS: u8 = 0xC0;
87    /// GetRssiInst.
88    pub const GET_RSSI_INST: u8 = 0x15;
89    /// GetRxBufferStatus.
90    pub const GET_RX_BUFFER_STATUS: u8 = 0x13;
91    /// GetPacketStatus.
92    pub const GET_PACKET_STATUS: u8 = 0x14;
93    /// GetDeviceErrors.
94    pub const GET_DEVICE_ERRORS: u8 = 0x17;
95    /// ClearDeviceErrors.
96    pub const CLEAR_DEVICE_ERRORS: u8 = 0x07;
97    /// GetStats.
98    pub const GET_STATS: u8 = 0x10;
99    /// ResetStats.
100    pub const RESET_STATS: u8 = 0x00;
101}
102
103/// The byte a host sends while it clocks an answer back.
104pub const NOP: u8 = 0x00;
105
106/// The calibration parameter that recalibrates every block, from Table 13-18.
107pub const CALIBRATE_ALL: u8 = 0x7F;
108
109/// The longest command the builders produce, in bytes.
110pub const MAX_LEN: usize = 10;
111
112/// The bytes of one command.
113///
114/// # Examples
115///
116/// ```
117/// use pamoja_radios::sx126x::command;
118///
119/// assert_eq!(command::set_rf_frequency(0x3641_999A).as_bytes(), [0x86, 0x36, 0x41, 0x99, 0x9A]);
120/// ```
121#[derive(Clone, Copy, PartialEq, Eq, Hash)]
122pub struct Command {
123    bytes: [u8; MAX_LEN],
124    len: u8,
125}
126
127impl Command {
128    /// Builds a command from its opcode and its parameter bytes.
129    ///
130    /// # Arguments
131    ///
132    /// * `opcode` - the opcode.
133    /// * `params` - the parameters, at most nine bytes.
134    ///
135    /// # Returns
136    ///
137    /// The command.
138    ///
139    /// # Panics
140    ///
141    /// Panics if `params` is longer than nine bytes.
142    pub const fn new(opcode: u8, params: &[u8]) -> Command {
143        assert!(
144            params.len() < MAX_LEN,
145            "an SX126x command carries at most nine parameter bytes"
146        );
147        let mut bytes = [0u8; MAX_LEN];
148        bytes[0] = opcode;
149        let mut index = 0;
150        while index < params.len() {
151            bytes[index + 1] = params[index];
152            index += 1;
153        }
154        Command {
155            bytes,
156            len: params.len() as u8 + 1,
157        }
158    }
159
160    /// Returns the bytes to send.
161    ///
162    /// # Returns
163    ///
164    /// The opcode followed by the parameters.
165    pub fn as_bytes(&self) -> &[u8] {
166        &self.bytes[..usize::from(self.len)]
167    }
168
169    /// Returns the opcode.
170    ///
171    /// # Returns
172    ///
173    /// The first byte.
174    pub const fn opcode(&self) -> u8 {
175        self.bytes[0]
176    }
177}
178
179impl core::fmt::Debug for Command {
180    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
181        f.debug_tuple("Command").field(&self.as_bytes()).finish()
182    }
183}
184
185/// A command that the chip answers in the same transaction.
186#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
187pub struct Query {
188    /// The bytes to send, including the NOP bytes that precede the answer.
189    pub command: Command,
190    /// How many bytes of answer to read after them.
191    pub answer_len: usize,
192}
193
194const fn u24(value: u32) -> [u8; 3] {
195    [(value >> 16) as u8, (value >> 8) as u8, value as u8]
196}
197
198/// SetSleep: puts the chip to sleep, from Table 13-2.
199///
200/// # Arguments
201///
202/// * `warm_start` - `true` to keep the configuration in retention memory.
203/// * `rtc_wake` - `true` to wake on the RTC timeout.
204///
205/// # Returns
206///
207/// The command.
208pub const fn set_sleep(warm_start: bool, rtc_wake: bool) -> Command {
209    Command::new(
210        opcode::SET_SLEEP,
211        &[((warm_start as u8) << 2) | rtc_wake as u8],
212    )
213}
214
215/// SetStandby: puts the chip in a standby mode.
216///
217/// # Arguments
218///
219/// * `mode` - the oscillator the chip stands by on.
220///
221/// # Returns
222///
223/// The command.
224pub const fn set_standby(mode: StandbyMode) -> Command {
225    Command::new(opcode::SET_STANDBY, &[mode.code()])
226}
227
228/// SetFs: puts the chip in frequency synthesis mode.
229///
230/// # Returns
231///
232/// The command.
233pub const fn set_fs() -> Command {
234    Command::new(opcode::SET_FS, &[])
235}
236
237/// SetTx: starts a transmission.
238///
239/// # Arguments
240///
241/// * `timeout` - the 24-bit timeout word; [`NO_TIMEOUT`](super::config::NO_TIMEOUT)
242///   transmits once with no timeout.
243///
244/// # Returns
245///
246/// The command.
247pub const fn set_tx(timeout: u32) -> Command {
248    Command::new(opcode::SET_TX, &u24(timeout))
249}
250
251/// SetRx: starts receiving.
252///
253/// # Arguments
254///
255/// * `timeout` - the 24-bit timeout word; [`NO_TIMEOUT`](super::config::NO_TIMEOUT)
256///   receives one packet, [`RX_CONTINUOUS`](super::config::RX_CONTINUOUS) keeps
257///   listening.
258///
259/// # Returns
260///
261/// The command.
262pub const fn set_rx(timeout: u32) -> Command {
263    Command::new(opcode::SET_RX, &u24(timeout))
264}
265
266/// StopTimerOnPreamble: chooses what stops the receive timeout.
267///
268/// # Arguments
269///
270/// * `on_preamble` - `true` to stop the timer on a preamble, `false` on a header or
271///   sync word.
272///
273/// # Returns
274///
275/// The command.
276pub const fn stop_timer_on_preamble(on_preamble: bool) -> Command {
277    Command::new(opcode::STOP_TIMER_ON_PREAMBLE, &[on_preamble as u8])
278}
279
280/// SetRxDutyCycle: alternates the chip between listening and sleeping.
281///
282/// # Arguments
283///
284/// * `rx_period` - the 24-bit listening period word.
285/// * `sleep_period` - the 24-bit sleeping period word.
286///
287/// # Returns
288///
289/// The command.
290pub const fn set_rx_duty_cycle(rx_period: u32, sleep_period: u32) -> Command {
291    let rx = u24(rx_period);
292    let sleep = u24(sleep_period);
293    Command::new(
294        opcode::SET_RX_DUTY_CYCLE,
295        &[rx[0], rx[1], rx[2], sleep[0], sleep[1], sleep[2]],
296    )
297}
298
299/// SetCad: starts channel activity detection.
300///
301/// # Returns
302///
303/// The command.
304pub const fn set_cad() -> Command {
305    Command::new(opcode::SET_CAD, &[])
306}
307
308/// SetTxContinuousWave: transmits an unmodulated carrier, a test command.
309///
310/// # Returns
311///
312/// The command.
313pub const fn set_tx_continuous_wave() -> Command {
314    Command::new(opcode::SET_TX_CONTINUOUS_WAVE, &[])
315}
316
317/// SetTxInfinitePreamble: transmits preamble symbols without end, a test command.
318///
319/// # Returns
320///
321/// The command.
322pub const fn set_tx_infinite_preamble() -> Command {
323    Command::new(opcode::SET_TX_INFINITE_PREAMBLE, &[])
324}
325
326/// SetRegulatorMode: selects the LDO or the DC-DC converter.
327///
328/// # Arguments
329///
330/// * `mode` - the regulator.
331///
332/// # Returns
333///
334/// The command.
335pub const fn set_regulator_mode(mode: RegulatorMode) -> Command {
336    Command::new(opcode::SET_REGULATOR_MODE, &[mode.code()])
337}
338
339/// Calibrate: recalibrates blocks of the chip, from Table 13-18.
340///
341/// # Arguments
342///
343/// * `blocks` - one bit per block, such as [`CALIBRATE_ALL`].
344///
345/// # Returns
346///
347/// The command.
348pub const fn calibrate(blocks: u8) -> Command {
349    Command::new(opcode::CALIBRATE, &[blocks])
350}
351
352/// CalibrateImage: calibrates image rejection over a band.
353///
354/// # Arguments
355///
356/// * `codes` - `freq1` and `freq2`, such as
357///   [`image_calibration`](super::config::image_calibration) returns.
358///
359/// # Returns
360///
361/// The command.
362pub const fn calibrate_image(codes: [u8; 2]) -> Command {
363    Command::new(opcode::CALIBRATE_IMAGE, &codes)
364}
365
366/// SetPaConfig: configures the power amplifier.
367///
368/// # Arguments
369///
370/// * `pa` - the amplifier configuration.
371///
372/// # Returns
373///
374/// The command.
375pub const fn set_pa_config(pa: PaConfig) -> Command {
376    Command::new(opcode::SET_PA_CONFIG, &pa.to_params())
377}
378
379/// SetRxTxFallbackMode: chooses the mode the chip returns to after a packet.
380///
381/// # Arguments
382///
383/// * `mode` - the fallback mode.
384///
385/// # Returns
386///
387/// The command.
388pub const fn set_rx_tx_fallback_mode(mode: FallbackMode) -> Command {
389    Command::new(opcode::SET_RX_TX_FALLBACK_MODE, &[mode.code()])
390}
391
392/// The header of WriteRegister; the register bytes follow in the same transaction.
393///
394/// # Arguments
395///
396/// * `address` - the first register to write.
397///
398/// # Returns
399///
400/// The opcode and the address.
401pub const fn write_register(address: u16) -> Command {
402    let address = address.to_be_bytes();
403    Command::new(opcode::WRITE_REGISTER, &address)
404}
405
406/// ReadRegister: reads consecutive registers.
407///
408/// # Arguments
409///
410/// * `address` - the first register to read.
411/// * `len` - how many registers to read.
412///
413/// # Returns
414///
415/// The opcode, the address, and the NOP before the answer.
416pub const fn read_register(address: u16, len: usize) -> Query {
417    let address = address.to_be_bytes();
418    Query {
419        command: Command::new(opcode::READ_REGISTER, &[address[0], address[1], NOP]),
420        answer_len: len,
421    }
422}
423
424/// The header of WriteBuffer; the payload follows in the same transaction.
425///
426/// # Arguments
427///
428/// * `offset` - where in the data buffer the payload starts.
429///
430/// # Returns
431///
432/// The opcode and the offset.
433pub const fn write_buffer(offset: u8) -> Command {
434    Command::new(opcode::WRITE_BUFFER, &[offset])
435}
436
437/// ReadBuffer: reads the data buffer.
438///
439/// # Arguments
440///
441/// * `offset` - where in the data buffer to start.
442/// * `len` - how many bytes to read.
443///
444/// # Returns
445///
446/// The opcode, the offset, and the NOP before the answer.
447pub const fn read_buffer(offset: u8, len: usize) -> Query {
448    Query {
449        command: Command::new(opcode::READ_BUFFER, &[offset, NOP]),
450        answer_len: len,
451    }
452}
453
454/// SetDioIrqParams: enables interrupts and routes them to the DIO lines.
455///
456/// # Arguments
457///
458/// * `irq` - the interrupts to enable.
459/// * `dio1` - the interrupts that raise DIO1.
460/// * `dio2` - the interrupts that raise DIO2.
461/// * `dio3` - the interrupts that raise DIO3.
462///
463/// # Returns
464///
465/// The command.
466pub const fn set_dio_irq_params(irq: Irq, dio1: Irq, dio2: Irq, dio3: Irq) -> Command {
467    let [a, b] = irq.to_bytes();
468    let [c, d] = dio1.to_bytes();
469    let [e, f] = dio2.to_bytes();
470    let [g, h] = dio3.to_bytes();
471    Command::new(opcode::SET_DIO_IRQ_PARAMS, &[a, b, c, d, e, f, g, h])
472}
473
474/// GetIrqStatus: reads the pending interrupts.
475///
476/// # Returns
477///
478/// The query, answered by the two IrqStatus bytes.
479pub const fn get_irq_status() -> Query {
480    Query {
481        command: Command::new(opcode::GET_IRQ_STATUS, &[NOP]),
482        answer_len: 2,
483    }
484}
485
486/// ClearIrqStatus: clears interrupts.
487///
488/// # Arguments
489///
490/// * `irq` - the interrupts to clear.
491///
492/// # Returns
493///
494/// The command.
495pub const fn clear_irq_status(irq: Irq) -> Command {
496    Command::new(opcode::CLEAR_IRQ_STATUS, &irq.to_bytes())
497}
498
499/// SetDIO2AsRfSwitchCtrl: lets DIO2 drive an RF switch, high in TX.
500///
501/// # Arguments
502///
503/// * `enable` - `true` to drive the switch from DIO2.
504///
505/// # Returns
506///
507/// The command.
508pub const fn set_dio2_as_rf_switch(enable: bool) -> Command {
509    Command::new(opcode::SET_DIO2_AS_RF_SWITCH_CTRL, &[enable as u8])
510}
511
512/// SetDIO3AsTcxoCtrl: powers a TCXO from DIO3.
513///
514/// # Arguments
515///
516/// * `voltage` - the supply voltage.
517/// * `delay` - the 24-bit word for how long the TCXO takes to settle.
518///
519/// # Returns
520///
521/// The command.
522pub const fn set_dio3_as_tcxo(voltage: TcxoVoltage, delay: u32) -> Command {
523    let delay = u24(delay);
524    Command::new(
525        opcode::SET_DIO3_AS_TCXO_CTRL,
526        &[voltage.code(), delay[0], delay[1], delay[2]],
527    )
528}
529
530/// SetRfFrequency: tunes the synthesizer.
531///
532/// # Arguments
533///
534/// * `word` - the frequency word, such as
535///   [`frequency_word`](super::config::frequency_word) returns.
536///
537/// # Returns
538///
539/// The command.
540pub const fn set_rf_frequency(word: u32) -> Command {
541    Command::new(opcode::SET_RF_FREQUENCY, &word.to_be_bytes())
542}
543
544/// SetPacketType: selects the modem, the first command of any configuration.
545///
546/// # Arguments
547///
548/// * `packet_type` - the modem.
549///
550/// # Returns
551///
552/// The command.
553pub const fn set_packet_type(packet_type: PacketType) -> Command {
554    Command::new(opcode::SET_PACKET_TYPE, &[packet_type.code()])
555}
556
557/// GetPacketType: reads the selected modem.
558///
559/// # Returns
560///
561/// The query, answered by the packet type byte.
562pub const fn get_packet_type() -> Query {
563    Query {
564        command: Command::new(opcode::GET_PACKET_TYPE, &[NOP]),
565        answer_len: 1,
566    }
567}
568
569/// SetTxParams: sets the output power and the ramp time.
570///
571/// # Arguments
572///
573/// * `power_dbm` - the power setting, in the range of the selected amplifier.
574/// * `ramp` - the ramp time.
575///
576/// # Returns
577///
578/// The command.
579pub const fn set_tx_params(power_dbm: i8, ramp: RampTime) -> Command {
580    Command::new(opcode::SET_TX_PARAMS, &[power_dbm as u8, ramp.code()])
581}
582
583/// SetModulationParams, for LoRa.
584///
585/// # Arguments
586///
587/// * `modulation` - the spreading factor, bandwidth, coding rate, and low data rate
588///   optimization.
589///
590/// # Returns
591///
592/// The command.
593pub const fn set_lora_modulation_params(modulation: LoraModulation) -> Command {
594    Command::new(opcode::SET_MODULATION_PARAMS, &modulation.to_params())
595}
596
597/// SetPacketParams, for LoRa.
598///
599/// # Arguments
600///
601/// * `packet` - the preamble, header type, payload length, CRC, and IQ polarity.
602///
603/// # Returns
604///
605/// The command.
606pub const fn set_lora_packet_params(packet: LoraPacket) -> Command {
607    Command::new(opcode::SET_PACKET_PARAMS, &packet.to_params())
608}
609
610/// SetCadParams: configures channel activity detection, from Table 13-71.
611///
612/// # Arguments
613///
614/// * `symbols` - the cadSymbolNum code, 0x00 to 0x04 for 1 to 16 symbols.
615/// * `detect_peak` - cadDetPeak.
616/// * `detect_min` - cadDetMin.
617/// * `exit_mode` - cadExitMode: 0x00 to return to standby, 0x01 to receive on activity.
618/// * `timeout` - the 24-bit receive timeout word after activity is detected.
619///
620/// # Returns
621///
622/// The command.
623pub const fn set_cad_params(
624    symbols: u8,
625    detect_peak: u8,
626    detect_min: u8,
627    exit_mode: u8,
628    timeout: u32,
629) -> Command {
630    let timeout = u24(timeout);
631    Command::new(
632        opcode::SET_CAD_PARAMS,
633        &[
634            symbols,
635            detect_peak,
636            detect_min,
637            exit_mode,
638            timeout[0],
639            timeout[1],
640            timeout[2],
641        ],
642    )
643}
644
645/// SetBufferBaseAddress: sets where transmit and receive data start in the buffer.
646///
647/// # Arguments
648///
649/// * `tx` - the transmit base address.
650/// * `rx` - the receive base address.
651///
652/// # Returns
653///
654/// The command.
655pub const fn set_buffer_base_address(tx: u8, rx: u8) -> Command {
656    Command::new(opcode::SET_BUFFER_BASE_ADDRESS, &[tx, rx])
657}
658
659/// SetLoRaSymbNumTimeout: sets how many symbols confirm a detected packet.
660///
661/// # Arguments
662///
663/// * `symbols` - the symbol count: even numbers up to 64, then steps of 8 up to 248.
664///
665/// # Returns
666///
667/// The command.
668pub const fn set_lora_symbol_timeout(symbols: u8) -> Command {
669    Command::new(opcode::SET_LORA_SYMB_NUM_TIMEOUT, &[symbols])
670}
671
672/// GetStatus: reads the status byte.
673///
674/// # Returns
675///
676/// The query, answered by the status byte.
677pub const fn get_status() -> Query {
678    Query {
679        command: Command::new(opcode::GET_STATUS, &[]),
680        answer_len: 1,
681    }
682}
683
684/// GetRssiInst: reads the instantaneous RSSI while receiving.
685///
686/// # Returns
687///
688/// The query, answered by the RssiInst byte.
689pub const fn get_rssi_inst() -> Query {
690    Query {
691        command: Command::new(opcode::GET_RSSI_INST, &[NOP]),
692        answer_len: 1,
693    }
694}
695
696/// GetRxBufferStatus: reads where the last payload sits.
697///
698/// # Returns
699///
700/// The query, answered by PayloadLengthRx and RxStartBufferPointer.
701pub const fn get_rx_buffer_status() -> Query {
702    Query {
703        command: Command::new(opcode::GET_RX_BUFFER_STATUS, &[NOP]),
704        answer_len: 2,
705    }
706}
707
708/// GetPacketStatus: reads the signal levels of the last packet.
709///
710/// # Returns
711///
712/// The query, answered in LoRa by RssiPkt, SnrPkt, and SignalRssiPkt.
713pub const fn get_packet_status() -> Query {
714    Query {
715        command: Command::new(opcode::GET_PACKET_STATUS, &[NOP]),
716        answer_len: 3,
717    }
718}
719
720/// GetDeviceErrors: reads the recorded errors.
721///
722/// # Returns
723///
724/// The query, answered by the two OpError bytes.
725pub const fn get_device_errors() -> Query {
726    Query {
727        command: Command::new(opcode::GET_DEVICE_ERRORS, &[NOP]),
728        answer_len: 2,
729    }
730}
731
732/// ClearDeviceErrors: clears every recorded error.
733///
734/// # Returns
735///
736/// The command.
737pub const fn clear_device_errors() -> Command {
738    Command::new(opcode::CLEAR_DEVICE_ERRORS, &[0x00, 0x00])
739}
740
741/// GetStats: reads the packet counters.
742///
743/// # Returns
744///
745/// The query, answered in LoRa by the received, CRC error, and header error counts.
746pub const fn get_stats() -> Query {
747    Query {
748        command: Command::new(opcode::GET_STATS, &[NOP]),
749        answer_len: 6,
750    }
751}
752
753/// ResetStats: zeroes the packet counters, an opcode and six zero bytes.
754///
755/// # Returns
756///
757/// The command.
758pub const fn reset_stats() -> Command {
759    Command::new(opcode::RESET_STATS, &[0; 6])
760}
761
762#[cfg(test)]
763mod tests {
764    use super::super::config::{CodingRate, LoraBandwidth, NO_TIMEOUT, RX_CONTINUOUS};
765    use super::*;
766
767    #[test]
768    fn the_operating_mode_commands_follow_table_11_1() {
769        assert_eq!(set_sleep(false, false).as_bytes(), [0x84, 0x00]);
770        assert_eq!(set_sleep(true, true).as_bytes(), [0x84, 0x05]);
771        assert_eq!(set_standby(StandbyMode::Rc).as_bytes(), [0x80, 0x00]);
772        assert_eq!(set_standby(StandbyMode::Xosc).as_bytes(), [0x80, 0x01]);
773        assert_eq!(set_fs().as_bytes(), [0xC1]);
774        assert_eq!(set_tx(NO_TIMEOUT).as_bytes(), [0x83, 0x00, 0x00, 0x00]);
775        assert_eq!(set_rx(RX_CONTINUOUS).as_bytes(), [0x82, 0xFF, 0xFF, 0xFF]);
776        assert_eq!(set_rx(0x01_2345).as_bytes(), [0x82, 0x01, 0x23, 0x45]);
777        assert_eq!(stop_timer_on_preamble(true).as_bytes(), [0x9F, 0x01]);
778        assert_eq!(
779            set_rx_duty_cycle(0x00_0100, 0x02_0000).as_bytes(),
780            [0x94, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00]
781        );
782        assert_eq!(set_cad().as_bytes(), [0xC5]);
783        assert_eq!(set_tx_continuous_wave().as_bytes(), [0xD1]);
784        assert_eq!(set_tx_infinite_preamble().as_bytes(), [0xD2]);
785        assert_eq!(
786            set_regulator_mode(RegulatorMode::DcDc).as_bytes(),
787            [0x96, 0x01]
788        );
789        assert_eq!(calibrate(CALIBRATE_ALL).as_bytes(), [0x89, 0x7F]);
790        assert_eq!(calibrate_image([0xD7, 0xDA]).as_bytes(), [0x98, 0xD7, 0xDA]);
791        assert_eq!(
792            set_pa_config(PaConfig::SX1262_22_DBM).as_bytes(),
793            [0x95, 0x04, 0x07, 0x00, 0x01]
794        );
795        assert_eq!(
796            set_rx_tx_fallback_mode(FallbackMode::StandbyXosc).as_bytes(),
797            [0x93, 0x30]
798        );
799    }
800
801    #[test]
802    fn the_register_and_buffer_commands_follow_tables_13_24_to_13_27() {
803        assert_eq!(write_register(0x0740).as_bytes(), [0x0D, 0x07, 0x40]);
804        let read = read_register(0x08D8, 1);
805        assert_eq!(read.command.as_bytes(), [0x1D, 0x08, 0xD8, 0x00]);
806        assert_eq!(read.answer_len, 1);
807        assert_eq!(write_buffer(0x00).as_bytes(), [0x0E, 0x00]);
808        let buffer = read_buffer(0x80, 12);
809        assert_eq!(buffer.command.as_bytes(), [0x1E, 0x80, 0x00]);
810        assert_eq!(buffer.answer_len, 12);
811    }
812
813    #[test]
814    fn the_irq_and_dio_commands_follow_tables_13_28_to_13_34() {
815        let done = Irq::TX_DONE | Irq::RX_DONE | Irq::TIMEOUT;
816        assert_eq!(
817            set_dio_irq_params(done, done, Irq::NONE, Irq::NONE).as_bytes(),
818            [0x08, 0x02, 0x03, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00]
819        );
820        assert_eq!(get_irq_status().command.as_bytes(), [0x12, 0x00]);
821        assert_eq!(get_irq_status().answer_len, 2);
822        assert_eq!(clear_irq_status(Irq::ALL).as_bytes(), [0x02, 0x43, 0xFF]);
823        assert_eq!(set_dio2_as_rf_switch(true).as_bytes(), [0x9D, 0x01]);
824        assert_eq!(
825            set_dio3_as_tcxo(TcxoVoltage::V1_8, 320).as_bytes(),
826            [0x97, 0x02, 0x00, 0x01, 0x40]
827        );
828    }
829
830    #[test]
831    fn the_rf_and_packet_commands_follow_tables_13_36_to_13_75() {
832        assert_eq!(set_packet_type(PacketType::Lora).as_bytes(), [0x8A, 0x01]);
833        assert_eq!(get_packet_type().command.as_bytes(), [0x11, 0x00]);
834        assert_eq!(
835            set_tx_params(22, RampTime::Us200).as_bytes(),
836            [0x8E, 0x16, 0x04]
837        );
838        assert_eq!(
839            set_tx_params(-9, RampTime::Us10).as_bytes(),
840            [0x8E, 0xF7, 0x00]
841        );
842        let modulation = LoraModulation {
843            spreading_factor: 9,
844            bandwidth: LoraBandwidth::Khz125,
845            coding_rate: CodingRate::Cr4_5,
846            low_data_rate_optimization: false,
847        };
848        assert_eq!(
849            set_lora_modulation_params(modulation).as_bytes(),
850            [0x8B, 0x09, 0x04, 0x01, 0x00]
851        );
852        let packet = LoraPacket {
853            preamble_symbols: 8,
854            explicit_header: true,
855            payload_len: 255,
856            crc: true,
857            invert_iq: false,
858        };
859        assert_eq!(
860            set_lora_packet_params(packet).as_bytes(),
861            [0x8C, 0x00, 0x08, 0x00, 0xFF, 0x01, 0x00]
862        );
863        assert_eq!(
864            set_cad_params(0x02, 22, 10, 0x00, 0).as_bytes(),
865            [0x88, 0x02, 0x16, 0x0A, 0x00, 0x00, 0x00, 0x00]
866        );
867        assert_eq!(
868            set_buffer_base_address(0x00, 0x80).as_bytes(),
869            [0x8F, 0x00, 0x80]
870        );
871        assert_eq!(set_lora_symbol_timeout(6).as_bytes(), [0xA0, 0x06]);
872    }
873
874    #[test]
875    fn the_status_commands_follow_tables_13_77_to_13_87() {
876        assert_eq!(get_status().command.as_bytes(), [0xC0]);
877        assert_eq!(get_status().answer_len, 1);
878        assert_eq!(get_rssi_inst().command.as_bytes(), [0x15, 0x00]);
879        assert_eq!(get_rx_buffer_status().command.as_bytes(), [0x13, 0x00]);
880        assert_eq!(get_rx_buffer_status().answer_len, 2);
881        assert_eq!(get_packet_status().command.as_bytes(), [0x14, 0x00]);
882        assert_eq!(get_packet_status().answer_len, 3);
883        assert_eq!(get_device_errors().command.as_bytes(), [0x17, 0x00]);
884        assert_eq!(clear_device_errors().as_bytes(), [0x07, 0x00, 0x00]);
885        assert_eq!(get_stats().command.as_bytes(), [0x10, 0x00]);
886        assert_eq!(get_stats().answer_len, 6);
887        assert_eq!(reset_stats().as_bytes(), [0x00; 7]);
888    }
889
890    #[test]
891    fn a_command_knows_its_opcode_and_length() {
892        let command = set_rf_frequency(0x3930_0000);
893        assert_eq!(command.opcode(), opcode::SET_RF_FREQUENCY);
894        assert_eq!(command.as_bytes().len(), 5);
895    }
896}