Using Particle Mesh Devices with the OpenThread Border Router


I hear you :slight_smile:, I’ll be getting into this sometime soon too. The networking models and pricing models offered by particle are just too constraining…
Something I don’t yet understand, do you register your devices with the particle cloud at all? Do they have any internet connectivity?


Working on both options. 1. Install openthread ncp on the Argon, interesting if it can be done but probably very limiting. 2. Connect regular Argons to a border router for interconnectivity. That is probably the best route. Then the border router could provide an internet connection or the particle cloud could be your internet.

A 3rd route is that Particle actually embraces and promotes their awesome hardware as having openthread connectivity, not easy to work with, like the Particle cloud is, but a free option for the advanced developer or cheap educator. :grinning:

Viability of Mesh based Smart-Home
split this topic #56

4 posts were merged into an existing topic: Viabiliity of Mesh based Smart-Home


So glad @ScruffR has joined this thread, perhaps he can use his big brain to help solve the Openthread-Particle Interconnection problem.

Lets define this problem with an example:

Lets say @ScruffR designs some Particle Mesh product that goes viral, “Your fellow hobbyist believe in you @ScruffR”, and it gets so much attention that @ScruffR gets an email from Google Nest:

“We would like to include your product in the Google Nest family and are willing to pay x millions, we are coming to visit you in 3 days and would like to see a demonstration of how well your product works with OpenThread”

But Particle Mesh products, that have been heavily advertised as being OpenThread based, do not work with Openthread. @ScruffR loses his chance to make money and goes back to helping people for free on the Particle Forum.

Why would a Product Creator choose Particle Mesh when so many other companies are designing products that will soon or already do work with OpenThread? Especially anyone who is following the issues
Particle is having with the Mesh devices. It’s only us Hobbyists that already love the Particle products that are loyal enough to hang around to see how this thing pans out.

Here is my Quickstart Guide to OpenThread. It is perhaps the ugliest web page I have every made, but the OpenThread documentation is very confusing and I am trying to simplify it.

@ScruffR you are really perhaps the best person to help Particle by solving the Openthread-Particle Interconnection problem, do you have any useful positive ideas?


@rocksetta I learned that @bsatrom has some info about how you can access and interact with the Particle Thread network using Zigbee devices.

You can thank @nrobinson2000 for bringing this up on your behalf in another discussion.


Not finding any information about Particle Thread with Zigbee. You guys: @RWB @bsatrom @nrobinson2000 got any links I can look at?


Hey @rocksetta I don’t have any resources on this yet, only plans to create them. :smiley: Hoping to do this in the next couple of weeks and I’ll start sharing as soon as I have something!

Border router or AP for non particle mesh devices?


That sounds so great. Don’t stop with Zigbee, lets connect the Particle Mesh hardware to as many platforms as possible. It is a win-win for everyone. Keep me in the loop, especially if you have any problems. Also nice if some of the Developer brainiacs like @peekay123 , @ScruffR, @nrobinson2000 and others could get involved.

That may have been a bit too enthusiastic. :smiley:

Good luck.

Raspberry Pi as gateway

Been a while, @peekay123 did you ever get anything interesting going with your nrf52840 USB dongle (s) ?

I have an interesting argon.bin file I would like to send you. What’s the best way for me to do that?


@rocksetta, I never got a chance to do anything with the dongle. I’ve been very busy on other projects. :disappointed_relieved:


@rocksetta great work so far! I just found this thread, read through it, but want to make sure I understand the state of the world.

It sounds like you are still working on getting Particle Mesh devices to talk with other, 3rd party Thread devices?


I am kind of like a dog with a bone when I get an idea, I purchased a class set of Argons and Xenons to teach OpenThread using the very easy to use Particle IDE. I was a bit surprised that things were more complex than I thought they would be.

If you use twitter say hi to @rocksetta we can DM, easier for me than using my laptop. As you saw at myblog/144 I have some connections going on, but it is far from useful yet.



It seems that the Argon version 1.4.2 bootloader will crash this program. Easy solution.

Downlaod version 1.4.1 bootloader from the releases page
or the direct link here

From that folder open a terminal and run

particle usb dfu
particle update
particle usb start-listening
particle flash --serial argon-bootloader@1.4.1.bin 
particle usb reset

Then flash my code again from the correct folder. Very sad that Particle has made a bootloader that will not work with an opensource solution to a Argon connectivity issue.
Another solution would just be to stay at version 1.4.1 or install version 1.4.1 from scratch.

