Search code examples
pythoniotamqpeclipse-hono

Difficulty in Sending AMQP 1.0 Message


I am using Eclipse Hono as an IoT Platform which is running locally in my private Kubernetes cluster. I have registered a device to receive commands through MQTT. In Hono it is mandatory to send commands through Qpid Network which uses AMQP 1.0 protocol for sending messages.

I have a Python code for the purpose of sending msg to my registered device which has subscribed to the specified topic for receiving commands (which the topic is "command/+/+/req/#"):

here is the code:

import Tkinter as tkinter
import proton
from cli_proton_python import sender
import ssl
import uuid
import json
from proton._reactor import _generate_uuid
#imports end


# mosquitto_sub -v -h 192.168.1.154 -u rover10@rover -P roversecret -t control/+/+/req/#



#const
scheme = "amqp://"
username = "consumer@HONO"
password = "verysecret"
hostAndPort = "192.168.1.150:15672"
tenant = "rover"
device = "rover1"
topic_to_publish = "command/"+tenant #+"/"+device
speed = 380


def sendControlMsg( msg ):
    print ("sending control msg " + str(msg))
    parser = sender.options.SenderOptions()
    opts, _= parser.parse_args()
        opts.broker_url = scheme + username + ":" + password +"@"+ hostAndPort +"/"+ topic_to_publish
    opts.msg_id = "0" # will be generated automatically
    opts.msg_subject = "RoverDriving"
        opts.msg_content = str(msg)
    opts.msg_reply_to = "command_response/"+tenant+"/"+str(_generate_uuid())
    opts.msg_correlation_id = "null"
    opts.msg_content_type = "application/json"
    opts.log_lib = "TRANSPORT_DRV"
#   opts.log_msgs = 'dict'
    opts.msg_address = "command/"+tenant+"/"+device    

    container = proton.reactor.Container(sender.Send(opts))
    container.run()

#funcs

def turn_right():
    sendControlMsg( "{\"command\":\"E\",\"speed\": "+ str(speed_var.get() + 360) +" }" )
    print("turn right clicked")

def turn_right_back():
    sendControlMsg( "{\"command\":\"D\",\"speed\": "+ str(speed_var.get() + 360) +" }" )
    print("turn right back clicked")

def turn_left():
    sendControlMsg("{\"command\":\"Q\",\"speed\": "+ str(speed_var.get() + 360) +" }")
    print("turn left clicked")

def turn_left_back():
    sendControlMsg("{\"command\":\"A\",\"speed\": "+ str(speed_var.get() + 360) +" }")
    print("turn left back clicked")

def move_forward():
    sendControlMsg("{\"command\":\"W\",\"speed\": "+ str(speed_var.get() + 360) +" }")
    print("move forward clicked")

def move_back():
    sendControlMsg("{\"command\":\"S\",\"speed\": "+ str(speed_var.get() + 360) +" }")
    print("move back clicked")

def spot_right():
    sendControlMsg("{\"command\":\"K\",\"speed\": "+ str(speed_var.get() + 360) +" }")
    print("move spot right clicked")

def spot_left():
    sendControlMsg("{\"command\":\"J\",\"speed\": "+ str(speed_var.get() + 360) +" }")
    print("move spot left clicked")

def stop_move():
    sendControlMsg("{\"command\":\"F\",\"speed\": "+ str(speed_var.get() + 360) +" }")
    print("Stop clicked")

def set_speed(event):
    speed = speed_var.get() + 360
    print("set speed to", speed)
     
def showPosEvent(event):
    print("posevt")
     
def onArrowUp(event):
    move_forward() 
    print("Arrow up")

def onArrowDown(event):
    move_back()
    print("Arrow down")

def onArrowLeft(event):
    turn_left()
    print("Arrow left")

def onArrowRight(event):
    turn_right()
    print("Arrow right")

def onSpaceBar(event):
    stop_move()
    print("Spacebar pressed")


