Azure IoT Hub - “Subscribe” code not working: “con

2019-03-05 09:09发布

问题:

I am trying to receive "notifications" from IoT Hub when some data is published to the MQTT topic devices/device_id/messages/events/. To receive data, I am using the following topic: devices/device_id/messages/devicebound/. My subscribe code is below (Python):

import paho.mqtt.client as mqtt
import ssl, random
from time import sleep

root_ca = "./certs/digicertbaltimoreroot.pem"
public_crt = './certs/rsa_cert.pem'
private_key = './certs/rsa_private.pem'

iothub_name = "myhub"
device_id = "mydevice"
mqtt_url = "{}.azure-devices.net".format(iothub_name)
mqtt_port = 8883
topic = "devices/{}/messages/devicebound/#".format(device_id)

def error_str(rc):
    return "Some error occurred. {}: {}".format(rc, mqtt.error_string(rc))

def on_disconnect(unused_client, unused_userdata, rc):
    print("on_disconnect", error_str(rc))

def on_connect(client, userdata, flags, response_code):
    print("Connected with status: {0}".format(response_code))
    client.subscribe(topic, 1)

def on_subscribe(client, userdata, mid, granted_qos):
    print("Subscribed: "+str(mid)+" "+str(granted_qos))

def on_message(client, userdata, msg):
    print("Topic: {0} -- Payload: {1}".format(msg.topic, str(msg.payload)))

if __name__ == "__main__":    
    client = mqtt.Client(device_id, protocol=mqtt.MQTTv311)

    client.tls_set(root_ca,
                   certfile = public_crt,
                   keyfile = private_key,
                   cert_reqs = ssl.CERT_REQUIRED,
                   tls_version = ssl.PROTOCOL_TLSv1_2,
                   ciphers = None)

    client.on_connect = on_connect
    client.on_message = on_message
    client.on_disconnect = on_disconnect

    print("Connecting to Azure IoT Hub...")
    client.connect(mqtt_url, mqtt_port, keepalive=60)
    client.loop_forever()

When I run, I am getting the following output:

Connecting to Azure IoT Hub...
Connected with status: 3
('on_disconnect', 'Some error occurred. 5: The connection was refused.')
Connected with status: 3
('on_disconnect', 'Some error occurred. 5: The connection was refused.')

Can anyone suggest what I am missing?

回答1:

Based on the document "Using the MQTT protocol directly", you need

to download and reference the DigiCert Baltimore Root Certificate. This certificate is the one that Azure uses to secure the connection.

As you did in the publishing process like this:

client.tls_set(ca_certs=path_to_root_cert,     
               certfile=None, keyfile=None, 
               cert_reqs=ssl.CERT_REQUIRED,       
               tls_version=ssl.PROTOCOL_TLSv1, 
               ciphers=None)

Also, you need set username and password:

client.username_pw_set(username=iot_hub_name+".azure-devices.net/" + device_id, password=sas_token)

At last, you can subscribe like this:

client.subscribe(topic=topic, qos=0)

Sample code for Security Keys authenticated device:

import paho.mqtt.client as mqtt
import ssl, random
from time import sleep

path_to_root_cert = "./certs/digicertbaltimoreroot.cer"

iothub_name = "your hub name"
device_id = "device1"
sas_token = "SharedAccessSignature sr=[your hub name].azure-devices.net%2Fdevices%2Fdevice1&sig=[sig]&se=1526955728"
mqtt_url = "{}.azure-devices.net".format(iothub_name)
mqtt_port = 8883
topic = "devices/{}/messages/devicebound/#".format(device_id)

def error_str(rc):
    return "Some error occurred. {}: {}".format(rc, mqtt.error_string(rc))

def on_disconnect(unused_client, unused_userdata, rc):
    print("on_disconnect", error_str(rc))

def on_connect(client, userdata, flags, response_code):
    print("Connected with status: {0}".format(response_code))
    client.subscribe(topic, 1)

def on_subscribe(client, userdata, mid, granted_qos):
    print("Subscribed: "+str(mid)+" "+str(granted_qos))

def on_message(client, userdata, msg):
    print("Topic: {0} -- Payload: {1}".format(msg.topic, str(msg.payload)))

