Bandwidth mesh Argon / Xenon


I have a Argon and Xenon connected together in mesh.

It’s work well, but I would like to estimate the bandwidth in different condition : Indoor, outdoor …
I’m not expecting thousand of megabyte, my goal is to have a better idea of the real capacity.

Currently, I did some first test with “marco-polo” script, it’s good for known the latency and maximum range available… but not for the capacity.

Do you know how can I do it ? Or the best way to do it.




@Tlams, the raw (low level) 802.15.4 6Lowpan top speed is 250Kbps. With OpenThread and Particle’s protocol overhead, the data rate will most likely be below 25KB/s.


Hi @peekay123,

Thanks for your answer.
Any way to prove it ?

You could create a very basic app that transmits data as fast as possible using mesh.publish(). Then have another node or gateway with another basic app that received those publishes and aggregates the statistics.

Here’s something I threw together. I am testing this out now.

Transmit Node:

unsigned long counter = 0;
char msg[20];

int LED = D7;               

unsigned long lastPub = 0;
unsigned long pubInterval = 0;

unsigned long lastLED = 0;
unsigned long LEDInterval = 1;

void setup()
    pinMode(LED, OUTPUT); 

void loop() {
    if (millis() - lastPub > pubInterval) {
        if (Mesh.ready()) {
            snprintf(msg, sizeof(msg), "%010lu", counter);
        digitalWrite(LED, HIGH);
        lastLED = millis();
        lastPub = lastLED;
        //Serial.printlnf("%u", counter);
    if (digitalRead(LED) && millis() - lastLED > LEDInterval) {
        digitalWrite(LED, LOW);

Receive Node:


//Average Bytes/Second tracking variables.
unsigned long counter = 0;          //Payload data from the XMit node.
unsigned long lastCounter = 0;      //Last payload received from the xmit node.
int missedCounter = 0;              //Number of missed publishes.

float intervalAvg = 0;              //Average bytes/second over the interval.
unsigned long intervalCount = 0;    //Number of received payloads over the interval.

float bwAvg = 0;                    //Average bytes/second since starting the application.
unsigned long bwSamples = 0;        //Number of samples used to obtain the average (adds "weight" to the older data points.)

const int bwLength = 10; //Bytes per payload recevied via Mesh.subscribe.

//Millis timer variables.
unsigned long lastMath = 0;         //Last time we calculated the interval.
unsigned long mathInterval = 1000;  //Sampling/calculation interval.

unsigned long lastPub = 0;          //Last time stats were published.
unsigned long pubInterval = 5000;   //Publishing interval.

bool resetFlag = false;

//Debugging Variables
char msg[50];

void setup() {
    Mesh.subscribe("BT", speedTestHandler);

void loop() {
    if (resetFlag) {
        Serial.println("Xmit node reset.");
        resetFlag = false;
    //Calculate the statistics at the math interval.
    if (millis() - lastMath >= mathInterval) {
        lastMath = millis(); //Reset the millis timer.
        //Debugging Only
        Serial.printlnf("Last msg: %s", msg);
        if (intervalCount == 0) { return; }  //Divide by zero safeguard.
        intervalAvg = (intervalCount * bwLength) / (mathInterval / 1000);   //Calculate the average bytes/second for this interval.
        intervalCount = 0;  //Reset interval counter.
        if (bwSamples = -1) { return; } //Divide by zero safeguard.
        bwAvg = ( (bwAvg * bwSamples) + intervalAvg ) / bwSamples++;    //Calculate the overall (since start) average bytes/second.

    //Publish the statistical data at the publish interval (using serial at the moment.)
    if (millis() - lastPub >= pubInterval) {
        Serial.printlnf("BWTest; Interval Avg: %6.2f; Missed: %i, Avg: %6.2f; c/lc: %u/%u", intervalAvg, missedCounter, bwAvg, counter, lastCounter);
        lastPub = millis(); //Reset the millis timer.

//This gets called everytime a Bandwidth Test "BT" message is received via Mesh.publish().
void speedTestHandler(const char *event, const char *data)
    //Set the counter variable to the message payload.
    counter = atol(data);
    //Debugging only.
    //Safeguard for the starting condition.
    if (lastCounter == 0) {
        lastCounter = counter;
    //Program was restarted on xmit node, reset the counters.
    if (counter < lastCounter) {
        resetFlag = true;

    missedCounter += (counter - lastCounter) - 1; //Track if there are gaps the in the sequential payload data. (i.e. missed publishes or subscribes)
    lastCounter = counter;  //Update last counter variable.
    intervalCount++;    //Increment the counter for this interval (number of publishes received.)

void resetCounters() {
    lastCounter = counter;
    bwAvg = 0;
    bwSamples = 0;
    intervalAvg = 0;
    intervalCount = 0;
    missedCounter = 0;

I debugged the above code and just updated it.

Realistically, I can’t get more than about 2 kbytes/sec. With the pubinterval set to 0 on the xmit node, I am seeing an interval average of about 2 kbytes/s but the “missed” packets is on the order of 2000-3000 for each receive node publish interval. When I set the publish interval on the xmit node to about 5ms, I get about 1.5 kbytes/s and the “missed” packets is only about 1-10 per receive node publish interval.

I should also note that I’m only measuring the payload bytes and I’m transmitting 10 characters (= 10 bytes) for each payload (the length of a long integer padded with zeros). The event name and any other overhead is ignored.


Well, shouldn’t have read this before sleeping, since I bet I ruined my night, but I am starting to realize that the use case I was dreaming of will not fit this architecture. Thinking about 20 or 30 xenons acting as intelligent BLE <–> mesh gateways that in turn would use an Argon/Ethernet Xenon as a gw for the cloud, but seeing the <2KB realistic limit I would saturate the mesh with only two or three nodes scanning BLE advertisements.
… Unless at least three or four gateways could be added to the same mesh to ingest and share traffic. Any hint before I start pulling my remaining hairs (no more than 100 left on board) ?

Not quite sure about the immediate connection between mesh throughput and scanning BLE advertisements.

A BLE scan doesn’t immediately have to create a mesh message nor does the act of scanning and potential finding a BLE ad necessarily give away how big a potential message to inform the mesh about the find should be.

What data would your BLE devices create and what would need to be uploaded?

Thank You @ScruffR

Although BLE needs are minimal, since the app would be interested only in advertising messages ( filtering out all the devices I’m not interested in), the potential crowd is high, since we expect a lot of devices, and we need to get and report messages almost realtime. Need to report potentially 30 advertisers spitting out one message/sec, but many nodes obviously can sense (and must report) the same message, so things gets even worse. We are talking of about 50 bytes per message.
So, in the end, if any node hops through N nodes to arrive at the gateway, and the same nodes need to send even their messages to the same gateway, isn’t this becoming too big to manage for a mesh ?

Why would they have to report a message already sent by another node?
Granted, the mesh topology and data “compression” will take some consideration on the application side, but clever planning can help pushing boundaries.
e.g. if on node signals responsibility for a particular BLE device others wouldn’t have to pay attention to any message coming from that device and hence won’t clutter the network with redundant messages.

The message is not “redundant”, since it will carry useful infos (e.g.: RSSI and detecting node). Think of a basic indoor positioning.


Thanks a lot for your code Ninjatill, I’ll try it soon.

I’m not surprised that you got 2kbytes/sec in these conditions. The publish system seem definitively not design to transfer data as a classic TCP/IP transfer.

I did some more basic range tests, I got between one Argon and one Xenon before desynchronization:

  • 65m indoor (old metallic building, 2 doors, 10 Wi-Fi points).
  • 120m outdoor, open area, no Wi-Fi.

It’s the max range, I believe for to be stable, you can remove ~30%. And the indoor will highly depend of your condition (building, interference…).