Using the Intel Edison board to securely connect sensors with MQTT

Using the Intel Edison board to securely connect sensors with MQTT

Introduction

In previous blog posts [1], [2], we’ve built the Mosquitto MQTT broker on Edison and created a sensor node for sensing motion, temperature, and light level. In this article, we will connect those sensors and actuators to the Mosquitto MQTT server we’ve built to turn those sensors into true IoT sensors.

Using MQTT
Basic MQTT Publish/Subscribe

MQTT is a publish/subscribe protocol built on top of TCP/IP protocol. MQTT is one of the popular protocols being used for M2M (Machine to Machine) communications. The two main components of MQTT are the MQTT clients and the MQTT broker. The MQTT clients publish messages to a particular topic or, subscribe and listen, to a particular topic. The MQTT broker receives all published messages from MQTT publishers and forward the relevant messages to all MQTT subscribers. Subscribers and publishers do not have to be aware of each other, only the topics and messages are relevant. To properly communicate, publishers and subscribers have to agree to use a common topic name and message format.

To publish or subscribe to an MQTT topic, a MQTT client program is needed. In the Mosquitto MQTT distribution, the publishing client is called ‘mosquitto_pub’ and the subscribing client is called‘mosquitto_sub’.

The screenshot below shows the sequence of activities we’ll be describing next. The red window shows outputs of the ‘mosquitto_sub’ commands and the black window shows the ‘mosquitto_pub’ commands. Commands were labeled so that we can refer to them conveniently.

The minimal arguments required for the MQTT clients are: the broker IP address, the topic name and for the publishing clients, the message. Assuming that we already have an MQTT broker running on 127.0.0.1 (localhost), the command below will listen to all messages published to a topic named ‘edison/hello’
1sub 3> mosquitto_sub –h localhost –t edison/hello
To publish to the topic ‘edison/hello’:

1 pub 3> mosquitto_pub –h localhost –t edison/hello –m “Message#1: Hello World!”

When the MQTT broker receives the publication to topic ‘edison/hello’ with the message ‘Message#1: Hello World’ from the publishing client (pub3), it scans for subscribers that was listening to the topic ‘edison/hello’. Having found that the subscribing client, (sub3) was listening to the same topic, the MQTT broker forward the message to (sub3). When subscribing client (sub3) receives the message, it echo the message content to the terminal window. In the (sub 3) and (pub 3) commands, the default MQTT port number was implicitly assumed to be 1883 and was not specificed on the commandline. In  (sub 4) and(pub 4) commands, the port number is explicitly provided. In  (sub 5) and (pub 5) commands, the option ‘-v’was added to the subscription argument to enable printing of the topic name as well as the message. This will be useful when subscribing to multiple topics using wildcards. The subscription client is persistent, so that additional messages published to the topic ‘edison/hello’ in command (pub 6) will be seen by the subscription client.

The default MQTT broker behavior is to discard the message as soon as it was delivered to subscribing clients. When a new topic ‘edison/hello1’ was published by client (pub 7), subscribing client (sub 5) was listening to the topic ‘edison/hello’, hence was not informed. The subscribing client then subscribes to the new topic (sub 6), the previous message published to the topic ‘edison/hello1’ was already discarded. It will receive subsequent publish to ‘edison/hello1’ topic as shown in command (pub 8). There are additional options to tell the MQTT broker to retain the last message published to a topic as well as other QOS (Quality of Service) directives on how messages should be delivered by the broker. More details on these capabilities can be found at the MQTT Wiki site.
Depending on the configuration of the ports, additional arguments may be needed. We will discuss these later in this document.

Subscribing to multiple MQTT topics

Usually, one would have an MQTT client subscribing to multiple topics from a publisher and perform certain action based on the message received. It is worth a few words discussing how MQTT topic are organized.

MQTT topics are organized in a directory structure with the ‘/’ character used to indicate sub-topics. The listing below are examples of various topics:

1 Temperature/Building1/Room1/Location1

2 Temperature/Building1/Room1/Location2

3 Temperature/Building2/Room2

4 Temperature/Outdoor/West

5 Temperature/Outdoor/East

6 Temperature/Average

