ST Thing Shield MQTT Gateway

I’m trying to connect MQTT sensors to SmartThings HUB using the ST Thing Shield. So far I can update the status of the virtual devices using MQTT. But after the shield is not responding to the changes on the SmartThings App. I thing it is due the low memory of the Arduino Uno. if I comment the line ** check_subscription();** in the sketch the Shield start to respond to the mobile App, but does not check the MQTT subscriptions. I ordered a Arduino DUE and will do more tests when it arrives.

Does anyone have any ideas how the code could be improved/fixed? It is just my initial code, I have to write the MQTT publish part, but there is no point on doing that until the rest is fully functional.

###########################################################################

/***************************************************

Reads from SmartThings Shield and send MQTT and vice versa

****************************************************/
#include <SPI.h>
#include “Adafruit_MQTT.h”
#include “Adafruit_MQTT_Client.h”

#include <Ethernet.h>
#include <EthernetClient.h>
#include <Dns.h>
#include <Dhcp.h>

#include <SoftwareSerial.h> //TODO need to set due to some weird wire language linker, should we absorb this whole library into smartthings
#include <SmartThings.h>

/************************* Ethernet Client Setup *****************************/
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};

//Uncomment the following, and set to a valid ip if you don’t have dhcp available.
//IPAddress iotIP (192, 168, 0, 42);
//Uncomment the following, and set to your preference if you don’t have automatic dns.
//IPAddress dnsIP (8, 8, 8, 8);
//If you uncommented either of the above lines, make sure to change “Ethernet.begin(mac)” to “Ethernet.begin(mac, iotIP)” or “Ethernet.begin(mac, iotIP, dnsIP)”

/************************* MQTT Server Setup *********************************/

#define AIO_SERVER “192.168.0.2”
#define AIO_SERVERPORT 1883
#define AIO_USERNAME “”
#define AIO_KEY “”

/************************* SmartThings Shield pins and variables *********************************/

#define PIN_THING_RX 3
#define PIN_THING_TX 2

SmartThingsCallout_t messageCallout; // call out function forward decalaration
SmartThings smartthing(PIN_THING_RX, PIN_THING_TX, messageCallout); // constructor

int ledPin = 13;
bool isDebugEnabled; // enable or disable debug in this example
int stateLED; // state to track last set value of LED

/************ Global State (you don’t need to change this!) ******************/

//Set up the ethernet client
EthernetClient client;

// Store the MQTT server, client ID, username, and password in flash memory.
// This is required for using the Adafruit MQTT library.
const char MQTT_SERVER[] PROGMEM = AIO_SERVER;
// Set a unique MQTT client ID using the AIO key + the date and time the sketch
// was compiled (so this should be unique across multiple devices for a user,
// alternatively you can manually set this to a GUID or other random value).
const char MQTT_CLIENTID[] PROGMEM = TIME AIO_USERNAME;
const char MQTT_USERNAME[] PROGMEM = AIO_USERNAME;
const char MQTT_PASSWORD[] PROGMEM = AIO_KEY;

Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, AIO_SERVERPORT, MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD);

// You don’t need to change anything below this line!
#define halt(s) { Serial.println(F( s )); while(1); }

/****************************** Feeds ***************************************/

// Setup a feed called ‘photocell’ for publishing.
// Notice MQTT paths for AIO follow the form: /feeds/
const char PHOTOCELL_FEED[] PROGMEM = AIO_USERNAME “myhouse/garage/door”;
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, PHOTOCELL_FEED);

// Setup a feed called ‘onoff’ for subscribing to changes.
const char ONOFF_FEED[] PROGMEM = AIO_USERNAME “myhouse/garage/open”;
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, ONOFF_FEED);

/*************************** Sketch Code ************************************/

