sandeepmistry / arduino-LoRa

An Arduino library for sending and receiving data using LoRa radios.
MIT License
1.65k stars 630 forks source link

How to wait for a packet? #374

Closed BrendonHills closed 4 years ago

BrendonHills commented 4 years ago

I have working transmitter and receiver code for a sensor using interrupts and sleep modes with this library which is now working really well. All that remains now is for me to ask the receiver to send and acknowledgement packet, and if the acknowledgement packet is received by the transmiter, it knows all is well, if not I will (later) get it to re-send the packet up to three times.

I have experimented with the library examples for callback and duplex modes but I cannot seem to get it to work. I think the problem is that I am checking for a packet in the main loop, which is undertaking other functions too. I suspect I am missing the returned packet whilst doing other things in the loop??

Am I right? and if so, how can I 'wait for a packet' to be received, with a timeout in case there is genuinely no response, before making a decision and moving on?

The transmitter code is below. The only changes to the working code I have made to receive this packet acknowledgement are in void loop(), between 'int packetSize = LoRa.parsePacket();' and 'gotoSleep();'

`//Initialisation ==============================================

define ledPin 13

//for battery voltage ============

define VBATPIN A9

float measuredvbat; int healthCheck;

// For the RTC ===========

include "Wire.h"

include "RTClibExtended.h"

define RTCwakePin 1

volatile bool clockAlarm = false; RTC_DS3231 RTC;

// For Sleep mode ===========

include "LowPower.h"

// For the sensor =============

define sensorWakePin 0

int waterLevel; volatile bool sensorAlarm = false; volatile int valA; int waterLevelSent;

// For LoRa ============

include

include

define RFM95_CS 8

define RFM95_RST 4

define RFM95_INT 7

bool QSL = false; //flag for message confirmed received at RX //int messageSent; //for QSL fn //int messageReceived; //for QSL fn

void setup() { pinMode (ledPin, OUTPUT); // For the RTC ==================================================== pinMode(RTCwakePin, INPUT_PULLUP); //Initialize communication with the clock Wire.begin(); RTC.begin(); // dECIDE IF TO LEAVE THE BELOW OR REMOVE - NEED TO SET RTC SEPERATELY IF REMOVED //RTC.adjust(DateTime(DATE, TIME)); //set RTC date and time to COMPILE time //Set SQW pin to OFF RTC.writeSqwPinMode(DS3231_OFF); initiateAlarm(); //call fn to clear and set alarms //Set alarm1 every day at 09:00 - minutes first then hour, 0 for secs no account of daylight saving time //RTC.setAlarm(ALM1_MATCH_HOURS, 0, 0, 09, 0); //Set alarm1 at 1 min past the hour past the minute REMOVE IN FINAL RTC.setAlarm(ALM1_MATCH_MINUTES, 0, 01, 0, 0);///REMOVE IN FINAL

// For the Water Sensor ==================================== pinMode(sensorWakePin, INPUT_PULLUP);

// For LoRa =============================== LoRa.setPins(RFM95_CS, RFM95_RST, RFM95_INT); while (!LoRa.begin(870E6)) { ; } LoRa.setTxPower(20); LoRa.enableCrc(); //prevent bad packets LoRa.setSyncWord(0x8e);//set to 142 out of 255 ranges from 0-0xFF LoRa.setSpreadingFactor(10); //Set Spreading Factor 6 to 12 for more robust, yet slowest signal LoRa.setCodingRate4(6); //Set coding rate for best signal immunity - range 5 - 8 - default 5 8 doubles TX time!! LoRa.setSignalBandwidth(250E3); //Supported values are 7.8E3, 10.4E3, 15.6E3, 20.8E3, 31.25E3, 41.7E3, 62.5E3, 125E3, and 250E3. }

void RTCWakeUp() { clockAlarm = true; }

void sensorWakeUp() { valA = digitalRead(sensorWakePin); sensorAlarm = true; }

void loop() { USBDevice.attach();//remove in final

// rtc interrupt response if (clockAlarm) { //clockAlarm = !clockAlarm; //reset flag initiateAlarm(); measuredvbat = analogRead(VBATPIN); measuredvbat = 2; // we divided by 2, so multiply back measuredvbat = 3.3; // Multiply by 3.3V, our reference voltage measuredvbat /= 1024; // convert to voltage if (measuredvbat >= 3.4) { healthCheck = 2; } else { healthCheck = 3; } LORATXHealth(); // parse for a packet, and call onReceive with the result: }

// sensor interrupt response if (sensorAlarm) { //sensorAlarm = !sensorAlarm;// reset flag sensorDebounce(); //only send packet if there has been a change to water level since last packet sent if ((waterLevel) != (waterLevelSent)) { LORATXSensor(); } } int packetSize = LoRa.parsePacket();//check for packet if (packetSize) { // received a packet clockAlarm = !clockAlarm; //reset flag moved from if (clockAlarm) statement above sensorAlarm = !sensorAlarm;// reset flag moved from if (sensorAlarm) statement above LoRa.beginPacket();//demonstrating this piece of code has been reached - substitute for serialPrint LoRa.print("QSL"); LoRa.endPacket(); } else { LoRa.beginPacket();//demonstrating this piece of code has been reached LoRa.print("NO QSL"); LoRa.endPacket(); } //onReceive(LoRa.parsePacket()); // I tried this too, with no success

goToSleep(); }

void goToSleep() { noInterrupts(); attachInterrupt(digitalPinToInterrupt(sensorWakePin), sensorWakeUp, CHANGE); attachInterrupt(digitalPinToInterrupt(RTCwakePin), RTCWakeUp, FALLING); //LoRa sleep??? interrupts(); LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); //Disable external pin interrupts on wake up detachInterrupt(digitalPinToInterrupt(RTCwakePin)); detachInterrupt(digitalPinToInterrupt(sensorWakePin)); }

void initiateAlarm() { //clear any pending alarms and reset RTC.armAlarm(1, false); RTC.clearAlarm(1); RTC.alarmInterrupt(1, false); RTC.armAlarm(2, false); RTC.clearAlarm(2); RTC.alarmInterrupt(2, false); RTC.alarmInterrupt(1, true);//reset }

void sensorDebounce() { //Checking if water level is the same 3 secs later and only take action if so. delay(3000); int valB = digitalRead(sensorWakePin); if ((valA) != (valB)) { goToSleep(); } else { waterLevel = !valB; } }

void LORATXHealth() { noInterrupts(); LoRa.beginPacket(); LoRa.print(healthCheck);//state of battery, 3 = OK, 4 = re-charge LoRa.endPacket(); //LORASLEEP?? if so need to wake on interrupt //what about an acknowledgement or re-send? interrupts(); }

void LORATXSensor() { noInterrupts(); LoRa.beginPacket(); LoRa.print(waterLevel); LoRa.endPacket(); waterLevelSent = waterLevel; ///careful - what if not received! digitalWrite (ledPin, HIGH); delay (250); digitalWrite (ledPin, LOW); //LORASLEEP?? if so need to wake on interrupt //what about an acknowledgement or re-send? interrupts(); } `