A subscribers can subscribe to one single topic, such as ‘Temperature/Outdoor/West’ or to multiple topics using wildcards. There are two wildcard characters in MQTT ‘+’ and ‘#’. The ‘+’ wildcard is used to subscribe to topics at the same hierarchical level. The ‘#’ wildcard is used to subscribe to all sub-topics below the specified topics. For example, subscribing to the topic ‘Temperature/#’ will return result when any of the topics in above list have a new message. Subscribing to ‘Temperature/+’ will return only ‘Temperature/Average’ since all other entries are sub-topic. More details on MQTT data organization can be found on the MQTT wiki site.

Configuring a secured Mosquitto broker

There are basically 4 methods to connect a MQTT client to the Mosquitto server.
Open port or no security. This is a convenient method to develop and test MQTT devices before deployment. Data transmission between broker and client are transmitted in plain text and can be snooped on by anyone with the right tool.

ser and password protection. This is used basically to authenticate a client to the broker. It doesn’t encrypt the data transmission.
TLS-PSK encryption. Clients and broker possess a shared secret key that they used to negotiate a secure connection. This is available in Mosquitto version 1.3.5 or later.

SSL encryption. This is the most advanced encryption and authentication scheme available in the Mosquitto MQTT distribution. With SSL encryption, one needs to obtain a server certificate from a trusted Certificate Authority (CA) such as Verisign or Equifax. Often, for testing, one can create a self-signed certificate that can then be used to sign the server certificate. This encryption method, while more secured, is cumbersome to implement as it requires all client devices to be provisioned with the correct certificates and mechanism for in-the-field upgrade must be developed to keep the certificate versions between clients and broker in sync.
To configure a secure Mosquitto MQTT broker, use the configuration directives shown below. The certificate we used in this case is a self-signed certificate for testing purpose and should not be used in production servers.

01 port 1883

02 password_file /home/mosquitto/conf/pwdfile.txt

03 log_type debug

04 log_type error

05 log_type warning

06 log_type information

07 log_type notice

08 user root

09 pid_file /home/mosquitto/logs/mosquitto.pid

10 persistence true

11 persistence_location /home/mosquitto/db/

12 log_dest file /home/mosquitto/logs/mosquitto.log

13
 

14 listener 1995

15 # port 1995 is the TLS-PSK secure port, client must provide

16 # –psk-identity and –psk to access

17 psk_file /home/mosquitto/conf/pskfile.txt

18 # psk_hint must be present or port 1995 won't work properly

19 psk_hint hint

20
 

21 listener 1994

22 # port 1994 is the SSL secure port, client must present

23 # a certificate from the certificate authority that this server trust

24 # e.g. ca.crt

25 cafile /home/mosquitto/certs/ca.crt

26 certfile /home/mosquitto/certs/server.crt

27 keyfile /home/mosquitto/certs/server.key

The file, pwdfile.txt, is a password file, encrypted in similar manner as the Linux password file. It is generated by using the mosquitto_passwd utility that comes with the Mosquitto software. The filepskfile.txt, is the pre-shared password file to be used in a TLS-PSK connection. The format of this file is a text file of one user:password pair per line. The passwords used for TLS-PSK must use only hexadecimal characters and are case insensitive. Sample of the password_file and psk_file is shown below.
 
1 /* sample Mosquitto password_file */

2 user:$6$Su4WZkkQ0LmqeD/X$Q57AYfcu27McE14/MojWgto7fmloRyrZ7BpTtKOkME8UZzJZ5hGXpOea81RlgcXttJbeRFY9s0f+UTY2dO5xdg==

3 /* sample Mosquitto PSK file */

4 user1:deadbeef

5 user2:DEADBEEF0123456789ABCDEF0123456789abcdef0123456789abcdef0123456789abcdef

Incorporating MQTT into an IoT Sensor

Since the Edison board is really a Linux board that ran an Arduino sketch as one of its processes, we will leverage the existing Mosquitto MQTT package we previously built. Within the Arduino sketch, we will simply use Linux programming facilities to call the ‘mosquitto_sub’ and ‘mosquitto_pub’ programs. There are several advantages to this approach:

We don’t have to rewrite the MQTT clients for Arduino. The Mosquitto MQTT package is a well- accepted and well-tested piece of software. We only need to create a wrapper class to enable usage within an Arduino sketch.

The Mosquitto MQTT clients are capable of secured connections using SSL. The small microcontroller in a generic Arduino, on the other hand, simply cannot handle the computational demands of SSL.

When a different or better MQTT software package is found or a newer version of the existing MQTT software is released, we can re-use most of the existing code and only add new code to leverage new capability of the new package.

The MQTTClient Class

Since the Intel Edison board is a full-fledged Linux operating system, all of the Linux programming facilities are available to our Arduino sketches. We will create a wrapper class called MQTTClient that will be used in the sketch. Within the MQTTClient methods, we will use Linux system calls to invoke themosquitto_sub and mosquitto_pub program that will perform the actual MQTT data transfer for us. Below is the class definition for a minimalist MQTTClient.

01 // File: MQTTClient.h

02 /*

03 Minimalist MQTT Client using mosquitto_sub and mosquitto_pub

04 Assume Mosquitto MQTT server already installed and mosquitto_pub/sub

05 are in search path

06 */

07
 

08 #ifndef __MQTTClient_H__

09 #define __MQTTClient_H__

10
 

11 #include <Arduino.h>

12 #include <stdio.h>

13
 

14 enum security_mode {OPEN = 0, SSL = 1, PSK = 2};

15
 

16 class MQTTClient {

17
 

18 public:

19 MQTTClient();

20 ~MQTTClient();

21 void    begin(char * broker, int port, security_mode mode,

22 char* certificate_file, char *psk_identity, char *psk);
                  

23 boolean publish(char *topic, char *message);
   

24 boolean subscribe(char* topic, void (*callback)(char *topic, char* message));
    

25 boolean loop();
   

26    boolean available();

27   void    close();
 

28
 

29  private:

30 void           parseDataBuffer();

31  FILE*          spipe;

32 char           mqtt_broker[32];

33  security_mode  mode;
  

34  char           topicString[64];
  

35  char           certificate_file[64];
  

36  char           psk_identity[32];
  

37  char           psk_password[32];
  

38    int            serverPort;

39  char           *topic;
  

40  char           *message;
  

41   boolean         retain_flag;
 

42  void           (*callback_function)(char* topic, char* message);
  

43    char           dataBuffer[256];

44 };

45 #endif

To use the class, one starts with creating an MQTTClient object, then use MQTTClient::begin() to initialize various variables that MQTTClient::subscribe() and MQTTClient::publish() will use to connect with the MQTT broker. These variables are:

  • mqtt_broker: name of the MQTT broker. This can be an IP address or a DNS name. For this article, we’ll use ‘localhost’ as the broker is running on the same Edison board as the Arduino sketch. If the Edison board was configured to use static IP address, this address can be used instead.
  • serverPort: the port to be used. We configured 3 different ports on the Mosquitto broker with different security policy:

1. Port 1883 is the MQTT default port that is open to all clients.
2. Port 1994 is configured as a secured SSL port. User must supply an SSL certificate from the CA that issued the server certificate.
3. Port 1995 is configured as a secured TLS-PSK port. Users access this port with a user identity and a pre-shared password.

  • mode: security mode to use. Currently, security mode can be one of OPEN, SSL or PSK. Depending on the selected security mode, the certificate, user identity and pre-shared password are supplied with the rest of the method’s arguments. Arguments that are not applicable to the selected security mode are ignored.

To subscribe to a topic, one calls MQTTClient::subscribe() with a topic name and a callback function to be  invoked when a new message is available. To publish to a topic, one calls MQTTClient::publish() and supply a topic name and message to be publish.
For both subscribe and publish method, the appropriate command string to invoke either mosquitto_sub ormosquitto_pub is formed using the method’s arguments and stored parameters. A Linux pipe is then opened and the command string is executed with results piped back to the Arduino sketch. When publishing, the pipe is immediately closed after the message was sent. When subscribing, the pipe need to be kept open so that new data can be received. Within the Arduino sketch, one needs to periodically check the pipe to see if there is any new data. The MQTTClient::loop() method was designed to check for data in the pipe and invoke the callback function to process new messages. Because the Linux pipe will block if the pipe is empty when checked, we’ve made the pipe non-blocking so that the MQTTClient::loop()method will return if the pipe is empty. Pseudo-code below shows typical usage of the MQTTClient class.