void setup() {

// SmartThings Shield state of global variables
isDebugEnabled = true;
stateLED = 0; // matches state of hardware pin set below

// SmartThings Shield setup hardware pins
pinMode(ledPin, OUTPUT); // define PIN_LED as an output
digitalWrite(ledPin, LOW); // set value to LOW (off) to match stateLED=0

Serial.begin(115200);
Serial.println(F(“Arduino SmartThings Shield - MQTT Client”));

if (isDebugEnabled)
{
Serial.println(F(“SmartThings Shield Debug enabled…”));
}

// Initialise the MQTT Client
Serial.print(F("\nInitiating Ethernet Client…"));
Ethernet.begin(mac);
delay(1000); //give the ethernet a second to initialize

mqtt.subscribe(&onoffbutton);
}

uint32_t x=0;

void loop() {
// Ensure the connection to the MQTT server is alive (this will make the first
// connection and automatically reconnect when disconnected). See the MQTT_connect
// function definition further below.
MQTT_connect();

check_subscription(); // this is our ‘wait for incoming subscription packets’ busy subloop

// publish(); // Publish MQTT message

smartthing.run(); // run smartthing logic

// ping the server to keep the mqtt connection alive
if(! mqtt.ping()) {
mqtt.disconnect();
}

}

void publish() {
// Now we can publish stuff!
Serial.print(F("\nPublishing val “));
Serial.print(x);
Serial.print(”…");
if (! photocell.publish(x)) { // Publish variable x
Serial.println(F(“Failed”));
} else {
Serial.println(F(“OK!”));
}
}

void check_subscription() {

// this is our ‘wait for incoming subscription packets’ busy subloop
Adafruit_MQTT_Subscribe *subscription;
while ((subscription = mqtt.readSubscription(1000))) {
if (subscription == &onoffbutton) {
Serial.print(F("Subscription received: "));
// Serial.println((char *)onoffbutton.lastread);
String str((char *)onoffbutton.lastread);
Serial.println(str);
if (str == “on”)
{
on();
}
else if (str == “off”)
{
off();
}

}

}

}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
int8_t ret;

// Stop if already connected.
if (mqtt.connected()) {
return;
}

Serial.print("Connecting to MQTT… ");

while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
Serial.println(mqtt.connectErrorString(ret));
Serial.println(“Retrying MQTT connection in 5 seconds…”);
mqtt.disconnect();
delay(5000); // wait 5 seconds
}
Serial.println(“MQTT Connected!”);
}

void on()
{
stateLED = 1; // save state as 1 (on)
digitalWrite(ledPin, HIGH); // turn LED on
smartthing.shieldSetLED(0, 0, 1);
smartthing.send(“on”); // send message to cloud
// Serial.println(“on”);
}

void off()
{
stateLED = 0; // set state to 0 (off)
digitalWrite(ledPin, LOW); // turn LED off
smartthing.shieldSetLED(0, 0, 0);
smartthing.send(“off”); // send message to cloud
// Serial.println(“off”);
}

void messageCallout(String message)
{
// if debug is enabled print out the received message
if (isDebugEnabled)
{
Serial.print(“Received message: '”);
Serial.println(message);
}

// if message contents equals to ‘on’ then call on() function
// else if message contents equals to ‘off’ then call off() function
if (message.equals(“on”))
{
on();
}
else if (message.equals(“off”))
{
off();
}
else if (message.equals(“hello”))
{
//hello();
}
}

The code is missing the include files, because of some weird html formatting from the forum.

 /***************************************************

Reads from SmartThings Shield and send MQTT and vice versa

 ****************************************************/
#include <SPI.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

#include <Ethernet.h>
#include <EthernetClient.h>
#include <Dns.h>
#include <Dhcp.h>

#include <SoftwareSerial.h>   //TODO need to set due to some weird wire language linker, should we absorb this whole library into smartthings
#include <SmartThings.h>


/************************* Ethernet Client Setup *****************************/
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};

//Uncomment the following, and set to a valid ip if you don't have dhcp available.
//IPAddress iotIP (192, 168, 0, 42);
//Uncomment the following, and set to your preference if you don't have automatic dns.
//IPAddress dnsIP (8, 8, 8, 8);
//If you uncommented either of the above lines, make sure to change "Ethernet.begin(mac)" to "Ethernet.begin(mac, iotIP)" or "Ethernet.begin(mac, iotIP, dnsIP)"


/************************* MQTT Server Setup *********************************/

#define AIO_SERVER      "192.168.0.2"
#define AIO_SERVERPORT  1883
#define AIO_USERNAME    ""
#define AIO_KEY         ""

/************************* SmartThings Shield pins and variables *********************************/

#define PIN_THING_RX    3
#define PIN_THING_TX    2

SmartThingsCallout_t messageCallout;    // call out function forward decalaration
SmartThings smartthing(PIN_THING_RX, PIN_THING_TX, messageCallout);  // constructor

int ledPin = 13;
bool isDebugEnabled;    // enable or disable debug in this example
int stateLED;           // state to track last set value of LED

/************ Global State (you don't need to change this!) ******************/

//Set up the ethernet client
EthernetClient client;

// Store the MQTT server, client ID, username, and password in flash memory.
// This is required for using the Adafruit MQTT library.
const char MQTT_SERVER[] PROGMEM    = AIO_SERVER;
// Set a unique MQTT client ID using the AIO key + the date and time the sketch
// was compiled (so this should be unique across multiple devices for a user,
// alternatively you can manually set this to a GUID or other random value).
const char MQTT_CLIENTID[] PROGMEM  = __TIME__ AIO_USERNAME;
const char MQTT_USERNAME[] PROGMEM  = AIO_USERNAME;
const char MQTT_PASSWORD[] PROGMEM  = AIO_KEY;

Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, AIO_SERVERPORT, MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD);

// You don't need to change anything below this line!
#define halt(s) { Serial.println(F( s )); while(1);  }


/****************************** Feeds ***************************************/

// Setup a feed called 'photocell' for publishing.
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
const char PHOTOCELL_FEED[] PROGMEM = AIO_USERNAME "myhouse/garage/door";
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, PHOTOCELL_FEED);

// Setup a feed called 'onoff' for subscribing to changes.
const char ONOFF_FEED[] PROGMEM = AIO_USERNAME "myhouse/garage/open";
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, ONOFF_FEED);

/*************************** Sketch Code ************************************/

void setup() {

  // SmartThings Shield state of global variables
  isDebugEnabled = true;
  stateLED = 0;                 // matches state of hardware pin set below
  
  // SmartThings Shield setup hardware pins 
  pinMode(ledPin, OUTPUT);     // define PIN_LED as an output
  digitalWrite(ledPin, LOW);   // set value to LOW (off) to match stateLED=0

  Serial.begin(115200);
  Serial.println(F("Arduino SmartThings Shield - MQTT Client"));

  if (isDebugEnabled)
  { 

Serial.println(F(“SmartThings Shield Debug enabled…”));
}

  // Initialise the MQTT Client
  Serial.print(F("\nInitiating Ethernet Client..."));
  Ethernet.begin(mac);
  delay(1000); //give the ethernet a second to initialize
  

  mqtt.subscribe(&onoffbutton);
}

uint32_t x=0;

void loop() {
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();

  check_subscription(); // this is our 'wait for incoming subscription packets' busy subloop

//  publish(); // Publish MQTT message
  
  smartthing.run(); // run smartthing logic
  
  // ping the server to keep the mqtt connection alive
  if(! mqtt.ping()) {

mqtt.disconnect();
}

}

void publish() {
  // Now we can publish stuff!
  Serial.print(F("\nPublishing val "));
  Serial.print(x);
  Serial.print("...");
  if (! photocell.publish(x)) {   // Publish variable x

Serial.println(F(“Failed”));
} else {
Serial.println(F(“OK!”));
}
}

void check_subscription() {

  // this is our 'wait for incoming subscription packets' busy subloop
  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(1000))) {

if (subscription == &onoffbutton) {
Serial.print(F("Subscription received: "));
// Serial.println((char *)onoffbutton.lastread);
String str((char *)onoffbutton.lastread);
Serial.println(str);
if (str == “on”)
{
on();
}
else if (str == “off”)
{
off();
}

}
}

}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {

return;
}

  Serial.print("Connecting to MQTT... ");

  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected

Serial.println(mqtt.connectErrorString(ret));
Serial.println(“Retrying MQTT connection in 5 seconds…”);
mqtt.disconnect();
delay(5000); // wait 5 seconds
}
Serial.println(“MQTT Connected!”);
}

void on()
{
  stateLED = 1;                 // save state as 1 (on)
  digitalWrite(ledPin, HIGH);  // turn LED on
  smartthing.shieldSetLED(0, 0, 1);
  smartthing.send("on");        // send message to cloud
//  Serial.println("on");
}

void off()
{
  stateLED = 0;                 // set state to 0 (off)
  digitalWrite(ledPin, LOW);   // turn LED off
  smartthing.shieldSetLED(0, 0, 0);
  smartthing.send("off");       // send message to cloud
//  Serial.println("off");
}


void messageCallout(String message)
{
  // if debug is enabled print out the received message
  if (isDebugEnabled)
  {

Serial.print(“Received message: '”);
Serial.println(message);
}

  // if message contents equals to 'on' then call on() function
  // else if message contents equals to 'off' then call off() function
  if (message.equals("on"))
  {

on();
}
else if (message.equals(“off”))
{
off();
}
else if (message.equals(“hello”))
{
//hello();
}
}

Please surround your code with three back-quotes prior and after, or highlight and press the </> format button.

Like so…

void messageCallout(String message)
{
  // if debug is enabled print out the received message
  if (isDebugEnabled)
  {
     Serial.print("Received message: '");
     Serial.println(message);
  }
  ...

(Or paste a GitHub URL instead of full code.)

Do you have this on GitHub? I’d like to see your device code. I’ve been looking for a way to setup a MQTT bridge.

Hi Mike,

Sorry for take so long to answer, I was really busy. Today I had some time and decide to have a look on the code again. I replaced the Adafruit mqtt librabry for MQTT by Joel Gaehwiler and now it is working even on the arduino UNO. Search for it on the Library Manager. My sketch is very basic but it is very easy to modify and expand it. I will finally try to make my garage remote control using the ST_Anything library.

 ```// This example uses an Arduino Uno together with
// an Ethernet Shield and Smartthings Thing Shield
// to connect to a MQTT Broker and update the Smartthing.
//
// It uses the the library MQTT by Joël Gähwiler (it can be obtained
// by Library Manager.


#include <Ethernet.h>
#include <MQTTClient.h>

#include <SoftwareSerial.h>   //TODO need to set due to some weird wire language linker, should we absorb this whole library into smartthings
#include <SmartThings.h>


byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(192, 168, 0, 177);
char broker[] = "192.168.0.2";

EthernetClient net;
MQTTClient client;

unsigned long lastMillis = 0;
String doorStatus = "off";

/************************* SmartThings Shield pins and variables *********************************/

#define PIN_THING_RX    3
#define PIN_THING_TX    2

SmartThingsCallout_t messageCallout;    // call out function forward decalaration
SmartThings smartthing(PIN_THING_RX, PIN_THING_TX, messageCallout);  // constructor

int ledPin = 13;
int stateLED = 0;           // state to track last set value of LED

/************************* Sketch *********************************/

void setup() {

  // SmartThings Shield setup hardware pins 
  pinMode(ledPin, OUTPUT);     // define PIN_LED as an output
  digitalWrite(ledPin, LOW);   // set value to LOW (off) to match stateLED=0

  
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {

; // wait for serial port to connect. Needed for native USB port only
}

  Serial.println("Smartthings MQTT Gateway");

  if (Ethernet.begin(mac) == 0) {

Serial.println(“Failed to configure Ethernet using DHCP”);
// try to congifure using IP address instead of DHCP:
Ethernet.begin(mac, ip);
}
// give the Ethernet shield a second to initialize:
delay(1000);

  client.begin(broker, net);

  connect();
}

void connect() {
  Serial.print("Connecting to MQTT Server...");
  while (!client.connect("arduino", "try", "try")) {

Serial.print(".");
}

  Serial.println("\nConnected!");

  client.subscribe("/garage/command"); // Subscribe to a TOPIC
  // client.unsubscribe("/example");
}

uint32_t x=0;

void loop() {
  client.loop();

  if(!client.connected()) {

connect();
}

  smartthing.run(); // run smartthing logic

  // publish a message roughly every 10 seconds.
  if(millis() - lastMillis > 10000) {

lastMillis = millis();
client.publish(“garage/door”, doorStatus);
}
}

void messageReceived(String topic, String payload, char * bytes, unsigned int length) {
  Serial.print("Incoming: ");
  Serial.print(topic);
  Serial.print(" - ");
  Serial.print(payload);
  Serial.println();
  if (payload == "on") {

doorStatus = “on”;
on();
}
else if (payload == “off”) {
doorStatus = “off”;
off();
}
}

void on()
{
  stateLED = 1;                 // save state as 1 (on)
  digitalWrite(ledPin, HIGH);  // turn LED on
  smartthing.shieldSetLED(0, 0, 1);
  smartthing.send("on");        // send message to cloud
//  Serial.println("on");
}

void off()
{
  stateLED = 0;                 // set state to 0 (off)
  digitalWrite(ledPin, LOW);   // turn LED off
  smartthing.shieldSetLED(0, 0, 0);
  smartthing.send("off");       // send message to cloud
//  Serial.println("off");
}


void messageCallout(String message) {
  
  Serial.print("Received message: '");
  Serial.println(message);

  // if message contents equals to 'on' then call on() function
  // else if message contents equals to 'off' then call off() function
  if (message.equals("on")) {

doorStatus = “on”;
on();
}
else if (message.equals(“off”)) {
doorStatus = “off”;
off();
}

}```

What other devices do you have using MQTT? I was thinking about using the thingshield/arduino as a mqtt bridge between the mqtt server and ST hub. This would make it easier for other devices that use the mqtt protocol(esp8266, TV,…) to communicate with ST.

That is my plan as well. I want to create virtual devices on the HUB and uptdate its values using MQTT and the ST Shield. I am very close to a solution, but I running on some silly problems. Last case scenario I will have to run it in 2 arduinos, one for the shield and one for the MQTT or a Arduino Yun perhaps.

I ordered a Ethernet shield off amazon yesterday so last night I was tried to connect the thingshield to a nodemcu esp8266. I could not get the thingshield to power up. What problems are you running into?

some kind of conflict when I run both libraries (smartthings and mqtt)together (the sketch became unstable). I think my solution will be run 2 arduinos (1 for each library) communicating though serial.

An Arduino MEGA 2560 is probably your best bet for gaining some very needed RAM, while maintaining excellent compatibility with all of the UNO code available online. The UNO also has 4 hardware uARTs for serial communications to the ThingShield and other shields.

Good luck with your project!

try using this mqtt library.

I am able to get ST events to mqtt via https://github.com/dpjanes/iotdb-smartthings. Now i need to be able to control ST things with mqtt messages. I have mqtt msg logging to ST via thingshield/arduino. I am getting I am going to need a smartapp or something to control multiple things.

So a buddy and I worked together and made a SmartApp/DeviceType that can shuttle properties back and forth between SmartThings and MQTT. Maybe it could help? At least the design: MQTT Bridge [device + app]

1 Like

Thanks John, It was a very nice work.