==================================================== And here is the receiver code:

`#include

include

define RFM95_CS 8

define RFM95_RST 4

define RFM95_INT 7

void setup() { LoRa.setPins(RFM95_CS, RFM95_RST, RFM95_INT); Serial.begin(9600);

Serial.println("LoRa Receiver");

if (!LoRa.begin(870E6)) { Serial.println("Starting LoRa failed!"); while (1); } LoRa.setTxPower(20); LoRa.enableCrc(); //prevent bad packets LoRa.setSyncWord(0x8e);//set to 142 out of 255 LoRa.setSpreadingFactor(10); //Set Spreading Factor 6 to 12 for more robust, yet slowest signal LoRa.setCodingRate4(6); //Set coding rate for best signal immunity - range 5 - 8 - default 5 8 doubles TX time!! LoRa.setSignalBandwidth(250E3); //Supported values are 7.8E3, 10.4E3, 15.6E3, 20.8E3, 31.25E3, 41.7E3, 62.5E3, 125E3, and 250E3. }

void loop() { // try to parse packet int packetSize = LoRa.parsePacket(); if (packetSize) { // received a packet Serial.print("Received packet ");

// read packet
while (LoRa.available()) {
  int message = ((char)LoRa.read());
  switch (message) {
    case 48:
      Serial.println("Water LOW");
      break;
    case 49:
      Serial.println("Water HIGH");
      break;
    case 50:
      Serial.println("Health OK");
      break;
    case 51:
      Serial.println("Recharge battery");
      break;
    }
 LoRa.beginPacket();
 LoRa.print(message);
 LoRa.endPacket();
}

// print RSSI of packet
//Serial.print("' with RSSI ");
//Serial.println(LoRa.packetRssi());

} } `

