Skip to content

Commit f3ff110

Browse files
authored
Merge pull request #11 from jimbobbennett/master
Adding MQTT support for Azure IoT Central and IoT Hub
2 parents 9af3b03 + 571f496 commit f3ff110

37 files changed

+2745
-439
lines changed

.github/workflows/build.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ jobs:
4545
run: git describe --dirty --always --tags
4646
- name: Check formatting
4747
run: |
48-
black --check --target-version=py35 .
48+
black --check --target-version=py35 --line-length=140 .
4949
- name: PyLint
5050
run: |
51-
pylint $( find . -path './adafruit*.py' )
52-
([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace $( find . -path "./examples/*.py" ))
51+
pylint $( find . -path './adafruit_azureiot/*.py' )
52+
([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace,wrong-import-position $( find . -path "./examples/*.py" ))
5353
- name: Build assets
5454
run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location .
5555
- name: Build docs

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ bundles
88
*.DS_Store
99
.eggs
1010
dist
11-
**/*.egg-info
11+
**/*.egg-info
12+
.vscode/settings.json

.pylintrc

+3-3
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ indent-after-paren=4
217217
indent-string=' '
218218

219219
# Maximum number of characters on a single line.
220-
max-line-length=100
220+
max-line-length=140
221221

222222
# Maximum number of lines in a module
223223
max-module-lines=1000
@@ -395,11 +395,11 @@ valid-metaclass-classmethod-first-arg=mcs
395395
[DESIGN]
396396

397397
# Maximum number of arguments for function / method
398-
max-args=5
398+
max-args=6
399399

400400
# Maximum number of attributes for a class (see R0902).
401401
# max-attributes=7
402-
max-attributes=11
402+
max-attributes=13
403403

404404
# Maximum number of boolean expressions in a if statement
405405
max-bool-expr=5

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2019 Brent Rubell for Adafruit Industries
3+
Copyright (c) 2020 Brent Rubell for Adafruit Industries, Jim Bennett, Elena Horton
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.rst

+187-15
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ Adafruit_CircuitPython_AzureIoT
1313
:target: https://github.com/adafruit/Adafruit_CircuitPython_AzureIoT/actions/
1414
:alt: Build Status
1515

16-
Access to `Microsoft Azure IoT <https://azure.microsoft.com/en-us/overview/iot/>`_ from a CircuitPython device. This library can perform device
17-
messaging services (cloud-to-device, device-to-cloud), device services, and job services.
16+
A CircuitPython device library for `Microsoft Azure IoT Services <https://azure.microsoft.com/overview/iot/?WT.mc_id=AdafruitCircuitPythonAzureIoT-github-jabenn>`_ from a CircuitPython device. This library only supports key-base authentication, it currently doesn't support X.509 certificates.
1817

1918
Installing from PyPI
2019
=====================
@@ -45,49 +44,222 @@ Dependencies
4544
This driver depends on:
4645

4746
* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
47+
* `Adafruit CircuitPython MiniMQTT <https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT>`_
48+
49+
* `CircuitPython Base64 <https://github.com/jimbobbennett/CircuitPython_Base64>`_
50+
* `CircuitPython HMAC <https://github.com/jimbobbennett/CircuitPython_HMAC>`_
51+
* `CircuitPython Parse <https://github.com/jimbobbennett/CircuitPython_Parse>`_
4852

4953
Please ensure all dependencies are available on the CircuitPython filesystem.
5054
This is easily achieved by downloading
51-
`the Adafruit library and driver bundle <https://github.com/adafruit/Adafruit_CircuitPython_Bundle>`_.
55+
`the Adafruit library and driver bundle <https://github.com/adafruit/Adafruit_CircuitPython_Bundle>`_
56+
and
57+
`the CircuitPython community library and driver bundle <https://github.com/adafruit/CircuitPython_Community_Bundle>`_
5258

5359
Usage Example
5460
=============
5561

56-
Create an instance of an Azure IoT Hub (you'll need your SAS Token).
62+
This library supports both `Azure IoT Hub <https://azure.microsoft.com/services/iot-hub/?WT.mc_id=AdafruitCircuitPythonAzureIoT-github-jabenn>`_ and `Azure IoT Central <https://azure.microsoft.com/services/iot-central/?WT.mc_id=AdafruitCircuitPythonAzureIoT-github-jabenn>`__.
63+
64+
To create an Azure IoT Hub instance or an Azure IoT Central app, you will need an Azure subscription. If you don't have an Azure subscription, you can sign up for free:
65+
66+
- If you are a student 18 or over, head to `aka.ms/FreeStudentAzure <https://aka.ms/FreeStudentAzure>`_ and sign up, validating with your student email address. This will give you $100 of Azure credit and free tiers of a load of service, renewable each year you are a student. You will not need a credit card.
67+
68+
- If you are not a student, head to `aka.ms/FreeAz <https://aka.ms/FreeAz>`_ and sign up to get $200 of credit for 30 days, as well as free tiers of a load of services. You will need a credit card for validation only, your card will not be charged.
69+
70+
To use this library, you will need to create an ESP32_SPI WifiManager, connected to WiFi. You will also need to set the current time, as this is used to generate time-based authentication keys. One way to do this is via the `Adafruit CircuitPython NTP <https://github.com/adafruit/Adafruit_CircuitPython_NTP>`_ library with the following code:
71+
72+
.. code-block:: python
73+
74+
ntp = NTP(esp)
75+
76+
# Wait for a valid time to be received
77+
while not ntp.valid_time:
78+
time.sleep(5)
79+
ntp.set_time()
80+
81+
Azure IoT Hub
82+
-------------
83+
84+
To interact with Azure IoT Hub, you will need to create a hub, and a register a device inside that hub. There is a free tier available, and this free tier allows up to 8,000 messages a day, so try not to send messages too often if you are using this tier.
85+
86+
- Open the `Azure Portal <https://aka.ms/AzurePortalHome>`_.
87+
- Follow the instructions in `Microsoft Docs <https://aka.ms/CreateIoTHub>`_ to create an Azure IoT Hub and register a device.
88+
- Copy the devices Primary or secondary connection string, and add this to your ``secrets.py`` file.
89+
90+
You can find the device connection string by selecting the IoT Hub in the `Azure Portal <https://aka.ms/AzurePortalHome>`_, *selecting Explorer -> IoT devices*, then selecting your device.
91+
92+
.. image:: iot-hub-device.png
93+
:alt: Locating the device in the IoT hub blade
94+
95+
*Locating the device in the IoT hub blade*
96+
97+
Then copy either the primary or secondary connection string using the copy button next to the value.
98+
99+
.. image:: iot-hub-device-keys.png
100+
:alt: Copy the primary connection string
101+
102+
*Copy the primary connection string*
103+
104+
**Connect your device to Azure IoT Hub**
105+
106+
.. code-block:: python
107+
108+
from adafruit_azureiot import IoTHubDevice
109+
110+
device = IoTHubDevice(wifi, secrets["device_connection_string"])
111+
device.connect()
112+
113+
Once the device is connected, you will regularly need to run a ``loop`` to poll for messages from the cloud.
114+
115+
.. code-block:: python
116+
117+
while True:
118+
device.loop()
119+
time.sleep(1)
120+
121+
**Send a device to cloud message**
122+
123+
.. code-block:: python
124+
125+
message = {"Temperature": temp}
126+
device.send_device_to_cloud_message(json.dumps(message))
127+
128+
**Receive device to cloud messages**
129+
130+
.. code-block:: python
131+
132+
def cloud_to_device_message_received(body: str, properties: dict):
133+
print("Received message with body", body, "and properties", json.dumps(properties))
134+
135+
# Subscribe to cloud to device messages
136+
device.on_cloud_to_device_message_received = cloud_to_device_message_received
137+
138+
**Receive direct methods**
57139

58140
.. code-block:: python
59141
60-
my_hub = IOT_HUB(wifi, 'Azure_IOT_Hub_Name', 'Azure_IOT_Hub_SAS_Token', 'Azure_Device_Identifier')
142+
def direct_method_invoked(method_name: str, payload) -> IoTResponse:
143+
print("Received direct method", method_name, "with data", str(payload))
144+
# return a status code and message to indicate if the direct method was handled correctly
145+
return IoTResponse(200, "OK")
146+
147+
# Subscribe to direct methods
148+
device.on_direct_method_invoked = direct_method_invoked
149+
150+
**Update reported properties on the device twin**
151+
152+
*This is not supported on Basic tier IoT Hubs, only on the free and standard tiers.*
153+
154+
.. code-block:: python
155+
156+
patch = {"Temperature": temp}
157+
device.update_twin(patch)
158+
159+
**Subscribe to desired property changes on the device twin**
61160

62-
Send a device-to-cloud message
161+
*This is not supported on Basic tier IoT Hubs, only on the free and standard tiers.*
63162

64163
.. code-block:: python
65164
66-
my_hub.send_device_message('Hello Azure IoT!')
165+
def device_twin_desired_updated(desired_property_name: str, desired_property_value, desired_version: int):
166+
print("Property", desired_property_name, "updated to", str(desired_property_value), "version", desired_version)
67167
68-
Enumerate all devices on an Azure IOT Hub
168+
# Subscribe to desired property changes
169+
device.on_device_twin_desired_updated = device_twin_desired_updated
170+
171+
Azure IoT Central
172+
-----------------
173+
174+
To use Azure IoT Central, you will need to create an Azure IoT Central app, create a device template and register a device against the template.
175+
176+
- Head to `Azure IoT Central <https://apps.azureiotcentral.com/?WT.mc_id=AdafruitCircuitPythonAzureIoT-github-jabenn>`__
177+
- Follow the instructions in the `Microsoft Docs <https://docs.microsoft.com/azure/iot-central/core/quick-deploy-iot-central?WT.mc_id=AdafruitCircuitPythonAzureIoT-github-jabenn>`__ to create an application. Every tier is free for up to 2 devices.
178+
- Follow the instructions in the `Microsoft Docs <https://docs.microsoft.com/azure/iot-central/core/quick-create-simulated-device?WT.mc_id=AdafruitCircuitPythonAzureIoT-github-jabenn>`__ to create a device template.
179+
- Create a device based off the template, and select **Connect** to get the device connection details. Store the ID Scope, Device ID and either the Primary or secondary Key in your ``secrets.py`` file.
180+
181+
.. image:: iot-central-connect-button.png
182+
:alt: The IoT Central connect button
183+
184+
*The connect button*
185+
186+
.. image:: iot-central-connect-dialog.png
187+
:alt: The IoT Central connection details dialog
188+
189+
*The connection details dialog*
69190

70191
.. code-block:: python
71192
72-
hub_devices = my_hub.get_devices()
193+
secrets = {
194+
# WiFi settings
195+
"ssid": "",
196+
"password": "",
73197
74-
Get information about the current device on an Azure IoT Hub
198+
# Azure IoT Central settings
199+
"id_scope": "",
200+
"device_id": "",
201+
"key": ""
202+
}
203+
204+
**Connect your device to your Azure IoT Central app**
75205

76206
.. code-block:: python
77207
78-
device_info = my_hub.get_device()
208+
from adafruit_azureiot import IoTCentralDevice
209+
210+
device = IoTCentralDevice(wifi, secrets["id_scope"], secrets["device_id"], secrets["key"])
211+
device.connect()
79212
80-
Get information about the current device's device twin
213+
Once the device is connected, you will regularly need to run a ``loop`` to poll for messages from the cloud.
81214

82215
.. code-block:: python
83216
84-
twin_info = my_hub.get_device_twin()
217+
while True:
218+
device.loop()
219+
time.sleep(1)
85220
86-
Update the current device's device twin properties
221+
**Send telemetry**
87222

88223
.. code-block:: python
89224
90-
my_hub.update_device_twin(device_properties)
225+
message = {"Temperature": temp}
226+
device.send_telemetry(json.dumps(message))
227+
228+
**Listen for commands**
229+
230+
.. code-block:: python
231+
232+
def command_executed(command_name: str, payload) -> IoTResponse:
233+
print("Command", command_name, "executed with payload", str(payload))
234+
# return a status code and message to indicate if the command was handled correctly
235+
return IoTResponse(200, "OK")
236+
237+
# Subscribe to commands
238+
device.on_command_executed = command_executed
239+
240+
**Update properties**
241+
242+
.. code-block:: python
243+
244+
device.send_property("Desired_Temperature", temp)
245+
246+
**Listen for property updates**
247+
248+
.. code-block:: python
249+
250+
def property_changed(property_name, property_value, version):
251+
print("Property", property_name, "updated to", str(property_value), "version", str(version))
252+
253+
# Subscribe to property updates
254+
device.on_property_changed = property_changed
255+
256+
Learning more about Azure IoT services
257+
--------------------------------------
258+
259+
If you want to learn more about setting up or using Azure IoT Services, check out the following resources:
260+
261+
- `Azure IoT documentation on Microsoft Docs <https://docs.microsoft.com/azure/iot-fundamentals/?WT.mc_id=AdafruitCircuitPythonAzureIoT-github-jabenn>`_
262+
- `IoT learning paths and modules on Microsoft Learn <https://docs.microsoft.com/learn/browse/?term=iot&WT.mc_id=AdafruitCircuitPythonAzureIoT-github-jabenn>`_ - Free, online, self-guided hands on learning with Azure IoT services
91263

92264
Contributing
93265
============

0 commit comments

Comments
 (0)