UMQTT.simple function to check client.ping() call back - mqtt

I am trying to get my head around UMQTT.simple. I am looking to handle instances in which my server might disconnect for a reboot. I want to check whether the client is connected, and if not, wait some period and try to reconnect.The guidance seems to be to use client.ping() for this (How to check Micropython umqtt client is connected?).
For the MQTT.paho client I see there is a way to access ping responses in the logs function (see here: http://www.steves-internet-guide.com/mqtt-keep-alive-by-example/). For UMQTT the docs indicate that ping response is handled automatically by wait_msg(): Ping server (response is automatically handled by wait_msg() (https://mpython.readthedocs.io/en/master/library/mPython/umqtt.simple.html). There does not appear to be any analogous logs function mentioned in the UMQTT.simple docs.
This is confounding for a couple of reasons:
If i use client.wait_msg() how do I call client.ping()? client.wait_msg() is a blocking function, so I can't make the ping. The system just disconnects when the keepalive time is reached.
If I call client.check_msg(), and client.ping() intermittently, I can't access the callback. My callback function doesn't have parameters to access pingresponse (params are f(topic, msg) in the docs).
The way I am solving this for now is to set a bunch of try-except calls on my client.connect and then connect-subscribe functions, but its quite verbose. Is this the way to handle or can i take advantage of the pingresponse in UMQTT.simple?
Below is a sample of the code i am running:
#Set broker variables and login credentials
#Connect to the network
#write the subscribe call back
def sub_cb(topic, msg):
print((topic, msg))
#write a function that handles connecting and subscribing
def connect_and_subscribe():
global CLIENT_NAME, BROKER_IP, USER, PASSWORD, TOPIC
client = MQTTClient(client_id=CLIENT_NAME,
server=BROKER_IP,
user=USER,
password=PASSWORD,
keepalive=60)
client.set_callback(sub_cb)
client.connect()
client.subscribe(TOPIC)
print('Connected to MQTT broker at: %s, subscribed to %s topic' % (BROKER_IP, TOPIC))
return(client) #return the client so that i can do stuff with it
client = connect_and_subscribe()
#Check messages
now = time.time()
while True:
try:
client.check_msg()
except OSError as message_error: #except if disconnected and check_msg() fails
if message_error == -1:
time.sleep(30) #wait for reboot
try:
client = connect_and_subscribe() #Try connect again to the server
except OSError as connect_error: #If the server is still down
time.sleep(30) #wait and try again
try:
client = connect_and_subscribe()
except:
quit() #Quite so that i don't get stuck in a loop
time.sleep(0.1)
if time.time() - now > 80: #ping to keepalive (60 * 1.5)
client.ping()
now = time.time() #reset the timer

Related

Cannot connect client to Broker

I'm coding on lua for the first time and have created a mqtt client instance for a IO Link Sensor to publish data on a MQTT topic. However, I can't figure out how to connect to my local mosquitto broker, since "the connection is refused" but the broker is online and working.
The setup to my mqtt client looks like this:
local client = MQTTClient.create()
local BROKER_IP = '127.0.0.1'
local USE_TLS = false
client:setIPAddress(BROKER_IP)
if (USE_TLS) then
client:setPort(1883)
client:setTLSEnabled(true)
client:setTLSVersion('TLS_V12')
client:setCABundle('resources/mosquitto/mybroker-cert.pem')
client:setClientCertificate(
'resources/mosquitto/mqtt-client-cert-2.pem',
'resources/mosquitto/mqtt-client-key-2.pem',
'changemeclient'
)
end
Now the error is marked on following client:publish line.
Remark: Here the data to be published is a distance and thus converted to string.
if dataValid == 'PI_STATUS_VALID' then
local sDistance = string.format('%d', distance)
client:publish('/topic/test', sDistance, "QOS0", "NO_RETAIN")
end
Does anyone see were the problem could be?

ThingsBoard IoT Gateway doesn't update MQTT values

I try to receive simple text values from external MQTT broker topics with IoT Gateway.
For this purpose I simplify the existing script (extensions/mqtt/custom_mqtt_uplink_converter.py):
from thingsboard_gateway.connectors.mqtt.mqtt_uplink_converter import MqttUplinkConverter, log
class CustomMqttUplinkConverter(MqttUplinkConverter):
def __init__(self, config):
self.__config = config.get('converter')
self.dict_result = {}
def convert(self, topic, body):
try:
log.debug("New data received: %s: %s" % (topic,body))
# if topic = '/devices/buzzer/controls/volume' device name will be 'buzzer'.
self.dict_result["deviceName"] = topic.split("/")[2]
# just hardcode this
self.dict_result["deviceType"] = "buzzer"
self.dict_result["telemetry"] = {"data": body}
log.debug("Result: %s" % (self.dict_result))
return self.dict_result
except ...
When I start gateway I see in his log that he successfully connected and read the values:
INFO ... MQTT Broker Connector connected to 10.1.1.2:1883 - successfully.'
DEBUG ... Client <paho.mqtt.client.Client object at 0x7fb42d19dd68>, userdata None, flags {'session present': 0}, extra_params ()'
DEBUG ... <module 'CustomMqttUplinkConverter' from '/var/lib/thingsboard_gateway/extensions/mqtt/custom_mqtt_uplink_converter.py'>'
DEBUG ... Import CustomMqttUplinkConverter from /var/lib/thingsboard_gateway/extensions/mqtt.'
DEBUG ... Converter CustomMqttUplinkConverter for topic /devices/buzzer/controls/volume - found!'
INFO ... Connector "MQTT Broker Connector" subscribe to /devices/buzzer/controls/volume'
DEBUG ... Received data: {}'
DEBUG ... (None,)'
INFO ... "MQTT Broker Connector" subscription success to topic /devices/buzzer/controls/volume, subscription message id = 1'
DEBUG ... New data received: /devices/buzzer/controls/volume: 66'
DEBUG ... Result: {'deviceName': 'buzzer', 'deviceType': 'buzzer', 'telemetry': {'data': 66}}'
But this values are the last values he can read. If I change volume one broker new values will not appear neither in the log nor in TB UI. (I control updates with mosquitto_sub.)
Seems this converter will never called again until gateway restarted. Is it correct behaveour?
How can I make sure that my code is correct if I don't see the result?
Hi I have tried your version of the custom converter, it didn't work, but when I changed
self.dict_result["telemetry"] = {"data": body}
to
self.dict_result["telemetry"] = [{"data": body}]
It sent data correctly.
The gateway requires an array of telemetry of attributes from the converter.

ESP8266, NodeMCU, soft AP - UDP server-like soft AP, independent access point

I am using NodeMCU (with ESP8266-E) with an upgraded firmware. All basic commands work perfectly but there is one problem.
I wanted to create an independent access point, which could have a behaviour like a UDP server. That means without direct connection to any other access points. A simple UDP server like soft AP.
I followed these steps:
I have uploaded a new firmware to NodeMCU.
I have downloaded ESPlorer for better work with NodeMCU.
I have uploaded the source code below.
I have connected to the NodeMCU access point on my desktop.
I have sent some strings to the NodeMCU using a Java UDP client program.
I have looked at the messages on ESPlorer.
NodeMCU has not received any such strings.
--
print("ESP8266 Server")
wifi.setmode(wifi.STATIONAP);
wifi.ap.config({ssid="test",pwd="12345678"});
print("Server IP Address:",wifi.ap.getip())
-- 30s timeout for an inactive client
srv = net.createServer(net.UDP, 30)
-- server listens on 5000, if data received, print data to console
srv:listen(5000, function(sk)
sk:on("receive", function(sck, data)
print("received: " .. data)
end)
sk:on("connection", function(s)
print("connection established")
end)
end)
When I tried to send a message using a Java application, there was no change in ESPlorer. Not even when I tried to send a message using the Hercules program (great program for TCP, UDP communication).
I guess that maybe it will be the wrong IP address. I am using the IP address of the AP and not the IP address of the station.
In other words I am using this address: wifi.ap.getip() and not this address wifi.sta.getip() for connections to the UDP server. But sta.getip() returns a nil object. Really I don't know.
I will be glad for any advice.
Thank you very much.
Ok, let's restart this since you updated the question. I should have switched on my brain before I gave you the first hints, sorry about this.
UDP is connectionless and, therefore, there's of course no s:on("connection"). As a consequence you can't register your callbacks on a socket but on the server itself. It is in the documentation but it's easy to miss.
This should get you going:
wifi.setmode(wifi.STATIONAP)
wifi.ap.config({ ssid = "test", pwd = "12345678" })
print("Server IP Address:", wifi.ap.getip())
srv = net.createServer(net.UDP)
srv:listen(5000)
srv:on("receive", function(s, data)
print("received: " .. data)
s:send("echo: " .. data)
end)
I ran this against a firmware from the dev branch and tested from the command line like so
$ echo "foo" | nc -w1 -u 192.168.4.1 5000
echo: foo
ESPlorer then also correctly printed "received: foo".
This line is invalid Lua code. connected is in the wrong place here. you can't just put a single word after a function call.
print(wifi.ap.getip()) connected
I guess you intended to do something like
print(wifi.ap.getip() .. " connected")
Although I think you should add som error handling here in case wifi.ap.getip() does not return an IP.
Here you do not finish the function definition. Neither did you complete the srv:on call
srv:on("receive", function(srv, pl)
print("Strings received")
srv:listen(port)
I assume you just did not copy/paste the complete code.

Lua websockets for heka

I am using lua-websockets https://github.com/lipp/lua-websockets to try and get a web socket server running.
Using the copas example they provided:
local copas = require'copas'
local server = require'websocket'.server.copas.listen
{
port = 8080,
protocols = {
echo = function(ws)
while true do
local message = ws:receive()
if message then
ws:send(message)
else
ws:close()
return
end
end
end
}
}
copas.loop()
This works and starts listening on port 8080 and I am able to connect and get a echo response back.
The problem is when I try and integrate it with heka. I start heka and it starts the websocket server but hangs at Loading plugin. When it tries to "load" a plugin, it executes the lua script.
Now my question is, how do I run the websocket server and send a "success" to heka to let it continue start up. Simply this would be: if the websocket is listening on 8080 return to heka and say the lua script has been executed successfully.
Thanks in advance!
Don't call copas.loop() as it enters an indefinite loop that handles all copas socket interactions. You need to use copas.step() instead (see controlling copas section) and call it at the appropriate time from your heka code (this call will return false on timeout and true when it handles something). In a GUI application it may be called from an IDLE handler.

How to test for asynchronous HTTP requests in ruby using EventMachine

I'm getting messages of a RabbitMQ queue and each message is a URL that I want to make a request to. Now I'm using the AMQP gem to subscribe to the queue and that uses EventMachine, so I'm using the the em-http-request library to make the http requests. According to the documentation here: https://github.com/igrigorik/em-http-request/wiki/Parallel-Requests
The following will issue asynchronous http-requests:
EventMachine.run {
http1 = EventMachine::HttpRequest.new('http://google.com/').get
http2 = EventMachine::HttpRequest.new('http://yahoo.com/').get
http1.callback { }
http2.callback { }
end
So when I subscribe to the RabbitMQ queue I have the following code:
x = 0
EventMachine.run do
connection = AMQP.connect(:host => '127.0.0.1')
channel = AMQP::Channel.new(connection)
channel.prefetch(50)
queue = channel.queue("http.requests")
exchange = channel.direct("")
queue.subscribe do |metadata, payload|
url = payload.inspect
eval "
#http#{x} = EventMachine::HttpRequest.new(url).get
#http#{x}.callback do
puts \"got a response\"
puts #http#{x}.response
end
x = x+1
"
end
end
This dynamically creates new variables and creates new http requests, similar to the way described in the em-http-request documentation. But is there a way to test whether the requests are actually being made asynchronously? Is it possible to write to the console every time a get request is fired off so I can see they are fired off one after the other without waiting for a response?
You can try running tcpdump and analysing the output. If you see the TCP three-way handshakes for the two connections being interleaved then the connections are happening in parallel.
This can't really be part of an automated test though, if that's what you're trying to aim for. I would be happy just to verify that the library does what it says it does once and not make it part of a test suite.
A very simple example, demonstrating exactly what you want:
require 'em-http-request'
EM.run do
# http://catnap.herokuapp.com/3 delays the HTTP response by 3 seconds.
http1 = EventMachine::HttpRequest.new('http://catnap.herokuapp.com/3').get
http1.callback { puts 'callback 1' }
http1
puts 'fired 1'
http2 = EventMachine::HttpRequest.new('https://www.google.com/').get
http2.callback { puts 'callback 2' }
puts 'fired 2'
end
Output (for me):
fired 1
fired 2
callback 2
callback 1
Depending on your internet connection, Heroku and Google, the response to the second HTTP request will likely come in first and you can be sure, the requests are indeed done in parallel.

Resources