Help for MultiHopping router network for data transfer

I want to implement a network of boards to be placed in a field. We have sender nodes(boards) which will collect data through saadc channels and transmit it to receiver node. We want to make a multihop network wherein in case a sender board disconnects, data should reroute itself to go to receiver. We are using udp protocol to send data in a colon seperated manner. The following code we tried, sends data when directly connected to receiver node, but fails to perform when sender node is not directly connected, rather indirectly connected through a intermediate sender node. Hoping for earliest replies.
#include "app_scheduler.h"
#include "app_timer.h"
#include "bsp_thread.h"
#include "nrf_log_ctrl.h"
#include "nrf_log.h"
#include "nrf_log_default_backends.h"
#include "nrf_delay.h"
#include "thread_utils.h"
#include <openthread/cli.h>
#include <openthread/thread.h>
#include <openthread/udp.h>
#include <openthread/ip6.h>
#include <openthread/link.h>
#include <openthread/udp.h>
#include <openthread/instance.h>
#include "dht11.h"
#include "bsp.h"

#include "nrfx_saadc.h"
#include "nrf_saadc.h"

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "nrf_drv_saadc.h"
#include "nrf_drv_ppi.h"
#include "nrf_drv_timer.h"
#include "boards.h"
#include "app_util_platform.h"
#include "nrf_pwr_mgmt.h"

#define SCHED_QUEUE_SIZE      32                              
#define SCHED_EVENT_DATA_SIZE APP_TIMER_SCHED_EVENT_DATA_SIZE

#define IPV6_ADDRESS_STRING_SIZE 40

#define SAADC_CHANNEL1 0
#define SAADC_CHANNEL2 1
#define SAADC_CHANNEL3 2
char mystr[10]="";

static void bsp_event_handler(bsp_event_t event);
APP_TIMER_DEF(m_our_char_timer_id);

/***************************************************************************************************
 * @section State
 **************************************************************************************************/

void saadc_callback(nrf_drv_saadc_evt_t const * p_event)
{
}

void saadc_init(void)
{
    ret_code_t err_code;
    nrf_saadc_channel_config_t channel1_config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN0);
    
    nrf_saadc_channel_config_t channel2_config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN1);

    nrf_saadc_channel_config_t channel3_config =
        NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);

    err_code = nrf_drv_saadc_init(NULL, saadc_callback);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(0, &channel1_config);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(1, &channel2_config);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_saadc_channel_init(2, &channel3_config);
    APP_ERROR_CHECK(err_code);
}

nrf_saadc_value_t sample1;
nrf_saadc_value_t sample2;
nrf_saadc_value_t sample3;

void read_rain(int *samp1, int *samp2, int *samp3)
{
    nrf_saadc_value_t sample1;
    nrf_saadc_value_t sample2;
    nrf_saadc_value_t sample3;

    nrfx_saadc_sample_convert(SAADC_CHANNEL1, &sample1);
    nrfx_saadc_sample_convert(SAADC_CHANNEL2, &sample2);
    nrfx_saadc_sample_convert(SAADC_CHANNEL3, &sample3);

    *samp1 = sample1;
    *samp2 = sample2;
    *samp3 = sample3;
}

uint16_t temp=0;
//void temp_humid_sense(){
//  uint16_t temperature, humidity;
//  DHTxx_ErrorCode dhtErrCode;
//  int i=2;
//  while(i>0){
//    dhtErrCode = DHTxx_Read(&temperature, &humidity);
//    if(dhtErrCode == DHT11_OK) {
//      break;
//    }
//    else{
//      i--;
//    }
//   }
 
//  strcat(mystr,":n5");
 
//}

static void thread_state_changed_callback(uint32_t flags, void * p_context)
{
    NRF_LOG_INFO("State changed! Flags: 0x%08x Current role: %d\r\n",
                 flags, otThreadGetDeviceRole(p_context));
}

/***************************************************************************************************
 * @section Initialization
 **************************************************************************************************/


