keep wifi and umqttsimple "alive"? (beginner question)

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
jaichhier
Posts: 3
Joined: Wed Jul 17, 2019 1:22 pm

keep wifi and umqttsimple "alive"? (beginner question)

Post by jaichhier » Wed Jul 17, 2019 1:48 pm

Hi everybody,

I have been working with nodeMCU for a while, but due to not knowing C (I am a self taught hobbyist and usually stick to bash or python) never worked with these boards directly, instead just flashed `tasmota` and went from there.

After stumbling upon micropython I decided I'd write my personal little programs for individual tasks that I need around the house. The code below is a test project to get used to the libraries etc. It will connect to my wifi, then to my mqtt broker, then act accordingly to incoming messages - it also works with a push button, which I later want to use to send mqtt payloads with.

This is a simplified version of my code. I am sure there are way better ways to solve this, but I am currently learning, so please bare with me.

```
from umqttsimple import MQTTClient
import machine
import time
import network

# soft vars
mynet = "somethingsomethingsomethingwifi"
mynetpw = "actual_password"
mqid = "nodemcu-testy"
mqhost = "10.10.10.10"
mquser = "mqttuser"
mqpw = "mqttpassword"
mqport = 1883

# hard vars
diode_blue = machine.Pin(14, machine.Pin.OUT)
diode_board = machine.Pin(16, machine.Pin.OUT)
mybutton = machine.Pin(15, machine.Pin.IN, machine.Pin.PULL_UP)
buzzer = machine.Pin(0, machine.Pin.OUT)

def buzzer():
# buzzer on, sleep, buzzer off

def flash(color):
diode_color.on()
time.sleep(.12)
diode_color.off()
time.sleep(.12)

# MQTT

def sub_cb(topic, msg):
print(msg)
# test 1
if msg == b'buzz':
buzzer()
for i in range(5):
flash(blue)
elif msg == b'door':
buzzer()
flash(red)
flash(yellow)

# wifi
wlan = network.WLAN(network.STA_IF)
wlan.connect(mynet, mypw)

# waiting to connect
while not wlan.isconnected():
flash(red)
print("trying to connect")
# connection successfully
for i in range (4)
flash(green)
print("connect successfully")

# for loop below
presses = 0

while True:
client.subscribe(topic="test/nodemcu-testy")
new_message = client.check_msg()

if mybutton():
presses += 1
buzzer()
flash(yellow)
print("%i button presses." % presses)

time.sleep(1)
```

This is a simplified version of my script; I typed it from scratch because all my variables etc. are in German, so if there are typos here, this is due to me manually typing this; the original script runs as expected, **but eventually** will just stop.

When I run it, I get
* multiple "trying to connect" lines and red diode flashing (until connected to wifi)
* "connected successfully" and green diode flashing (once connected to wifi)
button press => "1 button presses", "2 button presses", etc.
mqtt payload b"buzz" => buzzer does its thing, blue diode flashing
mqtt payload b"door" => buzzer does its thing, red and yellow diode flashing

However, after multiple button presses and/or mqtt payloads, nothing will happen any longer. I didn't notice this at first, because I'd usually run the script and test it, exit, change it, test again. But now I had to go away from my machine and when I returned got

```
1 button presses
2 button presses
Traceback (most recent call last):
File "<stdin", line 151, in <module>
File "umqttsimple.py", line 157, in subscribe
File "umqttsimple.py", line 171, in wait_msg
OSError: [Errno 103] ECONNABORTED
>
```

Before traceback and before that last `>` there is what looks like an envelope ascii symbol rotated 90 degrees (shot sides top/bottom, long sides left/right).

As I said before, I am sure my code isn't all that great. That's fine. But it does connect to the wifi and mqtt server, it does what I tell it to... and eventually it just crashes..? Could you please tell me why this is?

And unrelated bonus question: I usually use `paho.mqtt` for my "regular python" mqtt projects. It allows me to do something like `newmsg = msg.decode('utf-8')`, so that instead of `b'my payload'` I just get `my payload` as output. This does not seem to work on umqttsimple, however, there must be a way to get rid off that `b` and quotes around `b'<payload>`, right?

Sorry if this is a total beginner question. I have tried fixing this on my own but could not figure it out. Thanks in advance for your help :)

User avatar
jimmo
Posts: 892
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia

Re: keep wifi and umqttsimple "alive"? (beginner question)

Post by jimmo » Thu Jul 18, 2019 1:14 am

