BLYNK
HOME       📲 GETTING STARTED       📗 DOCS       ❓HELP CENTER       👉 SKETCH BUILDER

Particle Mesh + Blynk Tutorial

Over the past few days I tooled around with getting Particle Mesh devices working with Blynk. There’s actually a few ways to attack it.

This is a re-post from my blog. I figure it may be super valuable for folks so I’ll share it here. :slight_smile:

From Particle Cloud to Blynk

Let’s start with the most simple use case: getting data from any Particle Device to Blynk.

The Air Quality data from Particle Squared is perfect for this example. So, i’ll be using that.

First let’s create a new Blynk Project

Grab the Auth Token we’ll need that in a bit. You can tap the Auth Token to copy it to your clipboard.

Next, let’s add a SuperChart for this example.

Configure the SuperChart to use a Virtual Pin. We don’t have access to the actual hardware pins on the device. V0 is a good choice.

To update values in Blynk, we’ll have to connect somehow. The best way is to use an Integration in the Particle Console.

In Particle Console, click the icon below the terminal icon. Then click on New Integration.

Look at the example below to see how I filled everything out.

Particle Squared uses the Event Name as ****blob. For other projects this may be different. Remember: your event name is the same as from Particle.publish(eventName, data).

The URL is set to use the blink-cloud.com address. According to their API a sample URL looks like:

I’ll also include it here so it’s easier to copy

http://blynk-cloud.com/auth_token/update/pin?value=value

Replace auth_token with the Auth Token we got earlier.

Replace pin with the virtual pin we want to modify. In this case V0

Replace the value with the value you want to use.

We’ll reference one of the values in the Particle Squared blob. It’s organized like this:

{
  "temperature": 28.60,
  "humidity": 45.00,
  "sgp30_tvoc": 18,
  "sgp30_c02": 400,
  "bme680_temp": 27.36,
  "bme680_pres": 1012.43,
  "bme680_hum": 43.80,
  "bme680_iaq": 43.90,
  "bme680_temp_calc": 27.30,
  "bme680_hum_calc": 43.97
}

Particle uses mustache templates. As you can see in the screenshot above, you can set value to {{{temperature}}}.

Note: If you’re working on your own project, it’s important to publish with JSON. As a reference the Particle.publish command looks like:

// Publish data
Particle.publish("blob", String::format("{\"temperature\":%.2f,\"humidity\":%.2f}",si7021_data.temperature, si7021_data.humidity) , PRIVATE, WITH_ACK);

Click the big blue Save button at the bottom of the screen. Then we can move on to the next step!

Testing

Since creating our Particle Webhook Integration, it’s been publishing data to Blynk. Let’s go see if it’s working.

First, let’s go back to the Blynk app. Hit the Play Button in the top Right in Blynk screen.

If your integration has been running for a while, you should see the graph populate with data! In the case you don’t see anything, let’s check the logs.

Go back to your integration and scroll towards the bottom. We want to see if there are any errors.

Not sure what that looks like? Here’s an example of an integration with errors:

You can scroll further down to investigate why the error has occurred.

All the way at the bottom shows the response from the server. Depending on the service, they’ll give you information why your API call failed. In my case, I was missing values for two fields.

Particle to Blynk is working!

You now have a basic way of publishing to a virtual pin in Blynk. There are drawbacks though. Most importantly, you’ll have to create an integration for every signal virtual pin. If you have eight readings, that means eight integrations.

Bummer.

In the next section, you’ll learn a different way to configure Blynk. Let’s go!

Local Mesh Using Blynk Library

Unlike the first method, we’ll be focusing on changing firmware only.

We’ll use a Argon, Boron or Ethernet Connected Xenon and one regular Xenon. For the rest of this tutorial, we’ll call these devices an “edge router”.

The Xenon will run the Particle Squared code. Instead of using Particle.publish we’ll be using Mesh.publish. This allows us to publish only to the local mesh network.

Meanwhile the edge router is listening for the message. It collects the values and then uses the Blynk API to publish to the app.

Here are the steps:

Setup our Edge Router

Pull up the menu by pressing Cmd+Shift+P. Type Install Library.

Then enter blynk. The library should download if you haven’t already.

Once installed you can include the library at the top of your .ino file like so:

#include <blynk.h>

In our setup() function let’s init the Blynk library:

// Put initialization like pinMode and begin functions here.
Blynk.begin(auth);

In our setup() function, subscribe to the temperature event. The connected Xenon will generate this event.

