SECURELY CONNECT IOT SENSOR TO THE INTERNET WITH MQTT 2

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.

Summary

We’ve shown how to take a fairly complex piece of software such as Mosquitto MQTT and make them available for use in an Arduino sketch by leveraging the Linux programming facility within an Arduino sketch. By doing this, we can take advantage of many existing Arduino hardware sensor libraries developed for Arduino ecosystem. Using the Linux that underpinned all Arduino sketches, we can also take advantage of the open source software packages available for Linux in a fairly simple manner. In the next article, we will add more capability to our sensor node by connecting our sensors to Node-Red and perform some rudimentary analytics on the sensor data and sending appropriate alerts.

References and Additional Information

  1. Building and running Mosquitto MQTT on Intel Edison
  2. Using Intel Edison: Building an IoT Sensor Node
  3. MQTT Wiki Site – http://mqtt.org/wiki
  4. Mosquitto MQTT Broker – http://mosquitto.org
  5. Intel Edison Board Specification – http://www.intel.com/edison

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值