树莓派 pico lora 测试

📅 2026/7/2 3:38:42
树莓派 pico lora 测试
UART0 回环热身测试from machine import UART, Pin import time # Raspberry Pi Pico UART0 default pins: # GP0 UART0 TX # GP1 UART0 RX # For loopback test, connect GP0 directly to GP1. uart0 UART( 0, baudrate115200, txPin(0), rxPin(1), bits8, parityNone, stop1, ) counter 0 print(Pico UART0 loopback test) print(Wire GP0/TX to GP1/RX, baudrate 115200) while True: tx_data UART0 loopback test #{:04d}\r\n.format(counter) uart0.write(tx_data) print(TX:, tx_data.strip()) time.sleep_ms(100) if uart0.any(): rx_data uart0.read() try: print(RX:, rx_data.decode().strip()) except UnicodeError: print(RX bytes:, rx_data) else: print(RX: no data, check GP0-GP1 wire) counter 1 time.sleep(1)lora通信测试# SX1262 Universal MicroPython Demo for Waveshare Pico-LoRa-SX1262 / RP2040-LoRa # Save this file to the board as main.py, or run it directly from Thonny / mpremote. # Tested logic is merged from the two Waveshare-style direct-command demos. from machine import Pin, SPI import time import sys # # 1. Change these settings first # MODE tx # tx or rx BOARD pico_lora_sx1262 # pico_lora_sx1262, rp2040_lora FREQUENCY_HZ 865000000 # TX and RX must be the same, for example 433000000 / 470000000 / 868000000 / 915000000 TX_POWER_DBM 22 # SX1262 high-power PA, max 22 dBm # TCXO_MODE: # board use default value in BOARD_CONFIGS # off do not send SetDio3AsTcxoCtrl # 1v7 DIO3 TCXO 1.7 V, 5 ms # 3v3 DIO3 TCXO 3.3 V, 5 ms TCXO_MODE board DEBUG True TX_INTERVAL_MS 2000 RX_REARM_MS 10000 # Re-enter RX if no packet arrives for this long SPI_BAUDRATE 1000000 # Start stable first; after OK you may try 4_000_000 MAX_PAYLOAD_LEN 64 # LoRa parameters. TX and RX must be identical. LORA_SF 7 # 5..12 LORA_BW 4 # SX126x code: 4125kHz, 5250kHz, 6500kHz LORA_CR 1 # 14/5, 24/6, 34/7, 44/8 LORA_PREAMBLE 8 LORA_CRC 1 # 1CRC on, 0CRC off LORA_IQ 0 # 0normal, 1inverted SYNC_WORD_MODE private # private for point-to-point, public for LoRaWAN style sync word # If BOARDcustom, edit this section only. CUSTOM_PINS { spi_id: 1, nss: 13, sck: 14, mosi: 15, miso: 24, dio1: 16, busy: 18, reset: 23, ant_sw: 17, # None if the board has no external antenna switch GPIO ant_tx: 0, # antenna switch level for TX ant_rx: 1, # antenna switch level for RX tcxo: off, # off, 1v7, or 3v3 reset_release: input # input or high } # # 2. Board pin presets # BOARD_CONFIGS { # Waveshare Pico-LoRa-SX1262 style board / left module from your picture. # The original demo uses TCXO 1.7 V by default. pico_lora_sx1262: { spi_id: 1, nss: 3, sck: 10, mosi: 11, miso: 12, dio1: 20, busy: 2, reset: 15, ant_sw: None, ant_tx: 0, ant_rx: 1, tcxo: 1v7, reset_release: input, }, # Waveshare RP2040-LoRa style board / right module from your picture. # Your later program keeps TCXO off by default and has a separate ANT_SW pin. rp2040_lora: { spi_id: 1, nss: 13, sck: 14, mosi: 15, miso: 24, dio1: 16, busy: 18, reset: 23, ant_sw: 17, ant_tx: 0, ant_rx: 1, tcxo: off, reset_release: input, }, # Aliases matching your old program names. left: None, right: None, # User-defined custom board. custom: CUSTOM_PINS, } BOARD_CONFIGS[left] BOARD_CONFIGS[pico_lora_sx1262] BOARD_CONFIGS[right] BOARD_CONFIGS[rp2040_lora] # # 3. SX1262 constants # RADIO_GET_STATUS 0xC0 RADIO_WRITE_REGISTER 0x0D RADIO_READ_REGISTER 0x1D RADIO_WRITE_BUFFER 0x0E RADIO_READ_BUFFER 0x1E RADIO_SET_STANDBY 0x80 RADIO_SET_TX 0x83 RADIO_SET_RX 0x82 RADIO_SET_PACKETTYPE 0x8A RADIO_SET_RFFREQUENCY 0x86 RADIO_SET_TXPARAMS 0x8E RADIO_SET_PACONFIG 0x95 RADIO_SET_BUFFERBASEADDRESS 0x8F RADIO_SET_MODULATIONPARAMS 0x8B RADIO_SET_PACKETPARAMS 0x8C RADIO_GET_RXBUFFERSTATUS 0x13 RADIO_GET_PACKETSTATUS 0x14 RADIO_GET_ERROR 0x17 RADIO_CFG_DIOIRQ 0x08 RADIO_GET_IRQSTATUS 0x12 RADIO_CLR_IRQSTATUS 0x02 RADIO_CALIBRATE 0x89 RADIO_CALIBRATEIMAGE 0x98 RADIO_SET_REGULATORMODE 0x96 RADIO_CLR_ERROR 0x07 RADIO_SET_TCXOMODE 0x97 RADIO_SET_RFSWITCHMODE 0x9D RADIO_SET_STOPRXTIMERONPREAMBLE 0x9F RADIO_SET_LORASYMBTIMEOUT 0xA0 PACKET_TYPE_LORA 0x01 STDBY_RC 0x00 USE_DCDC 0x01 RADIO_RAMP_40_US 0x02 IRQ_TX_DONE 0x0001 IRQ_RX_DONE 0x0002 IRQ_PREAMBLE_DETECTED 0x0004 IRQ_SYNCWORD_VALID 0x0008 IRQ_HEADER_VALID 0x0010 IRQ_HEADER_ERROR 0x0020 IRQ_CRC_ERROR 0x0040 IRQ_CAD_DONE 0x0080 IRQ_CAD_DETECTED 0x0100 IRQ_RX_TX_TIMEOUT 0x0200 IRQ_ALL 0xFFFF REG_LR_SYNCWORD 0x0740 REG_IQ_POLARITY_SETUP 0x0736 REG_TX_MODULATION 0x0889 REG_TX_CLAMP_CONFIG 0x08D8 REG_OCP 0x08E7 PUBLIC_SYNCWORD 0x3444 PRIVATE_SYNCWORD 0x1424 # # 4. Driver # class SX1262: def __init__(self, board_name, pins, quietFalse): self.board_name board_name self.pins pins self.quiet quiet self.spi SPI( pins[spi_id], baudrateSPI_BAUDRATE, polarity0, phase0, bits8, firstbitSPI.MSB, sckPin(pins[sck]), mosiPin(pins[mosi]), misoPin(pins[miso]), ) self.nss Pin(pins[nss], Pin.OUT, value1) self.busy Pin(pins[busy], Pin.IN) self.dio1 Pin(pins[dio1], Pin.IN) self.reset_pin Pin(pins[reset], Pin.OUT, value1) self.ant_sw Pin(pins[ant_sw], Pin.OUT, value0) if pins.get(ant_sw) is not None else None def log(self, *args): if DEBUG and not self.quiet: print(*args) def wait_busy(self, timeout_ms1000): start time.ticks_ms() while self.busy.value(): if time.ticks_diff(time.ticks_ms(), start) timeout_ms: raise RuntimeError(SX1262 BUSY timeout, check BOARD setting and BUSY pin) time.sleep_ms(1) def xfer(self, data): tx bytes(data) rx bytearray(len(tx)) self.nss.value(0) self.spi.write_readinto(tx, rx) self.nss.value(1) return rx def write_cmd(self, cmd, data()): self.wait_busy() self.nss.value(0) self.spi.write(bytes([cmd]) bytes(data)) self.nss.value(1) if cmd ! 0x84: self.wait_busy() def read_cmd(self, cmd, size): self.wait_busy() rx self.xfer([cmd, 0x00] [0x00] * size) self.wait_busy() return bytes(rx[2:]) def write_reg(self, addr, data): if isinstance(data, int): data [data] self.write_cmd(RADIO_WRITE_REGISTER, [(addr 8) 0xFF, addr 0xFF] list(data)) def read_reg(self, addr, size1): self.wait_busy() rx self.xfer([RADIO_READ_REGISTER, (addr 8) 0xFF, addr 0xFF, 0x00] [0x00] * size) self.wait_busy() data bytes(rx[4:]) return data[0] if size 1 else data def write_buffer(self, offset, data): self.write_cmd(RADIO_WRITE_BUFFER, [offset] list(data)) def read_buffer(self, offset, size): self.wait_busy() rx self.xfer([RADIO_READ_BUFFER, offset, 0x00] [0x00] * size) self.wait_busy() return bytes(rx[3:]) def reset_radio(self): self.reset_pin.init(Pin.OUT, value0) time.sleep_ms(20) if self.pins.get(reset_release, input) high: self.reset_pin.init(Pin.OUT, value1) else: self.reset_pin.init(Pin.IN) time.sleep_ms(20) self.wait_busy() def get_status(self): self.wait_busy() rx self.xfer([RADIO_GET_STATUS, 0x00]) self.wait_busy() return rx[1] def get_errors(self): data self.read_cmd(RADIO_GET_ERROR, 2) return (data[0] 8) | data[1] def clear_errors(self): self.write_cmd(RADIO_CLR_ERROR, [0x00, 0x00]) def freq_to_pll(self, freq_hz): return (freq_hz 25) // 32000000 def set_frequency(self, freq_hz): if freq_hz 900000000: cal [0xE1, 0xE9] elif freq_hz 850000000: cal [0xD7, 0xDB] elif freq_hz 770000000: cal [0xC1, 0xC5] elif freq_hz 460000000: cal [0x75, 0x81] else: cal [0x6B, 0x6F] self.write_cmd(RADIO_CALIBRATEIMAGE, cal) pll self.freq_to_pll(freq_hz) self.write_cmd(RADIO_SET_RFFREQUENCY, [ (pll 24) 0xFF, (pll 16) 0xFF, (pll 8) 0xFF, pll 0xFF, ]) def set_dio_irq(self, irq_mask, dio1_mask): self.write_cmd(RADIO_CFG_DIOIRQ, [ (irq_mask 8) 0xFF, irq_mask 0xFF, (dio1_mask 8) 0xFF, dio1_mask 0xFF, 0x00, 0x00, 0x00, 0x00, ]) def get_irq(self): data self.read_cmd(RADIO_GET_IRQSTATUS, 2) return (data[0] 8) | data[1] def clear_irq(self, maskIRQ_ALL): self.write_cmd(RADIO_CLR_IRQSTATUS, [(mask 8) 0xFF, mask 0xFF]) def get_rx_buffer_status(self): data self.read_cmd(RADIO_GET_RXBUFFERSTATUS, 2) return data[0], data[1] def get_packet_status(self): data self.read_cmd(RADIO_GET_PACKETSTATUS, 3) rssi_pkt -data[0] // 2 snr_pkt data[1] if snr_pkt 0x80: snr_pkt - 256 snr_pkt (snr_pkt 2) // 4 signal_rssi -data[2] // 2 return rssi_pkt, snr_pkt, signal_rssi def set_lora_packet_params(self, payload_len): self.write_cmd(RADIO_SET_PACKETPARAMS, [ (LORA_PREAMBLE 8) 0xFF, LORA_PREAMBLE 0xFF, 0x00, # explicit header payload_len 0xFF, LORA_CRC, LORA_IQ, ]) def set_tcxo(self): mode TCXO_MODE if mode board: mode self.pins.get(tcxo, off) if mode off: return elif mode 1v7: self.write_cmd(RADIO_SET_TCXOMODE, [0x01, 0x00, 0x01, 0x40]) time.sleep_ms(5) elif mode 3v3: self.write_cmd(RADIO_SET_TCXOMODE, [0x07, 0x00, 0x01, 0x40]) time.sleep_ms(5) else: raise ValueError(TCXO_MODE must be board/off/1v7/3v3) def set_ant_tx(self): if self.ant_sw is not None: self.ant_sw.value(self.pins.get(ant_tx, 0)) def set_ant_rx(self): if self.ant_sw is not None: self.ant_sw.value(self.pins.get(ant_rx, 1)) def setup_radio(self): self.reset_radio() self.log(board , self.board_name, pins , self.pins) self.log(status after reset 0x%02X busy%d % (self.get_status(), self.busy.value())) self.clear_errors() self.write_cmd(RADIO_SET_STANDBY, [STDBY_RC]) self.set_tcxo() self.write_cmd(RADIO_CALIBRATE, [0x7F]) time.sleep_ms(10) self.clear_errors() self.write_cmd(RADIO_SET_REGULATORMODE, [USE_DCDC]) self.write_cmd(RADIO_SET_RFSWITCHMODE, [0x01]) self.write_cmd(RADIO_SET_BUFFERBASEADDRESS, [0x00, 0x00]) self.write_cmd(RADIO_SET_PACKETTYPE, [PACKET_TYPE_LORA]) self.write_cmd(RADIO_SET_STOPRXTIMERONPREAMBLE, [0x00]) self.set_frequency(FREQUENCY_HZ) low_data_rate_opt 1 if ((LORA_BW 4 and LORA_SF in (11, 12)) or (LORA_BW 5 and LORA_SF 12)) else 0 self.write_cmd(RADIO_SET_MODULATIONPARAMS, [LORA_SF, LORA_BW, LORA_CR, low_data_rate_opt]) self.set_lora_packet_params(MAX_PAYLOAD_LEN) sync_word PUBLIC_SYNCWORD if SYNC_WORD_MODE public else PRIVATE_SYNCWORD self.write_reg(REG_LR_SYNCWORD, [(sync_word 8) 0xFF, sync_word 0xFF]) # Semtech SX126x workaround registers used by the original examples. self.write_reg(REG_IQ_POLARITY_SETUP, self.read_reg(REG_IQ_POLARITY_SETUP) | (1 2)) self.write_reg(REG_TX_MODULATION, self.read_reg(REG_TX_MODULATION) | (1 2)) self.write_reg(REG_TX_CLAMP_CONFIG, self.read_reg(REG_TX_CLAMP_CONFIG) | (0x0F 1)) self.write_reg(REG_OCP, 0x38) self.write_cmd(RADIO_SET_PACONFIG, [0x04, 0x07, 0x00, 0x01]) self.write_cmd(RADIO_SET_TXPARAMS, [TX_POWER_DBM 0xFF, RADIO_RAMP_40_US]) self.clear_irq() self.log(status after setup 0x%02X errors0x%04X % (self.get_status(), self.get_errors())) def send_packet(self, payload): if isinstance(payload, str): payload payload.encode() if len(payload) 255: raise ValueError(payload too long) self.set_ant_tx() self.write_cmd(RADIO_SET_STANDBY, [STDBY_RC]) self.set_dio_irq(IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT, IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT) self.set_lora_packet_params(len(payload)) self.clear_irq() self.write_buffer(0x00, payload) self.write_cmd(RADIO_SET_TX, [0x00, 0x00, 0x00]) start time.ticks_ms() while True: irq self.get_irq() if irq IRQ_TX_DONE: self.clear_irq(irq) print(TX done:, payload) return True if irq IRQ_RX_TX_TIMEOUT: self.clear_irq(irq) print(TX timeout) return False if time.ticks_diff(time.ticks_ms(), start) 5000: print(TX wait timeout irq0x%04X status0x%02X errors0x%04X busy%d % (irq, self.get_status(), self.get_errors(), self.busy.value())) return False time.sleep_ms(20) def start_rx(self): self.set_ant_rx() self.write_cmd(RADIO_SET_STANDBY, [STDBY_RC]) self.set_lora_packet_params(MAX_PAYLOAD_LEN) rx_irq IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_HEADER_ERROR | IRQ_CRC_ERROR self.set_dio_irq(rx_irq, rx_irq) self.clear_irq() self.write_cmd(RADIO_SET_RX, [0xFF, 0xFF, 0xFF]) def poll_rx(self): if not self.dio1.value(): return False irq self.get_irq() if irq 0: return False self.clear_irq(irq) if irq 0xFFFF: print(RX irq read is 0xFFFF, check BOARD setting and SX1262 SPI/MISO pins) time.sleep_ms(500) self.start_rx() return False if irq IRQ_RX_DONE: if irq (IRQ_CRC_ERROR | IRQ_HEADER_ERROR): print(RX error irq0x%04X % irq) else: size, offset self.get_rx_buffer_status() payload self.read_buffer(offset, size) rssi, snr, signal_rssi self.get_packet_status() print(RX:, payload, RSSI:, rssi, SNR:, snr, SignalRSSI:, signal_rssi) self.start_rx() return True elif irq IRQ_RX_TX_TIMEOUT: print(RX timeout) self.start_rx() return False # # 5. Board selection and runner # def copy_dict(d): return dict(d) def try_detect_board(): # Auto mode is only a convenience. If detection is unstable, set BOARD manually. candidates [rp2040_lora, pico_lora_sx1262] for name in candidates: try: pins copy_dict(BOARD_CONFIGS[name]) radio SX1262(name, pins, quietTrue) radio.reset_radio() status radio.get_status() # Wrong SPI/MISO often gives 0x00 or 0xFF. if status not in (0x00, 0xFF): print(AUTO BOARD , name, status0x%02X % status) return name, pins else: print(AUTO skip, name, status0x%02X % status) except Exception as e: print(AUTO skip, name, e) raise RuntimeError(Auto detect failed. Set BOARD to pico_lora_sx1262 or rp2040_lora manually.) def get_board_config(): if BOARD auto: return try_detect_board() if BOARD not in BOARD_CONFIGS: raise ValueError(Unknown BOARD: %s % BOARD) return BOARD, copy_dict(BOARD_CONFIGS[BOARD]) def run_tx(radio): radio.setup_radio() count 0 print(SX1262 Universal TX) print(MODE, MODE, BOARD, radio.board_name, FREQ, FREQUENCY_HZ, PINS, radio.pins) while True: radio.send_packet(PING %04d % count) count 1 time.sleep_ms(TX_INTERVAL_MS) def run_rx(radio): radio.setup_radio() rx_count 0 last_rx_or_rearm time.ticks_ms() print(SX1262 Universal RX) print(MODE, MODE, BOARD, radio.board_name, FREQ, FREQUENCY_HZ, PINS, radio.pins) radio.start_rx() while True: if radio.poll_rx(): rx_count 1 last_rx_or_rearm time.ticks_ms() print(RX count:, rx_count) if time.ticks_diff(time.ticks_ms(), last_rx_or_rearm) RX_REARM_MS: print(RX rearm, waiting for packets...) radio.start_rx() last_rx_or_rearm time.ticks_ms() time.sleep_ms(50) def main(): board_name, pins get_board_config() radio SX1262(board_name, pins) if MODE tx: run_tx(radio) elif MODE rx: run_rx(radio) else: raise ValueError(MODE must be tx or rx) try: main() except KeyboardInterrupt: print(Stopped) except Exception as e: print(ERROR:, e) sys.print_exception(e)python -m mpremote connersal_micropython\sx1262_rp2040_main.py :main.py