// Subscribe to temperature events
Mesh.subscribe("temperature",tempHandler);

Define tempHandler like this for now:

// Temperature event handler for mesh
void tempHandler(const char *event, const char *data){
}

In the loop() function make sure we have Blynk.run();

// loop() runs over and over again, as quickly as it can execute.
void loop() {
  // The core of your code will likely live here.
  Blynk.run();
}

Finally, for tempHandler we can add a debug print to monitor events. I’ve used something like this:

Serial.printlnf("event=%s data=%s", event, data ? data : "NULL");

Particle uses this in some of their examples. It’s perfect for our purposes as well!

Note: make sure you have Serial.begin() called in your Setup() function!

So now we have tempHandler to receive data from the Xenon. The edge router can now take that data and upload it to Blynk. Let’s use the Blynk.virtualWrite function for this:

// Write the data
Blynk.virtualWrite(V0, data);

This will write the temperature value from a Xenon to the V0 pin. If you used something other than V0, be sure to change that value here. (This is the same setup as the previous Particle Cloud to Blynk example)

The final code for the edge router should look something like this. Compile a flash it to your device when you’re ready!

/*
 * Project blynk-argon-forwarder
 * Description: Argon Blynk forwarder for Particle Mesh. Forwards data from mesh connected devices to Blynk.
 * Author: Jared Wolff
 * Date: 7/25/2019
 */

#include <blynk.h>

char auth[] = "<ENTER YOUR AUTH KEY>";

// Temperature event handler for mesh
void tempHandler(const char *event, const char *data){
  Serial.printlnf("event=%s data=%s", event, data ? data : "NULL");

  // Write the data
  Blynk.virtualWrite(V0, data);
}

// setup() runs once, when the device is first turned on.
void setup() {

  // Serial for debugging
  Serial.begin();

  // Put initialization like pinMode and begin functions here.
  Blynk.begin(auth);

  // Subscribe to temperature events
  Mesh.subscribe("temperature",tempHandler);

}

// loop() runs over and over again, as quickly as it can execute.
void loop() {
  // The core of your code will likely live here.
  Blynk.run();

}

Remember to set auth using the AUTH TOKEN in the Blynk app!

Setting up a Xenon

Create a new project. This time it will be for the Xenon capturing “temperature data.”

Let’s add a variable called time_millis to the top of the file. The type is system_tick_t. We’ll use it to create a simple delay timer for the temperature readings.

// Global variable to track time (used for temp sensor readings)
system_tick_t time_millis;

For the interval, let’s use a preprocessor define

#define INTERVAL_MS 2000

Now let’s tie those together in the loop() function. We’ll use an if statement to compare our current system time with that of the last event plus offset. If you ever need a simple timer, this is one of the best ways to do it!

// Check if our interval > 2000ms
  if( millis() - time_millis > INTERVAL_MS ) {
  }

Once we’re inside, make sure you reset timer_millis:

		//Set time to the 'current time' in millis
    time_millis = millis();

Then, we’ll generate the temperature value using the random() function. We’ll use the two parameter variant. That way we can set the minimum value and the maximum value:

    // Create a random number
    int rand = random(20,30);

Finally we’ll Mesh.publish the value:

    // Publish our "temperature" value
    Mesh.publish("temperature",String::format("%d",rand));

When this example runs, the temperature is broadcast to the mesh network. Then, the edge router receives it and forwards it on to Blynk!

You can flash this firmware whenever you’re ready. Here’s the full code for the Xenon so you can cross compare:

/*
 * Project blynk-xenon-rgb
 * Description: Recieve RGB level from connected Edge Router. Sends simiulated temperature values via mesh to the Blynk cloud.
 * Author: Jared Wolff
 * Date: 7/25/2019
 */

// How often we update the temperature
#define INTERVAL_MS 2000

// Global variable to track time (used for temp sensor readings)
system_tick_t time_millis;
// setup() runs once, when the device is first turned on.
void setup() {

  // Set time to 0
  time_millis = 0;

}

// loop() runs over and over again, as quickly as it can execute.
void loop() {

  // Check if our interval > 2000ms
  if( millis() - time_millis > INTERVAL_MS ) {
    //Set time to the 'current time' in millis
    time_millis = millis();

    // Create a random number
    int rand = random(20,30);

    // Publish our "temperature" value
    Mesh.publish("temperature",String::format("%d",rand));

  }

}

Give it a test!

Now that we’ve programmed both devices let’s get them talking to each other.

I’ve already set up the Argon with a mesh network called 8f-9. I’ll explain how to get the Xenon connected with the CLI. You can also used the Particle App.