Any suggestions gratefully received.

Thank you.

IoTThinks commented 4 years ago

To use callback instead https://github.com/sandeepmistry/arduino-LoRa/blob/master/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino

You can wait for xxx seconds before going to sleep.

BrendonHills commented 4 years ago

Thanks for your reply. I tried using callback in an earlier version but it didn’t work either. I am stuck on how I can ‘wait xxx seconds before going to sleep.’ It seems to me I need to continually poll a receive command until a packet is received or a pre-determined time has passed, and then either do something or go to sleep The question is, how do I do that? Would a For loop work, or is there a better way? Thanks.

From: "IoTThinks.com" notifications@github.com Reply to: sandeepmistry/arduino-LoRa reply@reply.github.com Date: Tuesday, 9 June 2020 at 10:19 To: sandeepmistry/arduino-LoRa arduino-LoRa@noreply.github.com Cc: BrendonHills daimontilley@hotmail.com, Author author@noreply.github.com Subject: Re: [sandeepmistry/arduino-LoRa] How to wait for a packet? (#374)

To use callback instead https://github.com/sandeepmistry/arduino-LoRa/blob/master/examples/LoRaReceiverCallback/LoRaReceiverCallback.ino

You can wait for xxx seconds before going to sleep.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/sandeepmistry/arduino-LoRa/issues/374#issuecomment-641151616, or unsubscribehttps://github.com/notifications/unsubscribe-auth/APZV26D2HKBGHMXKP7EAHLTRVX5CDANCNFSM4NYWNZWA.

IoTThinks commented 4 years ago

"wait xxx seconds before going to sleep" is easy for Arduino. Search for Arduino Timer or mills()

BrendonHills commented 4 years ago

Thanks. I had considered this, but if I understand correctly, using mills() is a non-blocking delay, and by using it the code in the loop will continue to run? If that is the case, there is nothing preventing the ‘goToSleep’ function?

If I were to use mills() to continually run the loop so a packet might be received, I guess I could then have a conditional ‘goToSleep()’ once the time has expired or a packet received? I wonder if that might work? I can try it this evening unless you think I am on the wrong track?

Cheers.

From: "IoTThinks.com" notifications@github.com Reply to: sandeepmistry/arduino-LoRa reply@reply.github.com Date: Tuesday, 9 June 2020 at 11:06 To: sandeepmistry/arduino-LoRa arduino-LoRa@noreply.github.com Cc: BrendonHills daimontilley@hotmail.com, Author author@noreply.github.com Subject: Re: [sandeepmistry/arduino-LoRa] How to wait for a packet? (#374)

"wait xxx seconds before going to sleep" is easy for Arduino. Search for Arduino Timer or mills()

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/sandeepmistry/arduino-LoRa/issues/374#issuecomment-641184231, or unsubscribehttps://github.com/notifications/unsubscribe-auth/APZV26GNL4BSQUESJLDWOT3RVYCRBANCNFSM4NYWNZWA.

BrendonHills commented 4 years ago

@IoTThinks so tonight I have been playing. I am using the code that worked properly before, and after I send my LoRa transmissions, at the end I call another function, which is designed to wait for an acknowledgement from a reciever and set a flag called 'QSL'. This fn is called void waitForAck() and is set out below. When it is called it gets stuck. I know it gets stuck because the on-board LED comes on and stays on (not sure why it doesnt flash as written) and interrupts fail to work, so I dont think it is going back to the main loop. This fn is the only point in the code where the led is used, so it is definitely happening in this fn. I have tried for hours tonight various combinations of code in this function, but cannot fix it. Can you spot what is wrong and causing it to stick please? Many thanks.

`` void waitForAck() { noInterrupts(); int period = 1000; unsigned long timeNow = 0; timeNow = millis(); while (millis() < timeNow + period) { int packetSize = LoRa.parsePacket();//check for packet if (packetSize) { // received a packet QSL = true; } else { //no acknowledgement received QSL = false; } digitalWrite (ledPin, HIGH); //seems to get stuck here!! delay (250); digitalWrite (ledPin, LOW); }

interrupts(); } ``

BrendonHills commented 4 years ago

@IoTThinks I think I have sorted it at the last minute - I replaced 'while' with 'if' and it appears resolved! Not sure I understand why though! I need to do more research. Thanks for the help.