01 #include <MQTTClient.h>

02 #define SAMPLING_PERIOD   100

03 #define PUBLISH_INTERVAL  30000

04
 

05 MQTTClient mqttClient;

06
 

07 void setup() {

08  mqttClient.begin(“localhost”,1833,PSK,NULL,”psk_user”,”psk_password”);

09  mqttClient.subscribe(“edison/#”,myCallBack);

10 }

11
 

12 void myCallBack(char* topic, char* message) {

13  // scan for matching topic, scan for matching command, do something useful

14 }

15
 

16 unsigned long publishTime = millis();

17 void loop() {

18 mqttClient.loop();        // check for new message

19  if (millis() > publishTime) {
 

20  publishTime = millis() + PUBLISH_INTERVAL;
     

21  mqttClient.publish(“edison/sensor1”,”sensor1value”);
     

 

 

22 mqttClient.publish(“edison/sensor2”,”sensor2value”);

     

23   }

24 delay(SAMPLING_PERIOD);

25 }

The variable “SAMPLING_PERIOD” determines the frequency the code will check for new messages and should be chosen such that a new message that will cause some action from the sketch will not arrive too late for meaningful actions. Most of the time, MQTTClient::loop() method will return quickly and there is minimal overhead in sampling the loop frequently. The publication interval is determined by the variable“PUBLISH_INTERVAL” and should be chosen at the appropriate data rate for the given sensor e.g. a temperature sensor may publish its value once a minute if it is merely for informational purpose while a smoke sensor may want to update its result every few seconds so that a subscriber listening to the smoke sensor messages will have a chance to act before it is too late.
An implementation of the MQTTClient class is shown below.
001 // File: MQTTClient.cpp

002 #include "MQTTClient.h"

003 #include <fcntl.h>

004
 

005 /*======================================================================

006 Constructor/Destructor

007 ========================================================================*/

008 MQTTClient::MQTTClient()

009 {

010 }

011 MQTTClient::~MQTTClient()

012 {

013   close();

014 }

015 void MQTTClient::close()

016 {

017   if (spipe) {

018  fclose(spipe);

019 }

020 }

021 /*========================================================================

022  Initialization. Store variables to be used for subscribe/publish calls

023 ==========================================================================*/

024 void MQTTClient::begin(char *broker, int port, security_mode smode,

025 char* cafile, char *user, char *psk)
                      

026 {

027   strcpy(mqtt_broker, broker);

028  serverPort = port;

029  mode = smode;

030  if (mode == SSL) {

031   strcpy(certificate_file, cafile);

032  }

033  else if (mode == PSK) {

034   strcpy(psk_identity, user);

035    strcpy(psk_password, psk);

036  }

037  Serial.println("MQTTClient initialized");

038  Serial.print("Broker: "); Serial.println(mqtt_broker);

039  Serial.print("Port:   "); Serial.println(serverPort);

040
  Serial.print("Mode:   "); Serial.println(mode);

041 }

042 /*=======================================================================

043   Subscribe to a topic, (*callback) is a function to be called when client

044   receive a message

045 =========================================================================*/

046 boolean MQTTClient::subscribe(char* topic,

047  void (*callback)(char* topic, char* message))
                             

048 {

049   char cmdString[256];

050
   

051   if (mqtt_broker == NULL) {

052     return false;

053   }

054   if (topic == NULL) {

055   return false;

056   }

057
   

058  callback_function = callback;

059  switch(mode) {

060    case OPEN:

061   sprintf(cmdString,

062    "mosquitto_sub -h %s -p %d -t %s -v",
          

063   mqtt_broker, serverPort, topic);
           

064   break;
   

065    case SSL:

066    sprintf(cmdString,
  

067   "mosquitto_sub -h %s -p %d -t %s -v –cafile %s",
           

068   mqtt_broker, serverPort, topic, certificate_file);
            

069   break;
   

070 case PSK:
   

071  sprintf(cmdString,
    

072   "mosquitto_sub -h %s -p %d -t %s -v –psk-identity %s –psk %s",
           

073  mqtt_broker, serverPort, topic, psk_identity, psk_password);
            

074 break;
     

075  default:
  

076  break;
    

077  }

078  if ((spipe = (FILE*)popen(cmdString, "r")) != NULL) {

079   // we need to set the pipe read mode to non-blocking
 

080    int fd    = fileno(spipe);

081  int flags = fcntl(fd, F_GETFL, 0);
  

082   flags |= O_NONBLOCK;
 

083  fcntl(fd, F_SETFL, flags);
  

084    strcpy(topicString, topic);

085  return true;
  

086  }

087  else {

088   return false;
 

089  }

090 }

091 /*====================================================================

092 Check if there is data in the pipe,

093 if true, parse topic and message and execute callback function

094  return if pipe is empty

095 ======================================================================*/

096 boolean MQTTClient::loop()

097 {

098   if (fgets(dataBuffer, sizeof(dataBuffer), spipe)) {   

099   parseDataBuffer();   

100  callback_function(topic, message);

101   }

102 }

103 /*====================================================================

104  Publish a message on the given topic

105 ======================================================================*/

106 boolean MQTTClient::publish(char *topic, char *message)

107 {

108  FILE*   ppipe;

109  char    cmdString[256];

110  boolean retval = false;

111  if (this->mqtt_broker == NULL) {

112    return false;

113  }

114  if (topic == NULL) {

115    return false;

116  }

117  switch (this->mode) {

118    case OPEN:

119    sprintf(cmdString,

120     "mosquitto_pub -h %s -p %d -t %s -m \"%s\" %s",
         

121   mqtt_broker, serverPort, topic, message, retain_flag?"-r":"");
           

122   break;
   

123  case SSL:
  

124  sprintf(cmdString,
    

125  "mosquitto_pub -h %s -p %d –cafile %s -t %s -m \"%s\" %s",
            

126    mqtt_broker, serverPort, certificate_file,
           

127     topic, message, retain_flag?"-r":"");
          

128    break;
  

129   case PSK:
 

130    sprintf(cmdString,
  

131   "mosquitto_pub -h %s -p %d –psk-identity %s –psk %s -t %s -m \"%s\" %s",
       

132   mqtt_broker, serverPort, psk_identity, psk_password,
           

133    topic, message, retain_flag?"-r":"");
          

134  break;
    

135   }

136  if (!(ppipe = (FILE *)popen(cmdString, "w"))) {

137   retval = false;
 

138  }

139  if (fputs(cmdString, ppipe) != EOF) {

140   retval = true;
 

141  }

142  else {

143  retval = false;
  

144  }

145  fclose(ppipe);

146  return retval;

147}

148 /*======================================================================

149 Parse data in the data buffer to topic and message buffer

150 delimiter is the first space

151  if there is only one recognizable string, it is assumed a message

152  string and topic is set to NULL

153 ========================================================================*/

154 void MQTTClient::parseDataBuffer()

155 {

156  topic   = dataBuffer;

157   message = dataBuffer;

158   while((*message) != 0) {

159   if ((*message) == 0x20) {

160   // replace the first space with the NULL character

161    (*message) = 0;

162    message++;
  

163    break;
  

164    }

165   else {
 

166     message++;
 

167    }

168  }

169  if (strlen(message) == 0) {

170   topic   = NULL;
 

171    message = dataBuffer;

172  } 

173 }

The Sensor Node

In the blog posting “Using Intel Edison: Building an IoT Sensor Node”, we built a sensor node with a few sensors and actuators:
1. A motion sensor to detect motion
2. A temperature sensor to measure ambient temperature
3. A light sensor to measure ambient light level
4. A push button to detect a user action i.e. user pushing the button
5. A red LED to toggle when a button push is sensed
6. A green LED to light up when the motion sensor detects motion

Using MQTT, we will periodically publish various sensor readings to the Mosquitto broker running on the same Edison board and subscribe to all topics, ‘edison/#’. We made slight changes to the operation of the sensor node:
1. The push button no longer toggle the red LED. It has been repurposed as a signal to toggle a boolean variable that will be transmit to MQTT subscribers.
2. The red LED now can only be controlled through MQTT messages. This can be a proxy for an internet controlled lamp that can be turn on or off remotely.
3. The behavior of the green LED can now be controlled through MQTT. It can be programmed to track the motion sensor activity, or disabled.
The main Arduino sketch is shown below.

001 // File: MQTT_IoT_Sensor.ino

002
 

003 /******************************************************************************

004    Internet of Thing Sensor Node using Intel Edison Development board

005******************************************************************************/

006
 

007 #include <stdio.h>

008
#include <Arduino.h>

009
 

010 #include "sensors.h"

011 #include "MQTTClient.h"

012
 

013 // Global variables

014 unsigned long          updateTime = 0;

015 // PIR variables

016 volatile unsigned long activityMeasure;

017 volatile unsigned long activityStart;

018 volatile boolean       motionLED = true;

019 unsigned long          resetActivityCounterTime;

020
 

021 // button variables

022 boolean                toggleLED = false;

023 volatile unsigned long previousEdgeTime = 0;

024 volatile unsigned long count = 0;

025 volatile boolean       arm_alarm = false;

026
 

027 // MQTT Client

028 #define SECURE_MODE     2

029 MQTTClient              mqttClient;

030 char                    fmtString[256];     // utility string

031 char                    topicString[64];    // topic for publishing

032 char                    msgString[64];      // message

033
 

034 /*****************************************************************************

035   Setup

036 ******************************************************************************/

037 void setup() {

038 Serial.begin(115200);

039  delay(3000);

040   Serial.println("Ready");

041
 

042  pinMode(RED_LED,    OUTPUT);

043   pinMode(GREEN_LED,  OUTPUT);

044   pinMode(BUTTON,     INPUT_PULLUP);

045   pinMode(PIR_SENSOR, INPUT);

046
 

047  // Button interrupt. Trigger interrupt when button is released

048   attachInterrupt(BUTTON,     buttonISR, RISING); 

049
 

050   // PIR interrupt. We want both edges to compute pulse width

051   attachInterrupt(PIR_SENSOR, pirISR,    CHANGE);

052   digitalWrite(RED_LED,  HIGH);

053   digitalWrite(GREEN_LED,LOW);

054   resetActivityCounterTime = 0;

055
  

056   // initializing MQTTClient

057 #if ( SECURE_MODE == 0 )

058   Serial.println("No security");

059   mqttClient.begin("localhost", 1883, OPEN, NULL, NULL, NULL);

060 #elif ( SECURE_MODE == 1 )

061   Serial.println("SSL security");

062   mqttClient.begin("localhost", 1994, SSL,

063    "/home/mosquitto/certs/ca.crt", NULL, NULL);
               

064 #elif ( SECURE_MODE == 2 )

065   Serial.println("TLS-PSK security");

066   mqttClient.begin("localhost", 1995, PSK, NULL, "user", "deadbeef");

067 #endif

068
 

069   // subscribe to all topics published under edison

070   mqttClient.subscribe("edison/#", mqttCallback);

071   mqttClient.publish("edison/bootMsg","Booted");

072  digitalWrite(RED_LED, LOW);

073 }

074 /**************************************************************************

075   MQTT Callback function

076 **************************************************************************/

077 void mqttCallback(char* topic, char* message)

078 {

079   sprintf(fmtString, "mqttCallback(), topic: %s, message: %s",topic,message);

080   Serial.print(fmtString);

081   // check for matching topic first

082   if (strcmp(topic,"edison/LED") == 0) {

083     // then execute command as appropriate

084     if (message[0] == 'H') {

085     digitalWrite(RED_LED, HIGH);

086    toggleLED = false;

087   }

088  else if (message[0] == 'L') {

089   digitalWrite(RED_LED, LOW);

090   toggleLED = false;

091   }

092  else if (message[0] == 'B') {

093  toggleLED = true;

094   }

095  }

096  if (strcmp(topic, "edison/motionLED") == 0) {

097  // note that there is an extra carriage return at the end of the message

098   // using strncmp to exclude the last carriage return

099  if (strncmp(message, "OFF", 3) == 0) {

100   digitalWrite(GREEN_LED, LOW);

101     motionLED = false;

102    }

103  else if (strncmp(message, "ON", 2) == 0) {

104    motionLED = true;

105   }

106  }

107 }

108 /***********************************************************************

109  Main processing loop

110 ***********************************************************************/

111 void loop() {

112
   

113    // check for any new message from mqtt_sub

114    mqttClient.loop();

115
     

116    if (millis() > resetActivityCounterTime) {

117    resetActivityCounterTime = millis() + 60000;

118     // publish motion level

119
      sprintf(msgString,"%.0f",100.0*activityMeasure/60000.0);

120    mqttClient.publish("edison/ActivityLevel",msgString);

121  activityMeasure = 0;

122   }

123   if (millis() > updateTime) {

124   updateTime = millis() + 10000;   

125    // publish temperature

126    sprintf(msgString,"%.1f",readTemperature(TEMP_SENSOR));

127   mqttClient.publish("edison/Temperature",msgString);

128
       

129    // publish light sensor reading

130  sprintf(msgString,"%d",readLightSensor(LIGHT_SENSOR));

131  mqttClient.publish("edison/LightSensor",msgString);

132
       

133  // publish arm_alarm
    

134    sprintf(msgString,"%s", (arm_alarm == true)? "ARMED"  : "NOTARMED");
  

135   mqttClient.publish("edison/alarm_status", msgString);
   

136    }

137
     

138  if (toggleLED == true) {
  

139   digitalWrite(RED_LED, digitalRead(RED_LED) ^ 1);
   

140    }

141  delay(100);
  

142 }

Much of the code is self-explanatory. We have moved the sensors processing code to separate modules: sensors.h and sensors.cpp to improve readability. We allow for different security modes to be used by setting the SECURE_MODE variable in the main Arduino sketch. The callback function, mqttCallback(), is called whenever there is a new message from the MQTT broker. Subscribed MQTT topic name and message are passed to the callback function where they are scanned and acted upon if any of them match a set of pre-defined action. The callback function is the main mechanism to control the IOT sensor from the internet. In this case, messages published to topic ‘edison/LED’ is used to turn the red LED on, off, or blink and messages published to topic ‘edison/motionLED’ is used to control the behavior of the green LED to either track the motion sensor output or not.
01 // File: sensors.h

02 //

03 #define USE_TMP036     0

04
 

05 #define RED_LED       10            // Red LED

06 #define GREEN_LED     11            // Green LED

07 #define BUTTON        13            // push button with 10K pull up resistor

08 #define PIR_SENSOR    12            // Infrared motion sensor

09 #define LIGHT_SENSOR  A0            // light sensor

10 #define TEMP_SENSOR   A1            // TMP36 or LM35 analog temperature sensor

11
 

12 #define MIN_PULSE_SEPARATION     200   // for debouncing button

13 #define ADC_STEPSIZE             4.61  // in mV, for temperature conversion.

14
 

15 #if (USE_TMP036 == 1)

16 #define TEMP_SENSOR_OFFSET_VOLTAGE       750

17 #define TEMP_SENSOR_OFFSET_TEMPERATURE   25

18 #else // LM35 temperature sensor

19 #define TEMP_SENSOR_OFFSET_VOLTAGE        0

20 #define TEMP_SENSOR_OFFSET_TEMPERATURE    0

21 #endif

22
 

23 // Global variables

24 extern unsigned long          updateTime;

25 // PIR variables

26 extern volatile unsigned long activityMeasure;

27 extern volatile unsigned long activityStart;

28 extern volatile boolean       motionLED;

29 extern unsigned long          resetActivityCounterTime;

30
 

31 // button variables

32 extern boolean                toggleLED;

33 extern volatile unsigned long previousEdgeTime;

34 extern volatile unsigned long count;

35 extern volatile boolean       arm_alarm;

36 float readTemperature(int analogPin);

37 int   readLightSensor(int analogPin);

38 void  buttonISR();

39 void  pirISR();

01 // File: sensors.cpp

02 #include <Arduino.h>

03 #include "sensors.h"

04 /***********************************************************************************

05  PIR Sensor

06  Each time the sensor detect motion, the output pin goes HIGH and stay HIGH as

07  long as there is motion and goes LOW 2 or 3 seconds after motion ceased

08  We will count the duration when the PIR sensor output is HIGH as a measure of

09  Activity. The main loop will report the activity level as percentage of time in the

10  previous time interval that motion was detected

11 ************************************************************************************/

12 void pirISR()

13 {

14   int pirReading;

15 unsigned long timestamp;

16   timestamp = millis();

17   pirReading = digitalRead(PIR_SENSOR);

18  if (pirReading == 1) {

19   // mark the start of the pulse

20    activityStart = timestamp;

21  }

22  else {

23    int pulseWidth = timestamp-activityStart;

24    activityMeasure += pulseWidth;

25   }

26   // light up GREEN LED when motion is detected

27   if (motionLED == true) {

28     digitalWrite(GREEN_LED, pirReading);

29   }

30 }

31 /************************************************************************

32  return light sensor reading

33 ************************************************************************/

34 int readLightSensor(int sensorPin)

35 {

36   return analogRead(sensorPin);

37 }

38 /***********************************************************************

39   return temperature in Fahrenheit degrees

40 ***********************************************************************/

41 float readTemperature(int sensorPin)

42 {

43   int   sensorReading;

44   float temperature;

45   sensorReading = analogRead(sensorPin);

46   // convert to millivolts

47   temperature = sensorReading * ADC_STEPSIZE;

48   // Both LM35 and TMP036 temperature sensor has temperature slope of 10mV

49   // per degrees celsius

50   // LM35 offset voltage is 0 mv, TMP036 offset voltage is 750 mV

51   // LM35 offset temperature is 0 degree C, TMP036 offset temperature is 25 degrees C

52   temperature = (temperature – TEMP_SENSOR_OFFSET_VOLTAGE)/10.0 +

53  TEMP_SENSOR_OFFSET_TEMPERATURE;
               

54   // convert to fahrenheit

55   temperature = temperature * 1.8 + 32.0;       

56   return temperature;

57 }

58 /*************************************************************************

59   Interrupt handler for button press

60 **************************************************************************/

61 void buttonISR()

62 {

63  // if current edge occurs too close to previous edge, we consider that a bounce

64   if ((millis()-previousEdgeTime) >= MIN_PULSE_SEPARATION) {

65     arm_alarm = !arm_alarm;

66    Serial.print("Alarm is: ");

67
    if (arm_alarm == true) {

68    Serial.println("ARMED");

69    }

70   else {

71    Serial.println("NOT ARMED");

72  }

73    count++;

74   Serial.print("Count: "); Serial.println(count);

75  }

76  previousEdgeTime=millis();

77 }

Testing

To test the IoT sensor, we’ll need to use an MQTT client to subscribe to the topics published by the sensor, and another MQTT client to publish topics that the sensor will response to.  We can use ‘SSH’ to log into the Edison board and use the mosquitto_sub/pub command to observe and control the sensor node locally or we can use a different host that also have the Mosquitto package installed.

To test the sensor node:

On the subscribing client, subscribe to all topics
$> mosquitto_sub –h ipaddr –p 1883 –t edison/# -v
Where ipaddr is the IP address of the Edison board. Substitute “localhost” if running on the Edison board itself. We use the open port, 1883, to connect to the broker in this case. We can also use port 1994 with a certificate or port 1995 with PSK user name and password. After successfully subscribe to topic ‘edison/#’, we should see the sensor readings along with any commands issued through the publishing client.

On the publishing client, publish to topic edison/LED to control the red LED or to Edison/motionLED to enable/disable the green LED
$> mosquitto_pub –h ipaddr –p 1883 –t Edison/LED –m {H, L, B}
$> mosquitto_pub –h ipaddr –p 1883 –t Edison/motionLED –m {ON, OFF}
The red LED should turn ON, OFF or blink when each of the command above was published.
To stop the green LED from tracking the motion sensor, publish to topic ‘edison/motionLED’ with the message ON or OFF.
The attached video shows the screencast of the testing process.

For more such intel IoT resources and tools from Intel, please visit the Intel® Developer Zone

Source: https://software.intel.com/en-us/blogs/2015/04/06/using-edison-securely-connect-iot-sensor-to-the-internet-with-mqtt

Promotion
Digit.in
Logo
Digit.in
Logo