RaspberryPi on AWS IoT – MQTT simple PubSub Example

Simple RaspberryPi B+ with BMP180 and LED on GPIO22 for demonstration of AWS/IOT with MQTT.  The following code was modified from the Connecting your RaspberryPi to AWS IoT tutorial.

'''
/*
 * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
 '''

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import logging
import time
import argparse
import json

#import for GPIO Usage on RaspberryPi
import RPi.GPIO as GPIO
#Pins for LED Example
GPIO.setmode(GPIO.BCM)
GPIO.setup(22,GPIO.OUT)

# Import / Setup  BMP Sensor 
import Adafruit_BMP.BMP085 as BMP085
sensor = BMP085.BMP085()

AllowedActions = ['both', 'publish', 'subscribe']

# Custom MQTT message callback 
# Added Temp info from BMP Sensor and logic to turn on/off led
# when temp above 20.2C 
def customCallback(client, userdata, message):
    print("Received a new message: ")
    print(message.payload)
    Mytemp = json.loads(message.payload)
    print("MY TEMP IN THE OFFICE: ")
    print (Mytemp['Temp'])
    if (Mytemp['Temp'] > 20.2):
        GPIO.output(22,1)
    else:
        GPIO.output(22,0)
        
    print("from topic: ")
    print(message.topic)
    print("--------------\n\n")

# Read in command-line parameters
parser = argparse.ArgumentParser()
parser.add_argument("-e", "--endpoint", action="store", required=True, dest="host", help="Your AWS IoT custom endpoint")
parser.add_argument("-r", "--rootCA", action="store", required=True, dest="rootCAPath", help="Root CA file path")
parser.add_argument("-c", "--cert", action="store", dest="certificatePath", help="Certificate file path")
parser.add_argument("-k", "--key", action="store", dest="privateKeyPath", help="Private key file path")
parser.add_argument("-p", "--port", action="store", dest="port", type=int, help="Port number override")
parser.add_argument("-w", "--websocket", action="store_true", dest="useWebsocket", default=False,
                    help="Use MQTT over WebSocket")
parser.add_argument("-id", "--clientId", action="store", dest="clientId", default="basicPubSub",
                    help="Targeted client id")
parser.add_argument("-t", "--topic", action="store", dest="topic", default="sdk/test/Python", help="Targeted topic")
parser.add_argument("-m", "--mode", action="store", dest="mode", default="both",
                    help="Operation modes: %s"%str(AllowedActions))
parser.add_argument("-M", "--message", action="store", dest="message", default="Hello World!",
                    help="Message to publish")

args = parser.parse_args()
host = args.host
rootCAPath = args.rootCAPath
certificatePath = args.certificatePath
privateKeyPath = args.privateKeyPath
port = args.port
useWebsocket = args.useWebsocket
clientId = args.clientId
topic = args.topic

if args.mode not in AllowedActions:
    parser.error("Unknown --mode option %s. Must be one of %s" % (args.mode, str(AllowedActions)))
    exit(2)

if args.useWebsocket and args.certificatePath and args.privateKeyPath:
    parser.error("X.509 cert authentication and WebSocket are mutual exclusive. Please pick one.")
    exit(2)

if not args.useWebsocket and (not args.certificatePath or not args.privateKeyPath):
    parser.error("Missing credentials for authentication.")
    exit(2)

# Port defaults
if args.useWebsocket and not args.port:  # When no port override for WebSocket, default to 443
    port = 443
if not args.useWebsocket and not args.port:  # When no port override for non-WebSocket, default to 8883
    port = 8883

# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)

# Init AWSIoTMQTTClient
myAWSIoTMQTTClient = None
if useWebsocket:
    myAWSIoTMQTTClient = AWSIoTMQTTClient(clientId, useWebsocket=True)
    myAWSIoTMQTTClient.configureEndpoint(host, port)
    myAWSIoTMQTTClient.configureCredentials(rootCAPath)
else:
    myAWSIoTMQTTClient = AWSIoTMQTTClient(clientId)
    myAWSIoTMQTTClient.configureEndpoint(host, port)
    myAWSIoTMQTTClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)

# AWSIoTMQTTClient connection configuration
myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1)  # Infinite offline Publish queueing
myAWSIoTMQTTClient.configureDrainingFrequency(2)  # Draining: 2 Hz
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10)  # 10 sec
myAWSIoTMQTTClient.configureMQTTOperationTimeout(5)  # 5 sec

# Connect and subscribe to AWS IoT
myAWSIoTMQTTClient.connect()
if args.mode == 'both' or args.mode == 'subscribe':
    myAWSIoTMQTTClient.subscribe(topic, 1, customCallback)
time.sleep(2)

