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