Skip to content

Adding MQTT support for Azure IoT Central and IoT Hub #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Apr 30, 2020

Conversation

jimbobbennett
Copy link
Collaborator

@jimbobbennett jimbobbennett commented Apr 12, 2020

An implementation of #7.

Azure has 2 IoT Services:

  • Azure IoT Hub - this provides connectivity between devices and Azure
  • Azure IoT Central - this is an IoT software-as-a-service platform that sits on top of Azure IoT Hub

Currently the code is working based off copying it to a PyBadge with Airlift Featherwing and supports all the device capabilities of IoT Hub and Central:

IoT Hub

  • Device to cloud messages
  • Cloud to device messages
  • Direct Methods
  • Updating twin reported properties
  • Receiving twin desired properties

IoT Central

  • Telemetry
  • Receiving and setting properties
  • Commands

The code structure is:

  • device_registration.py - contains code to register a device with IoT Central and get the underlying IoT Hub connection details
  • iot_mqtt.py - the underlying MQTT communications layer. This talks to IoT Hub and can send device to cloud, receive direct methods, receive cloud to device messages, update the device twin and receive twin updates
  • iothub_device.py - the client for IoT Hub connections. This will connect to a hub via a device connection string, send device to cloud messages, update the twin and has callbacks you can set up for direct methods, cloud to device messages and twin updates
  • iotcentral_device.py - the client for IoT Central connections. This will connect to IoT Central via the id scope, device id and key using the device registration service. It can send telemetry, update properties and has callbacks for commands and property updates

I've removed the HTTP SDK as MQTT provides better capabilities. The HTTP SDK also had both device and client methods on it, the MQTT implementation is device only. If we want to add a client SDK with features such as querying for devices, then that should be a different library, to be in keeping with the official Azure IoT SDKs.

@brentru
Copy link
Member

brentru commented Apr 16, 2020

@jimbobbennett When you're ready for a review, please request me to review it or tag me. Thanks for continuing working on this.

@jimbobbennett
Copy link
Collaborator Author

@jimbobbennett When you're ready for a review, please request me to review it or tag me. Thanks for continuing working on this.

Will do. I just wanted to open it early to get the code reviewed by others, including some kind folks from the Azure IoT SDK team who have given me some pointers.

Appreciate all your help and support with this.

@jimbobbennett jimbobbennett marked this pull request as draft April 16, 2020 21:17
@jimbobbennett jimbobbennett changed the title WIP - Adding MQTT support for Azure IoT Central and IoT Hub Adding MQTT support for Azure IoT Central and IoT Hub Apr 17, 2020
@jimbobbennett jimbobbennett marked this pull request as ready for review April 17, 2020 02:47
@jimbobbennett
Copy link
Collaborator Author

jimbobbennett commented Apr 17, 2020

@brentru - this should be ready for review now. All the code appears to work, and I've tested all the examples with no issues. I don't seem to be able to set a reviewer, hence tagging you in.

@brentru brentru self-requested a review April 17, 2020 14:10
Copy link
Member

@brentru brentru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jimbobbennett This is awesome!!

Requesting some changes. I'll test on a PyPortal Titano after.

