Clever way to connect to Mqtt server

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
thalesmaoa
Posts: 19
Joined: Thu Feb 13, 2020 10:10 pm

Clever way to connect to Mqtt server

Post by thalesmaoa » Mon Feb 22, 2021 2:15 pm

Hi there, I'm not sure how to improve my code, so I need some help.

I'm using an RFID to open a door.
I'm also using MQTT to open the door and monitor its status.

The main code is like the one bellow:

Code: Select all

while True:
    try:
        client.check_msg()
    except OSError as e:
        restart_and_reconnect()

   # Bellow there is another code to monitor local RFID tag and open the door
   # Code here

Code: Select all

def restart_and_reconnect():
    print('Failed to connect to MQTT broker. Reconnecting...')
    time.sleep(10)
    machine.reset()
If server is ok, it can pass the try/except part and execute my code for RFID. The problem is if the MQTT server is offline. It keeps reseting until server is online, so my RFID code doesn't run.

Last night there was no power and my router was offline. That kept me outside until power restore.

Is there a way to reconnect without send a machine.reset()?

User avatar
pythoncoder
Posts: 4935
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Clever way to connect to Mqtt server

Post by pythoncoder » Mon Feb 22, 2021 4:23 pm

Peter Hinch

thalesmaoa
Posts: 19
Joined: Thu Feb 13, 2020 10:10 pm

Re: Clever way to connect to Mqtt server

Post by thalesmaoa » Mon Feb 22, 2021 5:07 pm

Perfect solution! I will try it.
If I face any other problem I can open an issue.

thanks

Divergentti
Posts: 67
Joined: Fri Sep 04, 2020 9:27 am
Location: Hanko, Finland
Contact:

Re: Clever way to connect to Mqtt server

Post by Divergentti » Tue Feb 23, 2021 11:25 am

Peter's mqtt_as.py is best approach.

The mqtt broker status can be updated as an example from broker's $sys messages. As an example, if you do not see $SYS/broker/uptime increasing, the broker is down, or if you don't receive anything, you have lost connection. As with Peter's mqtt_as.py, you can setup asynchronous loop to update and check statuses.

There are some ways to check if connection is up and re-establish connection too:
- no AP connected: network.WLAN(network.STA_IF).config('essid') == ''"
- no IP address: network.WLAN(network.STA_IF).ifconfig()[0] == '0.0.0.0':
- for connection re-establishment you may want to use network.WLAN(network.STA_IF).disconnect()

In this main.py https://github.com/divergentti/airquali ... en/main.py I use Peter's mqtt_as.py and reason why I splitted Peter's 'config' so complex to two parts is my WiFi setup for two different SSID's https://github.com/divergentti/airquali ... CONN_AS.py

thalesmaoa
Posts: 19
Joined: Thu Feb 13, 2020 10:10 pm

Re: Clever way to connect to Mqtt server

Post by thalesmaoa » Wed Feb 24, 2021 10:16 pm

This is the best way of coding async?
As mentioned, I need a pooling function to perform other tasks, which must be as fast as possible.

I'm starting from default example, and creating another task inside main.
I'm not sure if asyncio.sleep_ms(5) is correct.

Code: Select all

from mqtt_as import MQTTClient
from config import config
import uasyncio as asyncio

SERVER = '192.168.200.254'  # Change to suit e.g. 'iot.eclipse.org'

def callback(topic, msg, retained):
    print((topic, msg, retained))

async def conn_han(client):
    await client.subscribe('foo_topic', 1)

async def pooling():
    k = 0
    while True:
        print ("Task pooling", k)
        k += 1
        await asyncio.sleep_ms(5)

async def main(client):
    asyncio.create_task(pooling())
    await client.connect()
    n = 0
    while True:
        await asyncio.sleep(1)
        await client.publish('result', '{}'.format(n), qos = 0)
        n += 1

config['subs_cb'] = callback
config['connect_coro'] = conn_han
config['server'] = SERVER

MQTTClient.DEBUG = True  # Optional: print diagnostic messages
client = MQTTClient(config)
try:
    asyncio.run(main(client))
finally:
    client.close()  # Prevent LmacRxBlk:1 errors


Still trying to understand it.

User avatar
pythoncoder
Posts: 4935
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Clever way to connect to Mqtt server

Post by pythoncoder » Thu Feb 25, 2021 6:29 am

It looks like you've correctly created a concurrent task. A 5ms delay won't do any harm: you could even have a value of 0 there. Any delay causes your task to yield to the scheduler allowing the rest of the code to run. There are two things to note.

Longer delays are "good manners" in that your task completely suspends for the duration, giving other tasks more CPU time. So if you only need to check RFID once every 200ms, wait that long between checks.

Secondly, very short delays won't necessarily be honoured. If you ask for 5ms you will get at least 5ms, but it will probably be both longer and variable. Especially if some other task hogs the CPU between yielding (e.g. by itself issuing asyncio.sleep()).

You need to get your head round the basic concepts of cooperative multitasking. However it's how most serious firmware is written ;)
Peter Hinch

thalesmaoa
Posts: 19
Joined: Thu Feb 13, 2020 10:10 pm

Re: Clever way to connect to Mqtt server

Post by thalesmaoa » Thu Feb 25, 2021 1:18 pm

Yes, and I thank you for well detailed doc.
Still doing some tests.

thalesmaoa
Posts: 19
Joined: Thu Feb 13, 2020 10:10 pm

Re: Clever way to connect to Mqtt server

Post by thalesmaoa » Fri Feb 26, 2021 3:19 am

Ok, coroutines opens a new world for me. I was used to timers. It seems a high level approach.

After a whole day of readings and trials, my code is up and running using mqtt_as. Thanks again for the great work.

However, there is something that I wasn't able to clearly understand. Allow me to present a simple example.
def Z():
client.publish("something", "good")

async def myfunc():
Z()
# await client.publish("something", "good")
await async.sleep(1)
If I publish inside the coroutine myfunc, there is no problem, since I use await. However, cant use await inside Z, and thus It doesnt work.

Maybe I'm missing something.

User avatar
pythoncoder
Posts: 4935
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Clever way to connect to Mqtt server

Post by pythoncoder » Fri Feb 26, 2021 11:07 am

There is a distinction between asynchronous coroutines and synchronous functions and methods. You can only issue await in a coroutine (defined with async def). If you define a coroutine:

Code: Select all

async def my_coro():
    await something()
you can run it as a background task with asyncio.create_task(my_coro()) or you can await it in another coroutine. What you can't do is call it like a function

Code: Select all

my_coro()
This will fail silently.

The distinction between synchronous and asynchronous functions is known as the the red and blue function problem and is a bone of contention among language gurus. For mortals like me equipped with (hopefully) a 3-digit IQ, it's a case of learn the rules and obey ;)
Peter Hinch

thalesmaoa
Posts: 19
Joined: Thu Feb 13, 2020 10:10 pm

Re: Clever way to connect to Mqtt server

Post by thalesmaoa » Sun Feb 28, 2021 5:04 pm

Hum... Really tricky. I quit as well. Hehehe...

In your class, publish is defined as a coroutine, right? Is there a way to call it in a synchronous function?

If I understood it correctly, I need to create a task for it like:

Code: Select all

def sync_fun():
	asyncio.create_task(client.publish("topic", "msg"))

Post Reply