Hi,
Sorry I don't have a great answer to your main question. The topic of ensuring that the WiFi stays connected has been discussed a bit in the forum before, but I think it remains a bit of a tricky problem. I haven't looked in any detail myself though.

One thing that might be worth checking out is https://github.com/peterhinch/micropython-iot which might already solve this issue. (Or might just be worth knowing about for your projects!).
jaichhier wrote:
Wed Jul 17, 2019 1:48 pm
And unrelated bonus question: I usually use `paho.mqtt` for my "regular python" mqtt projects. It allows me to do something like `newmsg = msg.decode('utf-8')`, so that instead of `b'my payload'` I just get `my payload` as output. This does not seem to work on umqttsimple, however, there must be a way to get rid off that `b` and quotes around `b'<payload>`, right?
Can you elaborate a bit more about what the problem you're seeing is? It's unlikely this is an issue with umqttsimple -- it's just returning a bytes() or bytearray() back to you (i.e. the underlying uninterpreted string data). (This makes sense -- you might be sending binary payloads, so it's not up to mqtt to decide what to do with the data). On almost every MicroPython port, bytes and bytearray have the decode() method, so `msg.decode('utf-8')` should do the right thing. On other ports that don't have `str.decode()` you can use `str(msg, 'utf-8')` for the same result.

Can you try this on the REPL:

Code: Select all

>>> msg = b'abcd'
>>> msg.decode('utf-8')
'abcd'

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

Re: keep wifi and umqttsimple "alive"? (beginner question)

Post by pythoncoder » Thu Jul 18, 2019 4:53 am

@jaichhier I suggest you read this guide.
Peter Hinch

jaichhier
Posts: 3
Joined: Wed Jul 17, 2019 1:22 pm

Re: keep wifi and umqttsimple "alive"? (beginner question)

Post by jaichhier » Thu Jul 18, 2019 10:52 am

> Can you try this on the REPL:

Sorry, my mistake. This **does** work. I had another `print(msg)` in a function and didn't decode that one. It works fine now.

About the wifi: I'll see if I can loop my code in a way that will determine whether or not a connection is established. If not, re-run the loop (and re-connect). I don't particularly like this because what if an mqtt payload is sent just when the device is not connected (and does not have retain=True)? However, I'll check that link and see if the iot repo has some workarounds for me as well :)

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

Re: keep wifi and umqttsimple "alive"? (beginner question)

Post by pythoncoder » Fri Jul 19, 2019 5:30 am

Another relevant link is resilient MQTT which provides an asynchronous MQTT interface which can recover from outages. It offers true qos==1 service: if the WiFi fails a qos==1 message will be passed once connectivity is re-established.

If you plan a DIY approach, expect to spend a great deal of time on testing. Achieving a resilient link is not easy :(
Peter Hinch

jaichhier
Posts: 3
Joined: Wed Jul 17, 2019 1:22 pm

Re: keep wifi and umqttsimple "alive"? (beginner question)

Post by jaichhier » Fri Jul 19, 2019 10:49 am

thank you @pythoncoder :)

I will look into this next time I got some free time for my tinkering; this is a hobby and the project looks like I'll need a couple of hours to understand and implement this. But from what I've read so far, it seems like a much, much better solution for what I am trying to achieve.

However, is my current use case even something I can reliably achieve with a nodeMCU and micropython, or do I need a raspberry pi or something else I can connect to the ethernet?

My attempt is to build a little box with a few diodes, a buzzer, and a button. When particular mqtt payloads are received, do <x>; for example, if the yard door opens, flash red diode 5 times, or, if button is pressed on device <a>, then buzz buzzer on device <b> for 5 seconds (or rather if button is pressed on device <a>, send payload 'True' to '/test/nodemcuA/button' - the rest will be handled by device <b>). This seems to me like I'd need constant wifi connectivity, because these actions should happen immediately, or at least somewhat immediately.
(we adopted some rather anxious dogs from animal shelters, and the doorbell sound scares them, so they freak out each time it rings; my idea was to have a diode flash *as soon as* the front gate is opened (notification is triggered by zigbee sensor and then forwarded via mqtt) so if we are home, somebody can attend to the mail person or whoever is coming *before* they ring the doorbell ^^).
My current solution for this are both a notification on each smartphone via telegram and speech output via alexa in each room with an alexa device. These work, but this new project wouldn't even require internet access as everything would run locally, which is a big bonus imho.