@@ -0,0 +1,235 @@
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you include the same header used in __init__.py here? (reference: https://github.com/adafruit/Adafruit_CircuitPython_Wiznet5k/tree/master/adafruit_wiznet5k)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File headers added - can you verify they are ok please.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look good, may want to update the copyright to be current:
# Copyright (c) 2019 Jim Bennett
to
# Copyright (c) 2020 Jim Bennett

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

D'oh! Will fix that....

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

import json
import time
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
from adafruit_esp32spi.adafruit_esp32spi_wifimanager import ESPSPI_WiFiManager
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of importing esp32spi and the esp32spi socket, could we the socket object in instead?

This will allow us to use multiple types of socket objects with this library like wifi, ethernet, and cellular.

We do this here in the latest MiniMQTT: https://github.com/adafruit/Adafruit_CircuitPython_MiniMQTT/blob/master/examples/minimqtt_simpletest_eth.py#L64

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup - I was following the pattern of the existing code, relying on the WiFi manager. Happy to change it to a socket instead!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd be great. Let me know if you have any Q's. I'll test with ESP32 and Ethernet sockets.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use the socket and interface


while True:
# Poll every second for messages from the cloud
device.loop()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to handle network (physical, not mqtt) disconnection from the esp here. ESP32 can be flaky at times.

https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/blob/master/examples/esp32spi_aio_post.py#L66

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added exception catching and reconnection


message_counter = 60

while True:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may want to handle esp32 network and status issues in here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would that be too much for an example? The example is meant to show the simple case of sending telemetry.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would increase the length of the example, however, adding network handling in the user-code is important to make sure new users have a good "first five minutes" and don't hit errors they can't resolve (the ESP32 can be flaky).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In adding this, I'm also going to add reconnect logic. In doing this I hit another bug in MiniMQTT - the reconnect method gets stuck in an infinite loop. Bug here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added reconnection on errors

@@ -1,2 +1,6 @@
Adafruit-Blinka
Adafruit_CircuitPython_ESP32SPI
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before - I'd like to not add a requirement for esp32spi with this library to allow for new transport sockets in the future.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@jimbobbennett
Copy link
Collaborator Author

@jimbobbennett This is awesome!!

Requesting some changes. I'll test on a PyPortal Titano after.

All changes should be in, let me know what you think.

@ladyada
Copy link
Member

ladyada commented Apr 19, 2020

@jimbobbennett this is so awesome, thank you for the PR - we're really psyched to make Azure easier for folks to use. would you be interested in writing a guide/tutorial or publishing a project to show this off?

@jimbobbennett
Copy link
Collaborator Author

@jimbobbennett this is so awesome, thank you for the PR - we're really psyched to make Azure easier for folks to use. would you be interested in writing a guide/tutorial or publishing a project to show this off?

Yes, definitely! I'll be building a few hands on workshops in my own GitHub repo to show this in action, and we are hoping to run some kids events in Microsoft spaces using Adafruit boards later on in the year when things open up and we can get some hardware.

Would it be possible for me to create a guide for the Adafruit Learning System? I'm also building a CircuitPython library for one of our AI services which I'd love to build a guide for once it's complete.

@ladyada
Copy link
Member

ladyada commented Apr 19, 2020

oh this is so cool - yes Anne B (@TheKitty) will be happy to help you get set up on Learn to author a guide. You can model the guide off of @brentru 's Azure starter guide (you can also use it as a prereq so there's less duplication)

@brentru
Copy link
Member

brentru commented Apr 22, 2020

@jimbobbennett What does your secrets.py file look like for the simpletest? (Please strip out all tokens/private information before posting)

@jimbobbennett
Copy link
Collaborator Author

Here you go:

Wifi details are self explanatory.

For the IoT Central connection details, create a device in IoT Central and grab the details from the Connect dialog.

For the IoT Hub device connection string, create an IoT Hub, create a device in that hub and grab one of the connection strings for the device.

# This file is where you keep secret settings, passwords, and tokens!
# If you put them in the code you risk committing that info or sharing it
# which would be not great. So, instead, keep it all in this one file and
# keep it a secret.

"""
Contains the secrets for your app including WiFi connection details.
DO NOT CHECK THIS INTO SOURCE CODE CONTROL!!!!11!!!
"""

secrets = {
    # WiFi connection details
    "ssid": "",
    "password": "",

    # IoT Central device connection details
    "id_scope": "",
    "device_id": "",
    "key": "",

    # IoT Hub device connection string
    "device_connection_string": "",
}

@jimbobbennett
Copy link
Collaborator Author

The last commit added one small update to the reconnect logic to replace the workarounds for two MiniMQTT bugs.

@jpecor
Copy link

jpecor commented Apr 23, 2020

@jimbobbennett - thanks for all the work on this! @brentru pointed me to it earlier today in response to some questions I had about the AzureIoT library.