# Publish to the same topic in a loop forever
loopCount = 10
while True:
    
    Temp = sensor.read_temperature()
    print ("TEMP: " + str(Temp))
    if args.mode == 'both' or args.mode == 'publish':
        message = {}
        message['Temp'] = Temp
        message['sequence'] = loopCount
        messageJson = json.dumps(message)
        myAWSIoTMQTTClient.publish(topic, messageJson, 1)
        if args.mode == 'publish':
            print('Published topic %s: %s\n' % (topic, messageJson))
        loopCount += 1
    time.sleep(2) 

 

python basicPubSub.py -e YOURAWSIOTSHADOW.us-east-1.amazonaws.com -r root-CA.crt -c MyRasp.cert.pem -k MyRasp.private.key

I am still learning the AWS IoT basics and have posted this as a reminder to myself as to how it got setup. I planned on refining this into tutorial but really did not see the need as the AWS Samples are pretty good. Connecting your RaspberryPi to AWS IoT  is your best place to start.

Plotly via Python API

Spent some time working with the Plotly Python API.  Able to read my data logger files and upload to a plot with ease. Will need to comment code better I know…just started playing with it and wanted to share. Upload all environmental data and if the additional “Annotation” field exist add that to the plot as well.

More Info at: Plotly Python API

import plotly.plotly as py
from plotly.graph_objs import *

myname = 'plotlyid'
mykey = "plotlykey"

py.sign_in(myname,mykey)

x1 =[]
y1 =[]
y2 =[]
y3 =[]
MyAnnotation = []

f = open("LOGGER26.CSV",'r')
#f = open("smallset.csv",'r')

for lines in f:
	data = lines.split(',')
	x1.append(data[0])
	y1.append(data[1])
	y2.append(data[2])
	y3.append(data[3].strip())

	if len(data)==5:
		print data[4]
		Annotation = {'x':data[0],'y':data[1],'text':data[4] + data[0],'xref':'x','yref':'y','showarrow':True,'arrowhead':7,'bgcolor':'red'}
		MyAnnotation.append(Annotation)

# (2) Make dictionary linking x and y coordinate lists to 'x' and 'y' keys
#     (mandatory in plotly v.1.0.8 and up)
layout = Layout(
	title="MyEnvi",

	annotations=MyAnnotation,

    legend=Legend(
        x=100,
        y=1
    ),
    xaxis=XAxis(
    	domain=[0, 0.8],
        autorange=True,
        showgrid=False,
        zeroline=False,
        showline=True,
        autotick=True,
        ticks='',
        showticklabels=True
    ),
    yaxis=YAxis(
    	title="Light",
    	autorange=True,
        showgrid=False,
        zeroline=False,
        showline=True,
        autotick=True,
        ticks='',
        showticklabels=True
    ),

    yaxis2=YAxis(
        title='Barometric',
        showgrid=False,
        titlefont=Font(
            color='#ff7f0e'
        ),
        tickfont=Font(
            color='#ff7f0e'
        ),
        anchor='x',
        overlaying='y',
        side='right'

    ),

 	yaxis3=YAxis(
 		showgrid=False,
        title='Temp',
        titlefont=Font(
            color='#087804'
        ),
        tickfont=Font(
            color='#087804'
        ),
        anchor='free',
        overlaying='y',
        side='right',
        position = .9
    ),

)
trace1 = dict(x=x1,y=y1, name='Light')
trace2 = dict(x=x1,y=y2, name= 'Barometric',yaxis='y2')
trace3 = dict(x=x1,y=y3, name='Temp',yaxis='y3')
# (3) Make list of 1 trace, to be sent to Plotly
#     (mandatory in 1.0.8 and up)
data = [trace1,trace2,trace3]

fig = Figure(data=data,layout=layout)

plot_url = py.plot(fig, filename='MyEnvi')
f.close()

Short data file snippet:

2014-6-10 19:19:48,508,996.00,24.94
2014-6-10 19:19:49,508,995.95,24.94
2014-6-10 19:19:50,508,995.99,24.94
2014-6-10 19:19:51,508,995.95,24.92
2014-6-10 19:19:53,507,995.93,24.92,Migraine Severe
2014-6-10 19:19:54,507,995.99,24.94
2014-6-10 19:19:55,507,995.96,24.94
2014-6-10 19:19:56,507,996.00,24.92
2014-6-10 19:19:57,507,995.98,24.94
2014-6-10 19:19:58,507,995.97,24.92
2014-6-10 19:19:59,507,995.92,24.92
2014-6-10 19:20:0,507,995.92,24.92

Next steps: Create a way to add the annotation to the data file on the Arduino.

Using Plot.ly with Adafruit Data Logging Shield

I have played with a few IOT type services for showing off data collected and or sent from Arduino (and other micro controller devices) and have settled on Plot.ly for now.

I have used ThingSpeak and Xively as well but found Plotly worked better for importing from the data logger disk than the others. Xively appear to be more “live update” oriented and not as well geared for file upload of large data sets.

 

Plot.ly Graph from Arduino Barometric and Light Project