epeters13 / pyLoraRFM9x

Fork of the raspi-lora project with improved interrupt handling and reset support
MIT License
6 stars 9 forks source link

First message does not trigger ack #2

Open martynwheeler opened 3 years ago

martynwheeler commented 3 years ago

Hi,

I have your code working now and its seems very good. I have found two slight issues.

  1. If I set acks=True the code still sends an ack back to the client upon receiving a message. This could easily be fixed by changing line 318 from:

    if  header_to == self._this_address and header_flags & FLAGS_REQ_ACK and not header_flags & FLAGS_ACK:

    to:

    if  (header_to == self._this_address and header_flags & FLAGS_REQ_ACK and not header_flags & FLAGS_ACK) and self._ack:
  2. My client (an arduino with the example code from radiohead) always sends the message twice because I think that the ack is not being correctly triggered. It all works with raspi-lora and does not end up sending two messages. My code is below. I am not sure why the ack is not being sent each time I get a message? It always does it on the first retry from the client. Is it possibly to do with the interrupts?

Here is my code:

from pyLoraRFM9x import LoRa, ModemConfig
import time

# This is our callback function that runs when a message is received
def on_recv(payload):
    print("From:", payload.header_from)
    print("ID:", payload.header_id)
    print("Received:", payload.message)
    print("RSSI: {}; SNR: {}".format(payload.rssi, payload.snr))

# Use chip select 1. GPIO pin 5 will be used for interrupts and set reset pin to 25
# The address of this device will be set to 2
lora = LoRa(1, 5, 2, reset_pin = 25, modem_config=ModemConfig.Bw125Cr45Sf128, freq=868, tx_power=20, acks=True)
lora.on_recv = on_recv

lora.set_mode_rx()

try:
    while True:
        pass
except:
   print("some error") 

finally:
   print("clean up") 
   lora.close()

and here is the output:

From: 1 ID: 54 Received: b'Hello World!\x00' RSSI: -22; SNR: 10.25 SENT ACK From: 1 ID: 54 Received: b'Hello World!\x00' RSSI: -21; SNR: 9.75 From: 1 ID: 55 Received: b'Hello World!\x00' RSSI: -22; SNR: 9.75 SENT ACK From: 1 ID: 55 Received: b'Hello World!\x00' RSSI: -22; SNR: 9.75 From: 1 ID: 56 Received: b'Hello World!\x00' RSSI: -21; SNR: 9.5 SENT ACK From: 1 ID: 56 Received: b'Hello World!\x00' RSSI: -20; SNR: 11.25

epeters13 commented 3 years ago

Thanks for reporting this. I will look into this hopefully soon. So far I have not used the sending of acknowledgements from the pi, so this feature is rather untested. I have in the past observed some strange behavior as well with the interrupts, especially when sending a reply as part of the ISR.

martynwheeler commented 3 years ago

Thanks for your response. I have done some more testing with the code below for both raspi-lora and pyLoraRFM9x. The python code is identical in both cases (apart from the imports) and both receive the same signal from the same client. I am convinced that pyLoraRFM9x is missing the first transmission every time which suggests that the interrupt is not being triggered, but then it sees the second attempt to see the signal and sends back and ack.

This is the code for raspi-lora:

from raspi_lora import LoRa, ModemConfig
import time

# This is our callback function that runs when a message is received
def on_recv(payload):
    print("From:", payload.header_from)
    print("Received:", payload.message.decode("utf-8"))
    print("ID:", payload.header_id)
    print("RSSI: {}; SNR: {}".format(payload.rssi, payload.snr))

# Use chip select 1. GPIO pin 5 will be used for interrupts
# The address of this device will be set to 2
lora = LoRa(1, 5, 2, modem_config=ModemConfig.Bw125Cr45Sf128, freq=868, tx_power=20, acks=True)
lora.on_recv = on_recv

lora.set_mode_rx()

try:
    while True:
        pass
except:
   print("some error") 

finally:
   print("clean up") 
   lora.close()

and here’s the output which works as expected:

From: 1
Received: TEMP025.1
ID: 2
RSSI: -35.4; SNR: 9.75
From: 1
Received: TEMP025.1
ID: 3
RSSI: -35.4; SNR: 9.5
From: 1
Received: TEMP025.1
ID: 4
RSSI: -34.33; SNR: 9.5

and this is the code for pyLoraRFM9x

from pyLoraRFM9x import LoRa, ModemConfig
import time

# This is our callback function that runs when a message is received
def on_recv(payload):
    print("From:", payload.header_from)
    print("Received:", payload.message.decode("utf-8"))
    print("ID:", payload.header_id)
    print("RSSI: {}; SNR: {}".format(payload.rssi, payload.snr))

# Use chip select 1. GPIO pin 5 will be used for interrupts
# The address of this device will be set to 2
lora = LoRa(1, 5, 2, modem_config=ModemConfig.Bw125Cr45Sf128, freq=868, tx_power=20, acks=True)
lora.on_recv = on_recv

lora.set_mode_rx()

try:
    while True:
        pass
except:
   print("some error") 

finally:
   print("clean up") 
   lora.close()

and here’s the output showing the missed triggers:

From: 1
Received: TEMP025.1
ID: 8
RSSI: -22; SNR: 9.75
SENT ACK
From: 1
Received: TEMP025.1
ID: 8
RSSI: -22; SNR: 9.5
From: 1
Received: TEMP025.1
ID: 9
RSSI: -21; SNR: 9.5
SENT ACK
From: 1
Received: TEMP025.1
ID: 9
RSSI: -22; SNR: 9.5
From: 1
Received: TEMP025.1
ID: 10
RSSI: -21; SNR: 9.25
SENT ACK
From: 1
Received: TEMP025.1
ID: 10

I am sending the data from an esp32 with the following sketch:

// rf95_reliable_datagram_client.pde
// -*- mode: C++ -*-
// Example sketch showing how to create a simple addressed, reliable messaging client
// with the RHReliableDatagram class, using the RH_RF95 driver to control a RF95 radio.
// It is designed to work with the other example rf95_reliable_datagram_server
// Tested with Anarduino MiniWirelessLoRa, Rocket Scream Mini Ultra Pro with the RFM95W 

#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>

#define RFM95_RST 14
#define RFM95_CS 5
#define RFM95_INT 12
#define RF95_FREQ 868.0

#define CLIENT_ADDRESS 1
#define SERVER_ADDRESS 2

// Singleton instance of the radio driver
RH_RF95 driver(RFM95_CS, RFM95_INT);

// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram manager(driver, CLIENT_ADDRESS);

void setup() 
{
  Serial.begin(9600);
  while (!Serial) ; // Wait for serial port to be available
  Serial.println("ESP-32S LoRa TX Test!");

  //setup reset pin fpr output and perform manual reset
  pinMode(RFM95_RST, OUTPUT);
  digitalWrite(RFM95_RST, LOW);
  delay(10);
  digitalWrite(RFM95_RST, HIGH);
  delay(10);

  //initialise comms
  while (!manager.init()) {
    Serial.println("LoRa radio init failed");
    Serial.println("Uncomment '#define SERIAL_DEBUG' in RH_RF95.cpp for detailed debug info");
  }
  Serial.println("LoRa radio init OK!");

  // Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
  // You can optionally require this module to wait until Channel Activity
  // Detection shows no activity on the channel before transmitting by setting
  // the CAD timeout to non-zero:
  //driver.setCADTimeout(10000);
  driver.setFrequency(RF95_FREQ);
  driver.setTxPower(20, false);
  driver.setModemConfig(RH_RF95::Bw125Cr45Sf128);
//  manager.setRetries(1);
//  manager.setTimeout(5000);
}

uint8_t data[] = "TEMP025.1";
// Dont put this on the stack:
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];

void loop()
{
  Serial.println("Sending to rf95_reliable_datagram_server");
  // Send a message to manager_server
  if (!manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS))
  {
    Serial.println("sendtoWait failed");
  }
  delay(10000);
}
epeters13 commented 5 months ago

@martynwheeler I fixed an issue that caused a too fast state change in the RFM9x modem, leaving it hang in a bad state. Checkout the rpi.GPIO-final commit