ACK not received with external wake up

Hi,

Our sensor based on the RAK3172 is almost finalized but I have just encountered a problem with the acknowledgment of uplink messages.

The RAK is awakened by an external signal. This signal can come from an external RTC (RV-3028-C7) or from a magnet passing in front of a reed switch (measurement forced by the user when installing the sensor).

The program (very simplified) is as follows:

#include "RV-3028-C7.h"

#define WAKEUP_PIN  PA0
#define REED_PIN    PA8
#define LED_PIN     PA9

// RTC
RV3028 rtc;

// --------------------------------------------------------------------------------
// rtc_wakeup
// --------------------------------------------------------------------------------
void rtc_wakeup()
{
  measurement();  // <-- the message following this measurement is not acknowledged (callback TX status = 4)**
}

// --------------------------------------------------------------------------------
// reed_wakeup 
// --------------------------------------------------------------------------------
void reed_wakeup()
{
  measurement(); // <-- the message following this measurement is not acknowledged (callback TX status = 4)**
}

// --------------------------------------------------------------------------------
// send_packet
// --------------------------------------------------------------------------------
void send_packet(time_t timestamp, ushort p1, ushort p2, ushort p3, ushort iTemp1, ushort iTemp2, ushort iTemp3, ushort iVBatt)
{
  uint8_t payload[16] = {};
  
  [...]

  if (api.lorawan.njs.get() == 1)
  {
    if (api.lorawan.send(sizeof(payload), payload, 2, true, 1)) 
    {
      Serial1.println("Send Success");
      msg_count++;  
    } 
    else 
    {
      Serial1.println("Send fail");
    }
  }
}
// --------------------------------------------------------------------------------
// measurement
// --------------------------------------------------------------------------------
void measurement()
{
  ushort p1;
  ushort p2;
  ushort p3;
  ushort iTemp1;
  ushort iTemp2;
  ushort iTemp3;
  ushort iVBatt;

  [...]

  send_packet(time_now_epoch, p1, p2, p3, iTemp1, iTemp2, iTemp3, iVBatt);
}

// --------------------------------------------------------------------------------
// joinCallback
// --------------------------------------------------------------------------------
void joinCallback(int32_t status)
{
   [...]
}

// --------------------------------------------------------------------------------
// sendCallback
// --------------------------------------------------------------------------------
void sendCallback(int32_t status)
{
	Serial1.printf("TX-CB - TX status %d \r\n", status);
}

// --------------------------------------------------------------------------------
// linkcheckCallback
// --------------------------------------------------------------------------------
void linkcheckCallback(SERVICE_LORA_LINKCHECK_T *data) 
{
   [...]
}

// --------------------------------------------------------------------------------
// timeReqCallback
// --------------------------------------------------------------------------------
void timeReqCallback(int32_t status)
{
   [...]
}

// --------------------------------------------------------------------------------
// receiveCallback
// --------------------------------------------------------------------------------
void receiveCallback(SERVICE_LORA_RECEIVE_T *data)
{
  Serial1.printf("RX-CB - RX, port %d, DR %d, RSSI %d, SNR %d", data->Port, data->RxDatarate, data->Rssi, data->Snr);

  [...]
  
}

// --------------------------------------------------------------------------------
// setup
// --------------------------------------------------------------------------------
void setup() 
{
  delay(5000);

  [...]
  
  // Band = EU868
  Serial1.printf("Set LoRa region EU868 %s\r\n", api.lorawan.band.set(4) ? "Success" : "Fail");

  // Class = A
  Serial1.printf("Set class A %s\r\n", api.lorawan.deviceClass.set(0) ? "Success" : "Fail");
  
  // Join mode = OTAA
  Serial1.printf("Set network join mode OTAA %s\n\r", api.lorawan.njm.set(1) ? "Success" : "Fail");
  
  // Confirm mode disabled
  Serial1.printf("Set confirm mode status enabled %s\n\r", api.lorawan.cfm.set(2) ? "Success" : "Fail");

  // Adaptive data rate enabled
  Serial1.printf("Set adaptive data rate %s\n\r", api.lorawan.adr.set(true) ? "Success" : "Fail");

  // Data rate
  Serial1.printf("Get data rate : %d\r\n", api.lorawan.dr.get());
  
  // Linkcheck status
  Serial1.printf("Set Verifying network link status 0 %s\r\n", api.lorawan.linkcheck.set(0) ? "Success" : "Fail");

  api.lorawan.registerJoinCallback(joinCallback);	
  api.lorawan.registerSendCallback(sendCallback);
  api.lorawan.registerRecvCallback(receiveCallback);
  api.lorawan.registerLinkCheckCallback(linkcheckCallback);
  api.lorawan.registerTimereqCallback(timeReqCallback);
  
  while (api.lorawan.njs.get() == 0)
  {
    Serial1.printf("LoRaWan OTAA join ...\r\n");
	api.lorawan.join();
	delay(10000);
  }

  service_lora_set_timereq(1);

  // Reed switch
  pinMode(REED_PIN, INPUT_PULLDOWN);
  attachInterrupt(digitalPinToInterrupt(REED_PIN), reed_wakeup, RISING);      

  [...]
 
  // RTC Wake up
  pinMode(WAKEUP_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(WAKEUP_PIN), rtc_wakeup, FALLING);   

  measurement(); // <-- the message following this measurement is successfully acknowledged (callback TX status = 0)**
}

