Advertisement
Guest User

Untitled

a guest
Apr 6th, 2022
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.02 KB | None | 0 0
  1. import time
  2. import math
  3. from ucollections import namedtuple
  4. from urandom import getrandbits
  5. from machine import SoftSPI
  6. from machine import Pin
  7.  
  8. # Constants
  9. FLAGS_ACK = 0x80
  10. BROADCAST_ADDRESS = 255
  11.  
  12. REG_00_FIFO = 0x00
  13. REG_01_OP_MODE = 0x01
  14. REG_06_FRF_MSB = 0x06
  15. REG_07_FRF_MID = 0x07
  16. REG_08_FRF_LSB = 0x08
  17. REG_0E_FIFO_TX_BASE_ADDR = 0x0e
  18. REG_0F_FIFO_RX_BASE_ADDR = 0x0f
  19. REG_10_FIFO_RX_CURRENT_ADDR = 0x10
  20. REG_12_IRQ_FLAGS = 0x12
  21. REG_13_RX_NB_BYTES = 0x13
  22. REG_1D_MODEM_CONFIG1 = 0x1d
  23. REG_1E_MODEM_CONFIG2 = 0x1e
  24. REG_19_PKT_SNR_VALUE = 0x19
  25. REG_1A_PKT_RSSI_VALUE = 0x1a
  26. REG_20_PREAMBLE_MSB = 0x20
  27. REG_21_PREAMBLE_LSB = 0x21
  28. REG_22_PAYLOAD_LENGTH = 0x22
  29. REG_26_MODEM_CONFIG3 = 0x26
  30.  
  31. REG_4D_PA_DAC = 0x4d
  32. REG_40_DIO_MAPPING1 = 0x40
  33. REG_0D_FIFO_ADDR_PTR = 0x0d
  34.  
  35. PA_DAC_ENABLE = 0x07
  36. PA_DAC_DISABLE = 0x04
  37. PA_SELECT = 0x80
  38.  
  39. CAD_DETECTED_MASK = 0x01
  40. RX_DONE = 0x40
  41. TX_DONE = 0x08
  42. CAD_DONE = 0x04
  43. CAD_DETECTED = 0x01
  44.  
  45. LONG_RANGE_MODE = 0x80
  46. MODE_SLEEP = 0x00
  47. MODE_STDBY = 0x01
  48. MODE_TX = 0x03
  49. MODE_RXCONTINUOUS = 0x05
  50. MODE_CAD = 0x07
  51.  
  52. REG_09_PA_CONFIG = 0x09
  53. FXOSC = 32000000.0
  54. FSTEP = (FXOSC / 524288)
  55.  
  56.  
  57. class ModemConfig:
  58.     Bw125Cr45Sf128 = (0x72, 0x74,
  59.                       0x04)  # < Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium range
  60.     Bw500Cr45Sf128 = (0x92, 0x74,
  61.                       0x04)  # < Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short range
  62.     Bw31_25Cr48Sf512 = (0x48, 0x94,
  63.                         0x04)  # < Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long range
  64.     Bw125Cr48Sf4096 = (0x78, 0xc4,
  65.                        0x0c)  # /< Bw = 125 kHz, Cr = 4/8, Sf = 4096chips/symbol, low data rate, CRC on. Slow+long range
  66.     Bw125Cr45Sf2048 = (0x72, 0xb4,
  67.                        0x04)  # < Bw = 125 kHz, Cr = 4/5, Sf = 2048chips/symbol, CRC on. Slow+long range
  68.  
  69.  
  70. class SPIConfig:
  71.     # spi pin defs for various boards (channel, sck, mosi, miso)
  72.     rp2_0 = (0, 6, 7, 4)
  73.     rp2_1 = (1, 10, 11, 8)
  74.     esp8286_1 = (1, 14, 13, 12)
  75.     esp32_1 = (1, 14, 13, 12)
  76.     esp32_2 = (2, 18, 23, 19)
  77.  
  78.  
  79. class LoRa(object):
  80.     def __init__(self, spi_channel, interrupt, interrupt2, this_address, cs_pin, reset_pin=None,
  81.                  freq=868.0, tx_power=14,
  82.                  modem_config=ModemConfig.Bw125Cr45Sf128, receive_all=False, acks=False,
  83.                  crypto=None):
  84.         """
  85.        Lora(channel, interrupt, this_address, cs_pin, reset_pin=None, freq=868.0, tx_power=14,
  86.                 modem_config=ModemConfig.Bw125Cr45Sf128, receive_all=False, acks=False, crypto=None)
  87.        channel: SPI channel, check SPIConfig for preconfigured names
  88.        interrupt: GPIO interrupt pin
  89.        this_address: set address for this device [0-254]
  90.        cs_pin: chip select pin from microcontroller
  91.        reset_pin: the GPIO used to reset the RFM9x if connected
  92.        freq: frequency in MHz
  93.        tx_power: transmit power in dBm
  94.        modem_config: Check ModemConfig. Default is compatible with the Radiohead library
  95.        receive_all: if True, don't filter packets on address
  96.        acks: if True, request acknowledgments
  97.        crypto: if desired, an instance of ucrypto AES (https://docs.pycom.io/firmwareapi/micropython/ucrypto/) - not tested
  98.        """
  99.  
  100.         self._spi_channel = spi_channel
  101.         self._interrupt = interrupt
  102.         self._cs_pin = cs_pin
  103.  
  104.         self._mode = None
  105.         self._cad = None
  106.         self._freq = freq
  107.         self._tx_power = tx_power
  108.         self._modem_config = modem_config
  109.         self._receive_all = receive_all
  110.         self._acks = acks
  111.  
  112.         self._this_address = this_address
  113.         self._last_header_id = 0
  114.  
  115.         self._last_payload = None
  116.         self.crypto = crypto
  117.  
  118.         self.cad_timeout = 0
  119.         self.send_retries = 2
  120.         self.wait_packet_sent_timeout = 0.2
  121.         self.retry_timeout = 0.2
  122.  
  123.         # Setup the module
  124.         #        gpio_interrupt = Pin(self._interrupt, Pin.IN, Pin.PULL_DOWN)
  125.         gpio_interrupt = Pin(self._interrupt, Pin.IN)
  126.         gpio_interrupt.irq(trigger=Pin.IRQ_RISING, handler=self._handle_interrupt)
  127.  
  128.         gpio_interrupt2 = Pin(interrupt2, Pin.IN)
  129.         gpio_interrupt2.irq(trigger=Pin.IRQ_RISING, handler=self._handle_interrupt2)
  130.  
  131.         # reset the board
  132.         if reset_pin:
  133.             gpio_reset = Pin(reset_pin, Pin.OUT)
  134.             gpio_reset.value(0)
  135.             time.sleep(0.01)
  136.             gpio_reset.value(1)
  137.             time.sleep(0.01)
  138.  
  139.         # baud rate to 5MHz
  140.         self.spi = SoftSPI(baudrate=5000,
  141.                        sck=Pin(self._spi_channel[0], Pin.OUT), mosi=Pin(self._spi_channel[1], Pin.OUT),
  142.                        miso=Pin(self._spi_channel[2], Pin.IN))
  143.  
  144.         # cs gpio pin
  145.         self.cs = Pin(self._cs_pin, Pin.OUT)
  146.         self.cs.value(1)
  147.  
  148.         # set mode
  149.         self._spi_write(REG_01_OP_MODE, MODE_SLEEP | LONG_RANGE_MODE)
  150.         time.sleep(0.1)
  151.  
  152.         # check if mode is set
  153.         assert self._spi_read(REG_01_OP_MODE) == (MODE_SLEEP | LONG_RANGE_MODE), \
  154.             "LoRa initialization failed"
  155.  
  156.         self._spi_write(REG_0E_FIFO_TX_BASE_ADDR, 0)
  157.         self._spi_write(REG_0F_FIFO_RX_BASE_ADDR, 0)
  158.  
  159.         self.set_mode_idle()
  160.  
  161.         # set modem config (Bw125Cr45Sf128)
  162.         self._spi_write(REG_1D_MODEM_CONFIG1, self._modem_config[0])
  163.         self._spi_write(REG_1E_MODEM_CONFIG2, self._modem_config[1])
  164.         self._spi_write(REG_26_MODEM_CONFIG3, self._modem_config[2])
  165.  
  166.         # set preamble length (8)
  167.         self._spi_write(REG_20_PREAMBLE_MSB, 0)
  168.         self._spi_write(REG_21_PREAMBLE_LSB, 8)
  169.  
  170.         # set frequency
  171.         frf = int((self._freq * 1000000.0) / FSTEP)
  172.         self._spi_write(REG_06_FRF_MSB, (frf >> 16) & 0xff)
  173.         self._spi_write(REG_07_FRF_MID, (frf >> 8) & 0xff)
  174.         self._spi_write(REG_08_FRF_LSB, frf & 0xff)
  175.  
  176.         # Set tx power
  177.         if self._tx_power < 5:
  178.             self._tx_power = 5
  179.         if self._tx_power > 23:
  180.             self._tx_power = 23
  181.  
  182.         if self._tx_power < 20:
  183.             self._spi_write(REG_4D_PA_DAC, PA_DAC_ENABLE)
  184.             self._tx_power -= 3
  185.         else:
  186.             self._spi_write(REG_4D_PA_DAC, PA_DAC_DISABLE)
  187.  
  188.         self._spi_write(REG_09_PA_CONFIG, PA_SELECT | (self._tx_power - 5))
  189.  
  190.     def on_recv(self, message):
  191.         # This should be overridden by the user
  192.         pass
  193.  
  194.     def sleep(self):
  195.         if self._mode != MODE_SLEEP:
  196.             print('Set mode sleep')
  197.             self._spi_write(REG_01_OP_MODE, MODE_SLEEP)
  198.             self._mode = MODE_SLEEP
  199.  
  200.     def set_mode_tx(self):
  201.         if self._mode != MODE_TX:
  202.             print('Set mode TX')
  203.             self._spi_write(REG_01_OP_MODE, MODE_TX)
  204.             self._spi_write(REG_40_DIO_MAPPING1, 0x40)  # Interrupt on TxDone
  205.             self._mode = MODE_TX
  206.  
  207.     def set_mode_rx(self):
  208.         if self._mode != MODE_RXCONTINUOUS:
  209.             print('Set mode RX')
  210.             self._spi_write(REG_01_OP_MODE, MODE_RXCONTINUOUS)
  211.             self._spi_write(REG_40_DIO_MAPPING1, 0x00)  # Interrupt on RxDone
  212.             self._mode = MODE_RXCONTINUOUS
  213.  
  214.     def set_mode_cad(self):
  215.         if self._mode != MODE_CAD:
  216.             print('Set mode CAD')
  217.             self._spi_write(REG_01_OP_MODE, MODE_CAD)
  218.             self._spi_write(REG_40_DIO_MAPPING1, 0x80)  # Interrupt on CadDone
  219.             self._mode = MODE_CAD
  220.  
  221.     def _is_channel_active(self):
  222.         self.set_mode_cad()
  223.  
  224.         while self._mode == MODE_CAD:
  225.             yield
  226.  
  227.         return self._cad
  228.  
  229.     def wait_cad(self):
  230.         if not self.cad_timeout:
  231.             return True
  232.  
  233.         start = time.time()
  234.         for status in self._is_channel_active():
  235.             if time.time() - start < self.cad_timeout:
  236.                 return False
  237.  
  238.             if status is None:
  239.                 time.sleep(0.1)
  240.                 continue
  241.             else:
  242.                 return status
  243.  
  244.     def wait_packet_sent(self):
  245.         # wait for `_handle_interrupt` to switch the mode back
  246.         start = time.time()
  247.         while time.time() - start < self.wait_packet_sent_timeout:
  248.             if self._mode != MODE_TX:
  249.                 return True
  250.  
  251.         return False
  252.  
  253.     def set_mode_idle(self):
  254.         if self._mode != MODE_STDBY:
  255.             self._spi_write(REG_01_OP_MODE, MODE_STDBY)
  256.             self._mode = MODE_STDBY
  257.  
  258.     def send(self, data, header_to, header_id=0, header_flags=0):
  259.         self.wait_packet_sent()
  260.         self.set_mode_idle()
  261.         self.wait_cad()
  262.  
  263.         header = [header_to, self._this_address, header_id, header_flags]
  264.         if type(data) == int:
  265.             data = [data]
  266.         elif type(data) == bytes:
  267.             data = [p for p in data]
  268.         elif type(data) == str:
  269.             data = [ord(s) for s in data]
  270.  
  271.         if self.crypto:
  272.             data = [b for b in self._encrypt(bytes(data))]
  273.  
  274.         payload = header + data
  275.         self._spi_write(REG_0D_FIFO_ADDR_PTR, 0)
  276.         self._spi_write(REG_00_FIFO, payload)
  277.         self._spi_write(REG_22_PAYLOAD_LENGTH, len(payload))
  278.  
  279.         self.set_mode_tx()
  280.         return True
  281.  
  282.     def send_to_wait(self, data, header_to, header_flags=0, retries=3):
  283.         self._last_header_id += 1
  284.  
  285.         for _ in range(retries + 1):
  286.             self.send(data, header_to, header_id=self._last_header_id,
  287.                       header_flags=header_flags)
  288.             self.set_mode_rx()
  289.  
  290.             if header_to == BROADCAST_ADDRESS:  # Don't wait for acks from a broadcast message
  291.                 return True
  292.  
  293.             start = time.time()
  294.             while time.time() - start < self.retry_timeout + (
  295.                     self.retry_timeout * (getrandbits(16) / (2 ** 16 - 1))):
  296.                 if self._last_payload:
  297.                     if self._last_payload.header_to == self._this_address and \
  298.                             self._last_payload.header_flags & FLAGS_ACK and \
  299.                             self._last_payload.header_id == self._last_header_id:
  300.                         # We got an ACK
  301.                         return True
  302.         return False
  303.  
  304.     def send_ack(self, header_to, header_id):
  305.         self.send(b'!', header_to, header_id, FLAGS_ACK)
  306.         self.wait_packet_sent()
  307.  
  308.     def _spi_write(self, register, payload):
  309.         if type(payload) == int:
  310.             payload = [payload]
  311.         elif type(payload) == bytes:
  312.             payload = [p for p in payload]
  313.         elif type(payload) == str:
  314.             payload = [ord(s) for s in payload]
  315.         self.cs.value(0)
  316.         self.spi.write(bytearray([register | 0x80] + payload))
  317.         self.cs.value(1)
  318.  
  319.     def _spi_read(self, register, length=1):
  320.         self.cs.value(0)
  321.         if length == 1:
  322.             data = self.spi.read(length + 1, register)[1]
  323.         else:
  324.             data = self.spi.read(length + 1, register)[1:]
  325.         self.cs.value(1)
  326.         return data
  327.  
  328.     def _decrypt(self, message):
  329.         decrypted_msg = self.crypto.decrypt(message)
  330.         msg_length = decrypted_msg[0]
  331.         return decrypted_msg[1:msg_length + 1]
  332.  
  333.     def _encrypt(self, message):
  334.         msg_length = len(message)
  335.         padding = bytes(
  336.             ((math.ceil((msg_length + 1) / 16) * 16) - (msg_length + 1)) * [0])
  337.         msg_bytes = bytes([msg_length]) + message + padding
  338.         encrypted_msg = self.crypto.encrypt(msg_bytes)
  339.         return encrypted_msg
  340.  
  341.     def _handle_interrupt(self, channel):
  342.         irq_flags = self._spi_read(REG_12_IRQ_FLAGS)
  343.         print('Got interrupt')
  344.  
  345.         if self._mode == MODE_RXCONTINUOUS and (irq_flags & RX_DONE):
  346.             print('RX done')
  347.             packet_len = self._spi_read(REG_13_RX_NB_BYTES)
  348.             self._spi_write(REG_0D_FIFO_ADDR_PTR,
  349.                             self._spi_read(REG_10_FIFO_RX_CURRENT_ADDR))
  350.  
  351.             packet = self._spi_read(REG_00_FIFO, packet_len)
  352.             self._spi_write(REG_12_IRQ_FLAGS, 0xff)  # Clear all IRQ flags
  353.  
  354.             snr = self._spi_read(REG_19_PKT_SNR_VALUE) / 4
  355.             rssi = self._spi_read(REG_1A_PKT_RSSI_VALUE)
  356.  
  357.             if snr < 0:
  358.                 rssi = snr + rssi
  359.             else:
  360.                 rssi = rssi * 16 / 15
  361.  
  362.             if self._freq >= 779:
  363.                 rssi = round(rssi - 157, 2)
  364.             else:
  365.                 rssi = round(rssi - 164, 2)
  366.  
  367.             if packet_len >= 4:
  368.                 header_to = packet[0]
  369.                 header_from = packet[1]
  370.                 header_id = packet[2]
  371.                 header_flags = packet[3]
  372.                 message = bytes(packet[4:]) if packet_len > 4 else b''
  373.  
  374.                 if (self._this_address != header_to) and (
  375.                         (header_to != BROADCAST_ADDRESS) or (
  376.                         self._receive_all is False)):
  377.                     return
  378.  
  379.                 if self.crypto and len(message) % 16 == 0:
  380.                     message = self._decrypt(message)
  381.  
  382.                 if self._acks and header_to == self._this_address and not header_flags & FLAGS_ACK:
  383.                     self.send_ack(header_from, header_id)
  384.  
  385.                 self.set_mode_rx()
  386.  
  387.                 self._last_payload = namedtuple(
  388.                     "Payload",
  389.                     ['message', 'header_to', 'header_from', 'header_id', 'header_flags',
  390.                      'rssi', 'snr']
  391.                 )(message, header_to, header_from, header_id, header_flags, rssi, snr)
  392.  
  393.                 if not header_flags & FLAGS_ACK:
  394.                     self.on_recv(self._last_payload)
  395.  
  396.         elif self._mode == MODE_TX and (irq_flags & TX_DONE):
  397.             print('Transmission done')
  398.             self.set_mode_idle()
  399.  
  400.         elif self._mode == MODE_CAD and (irq_flags & CAD_DONE):
  401.             print('CAD done')
  402.             self._cad = irq_flags & CAD_DETECTED
  403.             self.set_mode_idle()
  404.  
  405.         self._spi_write(REG_12_IRQ_FLAGS, 0xff)
  406.  
  407.     def _handle_interrupt2(self, channel):
  408.         print('Interrupt 2')
  409.         irq_flags = self._spi_read(REG_12_IRQ_FLAGS)
  410.         print(irq_flags)
  411.  
  412.     def close(self):
  413.         self.spi.deinit()
  414.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement