Converting Cayenne Arduino LIbrary sketches to Cayenne MQTT Arduino


#1

MQTT is an exciting technology that’s driving much of the current development of Cayenne and ultimately will be a large part of the future of our platform. At one point Cayenne only had the Cayenne Arduino Library for Arduino connectivity before implementing the Cayenne MQTT Arduino Library for use with our Bring Your Own Thing API.

The purpose of this post is to share a step-by-step way to convert your sketch code from the non-MQTT library to the MQTT one. The intended audience is someone less familiar/comfortable with coding who was attracted to Cayenne for ease of use reasons and might not know where to begin with the MQTT library/API.

Experienced users and developers may find it easier to re-factor their code on their own, but may still find this article useful as a general reference while doing so.

Esp8266 Users: This post applies to you as well if you were connecting your ESP8266 device through our original Arduino option. The only change would be to grab the Cayenne-MQTT-ESP8266 library instead of the Arduino one linked in step #1 below.

Step-by-step conversion

  1. Install the Cayenne MQTT Arduino library by launching the Arduino IDE, then going to Sketch > Include Library > Manage Libraries… and searching for CayenneMQTT. Click “Install” on the entry that appears.

  1. In the Arduino IDE, go to File > Examples > CayenneMQTT > Connections and choose the connection type you were using for your original Arduino sketch. We’ll use this connection sketch as a base for your transition.

  2. Carry over any non-Cayenne connectivity related #include or #define statements from the old sketch into the new one. For example, if transitioning from our legacy DS18B20 sketch, you’d bring across:

    #include <OneWire.h>
    #include <DallasTemperature.h>

    But not:

    #include <CayenneEthernet.h>

    (because this is already replaced with #include <CayenneMQTTEthernet.h> in the new connection sketch)

  3. If you have any #define virtual pin constants, (things like ‘V1’, ‘V9’), we’ll convert them to numeric MQTT Channels, since the MQTT connectivity does not use the concept of virtual pins.

    So something like:
    #define VIRTUAL_PIN V8
    Becomes:
    const int VIRTUAL_PIN = 8;

  4. Replace this legacy authentication section:

    // Cayenne authentication token. This should be obtained from the Cayenne Dashboard.
    char token[] = "AuthenticationToken";

    with this MQTT authentication section:
    // Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
    char username[] = "MQTT_USERNAME";
    char password[] = "MQTT_PASSWORD";
    char clientID[] = "CLIENT_ID";

  5. Carry over any further code that sits outside of the setup() and loop() code blocks.

  6. In the setup() block replace:

    Cayenne.begin(token);
    With:
    Cayenne.begin(username, password, clientID);
    Then carry over any further code in the setup() block.

  7. In the loop() block replace:

    Cayenne.run();
    with:
    Cayenne.loop();

    Then carry over any further code in the loop() block. Add any carried over code into the if statement that maintains publishing timing in the new example sketch:

  8. If you have any CAYENNE_OUT statements, we’ll convert them to uniquely named stand-alone functions that can be called from the loop() function.

    For example, if your sketch has a block like:
    CAYENNE_OUT(VIRTUAL_PIN)
    {
    Your code here
    }

    Change it to:

    void newFunctionName(int VIRTUAL_PIN)
    {
    Your code here
    }

  9. The old system read CAYENNE_OUT functions automatically as part of Cayenne.run() , but the new one does not, so we need to actually call any CAYENNE_OUT functions we renamed in Step #8. You can do this by adding a single line in the body of the if statement in the loop().

    Using the function from Step #8 as an example, we’ll call it like this:
    newFunctionName(VIRTUAL_PIN);

  10. If you have any Cayenne.syncAll() or Cayenne.syncVirtual() functions, these are no longer necessary in the context of the MQTT library and can be removed. They will still compile if you leave them, but won’t do anything.

  11. At this point you should be ready add the device to your Cayenne dashboard. From there, go to Add New > Device/Widget > Bring Your Own Thing. On this page you’ll get the MQTT authentication info (Username, Password, Client ID) that you can use to fill in the section added to your sketch in step #4. You can also (optionally) name your device on this page.

  12. Upload your sketch and upon connection with our servers, the website will advance from the ‘Connect your device’ page to a blank dashboard.

With MQTT, any sensor data published from the sketch file will generate temporary widgets automatically, so there is no need to add them through the Add New menu on the Cayenne dashboard as you have done in the past!

TempWidget

To make the green temporary widgets permanent, just click the + icon in the upper-right corner of the widget.

Any actuators will need still need to be created on the Cayenne dashboard through Add New > Device/Widget > Custom Widgets. If the widget is set to an MQTT channel which matches the pin number of the actual device, there may be no need for additional code. For example, if you have a button widget set to MQTT channel 6 and your relay on physical pin 6, this should automatically be handled by the CAYENNE_IN_DEFAULT() function that is part of the example sketch we used. If you want to use different MQTT channels than pin numbers, or have additional code you’d like to add for a specific actuator, you can address them directly with a function for a specific channel (i.e. CAYENNE_IN(1) will be called when any actuator set to MQTT channel 1 is pressed on the Cayenne Dashboard).


Arduino Connectivity
Virtual pins not available on my dashboard
Raspberry Pi Offline
Arduino is Offine
Device offline every other day or so
Arduino mega offline
Triggers not working offline, backup solutions? [arduino]
#2

Example sketch conversion

Following is an Cayenne Arduino sketch which drives a TSL2561 luminosity sensor, a DS18B20 temperature sensor, and a LED associated with a Cayenne button actuator widget.

Original Arduino Library Sketch:

#define CAYENNE_PRINT Serial  // Comment this out to disable prints and save space
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_TSL2561_U.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// If you're not using the Ethernet W5100 shield, change this to match your connection type. See Communications examples.
#include <CayenneEthernet.h>

// Virtual Pin of the TSL2561 widget.
#define VIRTUAL_PIN V1
// Virtual Pin of the DS18B20 widget.
#define VIRTUAL_PINT V8

// Address used to read from the TSL2561. This is determined by the ADDR pin on the TSL2561.
// If ADDR is unconnected it means the sensor will use TSL2561_ADDR_FLOAT (0x39) for the address.  See the TSL2561 datasheet for more info.
const int address = TSL2561_ADDR_FLOAT;

//Physical pin of the DS18B20 sensor
const int tmpPin = 7;

Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(address, 12345);
OneWire oneWire(tmpPin);
DallasTemperature sensors(&oneWire);

// Cayenne authentication token. This should be obtained from the Cayenne Dashboard.
char token[] = "YOUR_CAYENNE_TOKEN";

void setup()
{
 Serial.begin(9600);
 Cayenne.begin(token);
 if (!tsl.begin())
 {
    CAYENNE_LOG("No TSL2561 detected");
 }

 tsl.enableAutoRange(true);
 /* Changing the integration time gives you better sensor resolution (402ms = 16-bit data) */
 tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS);      /* fast but low resolution */
 // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS);  /* medium resolution and speed   */
 // tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_402MS);  /* 16-bit data but slowest conversions */
 sensors.begin();
}

void loop()
{
 Cayenne.run();
}

// This function is called when the Cayenne widget requests data for the Virtual Pin for the TSL2561 Sensor
CAYENNE_OUT(VIRTUAL_PIN)
{
 // Send the command to get luminosity.
 sensors_event_t event;
 tsl.getEvent(&event);

 if (event.light)
 {
    // Send the value to Cayenne in lux.
    Cayenne.luxWrite(VIRTUAL_PIN, event.light);
 }
 else
 {
    /* If event.light = 0 lux the sensor is probably saturated
    and no reliable data could be generated! */
    CAYENNE_LOG("No sensor data");
 }
}

// This function is called when the Cayenne widget requests data for the Virtual Pin for the DS18B20 Sensor
CAYENNE_OUT(VIRTUAL_PINT)
{
 // Send the command to get temperatures.
 sensors.requestTemperatures();
 // This command writes the temperature in Celsius to the Virtual Pin.
 Cayenne.celsiusWrite(VIRTUAL_PINT, sensors.getTempCByIndex(0));
 // To send the temperature in Fahrenheit use the corresponding code below.
 //Cayenne.fahrenheitWrite(VIRTUAL_PIN, sensors.getTempFByIndex(0));
}

Cayenne MQTT Arduino LIbrary sketch:

#define CAYENNE_DEBUG
#define CAYENNE_PRINT Serial
#include <CayenneMQTTEthernet.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_TSL2561_U.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
char username[] = "YOUR_MQTT_USERNAME";
char password[] = "YOUR_MQTT_PASSWORD";
char clientID[] = "YOUR_MQTT_CLIENT_ID";

unsigned long lastMillis = 0;

// Virtual Pin of the TSL2561 widget.
const int VIRTUAL_PIN = 1;
// Virtual Pin of the DS18B20 widget.
const int VIRTUAL_PINT = 8;

// Address used to read from the TSL2561. This is determined by the ADDR pin on the TSL2561.
// If ADDR is unconnected it means the sensor will use TSL2561_ADDR_FLOAT (0x39) for the address.  See the TSL2561 datasheet for more info.
const int address = TSL2561_ADDR_FLOAT;

//Physical pin of the DS18B20 sensor
const int tmpPin = 7;

Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(address, 12345);
OneWire oneWire(tmpPin);
DallasTemperature sensors(&oneWire);

void setup() {
   Serial.begin(9600);
   Cayenne.begin(username, password, clientID);
 if (!tsl.begin())
 {
    CAYENNE_LOG("No TSL2561 detected");
 }

 tsl.enableAutoRange(true);

 tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_13MS);     
 sensors.begin();
}

void loop() {
   Cayenne.loop();

   //Publish data every 10 seconds (10000 milliseconds). Change this value to publish at a different interval.
   if(millis() - lastMillis > 10000) {
       lastMillis = millis();
       //Write data to Cayenne here. This example just sends the current uptime in milliseconds.
       Cayenne.virtualWrite(0, lastMillis);
    tslfunc(VIRTUAL_PIN);
    ds18b20func(VIRTUAL_PINT);
       //Some examples of other functions you can use to send data.
       //Cayenne.celsiusWrite(1, 22.0);
       //Cayenne.luxWrite(2, 700);
       //Cayenne.virtualWrite(3, 50, TYPE_PROXIMITY, UNIT_CENTIMETER);
   }
}

//Default function for processing actuator commands from the Cayenne Dashboard.
//You can also use functions for specific channels, e.g CAYENNE_IN(1) for channel 1 commands.
CAYENNE_IN_DEFAULT()
{
   CAYENNE_LOG("CAYENNE_IN_DEFAULT(%u) - %s, %s", request.channel, getValue.getId(), getValue.asString());
   //Process message here. If there is an error set an error message using getValue.setError(), e.g getValue.setError("Error message");
}

void ds18b20func(int VIRTUAL_PINT)
{
 // Send the command to get temperatures.
 sensors.requestTemperatures();
 // This command writes the temperature in Celsius to the Virtual Pin.
 Cayenne.celsiusWrite(VIRTUAL_PINT, sensors.getTempCByIndex(0));
 // To send the temperature in Fahrenheit use the corresponding code below.
 //Cayenne.fahrenheitWrite(VIRTUAL_PINT, sensors.getTempFByIndex(0));
}

// This function is called when the Cayenne widget requests data for the Virtual Pin for the TSL2561 Sensor
void tslfunc(int VIRTUAL_PIN)
{
 // Send the command to get luminosity.
 sensors_event_t event;
 tsl.getEvent(&event);

 if (event.light)
 {
    // Send the value to Cayenne in lux.
    Cayenne.luxWrite(VIRTUAL_PIN, event.light);
 }
 else
 {
    CAYENNE_LOG("No sensor data");
 }
}

MQTT - 433-module not reacting