Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3293541

Browse files
author
Emmanuel Florent
committed
Add Google IoT MQTT Client
1 parent 390fb8f commit 3293541

File tree

8 files changed

+528
-0
lines changed

8 files changed

+528
-0
lines changed

GoogleIOT/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
db/*
2+
*.pem
3+
flash/config.py

GoogleIOT/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<p align="center"><img src ="https://github.com/pycom/pycom-libraries/blob/master/img/logo.png" width="300"></p>
2+
3+
# Google Cloud Iot Core MQTT connection library
4+
5+
### requirement
6+
7+
Pycom Firmware >= 1.20.0.rc11
8+
9+
You will need to setup a Google IoT core registry as described here:
10+
11+
During the activation please collect the following informations: 'project_id',
12+
'cloud_region' and 'registry_id'.
13+
14+
### usage
15+
16+
- create a device registry:
17+
https://cloud.google.com/iot/docs/quickstart#create_a_device_registry
18+
- generate a key using the provided tool genkey.sh and add it to the platform
19+
- add the public key to Google IoT Core :
20+
https://cloud.google.com/iot/docs/quickstart#add_a_device_to_the_registry
21+
- copy config.example.py to config.py and edit the variable
22+
- upload the project using pymakr

GoogleIOT/flash/cert/.gitkeep

Whitespace-only changes.

GoogleIOT/flash/config.example.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
''' Set here you setup config in this example
2+
'''
3+
CONFIG = {
4+
'wifi_ssid': "somewifi",
5+
'wifi_password': 'iforgot',
6+
'ntp_server': 'time.google.com',
7+
'project_id': 'pybytes-101', # replace with your Google project_id
8+
'cloud_region': 'us-central1', # replace
9+
'registry_id': 'goinvent', # replace with your Google registry_id
10+
'topic': '/devices/pysense2/events', # replace so match your device
11+
'device_id': 'pysense2' #
12+
}

GoogleIOT/flash/google_iot_core.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
''' A MQTT wrapper for Google Cloud IoT MQTT bridge
2+
Extended from umqtt.robust by Paul Sokolovsky with wrap of Google credentials
3+
4+
https://github.com/micropython/micropython-lib/tree/master/umqtt.robust
5+
https://github.com/micropython/micropython-lib/tree/master/umqtt.simple
6+
7+
Quick API Reference:
8+
connect(...) - Connect to a server. Returns True if this connection uses
9+
persistent session stored on a server (this will be always
10+
False if clean_session=True argument is used (default)).
11+
disconnect() - Disconnect from a server, release resources.
12+
ping() - Ping server (response is processed automatically by wait_msg()).
13+
publish() - Publish a message.
14+
subscribe() - Subscribe to a topic.
15+
set_callback() - Set callback for received subscription messages.
16+
wait_msg() - Wait for a server message. A subscription message will be
17+
delivered to a callback set with set_callback(), any other
18+
messages will be processed internally.
19+
check_msg() - Check if there's pending message from server. If yes, process
20+
the same way as wait_msg(), if not, return immediately.
21+
'''
22+
23+
import json
24+
from binascii import b2a_base64
25+
from binascii import a2b_base64
26+
import ucrypto
27+
import utime
28+
import umqtt
29+
30+
def _create_unsigned_jwt(project_id, expires=60 * 60 * 24):
31+
header = {
32+
'alg': "RS256",
33+
'typ': 'JWT'
34+
}
35+
token = {
36+
'iat': utime.time(),
37+
'exp': utime.time() + expires,
38+
'aud': project_id
39+
}
40+
return b2a_base64(json.dumps(header)) + "." + \
41+
b2a_base64(json.dumps(token))
42+
43+
def _get_google_client_id(
44+
project_id,
45+
cloud_region,
46+
registry_id,
47+
device_id):
48+
return "projects/%s/locations/%s/registries/%s/devices/%s" % (
49+
project_id, cloud_region, registry_id, device_id)
50+
51+
def _create_google_jwt(project_id, private_key):
52+
to_sign = _create_unsigned_jwt(project_id)
53+
signed = ucrypto.generate_rsa_signature(to_sign, private_key)
54+
return to_sign + b'.' + b2a_base64(signed)
55+
56+
57+
class GoogleMQTTClient(umqtt.MQTTClient):
58+
''' Instanciate a mqtt client
59+
Args:
60+
var_int (int): An integer.
61+
var_str (str): A string.
62+
project_id (str): your google's project_id
63+
private_key (bytes): private key bytes in pk8s format
64+
cloud_region (str): your google's region
65+
registry_id (str): the name you had given to your registry
66+
device_id: (str): the human friendly device name
67+
'''
68+
69+
DELAY = 2
70+
DEBUG = True
71+
GOOGLE_CA = '/flash/cert/google_roots.pem'
72+
GOOGLE_MQTT = 'mqtt.googleapis.com'
73+
74+
def __init__(
75+
self,
76+
project_id,
77+
private_key,
78+
cloud_region,
79+
registry_id,
80+
device_id):
81+
self.private_key = private_key
82+
self.project_id = project_id
83+
self.jwt = _create_google_jwt(self.project_id, self.private_key)
84+
google_client_id = _get_google_client_id(
85+
project_id, cloud_region, registry_id, device_id)
86+
google_args = self._get_google_mqtt_args(self.jwt)
87+
super().__init__(google_client_id, self.GOOGLE_MQTT, **google_args)
88+
89+
def delay(self, i):
90+
utime.sleep(self.DELAY + i)
91+
92+
def log(self, in_reconnect, err):
93+
if self.DEBUG:
94+
if in_reconnect:
95+
print("mqtt reconnect: %r" % err)
96+
else:
97+
print("mqtt: %r" % err)
98+
99+
def reconnect(self):
100+
i = 0
101+
while True:
102+
if not self.is_jwt_valid():
103+
self.pswd = self.jwt = _create_google_jwt(
104+
self.project_id, self.private_key)
105+
try:
106+
return super().connect(False)
107+
except OSError as exception:
108+
self.log(True, exception)
109+
i += 1
110+
self.delay(i)
111+
112+
def publish(self, topic, msg, retain=False, qos=0):
113+
if qos == 2:
114+
raise Exception("qos=2 not supported by mqtt bridge")
115+
116+
while True:
117+
try:
118+
return super().publish(topic, msg, retain, qos)
119+
except OSError as exception:
120+
self.log(False, exception)
121+
self.reconnect()
122+
123+
def wait_msg(self):
124+
while True:
125+
try:
126+
return super().wait_msg()
127+
except OSError as exception:
128+
self.log(False, exception)
129+
self.reconnect()
130+
131+
def _get_google_mqtt_args(self, jwt):
132+
arguments = {
133+
'user': '',
134+
'password': jwt,
135+
'port': 8883,
136+
'ssl': True,
137+
'ssl_params': {
138+
'ca_certs': self.GOOGLE_CA
139+
}
140+
}
141+
return arguments
142+
143+
def is_jwt_valid(self):
144+
try:
145+
token = json.loads(a2b_base64(self.jwt.decode().split('.')[1]))
146+
except Exception:
147+
return False
148+
return utime.time() - token.get('iat') < 60 * 60 * 24
149+
150+
def set_last_will(self, topic, msg, retain=False, qos=0):
151+
raise Exception("set_last_will not supported by mqtt bridge")

GoogleIOT/flash/main.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
''' Example Google IoT Core connection
2+
'''
3+
import utime
4+
import machine
5+
import _thread
6+
from network import WLAN
7+
from google_iot_core import GoogleMQTTClient
8+
from config import CONFIG
9+
10+
# Connect to Wifi
11+
WLAN_I = WLAN(mode=WLAN.STA, max_tx_pwr=78)
12+
print('Connecting to WiFi %s' % CONFIG.get('wifi_ssid'))
13+
WLAN_I.connect(CONFIG.get('wifi_ssid'), (WLAN.WPA2, CONFIG.get('wifi_password')), timeout=60000)
14+
i = 0
15+
while not WLAN_I.isconnected():
16+
i = i + 1
17+
# print(".", end="")
18+
utime.sleep(1)
19+
if i > 60:
20+
print("\nWifi not available")
21+
break
22+
23+
# Syncing time
24+
RTCI = machine.RTC()
25+
print('Syncing time with %s' % CONFIG.get('ntp_server'), end='')
26+
RTCI.ntp_sync(CONFIG.get('ntp_server'))
27+
while not RTCI.synced():
28+
print('.', end='')
29+
utime.sleep(1)
30+
print('')
31+
32+
# read the private key
33+
FILE_HANDLE = open("cert/%s-pk8.key" % CONFIG.get('device_id'))
34+
PRIVATE_KEY = FILE_HANDLE.read()
35+
FILE_HANDLE.close()
36+
37+
# make a mqtt client, connect and publish an empty message
38+
MQTT_CLIENT = GoogleMQTTClient(CONFIG.get('project_id'),
39+
PRIVATE_KEY,
40+
CONFIG.get('cloud_region'),
41+
CONFIG.get('registry_id'),
42+
CONFIG.get('device_id'))
43+
44+
45+
MQTT_CLIENT.connect()
46+
MQTT_CLIENT.publish(CONFIG.get('topic'), b'test')
47+
48+
# make a demo callback
49+
def _sub_cb(topic, msg):
50+
''' handle your message received here ...
51+
'''
52+
print('received:', topic, msg)
53+
54+
# register callback
55+
MQTT_CLIENT.set_callback(_sub_cb)
56+
57+
# example subscription
58+
MQTT_CLIENT.subscribe('/devices/%s/config' % CONFIG.get('device_id'), qos=1)
59+
while True:
60+
# Non-blocking wait for message
61+
MQTT_CLIENT.check_msg()
62+
# Then need to sleep to avoid 100% CPU usage (in a real
63+
# app other useful actions would be performed instead)
64+
utime.sleep_ms(100)

0 commit comments

Comments
 (0)