// --------------------------------------------------------------------------------
// loop
// --------------------------------------------------------------------------------
void loop() 
{
  api.system.sleep.all();
}

When the measurement is launched from setup, the uplink message is acknowledged by the server (ChirpStack) and the status in the callback (sendCallback) is equal to 0. Good !

When the measurement is triggered by the external RTC or by the Reed switch, the message is sent and acknowledged by the server but the status given by the callback is equal to 4. Which seems to be an RX2 timeout. Not good !

Is it possible that waking up the RAK by an external signal rather than by a Timer (eg RAK_TIMER_0) could impact the reception of the ACK downlink from the server?

I’ve been racking my brains for hours! Thanks for your help.

Joch

I run a quick test with my RUI3-RAK13011-Alarm example, which is a reed relay (used as door open/close detector) and it gives me the proper +EVT:SEND_CONFIRMED_OK message.

Reed relay tend to bounce, do you “unbounce” the signal?

Yes, the reed switch is properly “debounced”.
The problem also appears when the RAK is woken up every hour by the RTC.

What RUI3 version?

So far I didn’t see any problems with external interrupts on the TX functionality.

void rtc_wakeup() and void reed_wakeup() are the interrupt handlers, directly called from the external interrupt.

Interrupt handlers should not do lengthy stuff, this might be a problem.

Try to decouple the IRQ event from your measurement and send routines:

/** Flag for wake up reason */
uint8_t wake_reason;

void rtc_wakeup()
{
	wake_reason = 1;
	api.system.timer.start(RAK_TIMER_1, 250, &wake_reason);
}

void reed_wakeup()
{
	wake_reason = 2;
	api.system.timer.start(RAK_TIMER_1, 250, &wake_reason);
}

void handle_wakeup(void *reason)
{
	// Get the wakeup reason
	uint8_t *wake_reason = (uint8_t *)reason;

	if (wake_reason[0] == 1)
	{
		// Do measurements due to RTC wakeup and send packet
	}
	if (wake_reason[0] == 2)
	{
		// Do measurements due to REED wakeup and send packet
	}
}

void setup(void)
{
	/// \todo other setup stuff

	//  Create timer for interrupt handler
	api.system.timer.create(RAK_TIMER_1, handle_wakeup, RAK_TIMER_ONESHOT);

	/// \todo other setup stuff
}

RUI version is RUI_4.0.6_RAK3172-T

Interrupt handlers are very light. Especially for the RTC interrupt handler which contains only a call to measurement() function. There is a very short led blink (10ms) in the reed switch handler before the call to measurement(). No more.

As you suggested, I tried to decouple the interrupt events with a system Timer. Unfortunately, there is no improvement.

My goal is to regularly ensure that the sensor is able to communicate. My other option is to use a LinkCheckReq before sending the message:

    api.lorawan.linkcheck.set(1);
    if (api.lorawan.send(sizeof(payload), payload, 2, true, 1)) 
    {
      Serial1.println("Send Success");
      msg_count++;
    } 

With the LinkCheckReq the status reported by the TX callback is now equal to 0 :

LC-CB - STATE Success, MARGIN 32, GW 6, RSSI -30, SNR 7
RX-CB - RX, port 0, DR 0, RSSI -30, SNR 7
TX-CB - TX status 0

If I delete the LinkCheckReq, the problem reappears.

Could this be a timing issue when receiving downlinks?

LinkCheckReq is not a separate function. With api.lorawan.linkcheck.set(1); you just request the linkcheck info from the server with the next transmission. Similar to confirmed packet, with this request, the LNS will send a downlink packet, only instead of a simple ACK it includes some information about the signal received by the LNS, including the number of gateways that have received the uplink.

When Linkcheck works, confirmed packets should work as well.

When you use “just” confirmed packets, is both your send-callback function called and you do not see the +EVT:SEND_CONFIRMED_OK ?

At least the +EVT: message should be shown, whether the confirmed transmission was successful or failed.

1 Like