if __name__ == "__main__":    
    client = mqtt.Client(device_id, protocol=mqtt.MQTTv311)

    client.username_pw_set(username=iothub_name+".azure-devices.net/" + device_id, password=sas_token)

    client.tls_set(ca_certs=path_to_root_cert, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1, ciphers=None)

    client.on_connect = on_connect
    client.on_message = on_message
    client.on_disconnect = on_disconnect


    print("Connecting to Azure IoT Hub...")
    client.connect(mqtt_url, mqtt_port, keepalive=60)
    client.subscribe(topic=topic, qos=0)
    client.loop_forever()

Sample code for x509 authenticated device: (Need provide device certificate and private key files. No need SAS token as password; just leave it empty. But still need set username.)

import paho.mqtt.client as mqtt
import ssl, random
from time import sleep

root_ca = "./certs/digicertbaltimoreroot.cer"
public_crt = './certs/mydevice-public.pem'
private_key = './certs/mydevice-private.pem'

iothub_name = "your hub name"
device_id = "mydevice"
mqtt_url = "{}.azure-devices.net".format(iothub_name)
mqtt_port = 8883
topic = "devices/{}/messages/devicebound/#".format(device_id)

def error_str(rc):
    return "Some error occurred. {}: {}".format(rc, mqtt.error_string(rc))

def on_disconnect(unused_client, unused_userdata, rc):
    print("on_disconnect", error_str(rc))

def on_connect(client, userdata, flags, response_code):
    print("Connected with status: {0}".format(response_code))
    client.subscribe(topic, 1)

def on_subscribe(client, userdata, mid, granted_qos):
    print("Subscribed: "+str(mid)+" "+str(granted_qos))

def on_message(client, userdata, msg):
    print("Topic: {0} -- Payload: {1}".format(msg.topic, str(msg.payload)))

if __name__ == "__main__":    
    client = mqtt.Client(device_id, protocol=mqtt.MQTTv311)

    client.on_connect = on_connect
    client.on_message = on_message
    client.on_disconnect = on_disconnect

    client.username_pw_set(username=iothub_name+".azure-devices.net/" + device_id, password="")

    client.tls_set(root_ca,
                   certfile = public_crt,
                   keyfile = private_key,
                   cert_reqs = ssl.CERT_REQUIRED,
                   tls_version = ssl.PROTOCOL_TLSv1_2,
                   ciphers = None)
    client.tls_insecure_set(False)


    print("Connecting to Azure IoT Hub...")
    client.connect(mqtt_url, mqtt_port, keepalive=60)
    client.subscribe(topic=topic, qos=0)
    client.loop_forever()

Note: The topic is "devices/{device-id}/messages/devicebound/#".

How to create device certificate files you can reference here.

Update: These sample code used to receive D2C messages that you can send like this for simple test:

From device explorer:

From azure portal:

Upate: Receive D2C messages:

As I pointed out in my comments, Azure IoT Hub is not generic MQTT brober, so you can't directly subscribe messages sent from the device. To workaround it, you can use the Event Hubs-compatible endpoint exposed by the IoT Hub like "Read the telemetry from your hub " part does. But unfortunately, Python doesn't support this way. You can use the iothub-explorer CLI utility or either create a Node.js or a .NET or Java event hub-based console app to read the device-to-cloud messages from IoT Hub.



回答2:

As Rita already mentioned that D2C message was not supported to received on the device side currently.

Azure IoT hub is a cloud gate way in the IoT solution like figure below:

Azure IoT hub provide the service for backed solution and device side based on the endpoints. Currently it provides the device to cloud feature like D2C messages, Device Twin's reported properties and file upload. And from cloud we can use the cloud to device feature like direct method, Twin's desired properties and Cloud-to-device messages.

I am trying to receive "notifications" from IoT Hub when some data is published to the MQTT topic devices/device_id/messages/events/

It depends on which side you want to receive the message. Currently there are two scenarios Azure IoT hub supports.

  1. Send Device-to-Cloud message on device side and receive the message on the cloud back-end solution(D2C message only support AMQP protocol)

  2. Send Could-to-device message on cloud back end solution and receive the message on the device side.

    And if you have any idea or feedback about Azure IoT Hub, you can submit it via Azure IoT Hub - user voice.