static void timer_init(void)
{
    uint32_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);
}

static void leds_init(void)
{
    LEDS_CONFIGURE(LEDS_MASK);
    LEDS_OFF(LEDS_MASK);
}


static void log_init(void)
{
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}


static void thread_instance_init(void)
{
    thread_configuration_t thread_configuration =
    {
        .radio_mode        = THREAD_RADIO_MODE_RX_ON_WHEN_IDLE,
        .autocommissioning = true,
        .autostart_disable = false,
    };

    thread_init(&thread_configuration);
    thread_cli_init();
    thread_state_changed_callback_set(thread_state_changed_callback);

    uint32_t err_code = bsp_thread_init(thread_ot_instance_get());
    APP_ERROR_CHECK(err_code);
}



static void scheduler_init(void)
{
    APP_SCHED_INIT(SCHED_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
}


/***************************************************************************************************
 * @section MultiHop
 **************************************************************************************************/

// Check if the destination address is directly reachable
bool can_reach_directly(const char *destAddr) {
    otInstance *instance = thread_ot_instance_get();
    otIp6Address destination;

    // Convert string address to otIp6Address
    if (otIp6AddressFromString(destAddr, &destination) != OT_ERROR_NONE) {
        return false; // Invalid address
    }

    // Assuming otIp6IsAddressValid() was intended to check the reachability
    // Use otIp6IsAddressUnspecified() as a proxy check for address validity
    return !otIp6IsAddressUnspecified(&destination);
}

// Convert otIp6Address to string
const char *ip6AddressToString(const otIp6Address *addr, char *buffer, size_t size) {
    snprintf(buffer, size, "%x:%x:%x:%x:%x:%x:%x:%x",
             (addr->mFields.m16[0]), (addr->mFields.m16[1]), (addr->mFields.m16[2]), (addr->mFields.m16[3]),
             (addr->mFields.m16[4]), (addr->mFields.m16[5]), (addr->mFields.m16[6]), (addr->mFields.m16[7]));
    return buffer;
}

// Find an intermediate node for multihop communication
const char* find_intermediate_node(otInstance *instance) {
    otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT;
    otNeighborInfo neighInfo;
    static char intermediateAddr[IPV6_ADDRESS_STRING_SIZE];
    const otIp6Address *localRloc;
    otIp6Address neighborRloc;

    // Get the local RLOC address
    localRloc = otThreadGetRloc(instance);

    while (otThreadGetNextNeighborInfo(instance, &iter, &neighInfo) == OT_ERROR_NONE) {
        // Skip if the neighbor is not a Full Thread Device
        if (!neighInfo.mFullThreadDevice) {
            continue;
        }

        // Construct the neighbor's RLOC address from local RLOC address and neighbor's RLOC16
        memcpy(&neighborRloc, localRloc, sizeof(otIp6Address));
        neighborRloc.mFields.m8[14] = (uint8_t)(neighInfo.mRloc16 >> 8);
        neighborRloc.mFields.m8[15] = (uint8_t)(neighInfo.mRloc16 & 0xff);

        // Convert otIp6Address to string
        if (ip6AddressToString(&neighborRloc, intermediateAddr, sizeof(intermediateAddr)) != NULL) {
            return intermediateAddr;
        }
    }

    // No intermediate node found
    return NULL;
}

// Send UDP data using multihop if necessary
void udp_multihop_send(const char *destAddr, const char *data) {
    otInstance *instance = thread_ot_instance_get();
    if (can_reach_directly(destAddr)) {
        NRF_LOG_INFO("Sending data directly to %s", destAddr);
        otCliOutputFormat("Sending data directly to %s\r\n", destAddr);
        udp_send(destAddr, data);
    } else {
        const char *intermediateAddr = find_intermediate_node(instance);
        if (intermediateAddr != NULL) {
            NRF_LOG_INFO("Sending data to %s via intermediate node %s", destAddr, intermediateAddr);
            otCliOutputFormat("Sending data to %s via intermediate node %s\r\n", destAddr, intermediateAddr);
            udp_send(intermediateAddr, data);
        } else {
            NRF_LOG_INFO("No intermediate node found, data not sent.");
            otCliOutputFormat("No intermediate node found, data not sent.\r\n");
        }
    }
}

/***************************************************************************************************
 * @section Main
 **************************************************************************************************/
int count=0;
bool udp=true;

void allCommands(){
    if (udp){
        char command2[] = "udp open\n";
        otCliConsoleInputLine(command2, strlen(command2));
        char command3[] = "udp bind :: 1234\n";
        otCliConsoleInputLine(command3, strlen(command3));
        udp = false;
    }
}

void udp_send(const char *destAddr, const char *data)
{
    char command[256];
    sprintf(command, "udp send %s 1234 %s\n", destAddr, data);
    otCliConsoleInputLine(command, strlen(command));
}

static void timer_timeout_handler(void * p_context)
{
    NRF_LOG_FLUSH();
    if (udp) {
        allCommands();   
    }

    read_rain(&sample1, &sample2, &sample3);

    char myrain[50];
    sprintf(myrain, "%i:%i:%i:%s", sample1, sample2, sample3, ":n5");

    const char *receiverAddr = "fdde:ad00:beef:0:b28c:1800:1ae9:28a9"; // replace with actual receiver address
    udp_multihop_send(receiverAddr, myrain);

    //temp_humid_sense();
    NRF_LOG_FLUSH();

    // Put the device to sleep between timer events
    nrf_pwr_mgmt_run();
}

void timer_start()
{
    ret_code_t err_code;
    err_code = app_timer_start(m_our_char_timer_id, APP_TIMER_TICKS(30000), NULL);
    APP_ERROR_CHECK(err_code);
}

void timer_stop()
{
    ret_code_t err_code;
    app_timer_stop(m_our_char_timer_id);
    APP_ERROR_CHECK(err_code);
}

static void bsp_event_handler(bsp_event_t event)
{
    switch (event) {
        case BSP_EVENT_KEY_0:
            timer_start();
            break;
        case BSP_EVENT_KEY_1:
            timer_stop();
            break;
        default:
            timer_start();
            return; // no implementation needed
    }
}
static void power_management_init(void)
{
    ret_code_t err_code = nrf_pwr_mgmt_init();
    APP_ERROR_CHECK(err_code);
}
int main(int argc, char *argv[])
{
    log_init();
    saadc_init();
    scheduler_init();
    timer_init();
    leds_init();
    power_management_init();  // Initialize power management

    uint32_t err_code = bsp_init(BSP_INIT_BUTTONS, bsp_event_handler);
    APP_ERROR_CHECK(err_code);

    thread_instance_init();
    app_timer_create(&m_our_char_timer_id, APP_TIMER_MODE_REPEATED, timer_timeout_handler);

    timer_start();
    timer_stop();
    
    while (true)
    {
        thread_process();
        app_sched_execute();
        if (NRF_LOG_PROCESS() == false)
        {
            nrf_pwr_mgmt_run();  // Put the device into low-power mode
        }
    }
}
/**
 *@}
 **/

Parents
  • Hello,

    If you have the destination address, is there a particular reason for why you are doing the routing manually? Did you try sending the message to the destination address? The thread network should handle the routing for you in that case. 

    And with your current implementation. What do you see? Does it find a route for the destination node? Does it send the message? Does the direct neighbor receive it?

    Best regards,

    Edvin

Reply
  • Hello,

    If you have the destination address, is there a particular reason for why you are doing the routing manually? Did you try sending the message to the destination address? The thread network should handle the routing for you in that case. 

    And with your current implementation. What do you see? Does it find a route for the destination node? Does it send the message? Does the direct neighbor receive it?

    Best regards,

    Edvin

Children
No Data
Related