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)]
128pub struct Command {
129 bytes: [u8; MAX_LEN],
130 len: u8,
131}
132
133impl Command {
134 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 pub fn as_bytes(&self) -> &[u8] {
172 &self.bytes[..usize::from(self.len)]
173 }
174
175 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
193pub struct Query {
194 pub command: Command,
196 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
204pub 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
221pub const fn set_standby(mode: StandbyMode) -> Command {
231 Command::new(opcode::SET_STANDBY, &[mode.code()])
232}
233
234pub const fn set_fs() -> Command {
240 Command::new(opcode::SET_FS, &[])
241}
242
243pub const fn set_tx(timeout: u32) -> Command {
254 Command::new(opcode::SET_TX, &u24(timeout))
255}
256
257pub const fn set_rx(timeout: u32) -> Command {
269 Command::new(opcode::SET_RX, &u24(timeout))
270}
271
272pub const fn stop_timer_on_preamble(on_preamble: bool) -> Command {
283 Command::new(opcode::STOP_TIMER_ON_PREAMBLE, &[on_preamble as u8])
284}
285
286pub 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
305pub const fn set_cad() -> Command {
311 Command::new(opcode::SET_CAD, &[])
312}
313
314pub const fn set_tx_continuous_wave() -> Command {
320 Command::new(opcode::SET_TX_CONTINUOUS_WAVE, &[])
321}
322
323pub const fn set_tx_infinite_preamble() -> Command {
329 Command::new(opcode::SET_TX_INFINITE_PREAMBLE, &[])
330}
331
332pub const fn set_regulator_mode(mode: RegulatorMode) -> Command {
342 Command::new(opcode::SET_REGULATOR_MODE, &[mode.code()])
343}
344
345pub const fn calibrate(blocks: u8) -> Command {
355 Command::new(opcode::CALIBRATE, &[blocks])
356}
357
358pub const fn calibrate_image(codes: [u8; 2]) -> Command {
369 Command::new(opcode::CALIBRATE_IMAGE, &codes)
370}
371
372pub const fn set_pa_config(pa: PaConfig) -> Command {
382 Command::new(opcode::SET_PA_CONFIG, &pa.to_params())
383}
384
385pub const fn set_rx_tx_fallback_mode(mode: FallbackMode) -> Command {
395 Command::new(opcode::SET_RX_TX_FALLBACK_MODE, &[mode.code()])
396}
397
398pub const fn write_register(address: u16) -> Command {
408 let address = address.to_be_bytes();
409 Command::new(opcode::WRITE_REGISTER, &address)
410}
411
412pub 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
430pub const fn write_buffer(offset: u8) -> Command {
440 Command::new(opcode::WRITE_BUFFER, &[offset])
441}
442
443pub 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
460pub 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
480pub const fn get_irq_status() -> Query {
486 Query {
487 command: Command::new(opcode::GET_IRQ_STATUS, &[NOP]),
488 answer_len: 2,
489 }
490}
491
492pub const fn clear_irq_status(irq: Irq) -> Command {
502 Command::new(opcode::CLEAR_IRQ_STATUS, &irq.to_bytes())
503}
504
505pub 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
518pub 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
536pub const fn set_rf_frequency(word: u32) -> Command {
547 Command::new(opcode::SET_RF_FREQUENCY, &word.to_be_bytes())
548}
549
550pub const fn set_packet_type(packet_type: PacketType) -> Command {
560 Command::new(opcode::SET_PACKET_TYPE, &[packet_type.code()])
561}
562
563pub const fn get_packet_type() -> Query {
569 Query {
570 command: Command::new(opcode::GET_PACKET_TYPE, &[NOP]),
571 answer_len: 1,
572 }
573}
574
575pub 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
589pub const fn set_lora_modulation_params(modulation: LoraModulation) -> Command {
600 Command::new(opcode::SET_MODULATION_PARAMS, &modulation.to_params())
601}
602
603pub const fn set_lora_packet_params(packet: LoraPacket) -> Command {
613 Command::new(opcode::SET_PACKET_PARAMS, &packet.to_params())
614}
615
616pub 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
651pub const fn set_buffer_base_address(tx: u8, rx: u8) -> Command {
662 Command::new(opcode::SET_BUFFER_BASE_ADDRESS, &[tx, rx])
663}
664
665pub const fn set_lora_symbol_timeout(symbols: u8) -> Command {
675 Command::new(opcode::SET_LORA_SYMB_NUM_TIMEOUT, &[symbols])
676}
677
678pub const fn get_status() -> Query {
684 Query {
685 command: Command::new(opcode::GET_STATUS, &[]),
686 answer_len: 1,
687 }
688}
689
690pub const fn get_rssi_inst() -> Query {
696 Query {
697 command: Command::new(opcode::GET_RSSI_INST, &[NOP]),
698 answer_len: 1,
699 }
700}
701
702pub 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
714pub const fn get_packet_status() -> Query {
720 Query {
721 command: Command::new(opcode::GET_PACKET_STATUS, &[NOP]),
722 answer_len: 3,
723 }
724}
725
726pub const fn get_device_errors() -> Query {
732 Query {
733 command: Command::new(opcode::GET_DEVICE_ERRORS, &[NOP]),
734 answer_len: 2,
735 }
736}
737
738pub const fn clear_device_errors() -> Command {
744 Command::new(opcode::CLEAR_DEVICE_ERRORS, &[0x00, 0x00])
745}
746
747pub const fn get_stats() -> Query {
753 Query {
754 command: Command::new(opcode::GET_STATS, &[NOP]),
755 answer_len: 6,
756 }
757}
758
759pub 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}