Particle Mesh + Blynk Tutorial (x2!)

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.

2 Likes

nice tutorial, Jared!
I did not know blynk had an API that we can shoot from a webhook.
If I got it right, this way we do not need to use the Blynk library in our projects, when all is needed is monitoring. Cool!
Thank you,
Gustavo.

1 Like

Thanks Gustavo!

The only caveat is that Blynk doesn’t know when your device is “Online” It may prevent you from activating certain widgets (buttons, RGB zebra, etc). I’m sure some type of keep alive endpoint exists, it’s just not documented. :wink:

HI @jaredwolff

Would you mind helping me getting a Webhook set up to send data to Blynk? Apologies for the trivial question on the back of such a good tutorial, but I am very new to this :slight_smile:

I will gladly send through my code if you can find the time to assist.

Many thanks!
Friedl.

Hi @friedl_1977

If you have specific questions or error messages i’d be happy to work through them here. If it’s something you’d rather not share you can email me hello@jaredwolff.com

Jared

HI @jaredwolff

Thanks for the prompt response. I have been all over the place trying to find a good and easy to use ‘Dashboard’. I have been using Ubidots so far, but the lack of a scheduler widget is problematic in this case, hence me looking at Blynk :slight_smile:

No problem, I will post it here, it might help someone else as well.

#include <blynk.h>
#include <cmath>


char auth[] = "my_secret_code";

// Establish Ubidots Webhook
const char* WEBHOOK_NAME = "Amp";       
   
#define CURRENT_SENSOR A5    
    
int led = D7;

int redPin = D3;       
int greenPin = D1;    
int bluePin = D2;

int i = 0;
 
float amplitude_current;     // Float amplitude current
float effective_value;       // Float effective current

void setup() {

    Serial.begin(115200);
    // Subscribe to the integration response event
    Blynk.begin(auth);
    
    pinMode(led, OUTPUT);
    
    pinMode(redPin, OUTPUT);
    pinMode(bluePin, OUTPUT);
    pinMode(greenPin, OUTPUT);
    
}

void pins_init()
{
    pinMode(CURRENT_SENSOR, INPUT);
}

const int Resolution = 3277; //4095 
const float FullScaleAmps = 40.0;
const int ZeroValue = 2047;    //2047  //2100
const float MilliVoltsPerAmp = float(Resolution) / FullScaleAmps;


float getRms()
{
    const int numSamples = 1000;
    // The integer value (between 0 and 4095) that we read from the sensor
    int sensorValue;
    // The timestamp of the "previous" sample
    int ts_prev = 0;
    // The difference between the "present" and "previous" timestamps
    int ts_delta = 0;
    // We keep the value of the first timestamp to use in the final
    // averaging step. Alternatively we could simply sum up all the
    // deltas from the start to the end.
    int ts_first = 0;
    // The integaer value of the "prevous" sample.
    int value_prev = 0;
    // Total elapsed time for the 1000 samples.
    int elapsedTime = 0;
    // The timestamp as read from the sensor.
    uint32_t timeStamp = 0;
    // The measured values (integers) are converted to floats for the calculation.
    float val_now = 0.0;
    float val_prev = 0.0;
    float accum = 0.0;
    float avgValue = 0.0;
    float rms = 0.0;
    
    for (int k=0; k<numSamples; k++) {
        sensorValue = analogRead(CURRENT_SENSOR);
        timeStamp = micros();
        if (ts_prev > 0) {
            ts_delta = timeStamp - ts_prev;
            if (ts_delta < 0) {
                // If we detect an overflow we return an invalid RMS current value immediately
                return -1;
            } else {
                val_now = (float(sensorValue) - float(ZeroValue)) / MilliVoltsPerAmp;
                val_prev = (float(value_prev) - float(ZeroValue)) / MilliVoltsPerAmp;
                accum += 0.5 * ts_delta * (val_now*val_now + val_prev*val_prev);
            }
        } else {
            // This is only executed the first time round the loop
            ts_first = timeStamp;
        }
        ts_prev = timeStamp;
        value_prev = sensorValue;
    }
  
    elapsedTime = timeStamp - ts_first;
    avgValue = accum / float(elapsedTime);
    rms = sqrt(avgValue);
    return rms;
}

int debug;

void loop() {
    
    Blynk.run();

    float sensor_value = getRms();
 
  if (sensor_value >= 0) {

     char data[16];
     snprintf(data, sizeof(data), "%.3f", sensor_value);
     Particle.publish("Amp", data, PRIVATE); 
     
     delay(10000);
  
    } else {
      
     System.reset(); 
     
           }
}

In a nutshell, I managed to get data to Blynk from the Webhooks, but only if I use a content value such as “1” or “2” as the VALUE under query parameters.

I suppose I can try to use the method of sending data to Blynk using their “timer method” but I prefer Webhooks.

Thanks again!!

Hey @friedl_1977 thanks for sharing. A few questions:

What type of device are you using? (Xenon, Argon, Boron?)
Are you having trouble receiving or sending data to Blynk? Can you highlight where you’re having trouble in the code?

HI @jaredwolff

  • I am using Photon on this particular product as I am sending “live” data. Boron would cost a fortune to run :smiley:
  • Having rouble sending data via Virtual pin and Webhooks from Photon to Blynk
  • I presume my problem lies in the Webhooks setup as this code publishes successfully to Particle Cloud as well as Ubidots.

Here are some screen grabs on my Webhooks setup:

If it is indeed in my code, I suspect in the Particle.publish statement:

if (sensor_value >= 0) {

     char data[16];
     snprintf(data, sizeof(data), "%.3f", sensor_value);
     Particle.publish("Amp", data, PRIVATE); 

but as I mentioned, this publishes successfully everywhere else.

Below is another screen grab from te Event monitor under the Webhooks section.

Thanks

Ahh ok,

So for writing using the Blynk API, you need to call

Blynk.virtualWrite(11, <your value>);

Where the first param is the virtual pin and the second is the value you’re trying to push. This should work a-ok on your Photon.

Make sure for your different elements, that you select the virtual pin. (note that when using virtualWrite you don’t have to include the V)

If you’re trying to publish with a webhook, when you do your Particle.publish you need to make sure the object is a JSON string. So instead you’ll have to publish something like:

Particle.publish("blob", String::format("{\"data\":%.3f}", sensor_value) , PRIVATE, WITH_ACK);

That way your data will get picked up and replaced into the {{{data}}} mustache template

Hope that helps!

1 Like

HI @jaredwolff -

GREAT Thanks. Let me make the changes, I will let you know. Might need one last bit of help in constructing the JSON string, but let me try first :slight_smile:

Regards,
Friedl.

EDIT:

SOLVED, thanks!!!

1 Like

That’s awesome @friedl_1977! Glad you got it working.

1 Like

Hi Friedl,
I got curious about that scheduler widget that you are missing in Ubidots.
What is the function of this scheduler and why do you need one? (out of curiosity)
Thanks,
Gustavo.

HI @gusgonnet -

Thanks for the interest :slight_smile:

In this case, it is not to launch a rocket to Mars (unfortunately as that would be awesome) but rather something simple such as triggering a relay for;

  • Various Specific start times i.e. tie of day
  • Various days of the week
  • Set ‘run’ times.

I will be posting this project soon, just want to solve the dashboard issue. Was going to use Blynk, but at $2000 p/a it is a bit too pricey.

I tried a workaround in Ubidots using a combinations of some variables and events to work together but after a couple of discussions with support realised it won’t work as the events won’t trigger again unless it went back to “normal” first as explained here:

At closer look here:

I thought it might work, but not sure whether the “Monday event” will trigger again nor does it allow you to set the “run time” of whatever you are trying to control. In addition to this, I would not want to relay on a client to edit events in order to use the even scheduler :smile: Lastly, I tried modifying the following widget as suggested by Ubidots support, I managed to het something done but it seems in the end you still need device specific hardware to make this work, or some serious coding skills.

In my case, the client will be controlling and monitoring pumps. I also realised I will have to improve and rely more on code running on the Particle device itself and less on Dashboard for doing calculations as the Synthetic variable seems relatively slow and will not suffice for live data streaming, even with fairly simple Watt calculations.

Hope this answers your question.

Kind Regards,
Friedl.

I see, thank you for your explanation!
I got in trouble in a similar way once with Ubidots, and at the end the workaround I was able to negotiate with Ubidots Support was to clear the variable once the alarm is triggered.
So to say, I had an integer variable alarm that had 3 levels of alarm: 0, 1 and 2.

alarm is 0 => no alarm triggered in ubidots
if alarm goes to 1 => alarm event triggered in ubidots, another event (in ubidots) that clears the alarm (sets to 0)
if alarm goes to 2 => alarm event triggered in ubidots, another event (in ubidots) that clears the alarm (sets to 0)

and so on. Otherwise like you observed, once the alarm was set to 1, if set again to 1 (by the particle device) was not triggering an extra alarm (and the user would have missed a notification email, for instance).

I think there was a schedule lib around for particle devices, would that help in your case?

An upside for the timings on your case being controlled by the device itself and not the cloud is more reliability, since it does not depend on any connection to a cloud (ubidots, in your case) for the schedule to work.

A downside would be more C work :slight_smile:
Gustavo.

Hi @gusgonnet -

First I created a Control Variable and Event. If this variable is set to 1, the Event tigers a web hook posting 1 to the Photon which in turn sets digitalPin HIGH. If the Control variable is set to 0, the event publishes 0 via web hook to Photon setting the pin LOW.

From here I now have three variables for scheduling days of the week (to simplify and avoid ending up with 1000's of events);

  • Even's
  • Odd's
  • All

Each variable is linked to a button on the dashboard to activate the appropriate one and setting the variable value to 1 if activated. From here I created 3 events, one for each variable. Each of these variables are then set to run a specific time of day, on either ODD, EVEN or ALL DAYS.

Lastly I have a RESET event. This event in essence determines the runtime of each scheduled event. After the time expires, the CONTROL Variable is set to 0.

I have only tested two consecutive days, so far so good, but I am sure I will run into some problems somewhere as this seem a very clumsy way to run a scheduler.

Honestly, Blynk seems like the better option for this and thanks to @jaredwolff I got going in couple of minutes, it is just too costly ($2000 pa), I did not have a great time in their forum and I am not comfortable with the way Blynk requires you to keep Void loop() section clear of anything. I get it is the way it works and they have their reasons, but I am quite new to C++ so this feels like an unnecessary curveball for me at this stage :rofl:

Thanks again!

Hi @gusgonnet

Gave it one more go in Ubidots before I take on C... :exploding_head:

Following your advice I (think) it is working with the odd exception of the webhook triggering but the Photon missing the payload for some reason. My concern is that the times it did miss, it was without fail, when tuning the pump off :astonished:

I think best will be to set the schedule in Ubidots, but leave the rest up to the Photon. This way, Ubidots can only send updated info the the Photon (or suppose Photon should rather check daily). My C is not nearly good enough to make this work though, so this should be fun :laughing:

Thanks again.
Friedl

1 Like

hi @jaredwolff

Just a quick question if you don’t mind. Am I correct in saying that if I follow the process above (using Webhooks sending data to Blynk rather thank Blynk timers and keeping the Void loop() clean) I will have to create a Webhook for each device as the Webhook contains the Blynk Authorisation code. Is this correct?

Apologies for the trivial question. Finding an affordable dashboard solution is proving more difficult than I would have thought :rofl:

My understanding is that if you opt for the $6000 license with Blynk this will not be a problem as the app you will be publishing will be handling the AUTH tokens, but $6k is quite a bit of money, especially due to our weak currency :see_no_evil:

Many thanks!
Friedl.

No, you can pass in the authorisation code via the publish event

1 Like

@ScruffR

Great news, thanks!! Let me see if I can figure this out :smile:

To start, do I remove it from the URL in the webhook correct?

Many thanks,
Friedl.

Not quite. You don't remove it but replace it with a handlebars variable (e.g. {{authCode}}) which you then provide in your event data as JSON field like "{ ...<otherFields> ..., \"authCode\":\"<yourCodeHere>\" }".