Unable to Join the Thread Network using Openthread API

I've been trying to establish the network using Open Thread API.

After enabling the IPv6 by using otIp6IsEnabled  , when try to start the joiner using otJoinerStart but it returns DISCOVER.

Same, I've tried using shell command it works well. I've also attached my code. Please help me to figure out the exact problem.

#ifndef AUTOMATION_H_
#define AUTOMATION_H_

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/printk.h>

#include <zephyr/net/openthread.h>
#include <openthread/link.h>
#include <openthread/thread.h>
#include <openthread/instance.h>
#include <openthread/dataset.h>
#include <openthread/joiner.h>
#include <openthread/ip6.h>



// Define network parameters (moved master key to a proper array to fix macro issues)
#define THREAD_CHANNEL 11
#define THREAD_PAN_ID 0xABCD
static const uint8_t thread_network_key[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
#define THREAD_NETWORK_NAME "MyThreadNetwork"

int automation_init(void);

void getIPaddr(otInstance *instance);
void print_active_dataset(otInstance *inst);

otError enable_ifconfig(otInstance *instance);
void joinerCallback(otJoinerState aState, void *aContext);
otError join_thread_network(otInstance *instance, const char *pskd);
void thread_init(void);

#endif /* AUTOMATION_H_ */

#include "automation.h"
#include <string.h>


static otInstance *sInstance = NULL;

/**
 * @brief Convert device role to string.
 * @param role The device role.
 */
static const char *role_to_str(otDeviceRole role)
{
	switch (role) {
	case OT_DEVICE_ROLE_DISABLED: return "DISABLED";
	case OT_DEVICE_ROLE_DETACHED: return "DETACHED";
	case OT_DEVICE_ROLE_CHILD:    return "CHILD";
	case OT_DEVICE_ROLE_ROUTER:   return "ROUTER";
	case OT_DEVICE_ROLE_LEADER:   return "LEADER";
	default:                      return "UNKNOWN";
	}
}

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

/**
 * @brief Joiner state change callback.
 * @param aState The new joiner state.
 * @param aContext A pointer to application-specific context.
 */
void joinerCallback(otJoinerState aState, void *aContext) {
    switch (aState) {
        case OT_JOINER_STATE_JOINED:
            printk("Device joined Thread network successfully!\n");
            break;
        case OT_JOINER_STATE_IDLE:
            printk("Joiner idle\n");
            break;
        // Add more cases as needed
        default:
            break;
    }
}

/**
 * @brief Retrieve and print the IPv6 addresses of the device.
 * @param instance Pointer to the OpenThread instance.
 */
void print_active_dataset(otInstance *inst)
{
    if (!inst) {
        printk("DATASET: instance NULL\n");
        return;
    }

    otOperationalDataset dataset;
    memset(&dataset, 0, sizeof(dataset));

    otError err = otDatasetGetActive(inst, &dataset);
    if (err != OT_ERROR_NONE) {
        printk("DATASET: otDatasetGetActive() failed: %d\n", err);
        return;
    }

    printk("DATASET: ---- Active Dataset (local) ----\n");

    if (dataset.mComponents.mIsChannelPresent) {
        printk("  Channel:       %u\n", dataset.mChannel);
    } else {
        printk("  Channel:       <not present>\n");
    }

    if (dataset.mComponents.mIsPanIdPresent) {
        printk("  PAN ID:        0x%04X\n", dataset.mPanId);
    } else {
        printk("  PAN ID:        <not present>\n");
    }

    if (dataset.mComponents.mIsNetworkNamePresent) {
        printk("  Network Name:  \"%s\"\n", dataset.mNetworkName.m8);
    } else {
        printk("  Network Name:  <not present>\n");
    }

    if (dataset.mComponents.mIsExtendedPanIdPresent) {
        const uint8_t *x = dataset.mExtendedPanId.m8;
        printk("  Ext PAN ID:    %02X%02X%02X%02X%02X%02X%02X%02X\n",
               x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]);
    } else {
        printk("  Ext PAN ID:    <not present>\n");
    }

    if (dataset.mComponents.mIsMeshLocalPrefixPresent) {
        const uint8_t *p = dataset.mMeshLocalPrefix.m8;
        printk("  MeshLocalPref: %02X%02X:%02X%02X:%02X%02X:%02X%02X::/64\n",
               p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
    } else {
        printk("  MeshLocalPref: <not present>\n");
    }

    if (dataset.mComponents.mIsNetworkKeyPresent) {
        const uint8_t *k = dataset.mNetworkKey.m8;
        printk("  Network Key:   ");
        for (int i = 0; i < OT_NETWORK_KEY_SIZE; i++) {
            printk("%02X", k[i]);
        }
        printk("\n");
    } else {
        printk("  Network Key:   <not present>\n");
    }

    if (dataset.mComponents.mIsPskcPresent) {
        const uint8_t *k = dataset.mPskc.m8;
        printk("  PSKc:          ");
        for (int i = 0; i < OT_PSKC_MAX_SIZE; i++) {
            printk("%02X", k[i]);
        }
        printk("\n");
    } else {
        printk("  PSKc:          <not present>\n");
    }

    printk("  Role:          %s (%d)\n",
           role_to_str(otThreadGetDeviceRole(inst)),
           otThreadGetDeviceRole(inst));

    printk("DATASET: ---- end ----\n");
}


/**
 * @brief Join the Thread network using the provided PSKD.
 * @param instance Pointer to the OpenThread instance.
 */
otError join_thread_network(otInstance *instance, const char *pskd) {
    otError error;
    
    if (instance == NULL) {
        return OT_ERROR_INVALID_ARGS;
    }

    #ifdef Dataset_Act
        // Set up active dataset
    otOperationalDataset dataset;
    memset(&dataset, 0, sizeof(dataset));
    dataset.mActiveTimestamp.mSeconds = 1;
    dataset.mChannel = THREAD_CHANNEL;
    dataset.mPanId = THREAD_PAN_ID;
    // Fixed: Use mNetworkKey (not mMasterKey)
    memcpy(dataset.mNetworkKey.m8, thread_network_key, sizeof(thread_network_key));
    // Copy the network name string into the array
    size_t nameLen = strlen(THREAD_NETWORK_NAME);
    if (nameLen > OT_NETWORK_NAME_MAX_SIZE) {
        return OT_ERROR_INVALID_ARGS;  // Name too long (use 16 if macro undefined)
    }
    memcpy(dataset.mNetworkName.m8, THREAD_NETWORK_NAME, nameLen + 1);  // +1 for null terminator
    dataset.mComponents.mIsActiveTimestampPresent = true;
    dataset.mComponents.mIsChannelPresent = true;
    dataset.mComponents.mIsNetworkKeyPresent = true;  // Correct flag for network key
    dataset.mComponents.mIsNetworkNamePresent = true;
    dataset.mComponents.mIsPanIdPresent = true;
    
    otError error = otDatasetSetActive(instance, &dataset);
    if (error != OT_ERROR_NONE) {
        return error;
    }
#endif
       // Enable Thread
    error = otIp6SetEnabled(instance, true);
    if (error != OT_ERROR_NONE) {
        return error;
    }
    // error = otThreadSetEnabled(instance, true);
    // if (error != OT_ERROR_NONE) {
    //     return error;
    // }
    // Start joiner with PSKD
    error = otJoinerStart(instance, pskd, NULL, NULL, NULL, NULL, NULL, joinerCallback, NULL);
    return error;
}

/**
 * @brief Initialize the Thread stack.
 */
void thread_init(void) {
    sInstance = otInstanceInitSingle();
    if (sInstance == NULL) {
        printk("Failed to initialize OpenThread instance\n");
        return;
    }
    printk("Thread stack initialized\n");
}

/**
 * @brief Retrieve and print the IPv6 addresses of the device.
 * @param instance Pointer to the OpenThread instance.
 */
void getIPaddr(otInstance *instance) {
    if (instance == NULL) {
        printk("Instance is NULL\n");
        return;
    }
    const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(instance);
    for (const otNetifAddress *addr = unicastAddrs; addr != NULL; addr = addr->mNext) {
        char ipStr[OT_IP6_ADDRESS_STRING_SIZE];
        otIp6AddressToString(&addr->mAddress, ipStr, sizeof(ipStr));
        printk("IP Address: %s\n", ipStr);
    }
}

/**
 * @brief Enable the IPv6 interface if it is not already enabled.
 * @param instance Pointer to the OpenThread instance.
 */
otError enable_ifconfig(otInstance *instance)
{
    otError err;
	bool ip6_enabled;
    if (instance == NULL) {
        return OT_ERROR_INVALID_ARGS;
    }

    // Check if IPv6 interface is enabled
	ip6_enabled   = otIp6IsEnabled(instance); //Indicates whether or not the IPv6 interface is up.
	printk("AUTOMATION: initial otIp6IsEnabled: %d\r\n", ip6_enabled ? 1 : 0);
    
	if (!ip6_enabled) {
		/* Bring up IP6 interface (equivalent to 'ot ifconfig up') */
		err = otIp6SetEnabled(instance, true);
		printk("AUTOMATION: ot ifconfig up -> otIp6SetEnabled(true) = %d\r\n", err);
		ip6_enabled   = otIp6IsEnabled(instance); //Indicates whether or not the IPv6 interface is up.
		printk("->AUTOMATION: initial otIp6IsEnabled: %d\r\n", ip6_enabled ? 1 : 0);
		getIPaddr(instance);
		k_msleep(2000);
	}

    return OT_ERROR_NONE;
}

/**
 * @brief Main automation initialization function.
 */
int automation_init(void) {
    thread_init();
   
    otError err = enable_ifconfig(sInstance);
    if (err != OT_ERROR_NONE) {
        printk("Interface enable failed: %d\n", err);
    }

    const char *pskd = "J01NME";  // Customize your PSKD
    otError error = join_thread_network(sInstance, pskd);
    print_active_dataset(sInstance);
    if (error != OT_ERROR_NONE) {
        printk("Failed to join network: %d\n", error);
        
    }

    error = otThreadSetEnabled(sInstance, true);
    if (error != OT_ERROR_NONE) {
        return error;
    }
    k_sleep(K_SECONDS(5));  // Wait for join
    getIPaddr(sInstance);
    return 0;
}

#
# Copyright (c) 2020 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

# Enable OpenThread
CONFIG_OPENTHREAD=y
CONFIG_OPENTHREAD_FTD=y
CONFIG_OPENTHREAD_MTD=y
CONFIG_OPENTHREAD_MTD_SED=y
CONFIG_OPENTHREAD_BORDER_ROUTER=n
CONFIG_OPENTHREAD_JOINER=y
# CONFIG_OPENTHREAD_JOINER_AUTOSTART=y
# CONFIG_OPENTHREAD_JOINER_PSKD="J01NME"


# Network shell
CONFIG_SHELL=y
CONFIG_OPENTHREAD_SHELL=y
CONFIG_SHELL_ARGC_MAX=26
CONFIG_SHELL_CMD_BUFF_SIZE=416


# CONFIG_NETWORKING=y
# CONFIG_NET_IF=y
# CONFIG_NET_IPV6=y
# CONFIG_NET_L2_OPENTHREAD=y

# Increase Settings storage size
CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE=0x8000

# Enable OpenThread features set
CONFIG_OPENTHREAD_NORDIC_LIBRARY_MASTER=y

CONFIG_GPIO_SHELL=y

uart:~$ *** Booting nRF Connect SDK v3.1.1-e2a97fe2578a ***
*** Using Zephyr OS v4.1.99-ff8f0c579eeb ***
Thread stack initialized
AUTOMATION: initial otIp6IsEnabled: 0
AUTOMATION: ot ifconfig up -> otIp6SetEnabled(true) = 0
->AUTOMATION: initial otIp6IsEnabled: 1
IP Address: fe80:0:0:0:88c9:be71:5b68:ce33
DATASET: otDatasetGetActive() failed: 23
IP Address: fdde:ad00:beef:0:82ad:c38a:4533:4272
IP Address: fe80:0:0:0:e04e:6e67:8d52:de75

uart:~$ ot state
detached
Done
uart:~$ ot joiner state
Discover
Done
uart:~$ 

Parents Reply Children
No Data
Related