Open dukeduck1984 opened 3 years ago
I think I have found the reason - it's in the method handle_on_receive
.
When the IRQ gets triggered, it's not necessarily that the full message has been received, hence if (irq_flags == IRQ_RX_DONE_MASK)
may be False
which then automatically set the module to single RX mode. As a result, the callback funciton will never be called again.
It should be at least set a timeout loop to wait until the full message has come in and irq_flags == IRQ_RX_DONE_MASK
for the callback to be executed.
I don't understand why in the original code it will in some cases set the module back to single RX mode - if the user has decided to use the module in countinuous mode, it should stick to that mode.
My modified asyncio version as following (need to use Peter Hinch's IRQ_EVENT primitives to handle the IRQ):
async def on_receive(self, callback):
self._on_receive_cb = callback
if self._pin_rx_done:
if callback:
# enable RxDone interrupt in DIO Mapping 1
self.write_register(REG_DIO_MAPPING_1, 0x00)
self._pin_rx_done.irq(handler=self._pin_rx_done_handler, trigger=Pin.IRQ_RISING)
await self.handle_on_receive()
else:
self._pin_rx_done.irq(handler=None, trigger=0)
def _pin_rx_done_handler(self, pin):
self._event_rx.set()
async def handle_on_receive(self):
while True:
print('waiting rx')
await self._event_rx.wait()
print('irq triggered')
rx_income_ms = utime.ticks_ms()
self.set_lock(True) # lock until TX_Done
while utime.ticks_diff(utime.ticks_ms(), rx_income_ms) < 3000: # Set timeout=3000ms
irq_flags = self.get_irq_flags()
if irq_flags == IRQ_RX_DONE_MASK: # RX_DONE only, irq_flags should be 0x40
# automatically standby when RX_DONE
if self._on_receive_cb:
print('msg coming in')
payload = self.read_payload()
self._on_receive_cb(payload)
break
else:
await asyncio.sleep(0)
# elif self.read_register(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE):
# print('no msg received')
# # no packet received.
# # reset FIFO address / # enter single RX mode
# self.write_register(REG_FIFO_ADDR_PTR, FifoRxBaseAddr)
# self.write_register(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)
self.set_lock(False) # unlock in any case.
self.collect_garbage()
Hi,
[Edit] From what I found behavior will be dependent on your board and on the configuration of the register Regdiomapping. This register maps which information is made available on which pin of the Lora transceiver.
Would you pls be more specific? Did you mean the timing of IRQ being triggered versus the RX status? Regarding the Regdiomapping configuration, I think it's set in the method on_receive()
as following:
def on_receive(self, callback):
self._on_receive = callback
if self._pin_rx_done:
if callback:
self.write_register(REG_DIO_MAPPING_1, 0x00)
self._pin_rx_done.irq(
trigger=Pin.IRQ_RISING, handler = self.handle_on_receive
)
else:
self._pin_rx_done.detach_irq()
According to the datasheet, writing 0x00
to REG_DIO_MAPPING
enables the interrupt on DIO0
pin upon RX. Is there any other configurations need to be done?
Sorry, you are totally right. I read the datasheet to quickly and mixed up the packet mode (which can involves more complex cases to handle due to the FIFO being only 64B) with the LORA mode.
What value do you have when you read the irq flags register ? Does it happen every time or only some times ? How long is the payload you are sending ?
It happened every time - I never managed to receive messages until I modified the code as shown above. The payload was 15 bytes long.
I didn't print out the irq flags value, so I have no idea what it was. But according to the info you posted, the interrupt triggers when the payload is readily available (RxDone), no more waiting is needed.
I read the datasheet again - the interrupt at DIO0 pin can occur in both Rx Single mode & Rx Continuous mode. In Rx Single mode, a Rx Timeout period should be set first, the LoRa module will wait for the preamble for that Rx Timeout period of time before triggers RxTimeout interruption and goes to standby mode. If the payload comes in before timeout, RxDone interruption will occur.
But in the original code, I didn't see anywhere that Rx Timeout period can be set (RegSymbTimeout).
There's a bit difference in the code here, in the comment it says the IRQ Flag should be 0x50, rather than 0x40 in this repo.
So I printed the value of IRQ flag, it's 80 which was 0x50, or 0b01010000, which meant RxDone & ValidHeader per datasheet on page 111. The working code as following:
async def handle_on_receive(self):
while True:
print('waiting rx')
await self._event_rx.wait()
print('irq triggered')
self.set_lock(True) # lock until TX_Done
irq_flags = self.get_irq_flags()
# print(irq_flags)
# irq_flags should be 0x50 (0b01010000): RxDone & ValidHeader
if irq_flags & IRQ_RX_DONE_MASK == IRQ_RX_DONE_MASK:
# automatically standby when RX_DONE
if self._on_receive_cb:
print('msg coming in')
payload = self.read_payload()
self._on_receive_cb(payload)
# elif self.read_register(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE):
# print('no msg received')
# # no packet received.
# # reset FIFO address / # enter single RX mode
# self.write_register(REG_FIFO_ADDR_PTR, FifoRxBaseAddr)
# self.write_register(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)
self.set_lock(False) # unlock in any case.
self.collect_garbage()
It happened every time - I never managed to receive messages until I modified the code as shown above. The payload was 15 bytes long.
I didn't print out the irq flags value, so I have no idea what it was. But according to the info you posted, the interrupt triggers when the payload is readily available (RxDone), no more waiting is needed.
Strange, it worked for me on a TTGO Lora board V1.
I agree with you regarding the rx single part in the code, I do not understand why it is here...
0x50 seems better but 0x40 should works fine too, worst case you get rubish data.
I spent a lot of time strugling with the IQ config as the datasheed is not very clear regarding this part. (LORAWan is using reverse IQ so that gateway can not see packets from other gateways; same thing for nodes which can not see packet from each other).
Would you share your asyncio version ? (I am very new to asyncio and I would be interested to learn it)
0x40 only worked for me as shown in my 2nd post from the top.
I only rewrote the interrupt part with asyncio in order to have it working with my other asyncio code base, as the interrupt may cause a racing condition. Below is the full driver for your reference (letter case also changed to comply with PEP)
import gc
import utime
from machine import Pin
from micropython import const
from primitives.irq_event import IRQ_EVENT
PA_OUTPUT_RFO_PIN = const(0)
PA_OUTPUT_PA_BOOST_PIN = const(1)
# registers
REG_FIFO = const(0x00)
REG_OP_MODE = const(0x01)
REG_FRF_MSB = const(0x06)
REG_FRF_MID = const(0x07)
REG_FRF_LSB = const(0x08)
REG_PA_CONFIG = const(0x09)
REG_LNA = const(0x0C)
REG_FIFO_ADDR_PTR = const(0x0D)
REG_FIFO_TX_BASE_ADDR = const(0x0E)
FifoTxBaseAddr = const(0x00)
# FifoTxBaseAddr = 0x80
REG_FIFO_RX_BASE_ADDR = const(0x0F)
FifoRxBaseAddr = const(0x00)
REG_FIFO_RX_CURRENT_ADDR = const(0x10)
REG_IRQ_FLAGS_MASK = const(0x11)
REG_IRQ_FLAGS = const(0x12)
REG_RX_NB_BYTES = const(0x13)
REG_PKT_RSSI_VALUE = const(0x1A)
REG_PKT_SNR_VALUE = const(0x1B)
REG_MODEM_CONFIG_1 = const(0x1D)
REG_MODEM_CONFIG_2 = const(0x1E)
REG_PREAMBLE_MSB = const(0x20)
REG_PREAMBLE_LSB = const(0x21)
REG_PAYLOAD_LENGTH = const(0x22)
REG_FIFO_RX_BYTE_ADDR = const(0x25)
REG_MODEM_CONFIG_3 = const(0x26)
REG_RSSI_WIDEBAND = const(0x2C)
REG_DETECTION_OPTIMIZE = const(0x31)
REG_DETECTION_THRESHOLD = const(0x37)
REG_SYNC_WORD = const(0x39)
REG_DIO_MAPPING_1 = const(0x40)
REG_VERSION = const(0x42)
# invert IQ
REG_INVERTIQ = const(0x33)
RFLR_INVERTIQ_RX_MASK = const(0xBF)
RFLR_INVERTIQ_RX_OFF = const(0x00)
RFLR_INVERTIQ_RX_ON = const(0x40)
RFLR_INVERTIQ_TX_MASK = const(0xFE)
RFLR_INVERTIQ_TX_OFF = const(0x01)
RFLR_INVERTIQ_TX_ON = const(0x00)
REG_INVERTIQ2 = const(0x3B)
RFLR_INVERTIQ2_ON = const(0x19)
RFLR_INVERTIQ2_OFF = const(0x1D)
# modes
MODE_LONG_RANGE_MODE = const(0x80) # bit 7: 1 => LoRa mode
MODE_SLEEP = const(0x00)
MODE_STDBY = const(0x01)
MODE_TX = const(0x03)
MODE_RX_CONTINUOUS = const(0x05)
MODE_RX_SINGLE = const(0x06)
# PA config
PA_BOOST = const(0x80)
# IRQ masks
IRQ_TX_DONE_MASK = const(0x08)
IRQ_PAYLOAD_CRC_ERROR_MASK = const(0x20)
IRQ_RX_DONE_MASK = const(0x40)
IRQ_RX_TIME_OUT_MASK = const(0x80)
# Buffer size
MAX_PKT_LENGTH = const(255)
class AS_SX127x:
"""
Slightly modified 'async' version in order to properly handle 'on_receive' interruption to work with asyncio.
See 'on_receive' and 'handle_on_receive' methods.
"""
default_params = {
'frequency': 433E6,
'tx_power_level': 14, # Range 2-17, 2 being the low end
'signal_bandwidth': 125E3,
'spreading_factor': 7, # Range 7-10, 7 being short range but faster
'coding_rate': 5,
'preamble_length': 8,
'implicit_header': False,
'sync_word': 0x12, # ranges from 0-0xFF. 0x12 for private network; 0x34 for public network such as TTN
'enable_crc': False,
'invert_iq': False,
}
frfs = {
169E6: (42, 64, 0),
433E6: (108, 64, 0),
434E6: (108, 128, 0),
866E6: (216, 128, 0),
868E6: (217, 0, 0),
915E6: (228, 192, 0)
}
def __init__(
self,
spi_obj,
nss_pin,
reset_pin,
dio_0_pin=None,
# pins,
params=None
):
self._spi = spi_obj
self._pin_ss = None
self._pin_rst = None
self._pin_rx_done = None
self.set_pins(nss_pin, reset_pin, dio_0_pin)
self.reset_module()
# self._pins = pins
if params:
self._params = params
else:
self._params = self.default_params
self._lock = False
self._event_rx = IRQ_EVENT() # IRQ_EVENT instance for receiving Pin
# setting pins
# if "dio_0" in self._pins:
# self._pin_rx_done = Pin(self._pins["dio_0"], Pin.IN)
# if "ss" in self._pins:
# self._pin_ss = Pin(self._pins["ss"], Pin.OUT)
# if "led" in self._pins:
# self._led_status = Pin(self._pins["led"], Pin.OUT)
# check hardware version
self.check_version()
# put in LoRa and sleep mode
self.sleep()
# config
self._frequency = None
self.set_frequency(self._params['frequency'])
self.set_signal_bandwidth(self._params['signal_bandwidth'])
# set LNA boost
self.write_register(REG_LNA, self.read_register(REG_LNA) | 0x03)
# set auto AGC
self.write_register(REG_MODEM_CONFIG_3, 0x04)
self._tx_power_level = None
self.set_tx_power(self._params['tx_power_level'])
self._implicit_header_mode = None
self.implicit_header_mode(self._params['implicit_header'])
self.set_spreading_factor(self._params['spreading_factor'])
self.set_coding_rate(self._params['coding_rate'])
self.set_preamble_length(self._params['preamble_length'])
self.set_sync_word(self._params['sync_word'])
self.enable_crc(self._params['enable_crc'])
self.invert_iq(self._params["invert_iq"])
self._on_receive_cb = None # Callback function used to handle the payload upon receiving new message
# set LowDataRateOptimize flag if symbol time > 16ms (default disable on reset)
# self.write_register(REG_MODEM_CONFIG_3, self.read_register(REG_MODEM_CONFIG_3) & 0xF7) # default disable on reset
bw = self._params["signal_bandwidth"]
sf = self._params["spreading_factor"]
if 1000 / (bw / 2**sf) > 16:
self.write_register(REG_MODEM_CONFIG_3, self.read_register(REG_MODEM_CONFIG_3) | 0x08)
# set base addresses
self.write_register(REG_FIFO_TX_BASE_ADDR, FifoTxBaseAddr)
self.write_register(REG_FIFO_RX_BASE_ADDR, FifoRxBaseAddr)
self.standby()
def check_version(self):
self._pin_ss.value(0)
version = 0
for _ in range(5):
version = self.read_register(REG_VERSION)
if version:
break
if version != 0x12:
print('Error: Invalid version.')
else:
print("SX version: {}".format(version))
self._pin_ss.value(1)
def begin_packet(self, implicit_header_mode=False):
self.standby()
self.implicit_header_mode(implicit_header_mode)
# reset FIFO address and payload length
self.write_register(REG_FIFO_ADDR_PTR, FifoTxBaseAddr)
self.write_register(REG_PAYLOAD_LENGTH, 0)
def end_packet(self):
# put in TX mode
self.write_register(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX)
# wait for TX done, standby automatically on TX_DONE
tmout = 0
while (self.read_register(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0:
utime.sleep_ms(10)
tmout += 1
if tmout > 20:
break
pass
# clear IRQ's
self.write_register(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK)
if tmout >= 20:
print('Send timeout.')
# self.collect_garbage()
def write(self, buffer):
current_length = self.read_register(REG_PAYLOAD_LENGTH)
size = len(buffer)
# check size
size = min(size, (MAX_PKT_LENGTH - FifoTxBaseAddr - current_length))
# write data
for i in range(size):
self.write_register(REG_FIFO, buffer[i])
# update length
self.write_register(REG_PAYLOAD_LENGTH, current_length + size)
return size
def set_lock(self, lock=False):
self._lock = lock
def println(self, msg, implicit_header=False):
self.set_lock(True) # wait until RX_Done, lock and begin writing.
self.begin_packet(implicit_header)
if isinstance(msg, str):
msg = msg.encode()
self.write(msg)
self.end_packet()
self.set_lock(False) # unlock when done writing
self.collect_garbage()
def get_irq_flags(self):
irq_flags = self.read_register(REG_IRQ_FLAGS)
self.write_register(REG_IRQ_FLAGS, irq_flags)
return irq_flags
def packet_rssi(self):
rssi = self.read_register(REG_PKT_RSSI_VALUE)
return rssi - (164 if self._frequency < 868E6 else 157)
def packet_snr(self):
return self.read_register(REG_PKT_SNR_VALUE) * 0.25
def standby(self):
self.write_register(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY)
def sleep(self):
self.write_register(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP)
def set_tx_power(self, level, output_pin=PA_OUTPUT_PA_BOOST_PIN):
self._tx_power_level = level
if output_pin == PA_OUTPUT_RFO_PIN:
# RFO
level = min(max(level, 0), 14)
self.write_register(REG_PA_CONFIG, 0x70 | level)
else:
# PA BOOST
level = min(max(level, 2), 17)
self.write_register(REG_PA_CONFIG, PA_BOOST | (level - 2))
# def set_frequency(self, frequency, freq_table=None):
# self._frequency = frequency
# if freq_table:
# table = freq_table
# else:
# table = self.frfs
# self.write_register(REG_FRF_MSB, table[frequency][0])
# self.write_register(REG_FRF_MID, table[frequency][1])
# self.write_register(REG_FRF_LSB, table[frequency][2])
def set_frequency(self, frequency):
self._frequency = frequency
freq_reg = int(int(int(frequency) << 19) / 32000000) & 0xFFFFFF
self.write_register(REG_FRF_MSB, (freq_reg & 0xFF0000) >> 16)
self.write_register(REG_FRF_MID, (freq_reg & 0xFF00) >> 8)
self.write_register(REG_FRF_LSB, (freq_reg & 0xFF))
def set_spreading_factor(self, sf):
sf = min(max(sf, 6), 12)
self.write_register(REG_DETECTION_OPTIMIZE, 0xC5 if sf == 6 else 0xC3)
self.write_register(REG_DETECTION_THRESHOLD, 0x0C if sf == 6 else 0x0A)
self.write_register(
REG_MODEM_CONFIG_2,
(self.read_register(REG_MODEM_CONFIG_2) & 0x0F) | ((sf << 4) & 0xF0)
)
def set_signal_bandwidth(self, sbw):
bins = (7.8E3, 10.4E3, 15.6E3, 20.8E3, 31.25E3, 41.7E3, 62.5E3, 125E3, 250E3)
bw = 9
if sbw < 10:
bw = sbw
else:
for i in range(len(bins)):
if sbw <= bins[i]:
bw = i
break
self.write_register(
REG_MODEM_CONFIG_1,
(self.read_register(REG_MODEM_CONFIG_1) & 0x0F) | (bw << 4)
)
def set_coding_rate(self, denominator):
denominator = min(max(denominator, 5), 8)
cr = denominator - 4
self.write_register(
REG_MODEM_CONFIG_1,
(self.read_register(REG_MODEM_CONFIG_1) & 0xF1) | (cr << 1)
)
def set_preamble_length(self, length):
self.write_register(REG_PREAMBLE_MSB, (length >> 8) & 0xFF)
self.write_register(REG_PREAMBLE_LSB, (length >> 0) & 0xFF)
def enable_crc(self, enable_crc=False):
modem_config_2 = self.read_register(REG_MODEM_CONFIG_2)
config = modem_config_2 | 0x04 if enable_crc else modem_config_2 & 0xFB
self.write_register(REG_MODEM_CONFIG_2, config)
def invert_iq(self, invert_iq):
self._params["invert_iq"] = invert_iq
if invert_iq:
self.write_register(
REG_INVERTIQ,
(
(
self.read_register(REG_INVERTIQ)
& RFLR_INVERTIQ_TX_MASK
& RFLR_INVERTIQ_RX_MASK
)
| RFLR_INVERTIQ_RX_ON
| RFLR_INVERTIQ_TX_ON
),
)
self.write_register(REG_INVERTIQ2, RFLR_INVERTIQ2_ON)
else:
self.write_register(
REG_INVERTIQ,
(
(
self.read_register(REG_INVERTIQ)
& RFLR_INVERTIQ_TX_MASK
& RFLR_INVERTIQ_RX_MASK
)
| RFLR_INVERTIQ_RX_OFF
| RFLR_INVERTIQ_TX_OFF
),
)
self.write_register(REG_INVERTIQ2, RFLR_INVERTIQ2_OFF)
def set_sync_word(self, sw):
self.write_register(REG_SYNC_WORD, sw)
def set_channel(self, parameters):
self.standby()
for key in parameters:
if key == "frequency":
self.set_frequency(parameters[key])
continue
if key == "invert_iq":
self.invert_iq(parameters[key])
continue
if key == "tx_power_level":
self.set_tx_power(parameters[key])
continue
def dump_registers(self):
for i in range(128):
print("0x{:02X}: {:02X}".format(i, self.read_register(i)), end="")
if (i + 1) % 4 == 0:
print()
else:
print(" | ", end="")
def implicit_header_mode(self, implicit_header_mode=False):
if self._implicit_header_mode != implicit_header_mode: # set value only if different.
self._implicit_header_mode = implicit_header_mode
modem_config_1 = self.read_register(REG_MODEM_CONFIG_1)
config = modem_config_1 | 0x01 if implicit_header_mode else modem_config_1 & 0xFE
self.write_register(REG_MODEM_CONFIG_1, config)
def receive(self, size=0):
self.implicit_header_mode(size > 0)
if size > 0:
self.write_register(REG_PAYLOAD_LENGTH, size & 0xFF)
# The last packet always starts at FIFO_RX_CURRENT_ADDR
# no need to reset FIFO_ADDR_PTR
self.write_register(
REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS
)
print('Into RxContinuous Mode')
async def on_receive(self, callback):
self._on_receive_cb = callback
if self._pin_rx_done:
if callback:
# enable RxDone interrupt in DIO Mapping 1
self.write_register(REG_DIO_MAPPING_1, 0x00)
self._pin_rx_done.irq(handler=lambda e: self._event_rx.set(), trigger=Pin.IRQ_RISING)
await self.handle_on_receive()
else:
self._pin_rx_done.irq(handler=None, trigger=0)
async def handle_on_receive(self):
while True:
print('waiting rx')
await self._event_rx.wait()
print('irq triggered')
self.set_lock(True) # lock until TX_Done
irq_flags = self.get_irq_flags()
# print(irq_flags)
# irq_flags should be 0x50 (0b01010000): RxDone & ValidHeader
if irq_flags & IRQ_RX_DONE_MASK == IRQ_RX_DONE_MASK:
# automatically standby when RX_DONE
if self._on_receive_cb:
print('msg coming in')
payload = self.read_payload()
self._on_receive_cb(payload)
# elif self.read_register(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE):
# print('no msg received')
# # no packet received.
# # reset FIFO address / # enter single RX mode
# self.write_register(REG_FIFO_ADDR_PTR, FifoRxBaseAddr)
# self.write_register(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)
self.set_lock(False) # unlock in any case.
self.collect_garbage()
def received_packet(self, size=0):
# Don't check Rx until Tx is done
if self._lock:
return not self._lock
irq_flags = self.get_irq_flags()
self.implicit_header_mode(size > 0)
if size > 0:
self.write_register(REG_PAYLOAD_LENGTH, size & 0xFF)
# if (irq_flags & IRQ_RX_DONE_MASK) and \
# (irq_flags & IRQ_RX_TIME_OUT_MASK == 0) and \
# (irq_flags & IRQ_PAYLOAD_CRC_ERROR_MASK == 0):
if irq_flags == IRQ_RX_DONE_MASK:
# RX_DONE only, irq_flags should be 0x40
# automatically standby when RX_DONE
return True
elif self.read_register(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE):
# no packet received.
# reset FIFO address / # enter single RX mode
self.write_register(REG_FIFO_ADDR_PTR, FifoRxBaseAddr)
self.write_register(
REG_OP_MODE,
MODE_LONG_RANGE_MODE | MODE_RX_SINGLE
)
def read_payload(self):
# set FIFO address to current RX address
# fifo_rx_current_addr = self.read_register(REG_FIFO_RX_CURRENT_ADDR)
self.write_register(REG_FIFO_ADDR_PTR, self.read_register(REG_FIFO_RX_CURRENT_ADDR))
# read packet length
if self._implicit_header_mode:
packet_length = self.read_register(REG_PAYLOAD_LENGTH)
else:
packet_length = self.read_register(REG_RX_NB_BYTES)
payload = bytearray()
for i in range(packet_length):
payload.append(self.read_register(REG_FIFO))
self.collect_garbage()
return bytes(payload)
def read_register(self, address, byteorder='big', signed=False):
response = self.transfer(address & 0x7F)
return int.from_bytes(response, byteorder)
def write_register(self, address, value):
self.transfer(address | 0x80, value)
def transfer(self, address, value=0x00):
response = bytearray(1)
self._pin_ss.value(0)
self._spi.write(bytes([address]))
self._spi.write_readinto(bytes([value]), response)
self._pin_ss.value(1)
return response
def reset_module(self, duration_low_ms=50, duration_high_ms=50):
self._pin_rst.off()
utime.sleep_ms(duration_low_ms)
self._pin_rst.on()
utime.sleep_ms(duration_high_ms)
def set_pins(self, ss, rst, dio0=None):
self._pin_ss = Pin(ss, Pin.OUT, value=1)
self._pin_rst = Pin(rst, Pin.OUT, value=1)
self._pin_rx_done = Pin(dio0, Pin.IN) if dio0 else None
@staticmethod
def collect_garbage():
gc.collect()
# print('[Memory - free: {} allocated: {}]'.format(gc.mem_free(), gc.mem_alloc()))
@jbfuzier Is the invert IQ documented somewhere? I'd like to learn about it in case someday I will be using LoRaWan as well. Thanks.
Thanks.
Unfortunately I have not found much information regarding this invert IQ mecanism.
Ultimately, the goal is to reduce useless load on the node & gateway by making sure nodes don't see messages from each other and gateway does not see messages from each other (which would be useless).
To do that Gateways send messages with IQ inverted (which physically changes the phase of the signal), nodes must be expected IQ to be inverted in order to receive the messages.
The datasheet is very misleading (not to say wrong) regarding the INVERTIQ register
For example if you want to only have nodes that can all talk to each other (= do not invertIQ), you need to set the register to 0x01, otherwise nodes can not talk to each other.
If you want to have a gateway/nodes scenario, register value need to be 0x41 on gateway and 0x00 on the nodes (or the other way around).
To add lorawan I think the best course of action is to port a working arduino lib or a python lib to micropython. I am not using Lorawan (too complex for a private network IMHO), I just have a use case with several nodes and one receiver; I added on top a simple layer which do the encryption, integrity check and ack/retry. The issue is that encryption adds a lot of overhead to the payload 32bytes for AES128.
Thanks for the explaination. My use case is similar to yours - multiple nodes & one receiver, TTN not involved. What hardware (LoRa module & microcontroller) do you use for the receiver? Same as the nodes?
Hi @dukeduck1984 @jbfuzier I am using a customized board and I also faced the same issue.
The RX handler is not being triggered at all. Only the original polling mode is working for me.
I even used a print inside the handle_on_receive
to see if the interruption was being triggered,
but it seems it isn't.
Did it also happen to you @dukeduck1984 ?
I mean, was your handler triggered and you couldn't retrieve the data or have your callback called or your handler was
not triggered at all, just like me?
I still didn't have the chance to try your async mode, I have to understand how it works.
You need to change the if
statement inside the handle_on_receive
to: if irq_flags & IRQ_RX_DONE_MASK == IRQ_RX_DONE_MASK:
, then it will work.
So it should look like:
if irq_flags & IRQ_RX_DONE_MASK == IRQ_RX_DONE_MASK:
if self._on_receive_cb:
print('msg coming in')
payload = self.read_payload()
self._on_receive_cb(payload)
Hum.. ok, thank you @dukeduck1984
But in my case, handle_on_receive
isn't being triggered.
@dukeduck1984 I used a LA to monitor the DI0 pin and I figured out it is not changing level and I have another node continuously sending data. My irq_flags
are 0x00.
@dukeduck1984 my issue was solved. I forgot to call lora.receive() to change the LoRa mode. ¬¬
@dukeduck1984 The IRQ_EVENT is deprecated in the newer version of uasyncio. (https://forum.micropython.org/viewtopic.php?t=9867) Do you have maybe an updated your lorawan program with this new version? If not, could please guide me, how to do that?
Thanks is advance.
Hi @Bujtar ,
As Peter pointed out in his post, you can use ThreadSafeFlag
, as per the example shown here.
According to note [1] at the bottom of https://lora-developers.semtech.com/documentation/tech-papers-and-guides/channel-activity-detection-ensuring-your-lora-packets-are-sent/how-to-ensure-your-lora-packets-are-sent-properly/ the sx1276 should be able to receive lora packets without host mcu involvement.
I'm trying use dio0 to wake an esp32 from deepsleep when the sx1276 receives a packet. It's working but when the esp32 wakes up I can't recover the packet with lora.read_payload()
Would you tell me how you configured DIO0 to wake-up the ESP32? What happens after the ESP32 wakes-up? Can you tell where things stall?
Let me put what I did in a proper program that you can easily run instead of the interactive nightmare I was developing with. That way I can also send you a copy of the while loop code benchmark code I was using previously with the much reduced sx1276 current. Might take me a day or so to put some lipstick on the pig. I'll also put it on https://github.com/orgs/micropython/discussions/11515 so our lora chats are all in one place.
Good. I will take me some time to swap-over to his code on my test link.
Hi,
I have set the
dio_0
pin number inconfig.py
and changed the code inexamples/LoRaReceiver.py
as following:But it didn't work - couldn't receive any data. It only worked with
while True
loop as shown in the original example.Did I miss something? Pls help!
It's an AI-Thinker Ra-02 LoRa module with ESP32. The MicroPython firmware is V1.13 stable.
I used the sx127x driver in the master branch, and the readme title is uPyLora.