User avatar
Frida
Posts: 40
Joined: Sat Jan 30, 2016 2:20 pm
Location: Middelfart, Denmark

Re: keep wifi and umqttsimple "alive"? (beginner question)

Post by Frida » Fri Jul 19, 2019 12:54 pm

Maybe you can use something from my program?
I use a Sonoff to turn on some light at sunset. They get the commando from a Node Red server running on a Raspberry pi. Every time its reset, the counter gets reset also. IF the counter is zero, Node Red send the command again.

Code: Select all

from machine import Pin, PWM
import network
wlan = network.WLAN(network.STA_IF)
#from simple import MQTTClient
from umqtt.simple import MQTTClient
import time

push = Pin(0, Pin.IN)
tState = True # OFF
led = Pin(13, Pin.OUT, value = tState)
relay = Pin(12, Pin.OUT, value = not tState)
pled = PWM(led,freq=1, duty=1000)

def push_cb(p):
	time.sleep_ms(20)
	global tState
	tState = not tState
	toggle(tState)

push.irq(trigger=Pin.IRQ_FALLING, handler=push_cb)

pled = PWM(led,freq=5, duty=512)
while (wlan.ifconfig()[0]=='0.0.0.0'):
    pass
serverId = "192.168.20.80"
#serverId = "192.168.11.21"
clientId = "/esp8266/" + wlan.ifconfig()[0] + '/'
c = MQTTClient(clientId, serverId)

print(clientId)
print('network config:', wlan.ifconfig())

def toggle(State):
	global tState
	tState = State
	#pled.duty(1009 ^ pled.duty())	
	if(State): # OFF
		pled.duty(1000)
		relay.value(not State)
  		c.publish(clientId + "sta", 'OFF')
	else: # ON
		pled.duty(25)
		relay.value(not State)
  		c.publish(clientId + "sta", 'ON')

def sub_cb(topic, msg):
	#Warning: Comparison between bytes and str
	#if(topic == b'/esp8266/' + wlan.ifconfig()[0] + '/cmd'):
	if(topic == b'{}'.format(clientId + 'cmd')):
		print((topic, msg))
		if msg == b"ON":
			toggle(False)
		elif msg == b"OFF":
			toggle(True)

def reconnect():
	pled = PWM(led,freq=1, duty=512)
	while True:
		if not (wlan.ifconfig()[0]=='0.0.0.0'):
			try:
				c.connect() # fejl hvis ikke connected
				break
			except Exception as ex:
				print('error connect', ex)
				time.sleep(5)
	c.subscribe(clientId + 'cmd')
	print("subscribe to %s " % (clientId + 'cmd'))
	c.publish(clientId + "sta", 'OFF')

def main():
	CONNECT = const(0)
	CONNECTED = const(1)
	FSMa = CONNECT
	c.set_callback(sub_cb)

	while True:
		FSM = FSMa	
		if(FSM == CONNECT):
			reconnect()
			pled = PWM(led,freq=1, duty=1000)
			cnt = 0
			ttime = time.ticks_add(time.ticks_ms(), 5000)
			FSMa = CONNECTED
		if(FSM == CONNECTED):
			try:
				c.check_msg() # fejl hvis ikke connected
				if time.ticks_diff(time.ticks_ms(), ttime) > 0:
					ttime = time.ticks_add(time.ticks_ms(), 5000)
					c.publish(clientId + 'cnt', str(cnt))
					print('count er nu ' + str(cnt))
					cnt += 1
			except Exception as ex:
				print('error check_msg', ex)
				FSMa = CONNECT
		time.sleep_ms(10)

	c.disconnect()

main()
Yes Frida is my watchdog!

kevinkk525
Posts: 528
Joined: Sat Feb 03, 2018 7:02 pm

Re: keep wifi and umqttsimple "alive"? (beginner question)

Post by kevinkk525 » Fri Jul 19, 2019 6:29 pm

Maybe you'll find my project useful which is a smart home framework using mqtt and made for home-assistant with mqtt discovery (no config needed in home-assistant) . I use a bell myself and have multiple nodemcu that flash their LEDs and sound a buzzer as my bell was not loud enough.

It should be simple and straight forward to use with many of the components that you'll need already available. But you'll have to build the esp8266 firmware yourself and check how to configure your components and devices.
It takes care of keeping the device connected to your mqtt broker and should make your entry easier.

https://github.com/kevinkk525/pysmartnode
Kevin Köck
Micropython Smarthome Firmware: https://github.com/kevinkk525/pysmartnode

Post Reply