if __name__=="__main__":
    
    tkroot = tkinter.Tk()
    tkroot.title("Rover Controller")
    speed_var = tkinter.DoubleVar() #tkinter.DoubleVar()
    labelfont = ('arial', 15, 'bold')     

    #Keyboard Input           
    tkroot.bind('<Up>',onArrowUp)
    tkroot.bind('<Down>',onArrowDown)
    tkroot.bind('<Left>',onArrowLeft)
    tkroot.bind('<Right>',onArrowRight)
    tkroot.bind('<space>',onSpaceBar)
    tkroot.focus()
    #Keyboard Input End

    #Buttons
    B_right = tkinter.Button(tkroot, text =">" , command= turn_right)
    B_right.place(x = 65, y= 25, height=25, width=25)
   
    B_right_back = tkinter.Button(tkroot, text =">" , command= turn_right_back)
    B_right_back.place(x = 65, y= 55, height=25, width=25)

    B_left  = tkinter.Button(tkroot, text ="<", command= turn_left)
    B_left.place(x= 15,y = 25, height=25, width=25)

    B_left_back  = tkinter.Button(tkroot, text ="<", command= turn_left_back)
    B_left_back.place(x= 15,y = 55, height=25, width=25)

    B_up = tkinter.Button(tkroot, text ="^", command= move_forward)
    B_up.place(x= 40, y = 10 ,  height=25, width=25)

    B_down = tkinter.Button(tkroot, text ="v", command= move_back)
    B_down.place(x= 40, y = 70, height=25, width=25)

    B_stop = tkinter.Button(tkroot, text ="Stop", command= stop_move)
    B_stop.place(x = 10,y = 160 , height = 30, width=85)

    B_spot_left = tkinter.Button(tkroot, text ="<)", command= spot_left)
    B_spot_left.place(x = 100,y = 160 , height = 30, width=35)

    B_spot_right = tkinter.Button(tkroot, text =">(", command= spot_right)
    B_spot_right.place(x = 140,y = 160 , height = 30, width=35)

    speed_label = tkinter.Label(tkroot , text = "Speed")
    speed_label.place(x= 10, y = 105, height=50, width=50)

    speed_scale = tkinter.Scale(tkroot, variable = speed_var,orient = tkinter.HORIZONTAL)
    speed_scale.bind("<ButtonRelease-1>", set_speed)
    speed_scale.place(x= 60, y = 105, height=50, width=85)
    #Buttons end

    tkroot.mainloop()

when I'm trying to send commands nothing happens at the other side and it does not deliver the msg! then I tried to sniff the packets and it turned out that there is an error in my msg which the broker responses with 'Bad Request' error.

Sniffed packets stream

I'm wondering what's wrong with the message? is it the type or is it missing something!?


Solution

  • The following Python code using the Qpid Proton library works with the Hono Sandbox.

    from __future__ import print_function, unicode_literals
    from proton import Message
    from proton.handlers import MessagingHandler
    from proton.reactor import Container
    
    class HelloWorld(MessagingHandler):
        def __init__(self, server, address):
            super(HelloWorld, self).__init__()
            self.server = server
            self.address = address
    
        def on_start(self, event):
            print("connecting ...")
            conn = event.container.connect(self.server, sasl_enabled=True, allowed_mechs="PLAIN", allow_insecure_mechs=True, user="consumer@HONO", password="verysecret")
            event.container.create_sender(conn, self.address)
    
        def on_sendable(self, event):
            print("sending command")
            event.sender.send(Message(body="Hello World!", address="command/DEFAULT_TENANT/4711", content_type="text/plain", subject="call"))
            event.sender.close()
            event.connection.close()
    
    Container(HelloWorld("amqp://hono.eclipseprojects.io:15672", "command/DEFAULT_TENANT")).run()
    

    I guess in your code you are mainly missing to set the SASL properties. I also think that the username and password should better be set explicitly instead of as part of the URL because the username contains a @ character which also seems to be used in the URL as a delimiter.