First, let’s connect the Xenon to USB and get it into Listening Mode. After connect, hold the Mode button until blinking blue.

Then use the CLI to set up the mesh network. First let’s get the device ID:

Jareds-MacBook-Pro:nrfjprog.sh jaredwolff$ particle identify
? Which device did you mean?
  /dev/tty.usbmodem146401 - Argon
❯ /dev/tty.usbmodem146101 - Xenon

If you have multiple devices connect, make sure you select the right one! If prompted, select a device. Your output should look something like:

Your device id is e00fce682d9285fbf4412345
Your system firmware version is 1.3.0-rc.1

We’ll need the id for the next step. Now, let’s run the particle mesh command.

particle mesh add <xenon id> <id of your argon, boron, etc>

Here’s an example below:

particle mesh add e00fce682d9285fbf4412345 hamster_turkey
? Enter the network password [hidden]
▄ Adding the device to the network...

At the end of it you’ll see:

Done! The device should now connect to the cloud.

This process is not perfect. During the Adding the device to the network... stage, I had to remove the Xenon using particle mesh remove. Then re-run the particle mesh add command after resetting the Argon.

Now here comes to finale.

Connect the two devices to serial using particle serial monitor --follow

If you have the two devices connected, particle serial monitor will prompt you to select:

Jareds-MacBook-Pro:blynk-xenon-rgb jaredwolff$ particle serial monitor --follow
Polling for available serial device...
? Which device did you mean? /dev/tty.usbmodem146101 - Xenon
Opening serial monitor for com port: "/dev/tty.usbmodem146101"
Serial monitor opened successfully:

Remember: You have to run particle serial monitor for each device you want to connect to.

If all is working, you’ll likely see some output from the edge router!

Serial monitor opened successfully:
event=temperature data=21
event=temperature data=28
event=temperature data=21
event=temperature data=27
event=temperature data=28
event=temperature data=26
event=temperature data=23
event=temperature data=26
event=temperature data=21

Looking at the app, the Super Chart should be reacting to this new data.

Compare the last value in the command line to the last on the chart? Do they match? If so, you made it to the end of this example!

Conclusion

In this tutorial you’ve learned how to forward Particle Cloud data to Blynk. You’ve also learned how to do the same using a Particle Argon, Boron or ethernet connected Xenon. Awe yea. :sunglasses::+1:

Now that you have the tools to Blink-ify your Particle Mesh powered projects, it’s time to get to work!

By the way, this post is an excerpt from my upcoming Ultimate Guide to Particle Mesh. I’ll be sharing more exclusive content with my mailing list as it get’s closer to launch. You can sign up here for updates.

This. Is. Brilliant. Thanks for a nice and detailed write-up!

@vshymanskyy glad you like it! :slight_smile:

Thanks for the post. It’s an awesome tutorial! :clap:

However, you can run Blynk directly on Mesh without using webhooks. Which makes it whole more simple. At least this is how I use it :slight_smile:

1 Like

@Pavel that’s awesome. I didn’t realize it. I only saw some folks struggling to get a mesh only configuration working. Did you guys implement a UDP client/server setup so Blynk could run over mesh?

Well, it was a bit of a workaround, but not a difficult one. The first test app was to show nodes available on the mesh. The gateway would run one app and be connected to Blynk cloud, the nodes would run a different app and publish/subscribe to mesh. Then I would identify nodes by device name and update values on the gateway accordingly.

What I wasn’t able to do is to make mesh work if gateway is offline.

Can’t find the latest code I used, will post if I find it.

1 Like

As I remember this code has a ton of bugs with either table population or list duplicate findings, so be careful. Can’t find the final clean one… :man_shrugging:

#include <SparkCorePolledTimer.h>
#include <blynk.h>
#define BLYNK_PRINT Serial  // Set serial output for debug prints
//#define BLYNK_DEBUG       // Uncomment this to see detailed prints

int id,i = 0;
bool isHere = false;
const char *gateName;

char auth[] = "gatewaytoken-***********";

SparkCorePolledTimer updateTimer(1000);  //Create a timer object and set it's timeout in milliseconds

String nodesList[10]; // array to keep node names

WidgetTerminal terminal(V11);


void setup()
{
    Serial.begin(9600);
    delay(2000); // Allow board to settle
    
    Particle.subscribe("particle/device/name", handlerName);
    Particle.publish("particle/device/name");
    
    
    updateTimer.SetCallback(OnTimer);
    Blynk.begin(auth);
    
    
    if(Blynk.connected()){
        Serial.println("Connected to Blynk Cloud");
        Blynk.virtualWrite(V6, "clr");
        
        delay(100);       
        terminal.println(F("    ___  __          __"));
        delay(100);
        terminal.println(F("   / _ )/ /_ _____  / /__" ));
        delay(100);
        terminal.println(F("  / _  / / // / _ \\/  '_/" ));
        delay(100);
        terminal.println(F(" /____/_/\\_, /_//_/_/\\_\\" ));
        delay(100);
        terminal.println(F("        /___/ v" BLYNK_VERSION " on " BLYNK_INFO_DEVICE ));
    }
    
    if(Mesh.connecting()==false){
       Serial.println("Mesh ready");
    }
    
    Mesh.subscribe("device-name", myHandler);
    Mesh.publish("led-control", "gateway-online");
    

}




void handlerName(const char *topic, const char *data) {
    Serial.println("Device name:" + String(data));
    gateName = data;
    
}

BLYNK_WRITE(V1) {
    if (param.asInt() == 1) { // On button down...
        Mesh.publish("led-control", "on");
    } else if (param.asInt() == 0){
        Mesh.publish("led-control", "off");
    }
    
    
}

BLYNK_WRITE(V5) { // simulate internet drop
    if (param.asInt() == 1){
        Blynk.disconnect();
        Particle.disconnect();
    }
}

// Attach a ZeRGBa widget (mode: Merge) to the Virtual pin 2 - and control the built-in RGB led!
BLYNK_WRITE(V2) {
    int r = param[0].asInt();
    int g = param[1].asInt();
    int b = param[2].asInt();
    if (r > 0 || g > 0 || b > 0) {
        RGB.control(true);
        RGB.color(r, g, b);
    } else {
        RGB.control(false);
    }
}

void loop()
{
    Blynk.run();
    updateTimer.Update();
    
}


void OnTimer(void) {  
    
    if (Blynk.connected() == false){
        RGB.control(true);
        RGB.color(255, 0, 0);
        Mesh.publish("led-control", "blynk-offline");
        
        //Blynk.virtualWrite(V6, "clr");
        delay(7000);
        Blynk.connect();
        Particle.connect();
    } else {
        RGB.control(true);
        RGB.color(50, 255, 100);
        Mesh.publish("led-control", "blynk-online");
    }
    
    Blynk.virtualWrite(V10, WiFi.RSSI());
}





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

  String nodeName = String(data);

  int duplicate = 0;
  Blynk.virtualWrite(V6, "clr");
  
  Serial.printlnf("Device connected: %s", data ? data : "NULL");
  
    for(int k = 0; k <= i; k++)
    {
        if (nodesList[k] == nodeName)
        {
            isHere = true;
            Serial.print(nodeName);
            Serial.println(" is in the list, skipping...");
            //duplicate++;
        }
        
        if (isHere == false){
            nodesList[i] = nodeName;
            i++;
            /*
            if (duplicate > 1){
                Serial.println("duplicate found");
                nodesList[k] = "";
                i = i-1;
            }
            */
            
            //isHere = true;
            break;
        }
    }
    
    isHere = false;
    
    delay(100);
        

  //Serial.printlnf("Total nodes: %d ",i);
    
    Serial.println("List:");
    
    for(int c = 0; c < i; c++)
    {
        Serial.print(" - ");
        Serial.println(nodesList[c]);
        Blynk.virtualWrite(V6, "add", c+1, (nodesList[c])," ");
    }

  
} 


1 Like

Oooh gotcha. My tutorial above has both examples. (one using Webhooks, the other publishing to local mesh then forwarded by the Blynk library) You can search for Local Mesh Using Blynk Library and it should get you there (I know, the tutorial is a bit… hefty)

Glad to see we came to the same conclusion there. :slight_smile:

1 Like

Did you manage to make mesh work offline?

After setting up a Blynk and Cloud connection, I disconnected the Wifi completely using Wifi.off(); The mesh data still came in ok

event=temperature data=27
event=temperature data=29
event=temperature data=20
event=temperature data=27
event=temperature data=23
event=temperature data=29
event=temperature data=21
event=temperature data=24
event=temperature data=24
event=temperature data=28
event=temperature data=27
event=temperature data=27
event=temperature data=23

The edge router would have to cache it in memory or in flash. That way if the connection returned those values could be written to the cloud.

I’m not 100% sure if that answered your question. Let me know if I didn’t!

1 Like