#!/usr/bin/python # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License 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. import argparse import datetime import json import time import ssl import jwt import paho.mqtt.client as mqtt from Adafruit_BME280 import * import RPi.GPIO as GPIO FAN_GPIO = 21 sensor = BME280(t_mode=BME280_OSAMPLE_8, p_mode=BME280_OSAMPLE_8, h_mode=BME280_OSAMPLE_8) # Update and publish temperature readings at a rate of SENSOR_POLL per second. SENSOR_POLL=5 def create_jwt(project_id, private_key_file, algorithm): """Create a JWT (https://jwt.io) to establish an MQTT connection.""" token = { 'iat': datetime.datetime.utcnow(), 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=60), 'aud': project_id } with open(private_key_file, 'r') as f: private_key = f.read() print 'Creating JWT using {} from private key file {}'.format( algorithm, private_key_file) return jwt.encode(token, private_key, algorithm=algorithm) def error_str(rc): """Convert a Paho error to a human readable string.""" return '{}: {}'.format(rc, mqtt.error_string(rc)) class Device(object): """Represents the state of a single device.""" def __init__(self): self.temperature = 0 self.fan_on = False self.connected = False def update_sensor_data(self): self.temperature = int(sensor.read_temperature()) def wait_for_connection(self, timeout): """Wait for the device to become connected.""" total_time = 0 while not self.connected and total_time < timeout: time.sleep(1) total_time += 1 if not self.connected: raise RuntimeError('Could not connect to MQTT bridge.') def on_connect(self, unused_client, unused_userdata, unused_flags, rc): """Callback for when a device connects.""" print 'Connection Result:', error_str(rc) self.connected = True def on_disconnect(self, unused_client, unused_userdata, rc): """Callback for when a device disconnects.""" print 'Disconnected:', error_str(rc) self.connected = False def on_publish(self, unused_client, unused_userdata, unused_mid): """Callback when the device receives a PUBACK from the MQTT bridge.""" print 'Published message acked.' def on_subscribe(self, unused_client, unused_userdata, unused_mid, granted_qos): """Callback when the device receives a SUBACK from the MQTT bridge.""" print 'Subscribed: ', granted_qos if granted_qos[0] == 128: print 'Subscription failed.' def on_message(self, unused_client, unused_userdata, message): """Callback when the device receives a message on a subscription.""" payload = message.payload print "Received message '{}' on topic '{}' with Qos {}".format( payload, message.topic, str(message.qos)) # The device will receive its latest config when it subscribes to the config # topic. If there is no configuration for the device, the device will # receive an config with an empty payload. if not payload: return # The config is passed in the payload of the message. In this example, the # server sends a serialized JSON string. data = json.loads(payload) if data['fan_on'] != self.fan_on: # If we're changing the state of the fan, print a message and update our # internal state. self.fan_on = data['fan_on'] if self.fan_on: GPIO.output(FAN_GPIO, GPIO.HIGH) print 'Fan turned on.' else: GPIO.output(FAN_GPIO, GPIO.LOW) print 'Fan turned off.' def parse_command_line_args(): """Parse command line arguments.""" parser = argparse.ArgumentParser( description='Example Google Cloud IoT MQTT device connection code.') parser.add_argument( '--project_id', required=True, help='GCP cloud project name') parser.add_argument( '--registry_id', required=True, help='Cloud IoT registry id') parser.add_argument('--device_id', required=True, help='Cloud IoT device id') parser.add_argument( '--private_key_file', required=True, help='Path to private key file.') parser.add_argument( '--algorithm', choices=('RS256', 'ES256'), required=True, help='Which encryption algorithm to use to generate the JWT.') parser.add_argument( '--cloud_region', default='us-central1', help='GCP cloud region') parser.add_argument( '--ca_certs', default='roots.pem', help='CA root certificate. Get from https://pki.google.com/roots.pem') parser.add_argument( '--num_messages', type=int, default=100, help='Number of messages to publish.') parser.add_argument( '--mqtt_bridge_hostname', default='mqtt.googleapis.com', help='MQTT bridge hostname.') parser.add_argument( '--mqtt_bridge_port', default=8883, help='MQTT bridge port.') return parser.parse_args() def main(): args = parse_command_line_args() # Setup GPIOs for the RasPi3 and cobbler GPIO.setmode(GPIO.BCM) GPIO.setup(FAN_GPIO, GPIO.OUT) # Create our MQTT client and connect to Cloud IoT. client = mqtt.Client( client_id='projects/{}/locations/{}/registries/{}/devices/{}'.format( args.project_id, args.cloud_region, args.registry_id, args.device_id)) client.username_pw_set( username='unused', password=create_jwt(args.project_id, args.private_key_file, args.algorithm)) client.tls_set(ca_certs=args.ca_certs, tls_version=ssl.PROTOCOL_TLSv1_2) device = Device() client.on_connect = device.on_connect client.on_publish = device.on_publish client.on_disconnect = device.on_disconnect client.on_subscribe = device.on_subscribe client.on_message = device.on_message client.connect(args.mqtt_bridge_hostname, args.mqtt_bridge_port) client.loop_start() # This is the topic that the device will publish telemetry events (temperature # data) to. mqtt_telemetry_topic = '/devices/{}/events'.format(args.device_id) # This is the topic that the device will receive configuration updates on. mqtt_config_topic = '/devices/{}/config'.format(args.device_id) # Wait up to 5 seconds for the device to connect. device.wait_for_connection(5) # Subscribe to the config topic. client.subscribe(mqtt_config_topic, qos=1) # Update and publish temperature readings at a rate of one per second. for _ in range(args.num_messages): device.update_sensor_data() # Report the device's temperature to the server, by serializing it as a JSON # string. payload = json.dumps({'temperature': device.temperature}) print 'Publishing payload', payload client.publish(mqtt_telemetry_topic, payload, qos=1) time.sleep(SENSOR_POLL) client.disconnect() client.loop_stop() GPIO.cleanup() print 'Finished loop successfully. Goodbye!' if __name__ == '__main__': main()