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:
- A motion sensor to detect motion
- A temperature sensor to measure ambient temperature
- A light sensor to measure ambient light level
- A push button to detect a user action i.e. user pushing the button
- A red LED to toggle when a button push is sensed
- A green LED to light up when the motion sensor detects motion
![Edison MQTT Sensor Node](https://software.intel.com/sites/default/files/managed/da/e3/mqtt_sensor_node.jpg)
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:
- 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.
- 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.
- 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.
011 | #include "MQTTClient.h" |
014 | unsigned long updateTime = 0; |
016 | volatile unsigned long activityMeasure; |
017 | volatile unsigned long activityStart; |
018 | volatile boolean motionLED = true ; |
019 | unsigned long resetActivityCounterTime; |
022 | boolean toggleLED = false ; |
023 | volatile unsigned long previousEdgeTime = 0; |
024 | volatile unsigned long count = 0; |
025 | volatile boolean arm_alarm = false ; |
028 | #define SECURE_MODE 2 |
029 | MQTTClient mqttClient; |
031 | char topicString[64]; |
038 | Serial.begin(115200); |
040 | Serial.println( "Ready" ); |
042 | pinMode(RED_LED, OUTPUT); |
043 | pinMode(GREEN_LED, OUTPUT); |
044 | pinMode(BUTTON, INPUT_PULLUP); |
045 | pinMode(PIR_SENSOR, INPUT); |
048 | attachInterrupt(BUTTON, buttonISR, RISING); |
051 | attachInterrupt(PIR_SENSOR, pirISR, CHANGE); |
052 | digitalWrite(RED_LED, HIGH); |
053 | digitalWrite(GREEN_LED,LOW); |
054 | resetActivityCounterTime = 0; |
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" ); |
070 | mqttClient.subscribe( "edison/#" , mqttCallback); |
071 | mqttClient.publish( "edison/bootMsg" , "Booted" ); |
072 | digitalWrite(RED_LED, LOW); |
077 | void mqttCallback( char * topic, char * message) |
079 | sprintf (fmtString, "mqttCallback(), topic: %s, message: %s" ,topic,message); |
080 | Serial.print(fmtString); |
082 | if ( strcmp (topic, "edison/LED" ) == 0) { |
084 | if (message[0] == 'H' ) { |
085 | digitalWrite(RED_LED, HIGH); |
088 | else if (message[0] == 'L' ) { |
089 | digitalWrite(RED_LED, LOW); |
092 | else if (message[0] == 'B' ) { |
096 | if ( strcmp (topic, "edison/motionLED" ) == 0) { |
099 | if ( strncmp (message, "OFF" , 3) == 0) { |
100 | digitalWrite(GREEN_LED, LOW); |
103 | else if ( strncmp (message, "ON" , 2) == 0) { |
116 | if (millis() > resetActivityCounterTime) { |
117 | resetActivityCounterTime = millis() + 60000; |
119 | sprintf (msgString, "%.0f" ,100.0*activityMeasure/60000.0); |
120 | mqttClient.publish( "edison/ActivityLevel" ,msgString); |
123 | if (millis() > updateTime) { |
124 | updateTime = millis() + 10000; |
126 | sprintf (msgString, "%.1f" ,readTemperature(TEMP_SENSOR)); |
127 | mqttClient.publish( "edison/Temperature" ,msgString); |
130 | sprintf (msgString, "%d" ,readLightSensor(LIGHT_SENSOR)); |
131 | mqttClient.publish( "edison/LightSensor" ,msgString); |
134 | sprintf (msgString, "%s" , (arm_alarm == true )? "ARMED" : "NOTARMED" ); |
135 | mqttClient.publish( "edison/alarm_status" , msgString); |
138 | if (toggleLED == true ) { |
139 | digitalWrite(RED_LED, digitalRead(RED_LED) ^ 1); |
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.
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 |
12 | #define MIN_PULSE_SEPARATION 200 // for debouncing button |
13 | #define ADC_STEPSIZE 4.61 // in mV, for temperature conversion. |
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 |
24 | extern unsigned long updateTime; |
26 | extern volatile unsigned long activityMeasure; |
27 | extern volatile unsigned long activityStart; |
28 | extern volatile boolean motionLED; |
29 | extern unsigned long resetActivityCounterTime; |
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); |
15 | unsigned long timestamp; |
17 | pirReading = digitalRead(PIR_SENSOR); |
18 | if (pirReading == 1) { |
20 | activityStart = timestamp; |
23 | int pulseWidth = timestamp-activityStart; |
24 | activityMeasure += pulseWidth; |
27 | if (motionLED == true ) { |
28 | digitalWrite(GREEN_LED, pirReading); |
34 | int readLightSensor( int sensorPin) |
36 | return analogRead(sensorPin); |
41 | float readTemperature( int sensorPin) |
45 | sensorReading = analogRead(sensorPin); |
47 | temperature = sensorReading * ADC_STEPSIZE; |
52 | temperature = (temperature - TEMP_SENSOR_OFFSET_VOLTAGE)/10.0 + |
53 | TEMP_SENSOR_OFFSET_TEMPERATURE; |
55 | temperature = temperature * 1.8 + 32.0; |
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" ); |
71 | Serial.println( "NOT ARMED" ); |
74 | Serial.print( "Count: " ); Serial.println(count); |
76 | previousEdgeTime=millis(); |
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
- Building and running Mosquitto MQTT on Intel Edison
- Using Intel Edison: Building an IoT Sensor Node
- MQTT Wiki Site – http://mqtt.org/wiki
- Mosquitto MQTT Broker – http://mosquitto.org
- Intel Edison Board Specification – http://www.intel.com/edison