1use super::config::{
12 FallbackMode, LoraModulation, LoraPacket, PaConfig, PacketType, RampTime, RegulatorMode,
13 StandbyMode, TcxoVoltage,
14};
15use super::irq::Irq;
16
17pub mod opcode {
19 pub const SET_SLEEP: u8 = 0x84;
21 pub const SET_STANDBY: u8 = 0x80;
23 pub const SET_FS: u8 = 0xC1;
25 pub const SET_TX: u8 = 0x83;
27 pub const SET_RX: u8 = 0x82;
29 pub const STOP_TIMER_ON_PREAMBLE: u8 = 0x9F;
31 pub const SET_RX_DUTY_CYCLE: u8 = 0x94;
33 pub const SET_CAD: u8 = 0xC5;
35 pub const SET_TX_CONTINUOUS_WAVE: u8 = 0xD1;
37 pub const SET_TX_INFINITE_PREAMBLE: u8 = 0xD2;
39 pub const SET_REGULATOR_MODE: u8 = 0x96;
41 pub const CALIBRATE: u8 = 0x89;
43 pub const CALIBRATE_IMAGE: u8 = 0x98;
45 pub const SET_PA_CONFIG: u8 = 0x95;
47 pub const SET_RX_TX_FALLBACK_MODE: u8 = 0x93;
49 pub const WRITE_REGISTER: u8 = 0x0D;
51 pub const READ_REGISTER: u8 = 0x1D;
53 pub const WRITE_BUFFER: u8 = 0x0E;
55 pub const READ_BUFFER: u8 = 0x1E;
57 pub const SET_DIO_IRQ_PARAMS: u8 = 0x08;
59 pub const GET_IRQ_STATUS: u8 = 0x12;
61 pub const CLEAR_IRQ_STATUS: u8 = 0x02;
63 pub const SET_DIO2_AS_RF_SWITCH_CTRL: u8 = 0x9D;
65 pub const SET_DIO3_AS_TCXO_CTRL: u8 = 0x97;
67 pub const SET_RF_FREQUENCY: u8 = 0x86;
69 pub const SET_PACKET_TYPE: u8 = 0x8A;
71 pub const GET_PACKET_TYPE: u8 = 0x11;
73 pub const SET_TX_PARAMS: u8 = 0x8E;
75 pub const SET_MODULATION_PARAMS: u8 = 0x8B;
77 pub const SET_PACKET_PARAMS: u8 = 0x8C;
79 pub const SET_CAD_PARAMS: u8 = 0x88;
81 pub const SET_BUFFER_BASE_ADDRESS: u8 = 0x8F;
83 pub const SET_LORA_SYMB_NUM_TIMEOUT: u8 = 0xA0;
85 pub const GET_STATUS: u8 = 0xC0;
87 pub const GET_RSSI_INST: u8 = 0x15;
89 pub const GET_RX_BUFFER_STATUS: u8 = 0x13;
91 pub const GET_PACKET_STATUS: u8 = 0x14;
93 pub const GET_DEVICE_ERRORS: u8 = 0x17;
95 pub const CLEAR_DEVICE_ERRORS: u8 = 0x07;
97 pub const GET_STATS: u8 = 0x10;
99 pub const RESET_STATS: u8 = 0x00;
101}
102
103pub const NOP: u8 = 0x00;
105
106pub const CALIBRATE_ALL: u8 = 0x7F;
108
109pub const MAX_LEN: usize = 10;
111
112#[derive(Clone, Copy, PartialEq, Eq, Hash)]
122pub struct Command {
123 bytes: [u8; MAX_LEN],
124 len: u8,
125}
126
127impl Command {
128 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 pub fn as_bytes(&self) -> &[u8] {
166 &self.bytes[..usize::from(self.len)]
167 }
168
169 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
187pub struct Query {
188 pub command: Command,
190 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
198pub 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
215pub const fn set_standby(mode: StandbyMode) -> Command {
225 Command::new(opcode::SET_STANDBY, &[mode.code()])
226}
227
228pub const fn set_fs() -> Command {
234 Command::new(opcode::SET_FS, &[])
235}
236
237pub const fn set_tx(timeout: u32) -> Command {
248 Command::new(opcode::SET_TX, &u24(timeout))
249}
250
251pub const fn set_rx(timeout: u32) -> Command {
263 Command::new(opcode::SET_RX, &u24(timeout))
264}
265
266pub const fn stop_timer_on_preamble(on_preamble: bool) -> Command {
277 Command::new(opcode::STOP_TIMER_ON_PREAMBLE, &[on_preamble as u8])
278}
279
280pub 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
299pub const fn set_cad() -> Command {
305 Command::new(opcode::SET_CAD, &[])
306}
307
308pub const fn set_tx_continuous_wave() -> Command {
314 Command::new(opcode::SET_TX_CONTINUOUS_WAVE, &[])
315}
316
317pub const fn set_tx_infinite_preamble() -> Command {
323 Command::new(opcode::SET_TX_INFINITE_PREAMBLE, &[])
324}
325
326pub const fn set_regulator_mode(mode: RegulatorMode) -> Command {
336 Command::new(opcode::SET_REGULATOR_MODE, &[mode.code()])
337}
338
339pub const fn calibrate(blocks: u8) -> Command {
349 Command::new(opcode::CALIBRATE, &[blocks])
350}
351
352pub const fn calibrate_image(codes: [u8; 2]) -> Command {
363 Command::new(opcode::CALIBRATE_IMAGE, &codes)
364}
365
366pub const fn set_pa_config(pa: PaConfig) -> Command {
376 Command::new(opcode::SET_PA_CONFIG, &pa.to_params())
377}
378
379pub const fn set_rx_tx_fallback_mode(mode: FallbackMode) -> Command {
389 Command::new(opcode::SET_RX_TX_FALLBACK_MODE, &[mode.code()])
390}
391
392pub const fn write_register(address: u16) -> Command {
402 let address = address.to_be_bytes();
403 Command::new(opcode::WRITE_REGISTER, &address)
404}
405
406pub 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
424pub const fn write_buffer(offset: u8) -> Command {
434 Command::new(opcode::WRITE_BUFFER, &[offset])
435}
436
437pub 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
454pub 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
474pub const fn get_irq_status() -> Query {
480 Query {
481 command: Command::new(opcode::GET_IRQ_STATUS, &[NOP]),
482 answer_len: 2,
483 }
484}
485
486pub const fn clear_irq_status(irq: Irq) -> Command {
496 Command::new(opcode::CLEAR_IRQ_STATUS, &irq.to_bytes())
497}
498
499pub 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
512pub 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
530pub const fn set_rf_frequency(word: u32) -> Command {
541 Command::new(opcode::SET_RF_FREQUENCY, &word.to_be_bytes())
542}
543
544pub const fn set_packet_type(packet_type: PacketType) -> Command {
554 Command::new(opcode::SET_PACKET_TYPE, &[packet_type.code()])
555}
556
557pub const fn get_packet_type() -> Query {
563 Query {
564 command: Command::new(opcode::GET_PACKET_TYPE, &[NOP]),
565 answer_len: 1,
566 }
567}
568
569pub 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
583pub const fn set_lora_modulation_params(modulation: LoraModulation) -> Command {
594 Command::new(opcode::SET_MODULATION_PARAMS, &modulation.to_params())
595}
596
597pub const fn set_lora_packet_params(packet: LoraPacket) -> Command {
607 Command::new(opcode::SET_PACKET_PARAMS, &packet.to_params())
608}
609
610pub 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
645pub const fn set_buffer_base_address(tx: u8, rx: u8) -> Command {
656 Command::new(opcode::SET_BUFFER_BASE_ADDRESS, &[tx, rx])
657}
658
659pub const fn set_lora_symbol_timeout(symbols: u8) -> Command {
669 Command::new(opcode::SET_LORA_SYMB_NUM_TIMEOUT, &[symbols])
670}
671
672pub const fn get_status() -> Query {
678 Query {
679 command: Command::new(opcode::GET_STATUS, &[]),
680 answer_len: 1,
681 }
682}
683
684pub const fn get_rssi_inst() -> Query {
690 Query {
691 command: Command::new(opcode::GET_RSSI_INST, &[NOP]),
692 answer_len: 1,
693 }
694}
695
696pub 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
708pub const fn get_packet_status() -> Query {
714 Query {
715 command: Command::new(opcode::GET_PACKET_STATUS, &[NOP]),
716 answer_len: 3,
717 }
718}
719
720pub const fn get_device_errors() -> Query {
726 Query {
727 command: Command::new(opcode::GET_DEVICE_ERRORS, &[NOP]),
728 answer_len: 2,
729 }
730}
731
732pub const fn clear_device_errors() -> Command {
738 Command::new(opcode::CLEAR_DEVICE_ERRORS, &[0x00, 0x00])
739}
740
741pub const fn get_stats() -> Query {
747 Query {
748 command: Command::new(opcode::GET_STATS, &[NOP]),
749 answer_len: 6,
750 }
751}
752
753pub 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}