As per your message request @bsatrom here are the steps to load Openthread compatible software onto an Argon or Xenon. I have a Boron but have not done any testing with it yet. The following code simply sets up a HA network using multiple combinations of Argons and Xenons. These work without an OpenThread Border Router. Border Router connection is for another post.

  1. I do not know how to do a modular build using particle-cli or the webIDE (A good time for someone from Particle to make a tutorial about doing that.) so you have to load po-util at Installation of po-util on Linux is just
bash <(curl -sL
  1. Then run these commands on a Mac or Linux machine, takes a while for the last step to complete.
po init argon myArgonProject
cd myArgonProject
po config mesh-develop 
po setup-mesh
MODULAR=n po argon build

  1. You have now compiled an empty project. Find the main.c program which is probably in the firmware folder.

  2. Replace main.c with the following code. (If compiling for the Xenon it is the exact same code just the po command uses the word xenon instead of argon)

  3. Plug in your Argon (that has wifi pre-setup) and put in DFU mode. Hold down BOTH buttons. Release only the RESET button, while holding down the SETUP button. Wait for the LED to start flashing yellow (it will flash magenta first). (Don’t hold too long as it eventually does a factory reset which causes other issues.)

  4. Then do these steps (I manually delete the myArgonProject-argon.bin file generated in the above step in the main folder, clean does not delete it )

po argon clean
MODULAR=n po argon build
particle flash --usb myArgonProject-argon.bin

  1. Now repeat the last step to as many Argons as you want. (Best to just try 2 to start with)

  2. Connect 3V3 to D0 and see if all Argons flash. (Sort of proof that the Mesh is active)

  3. Connect 3V3 to D6 to shut of wifi with any of the Argons, the Mesh publish should still work.

  4. Following is the main.c code to load.


(There is a strong chance that I messed up a bit of my code when I tried to make it look all pretty so I have pasted working un-pretty code here)

// Particle Mesh Devices Connection Information Testing
// By Jeremy Ellis
// MIT Licence

// Install po-util with the following
// sudo apt install curl
// bash <(curl -sL
// Might want to pre install git, node, npm and particle-cli
// To check po-util version type
// po

// Start a po-util project
// po init argon myArgonProject
// cd myArgonProject

// po config mesh-develop 
// po setup-mesh

// MODULAR=n po argon build

// particle login
// particle list
// particle flash --usb myArgonProject-argon.bin

#include "Particle.h"

// uncomment the next line to improve stability
SYSTEM_THREAD(ENABLED);  // causes a small delay for Mesh.publish()

SYSTEM_MODE(SEMI_AUTOMATIC);  // so wifi not needed

//Next line allows you to use the Particle Featherwing plug and play


#include <assert.h>
#include "ot_api.h"
#include "openthread/dataset.h"

//#include "spark_wiring_udp.h"

//#include <openthread/thread_ftd.h>

#include <string.h>

extern "C" {

int8_t    otLinkGetChannel(otInstance *aInstance);
uint16_t  otLinkGetPanId(otInstance *aInstance);         // type otPanId
otMasterKey *otThreadGetMasterKey(otInstance *aInstance);
char     *otThreadGetNetworkName(otInstance *aInstance);

        otOperationalDataset aDataset;

void myHandler(const char *event, const char *data); 


/////////////////////////// important globals here ///////////////////////////////

int  myCode =5;  // number of flashes
bool myXenonAntennaAttached = false;  
bool myArgonBothAntennaAttached = false;  
bool myPublishToConsole = true;        // set true for Argon or debugging

int myCount = 0;
bool myButtonReady = true;

/////////////////////////////// end globals ////////////////////////////

void setup() {

   pinMode(D0, INPUT_PULLDOWN);
     pinMode(D6, INPUT_PULLDOWN);  // set pin D6 as an input at zero
    pinMode(D7, OUTPUT);          // our trusty D7 LED
    if (digitalRead(D6) == 0){

   Mesh.subscribe("mySendToAll", myHandler);

   if (myXenonAntennaAttached){
	   digitalWrite(ANTSW1, 0);
	   digitalWrite(ANTSW2, 1);
   if (myArgonBothAntennaAttached){
	   digitalWrite(ANTSW1, 1);
	   digitalWrite(ANTSW2, 0);

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

        /* Set Network Name to OTCodelab */
        static char          aNetworkName[] = "OTCodelab";
        size_t length = strlen(aNetworkName);
        assert(length <= OT_NETWORK_NAME_MAX_SIZE);
        memcpy(aDataset.mNetworkName.m8, aNetworkName, length);
        aDataset.mComponents.mIsNetworkNamePresent = true;

     * Fields that can be configured in otOperationDataset to override defaults:
     *     Network Name, Mesh Local Prefix, Extended PAN ID, PAN ID, Delay Timer,
     *     Channel, Channel Mask Page 0, Network Master Key, PSKc, Security Policy
    aDataset.mActiveTimestamp                      = 1;
    aDataset.mComponents.mIsActiveTimestampPresent = true;

    /* Set Channel to 15 */
    aDataset.mChannel                      = 15;
    aDataset.mComponents.mIsChannelPresent = true;

    /* Set Pan ID to 2222 */
    aDataset.mPanId                      = (otPanId)0x2222;
    aDataset.mComponents.mIsPanIdPresent = true;

    /* Set Extended Pan ID to C0DE1AB5C0DE1AB5 */
    uint8_t extPanId[OT_EXT_PAN_ID_SIZE] = {0xC0, 0xDE, 0x1A, 0xB5, 0xC0, 0xDE, 0x1A, 0xB5};
    memcpy(aDataset.mExtendedPanId.m8, extPanId, sizeof(aDataset.mExtendedPanId));
    aDataset.mComponents.mIsExtendedPanIdPresent = true;

    /* Set master key to 1234C0DE1AB51234C0DE1AB51234C0DE */
    uint8_t key[OT_MASTER_KEY_SIZE] = {0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5, 0x12, 0x34, 0xC0, 0xDE, 0x1A, 0xB5};
    memcpy(aDataset.mMasterKey.m8, key, sizeof(aDataset.mMasterKey));
    aDataset.mComponents.mIsMasterKeyPresent = true;

        otInstance*  ot = ot_get_instance();

        otDatasetSetActive(ot, &aDataset);

        char *myNetworkName = otThreadGetNetworkName(ot);
        char myNetworkNameBuff[255];
        snprintf(myNetworkNameBuff, sizeof(myNetworkNameBuff), "%s", myNetworkName); // network name as a string

        Particle.publish("----------------","-------------", 60, PRIVATE); // just to show a space between samples
        Particle.publish("My Network Name: ", String(myNetworkNameBuff), 60, PRIVATE); //shows printing an integer variable

        // Mesh.localIP().toString().c_str());

        Particle.publish("My local ip: ", String(Mesh.localIP()), 60, PRIVATE); //shows printing an integer variable


void loop() {

 myCount +=1;
    if (digitalRead(D6) == 0){   
        Particle.connect();      // Cool I am a Photon
    } else {
        Particle.disconnect();  // Now I am an Arduino

    if (myButtonReady && digitalRead(D0) == 1){
        myCount = 0;
        digitalWrite(D7, 1);
        myButtonReady = false;
        Particle.publish("Sending Broadcast", "...", 60, PRIVATE); 
        Mesh.publish("mySendToAll", String(myCode));  
        digitalWrite(D7, 0);
    if (myCount >= 1000){
        if (! myButtonReady){
           Particle.publish("Device Reset", "...", 60, PRIVATE); 
        myButtonReady = true; 

    delay(5);  // becomes about 5 seconds


// Event listener for "mySendToAll" event from Xenons
void myHandler(const char *event, const char *data) {

    String parse = data;  //Cast char*data to String class
    int myNumber = parse.toInt();
    if (myPublishToConsole){  // mainly for Argon gateway unless debugging
        Particle.publish("Flashing D7 this many times:", parse, 60, PRIVATE); 
       // Particle.publish("udp ort number:", String(_remotePort), 60, PRIVATE); // did not compile
    for (int myLoop = 0; myLoop < myNumber; myLoop++){
       digitalWrite(D7, HIGH);
       delay(200);   // very quick flash
       digitalWrite(D7, LOW);
    delay(500); // to tell if 2 devices signal at similar times

Reminder to refresh your devices so they can work with particle again use a fresh .bin from particle. or just run particle update

New Video here


It seems that the Argon version 1.4.2 bootloader will crash this program. Easy solution.

Downlaod version 1.4.1 bootloader from the releases page
or the direct link here

From that folder open a terminal and run

particle usb dfu
particle update
particle usb start-listening
particle flash --serial argon-bootloader@1.4.1.bin 
particle usb reset

Then flash my code again from the correct folder. Very sad that Particle has made a bootloader that will not work with an opensource solution to a Argon connectivity issue.
Another solution would just be to stay at version 1.4.1 or install version 1.4.1 from scratch.

Particle MESH - Node Quantity Limit?
High School Robotics Course using the Mesh Devices Blog
2 Argon boards on same mesh network?
How to unbrick Xenon (return to factory default firmware)

I have 9 x nrf52840 usb dongles, 3 fanstel, 3 nordic and 3 makerdiary. (Which is kind of useless for Mesh owners, would be much better to make Particle devices talk to openthread.) My dongles all work with the Openthread border router and can ping or scan for the Mesh devices. I can’t communicate with the mesh devices since the Mesh.publish() command is much more complex behind the scenes than openthread commands. Example below of a basic udp message using openthread.

udp send ff02::1 1234 Hello Mesh

The Particle Mesh devices should be able to connect to OTBR using a serial USB connection. Pre-made software needs to be installed to run the commands, but that is for a later date. Presently I just want to get the mesh devices talking to the linux screen (or putty) program. I found this code from:

Not sure if that will get the Mesh devices talking to putty or screen but I will give it a try, my backup plan is just to use my normal putty communication program

// Use putty serial mode 
// gete putty here
// To find which COM port use the device manager
// If no access to the device manager type on a windows command line   CMD or powershell
// mode

int incomingByte = 0; // for incoming serial data

void setup() {
    Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
    Serial.print("Press any key, then try the letter A");

void loop() {
  // send data only when you receive data:
    if (Serial.available() > 0) {
    // read the incoming byte:
        incomingByte =;

        Serial.print("I received: ");
        Serial.println(incomingByte, DEC);
        if ((char)incomingByte == 'A'){   // #65
            Serial.println("Cool, you entered an 'A' ");
        if ((char)incomingByte == 'a'){   // #97
            Serial.println("Sweet, you entered an 'a' ");

The hard part is going to be getting the opehthread-cli working on mesh devices. If anyone wants to mess around with openthread I have a github/gitpod ready to go

or run this pre-made gitpod: (fairly advanced stuff and takes about 5 min to load.)

Open in Gitpod

Commands like the following should run (the commands below generate the .hex files to load on my usb dongles so you can send OTBR commands using “screen”).


              arm-none-eabi-objcopy output/nrf52840/bin/ot-ncp-ftd -O ihex output/nrf52840/bin/ncp_app.hex       
              arm-none-eabi-objcopy output/nrf52840/bin/ot-cli-ftd -O ihex output/nrf52840/bin/ot-cli-ftd.hex                 


Can’t believe I just got working on gitpod.

Cloud po-util now even windows users can build Particle Mesh devices.

Open in Gitpod

Obviously you can’t do, from the browser

particle flash --usb myArgonProject-argon.bin

But particle-cli does install on windows so just download the .bin file and use your windows machine to install it.

To refix

Thanks @ScruffR This is very useful for fixing the Argons

(put in DFU mode)
particle flash --usb tinker

(put in DFU mode)
particle update

(put in DFU mode)
particle flash --usb tinker

(Should be in listening mode flashing blue)
particle serial wifi
(setup your wifi)


It’s possible to put devices into dfu mode without pressing the buttons by using the following particle-cli command. Just leaving this here for future reference.

particle usb dfu


Thanks. Any idea if particle update, updates the bootloader? I have a couple of really dead Argons.


So this was successful in getting one Argon back to life:

  1. Download from look for heading System Binaries (All Devices) about 2 pages down. (In this case v1.4.2)

  2. Argon listening mode flashing blue

particle serial flash argon-bootloader@1.4.2.bin

  1. DFU mode just to flashing yellow

particle flash --usb argon-system-part1@1.4.2.bin

  1. ( I flashed the tinker as well but don’t think that was needed, and connected wifi for wifi put in listening mode)

particle serial wifi

  1. Put device in listening mode and use the android app to re-setup everything
    (very fast since the firmware is uptodate.)

I will try with my other dead Argons to see if this also works.


IIRC with 1.4.0 (or 1.4.1 ??) the bootloader has been made DFU flashable. So from then on particle update -v (I prefer the verbose version to see what’s going on) should also take care of that.


Thanks @ScruffR I got all 4 of my very dead Argons working again.

For 3 of them I did

particle update

Then used the Android App since the firmware is already installed and the app doesn’t need to go through the OS update, (which in a school with busy wifi is a horrible experience).

For one of the Argons I had to do the OS update, but before that I did a full factory reset past DFU until the white LED. Then installed the bootloader and the firmware, then did particle update and used the android app. Now it works but strangely the device name is not updating and looks kind of like the Device ID. Oh well at least it is working and I can go back to breaking Argons (What I do best!)