Help for RUI3-Best-Practice to receive AT Command from Downlink messages

Please include the following information, in order for us to help you as effectively as possible.

  • What product do you wish to discuss? RAK4631, RAK3372, RAK11200, RAK11310, RAK11722?

RAK 4331

  • What firmware are you using? (RUI3 or Arduino BSP or other IDE (e.g. STM32CubeIDE)?
    RUI3

  • What firmware version? Can it be obtained with AT+VER=?
    AT+VER=RUI_4.1.1_RAK4631

  • Computer OS? (MacOS, Linux, Windows)
    Windows

  • What Computer OS version?

11

  • How often does the problem happen?

I want to modify AT custom parameters from downlink messages, I followed an example but the code remains checked and does not execute the AT+SENDINT command.

  • How can we replicate the problem?

  • Provide source code if custom firmware is used or link to example if RAKwireless example code is used.

type or paste c/**
 * @file RUI3-Modular.ino
 * @author Bernd Giesecke ([email protected])
 * @brief RUI3 based code for low power practice
 * @version 0.1
 * @date 2023-03-29
 *
 * @copyright Copyright (c) 2023
 *
 */
#include "app.h"

/** Packet is confirmed/unconfirmed (Set with AT commands) */
bool g_confirmed_mode = false;
/** If confirmed packet, number or retries (Set with AT commands) */
uint8_t g_confirmed_retry = 0;
/** Data rate  (Set with AT commands) */
uint8_t g_data_rate = 3;

/** Flag if transmit is active, used by some sensors */
volatile bool tx_active = false;

/** fPort to send packages */
uint8_t set_fPort = 2;

/** Payload buffer */
WisCayenne g_solution_data(255);

uint32_t g_send_repeat_time = 0;

/** Flag if RAK1901 was found */
bool has_rak1901 = false;

/** Flag if RAK1902 was found */
bool has_rak1902 = false;

/**
 * @brief Callback after join request cycle
 *
 * @param status Join result
 */
void joinCallback(int32_t status)
{
	if (status != 0)
	{
		MYLOG("JOIN-CB", "LoRaWan OTAA - join fail! \r\n");
		// To be checked if this makes sense
		// api.lorawan.join();
	}
	else
	{
		MYLOG("JOIN-CB", "LoRaWan OTAA - joined! \r\n");
		digitalWrite(LED_BLUE, LOW);
	}
}

/**
 * @brief LoRaWAN callback after packet was received
 *
 * @param data pointer to structure with the received data
 */
void receiveCallback(SERVICE_LORA_RECEIVE_T *data)
{
    MYLOG("RX-CB", "RX, port %d, DR %d, RSSI %d, SNR %d", data->Port, data->RxDatarate, data->Rssi, data->Snr);
    for (int i = 0; i < data->BufferSize; i++)
    {
        Serial.printf("%02X", data->Buffer[i]);
    }
    Serial.print("\r\n");
    tx_active = false;

    // Parse the incoming command
    switch (data->Buffer[0])
    {
    // Change send interval (custom AT command)
    case 0x41:
    {
        uint32_t new_value = 0;
        new_value |= data->Buffer[1] << 0;
        new_value |= data->Buffer[2] << 8;
        new_value |= data->Buffer[3] << 16;
        new_value |= data->Buffer[4] << 24;
        save_and_set_sendfreq(new_value);
        break;
    }
    // Change data rate (standard AT command)
    case 0x02:
    {
        uint8_t new_value = data->Buffer[1];
        api.lorawan.dr.set(new_value);
        break;
    }
    // Other commands
    default:
        // Add additional cases for other commands if needed
        break;
    }
}

/**
 * @brief Handler for send frequency AT commands
 *
 * @param port Serial port used
 * @param cmd char array with the received AT command
 * @param param char array with the received AT command parameters
 * @return int result of command parsing
 * 			AT_OK AT command & parameters valid
 * 			AT_PARAM_ERROR command or parameters invalid
 */
int interval_send_handler1(SERIAL_PORT port, char *cmd, stParam *param)
{
    if (param->argc == 1 && !strcmp(param->argv[0], "?"))
    {
        Serial.print(cmd);
        Serial.printf("=%lds\r\n", g_send_repeat_time / 1000);
    }
    else if (param->argc == 1)
    {
        for (int i = 0; i < strlen(param->argv[0]); i++)
        {
            if (!isdigit(*(param->argv[0] + i)))
            {
                return AT_PARAM_ERROR;
            }
        }

        uint32_t new_send_freq = strtoul(param->argv[0], NULL, 10);

        g_send_repeat_time = new_send_freq * 1000;

        save_and_set_sendfreq(g_send_repeat_time);
    }
    else
    {
        return AT_PARAM_ERROR;
    }

    return AT_OK;
}

/**
 * @brief Save and set new send interval
 * 
 * @param new_interval new interval time in milliseconds
 */
void save_and_set_sendfreq(uint32_t new_interval)
{
    // Stop the timer
    api.system.timer.stop(RAK_TIMER_0);
    if (g_send_repeat_time != 0)
    {
        // Restart the timer
        api.system.timer.start(RAK_TIMER_0, g_send_repeat_time, NULL);
    }
    // Save custom settings
    save_at_setting();
}

/**
 * @brief Callback for LinkCheck result
 *
 * @param data pointer to structure with the linkcheck result
 */
void linkcheckCallback(SERVICE_LORA_LINKCHECK_T *data)
{
	MYLOG("LC_CB", "%s Margin %d GW# %d RSSI%d SNR %d", data->State == 0 ? "Success" : "Failed",
		  data->DemodMargin, data->NbGateways,
		  data->Rssi, data->Snr);
}


/**
 * @brief LoRaWAN callback after TX is finished
 *
 * @param status TX status
 */
void sendCallback(int32_t status)
{
	MYLOG("TX-CB", "TX status %d", status);
	digitalWrite(LED_BLUE, LOW);
	tx_active = false;
}

/**
 * @brief LoRa P2P callback if a packet was received
 *
 * @param data pointer to the data with the received data
 */
void recv_cb(rui_lora_p2p_recv_t data)
{
	MYLOG("RX-P2P-CB", "P2P RX, RSSI %d, SNR %d", data.Rssi, data.Snr);
	for (int i = 0; i < data.BufferSize; i++)
	{
		Serial.printf("%02X", data.Buffer[i]);
	}
	Serial.print("\r\n");
	tx_active = false;
}

/**
 * @brief LoRa P2P callback if a packet was sent
 *
 */
void send_cb(void)
{
	MYLOG("TX-P2P-CB", "P2P TX finished");
	digitalWrite(LED_BLUE, LOW);
	tx_active = false;
}

/**
 * @brief LoRa P2P callback for CAD result
 *
 * @param result true if activity was detected, false if no activity was detected
 */
void cad_cb(bool result)
{
	MYLOG("CAD-P2P-CB", "P2P CAD reports %s", result ? "activity" : "no activity");
}

/**
 * @brief Arduino setup, called once after reboot/power-up
 *
 */
void setup()
{
	// Setup for LoRaWAN
	if (api.lorawan.nwm.get() == 1)
	{
		g_confirmed_mode = api.lorawan.cfm.get();

		g_confirmed_retry = api.lorawan.rety.get();

		g_data_rate = api.lorawan.dr.get();

		// Setup the callbacks for joined and send finished
		api.lorawan.registerRecvCallback(receiveCallback);
		api.lorawan.registerSendCallback(sendCallback);
		api.lorawan.registerJoinCallback(joinCallback);
		api.lorawan.registerLinkCheckCallback(linkcheckCallback);
	}
	else // Setup for LoRa P2P
	{
		api.lora.registerPRecvCallback(recv_cb);
		api.lora.registerPSendCallback(send_cb);
		api.lora.registerPSendCADCallback(cad_cb);
	}

	pinMode(LED_GREEN, OUTPUT);
	digitalWrite(LED_GREEN, HIGH);
	pinMode(LED_BLUE, OUTPUT);
	digitalWrite(LED_BLUE, HIGH);

	pinMode(WB_IO1, OUTPUT);
	digitalWrite(WB_IO1, LOW);
	pinMode(WB_IO2, OUTPUT);
	digitalWrite(WB_IO2, LOW);

	// Start Serial
	Serial.begin(115200);

	// Delay for 5 seconds to give the chance for AT+BOOT
	delay(5000);

	api.system.firmwareVersion.set("RUI3-T_H_P-V1.0.0");

	Serial.println("RAKwireless RUI3 Node");
	Serial.println("------------------------------------------------------");
	Serial.println("Setup the device with WisToolBox or AT commands before using it");
	Serial.printf("Version %s\n", api.system.firmwareVersion.get().c_str());
	Serial.println("------------------------------------------------------");

	// Initialize module
	Wire.begin();

	// Register the custom AT command to get device status
	if (!init_status_at())
	{
		MYLOG("SETUP", "Add custom AT command STATUS fail");
	}

	// Register the custom AT command to set the send interval
	if (!init_interval_at())
	{
		MYLOG("SETUP", "Add custom AT command Send Interval fail");
	}

	// Get saved sending interval from flash
	get_at_setting();

	digitalWrite(LED_GREEN, LOW);

	// Create a timer.
	api.system.timer.create(RAK_TIMER_0, sensor_handler, RAK_TIMER_PERIODIC);
	if (custom_parameters.send_interval != 0)
	{
		// Start a timer.
		api.system.timer.start(RAK_TIMER_0, custom_parameters.send_interval, NULL);
	}

	// Check if it is LoRa P2P
	if (api.lorawan.nwm.get() == 0)
	{
		digitalWrite(LED_BLUE, LOW);

		sensor_handler(NULL);
	}

	if (api.lorawan.nwm.get() == 1)
	{
		if (g_confirmed_mode)
		{
			MYLOG("SETUP", "Confirmed enabled");
		}
		else
		{
			MYLOG("SETUP", "Confirmed disabled");
		}

		MYLOG("SETUP", "Retry = %d", g_confirmed_retry);

		MYLOG("SETUP", "DR = %d", g_data_rate);
	}

	// Enable low power mode
	api.system.lpm.set(1);

	// If available, enable BLE advertising for 30 seconds and open the BLE UART channel
#if defined(_VARIANT_RAK3172_) || defined(_VARIANT_RAK3172_SIP_)
// No BLE
#else
	Serial6.begin(115200, RAK_AT_MODE);
	api.ble.advertise.start(30);
#endif

	// Check if sensors are connected and initialize them
	has_rak1901 = init_rak1901();
	if (has_rak1901)
	{
		Serial.println("+EVT:RAK1901");
	}
	
	digitalWrite(LED_BLUE, LOW);
}

/**
 * @brief sensor_handler is a timer function called every
 * custom_parameters.send_interval milliseconds. Default is 120000. Can be
 * changed with ATC+SENDINT command
 *
 */
void sensor_handler(void *)
{
	MYLOG("UPLINK", "Start");
	digitalWrite(LED_BLUE, HIGH);

	if (api.lorawan.nwm.get() == 1)
	{
		// Check if the node has joined the network
		if (!api.lorawan.njs.get())
		{
			MYLOG("UPLINK", "Not joined, skip sending");
			return;
		}
	}

	// Clear payload
	g_solution_data.reset();

	// Get sensor values
	if (has_rak1901)
	{
		read_rak1901();
	}

	float battery_reading = 0.0;
	// Add battery voltage
	for (int i = 0; i < 10; i++)
	{
		battery_reading += api.system.bat.get(); // get battery voltage
	}

	battery_reading = battery_reading / 10;

	g_solution_data.addVoltage(1, battery_reading);

	// Send the packet
	send_packet();
}

/**
 * @brief This example is complete timer driven.
 * The loop() does nothing than sleep.
 *
 */
void loop()
{
	api.system.sleep.all();
}

/**
 * @brief Send the data packet that was prepared in
 * Cayenne LPP format by the different sensor and location
 * aqcuision functions
 *
 */
void send_packet(void)
{
	// Check if it is LoRaWAN
	if (api.lorawan.nwm.get() == 1)
	{
		MYLOG("UPLINK", "Sending packet over LoRaWAN");
		// Send the packet
		if (api.lorawan.send(g_solution_data.getSize(), g_solution_data.getBuffer(), set_fPort, g_confirmed_mode, g_confirmed_retry))
		{
			MYLOG("UPLINK", "Packet enqueued, size %d", g_solution_data.getSize());
			tx_active = true;
		}
		else
		{
			MYLOG("UPLINK", "Send failed");
			tx_active = false;
		}
	}
	// It is P2P
	else
	{
		MYLOG("UPLINK", "Send packet with size %d over P2P", g_solution_data.getSize());

		digitalWrite(LED_BLUE, LOW);

		if (api.lora.psend(g_solution_data.getSize(), g_solution_data.getBuffer(), true))
		{
			MYLOG("UPLINK", "Packet enqueued");
		}
		else
		{
			MYLOG("UPLINK", "Send failed");
		}
	}
}
ode here

In save_and_set_sendfreq(uint32_t new-interval) you are giving the new send interval as parameter, but you do not use it. :thinking:

Try this one:

void save_and_set_sendfreq(uint32_t new_interval)
{
	// Assign new value !!!!!!!!!!!!!!!!
	g_send_repeat_time = new_interval;
	
	// Stop the timer
	api.system.timer.stop(RAK_TIMER_0);
	if (g_send_repeat_time != 0)
	{
		// Restart the timer
		api.system.timer.start(RAK_TIMER_0, g_send_repeat_time, NULL);
	}
	// Save custom settings
	save_at_setting();
}

With the code frame it performs the task but it hangs and I must send an AT+SENDINT command to send new data but it hangs again because it executes the task again as if it were receiving a Dowmlink with AT instructions.

The device is in CLASS A because I don’t want it to always be awake and only if I need to make AT command changes I don’t necessarily have to go to the point where the equipment is since if we have several hundred this would delay any global change too much.

thank you very much MR. Bee Gee

This Code: RUI3_RAK1901_V3_AT_DOWNLINK

Works for me.
Keep in mind, 10 seconds (that’s what you sent to the device) is a quite short send interval for LoRaWAN. If you are in EU868, you might quickly go over the DutyCycle limitations.

I implemented the downlink parse in a slightly different code than yours, Difference is mainly in the way custom settings are saved (using a structure instead of single variables and flash address).

Here is the code that works for me.
RUI3-Downlink-AT.zip (8.0 KB)

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.