I was able to get the iothub_messages.py example to work, and I'm seeing data in the Azure cloud shell.

I did have to install several additional libraries from your GitHub repos for it to work:

  • circuitpython_base64
  • circuitpython_hmac
  • circuitpython_parse

Will these eventually get folded into the final library in some way?

@jimbobbennett
Copy link
Collaborator Author

@jimbobbennett - thanks for all the work on this! @brentru pointed me to it earlier today in response to some questions I had about the AzureIoT library.

I was able to get the iothub_messages.py example to work, and I'm seeing data in the Azure cloud shell.

I did have to install several additional libraries from your GitHub repos for it to work:

  • circuitpython_base64
  • circuitpython_hmac
  • circuitpython_parse

Will these eventually get folded into the final library in some way?

These are available in the CircuitPython community bundle. This is in the Readme, but I probably should add a note to all the examples as well.

@jpecor
Copy link

jpecor commented Apr 23, 2020

Ah! Sure enough. Ok, thanks!

@brentru
Copy link
Member

brentru commented Apr 29, 2020

@jimbobbennett Ok, I looked through the code, didnt see the connection string started with HostName=. Looks great!

mu
Mu_1_0_2_-_code_py_and_Send_telemetry_to_Azure_IoT_Hub_quickstart__Python____Microsoft_Docs

azure
IoT_Hub_-_Microsoft_Azure

Ok - one final ask before I pull this in and release: Could you please add the secrets file to examples as azureiot_secrets_example.py. Please include example values for what the id_scope, device_id, key and device_connection_string values should look like so users don't get confused like I did.

@jimbobbennett
Copy link
Collaborator Author

@brentru - added more info, a secrets file and some images in the readme to show where to get the values from. Took a few commits because I couldn't get the rest syntax right, then PyLint became case sensitive for ignores where it wasn't before, then couldn't get sphinx working! all good fun.

@brentru
Copy link
Member

brentru commented Apr 30, 2020

@jimbobbennett The library check process is a bit tricky. But it looks perfect now.

@brentru brentru merged commit f3ff110 into adafruit:master Apr 30, 2020
adafruit-adabot added a commit to adafruit/Adafruit_CircuitPython_Bundle that referenced this pull request May 1, 2020
Updating https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel to 6.0.0 from 5.1.1:
  > Merge pull request adafruit/Adafruit_CircuitPython_NeoPixel#83 from adafruit/tannewt-patch-1
  > Merge pull request adafruit/Adafruit_CircuitPython_NeoPixel#82 from dunkmann00/pixelbuf

Updating https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel_SPI to 0.8.0 from 0.4.1:
  > Merge pull request adafruit/Adafruit_CircuitPython_NeoPixel_SPI#15 from dunkmann00/pypixelbuf-2.0-compat

Updating https://github.com/adafruit/Adafruit_CircuitPython_AzureIoT to 2.0.0 from 1.1.0:
  > Merge pull request adafruit/Adafruit_CircuitPython_AzureIoT#11 from jimbobbennett/master
  > build.yml: add black formatting check

Updating https://github.com/adafruit/Adafruit_CircuitPython_Pypixelbuf to 2.0.0 from 1.0.3:
  > Merge pull request adafruit/Adafruit_CircuitPython_Pypixelbuf#19 from dunkmann00/dotstar_brightness
  > Merge pull request adafruit/Adafruit_CircuitPython_Pypixelbuf#18 from dunkmann00/_pixelbuf_cp
  > Merge pull request adafruit/Adafruit_CircuitPython_Pypixelbuf#17 from adafruit/black-update
  > build.yml: add black formatting check
  > Merge pull request adafruit/Adafruit_CircuitPython_Pypixelbuf#16 from kattni/update-example-name
  > Merge pull request adafruit/Adafruit_CircuitPython_Pypixelbuf#12 from adafruit/pylint-update
  > update code of coduct: discord moderation contact section
  > Merge pull request adafruit/Adafruit_CircuitPython_Pypixelbuf#11 from sommersoft/patch_coc
  > update pylintrc for black
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants