Cooperation on different levels? #12268
-
Two assyncronous tasks in one script.
Working ok after one day test.
|
Beta Was this translation helpful? Give feedback.
Replies: 7 comments 12 replies
-
Regarding the timer callback, see Writing Interrupt Handlers in the docs. For me, the use of the Timer for such a simple function looks overly complicated when a simple loop with a Without proper locking, anything can happen to the resources shared by your "tasks" (in your case, most likely the files on the SD card). Maybe you want to look into asyncio. There you have multiple tasks, queues, locking primitives, .... |
Beta Was this translation helpful? Give feedback.
-
The official MQTT client has a number of issues discussed in mqtt_as. I agree with @karfas : my recommendation is to use |
Beta Was this translation helpful? Give feedback.
-
Hey @Fisherman48, since you asked about cooperative tasks based on callback and interrupt, may I suggest the following script. It may not be the right way to do it. It may not work for your project. But you can give it a try. You need to add web-server and mqtt-client instead of import os
from machine import SDCard, Timer, Pin
class SD():
def __init__(my):
sd = SDCard() # esp32 slot1 sdmmc - default
os.mount(sd, '/sd')
print('Info:', sd.info()) # error if not ready
my.cr = '' # critical region
def put(my, msg):
w = 0
if my.cr == '':
my.cr = 'w'
try:
with open('/sd/data.log', 'a') as fw:
fw.write(msg)
w = len(msg)
touch.irq(trigger=Pin.IRQ_FALLING, handler=pin_cb)
except Exception as e:
print('Err:', e)
my.cr = ''
else:
print('@put CR:', my.cr, 'wait...')
return w
def get(my):
r = ''
if my.cr == '':
my.cr = 'r'
try:
os.stat('/sd/data.log')
except:
pass
else:
try:
with open('/sd/data.log', 'r') as fr:
r = fr.read()
os.remove('/sd/data.log') # delete after read
except Exception as e:
print('Err:', e)
my.cr = ''
else:
print('@get CR:', my.cr, 'wait...')
return r
def t1_cb(t):
put_ref = sd.put
msg = f'{esp32.hall_sensor()} {esp32.raw_temperature()}\r\n'
schedule(put_ref, (msg))
def pin_cb(p):
touch.irq(handler=None)
print('Data:', sd.get())
# simulate
from micropython import schedule
import esp32
sd = SD()
t1 = Timer(1)
touch = Pin(0, Pin.IN, Pin.PULL_UP) # Touch the pin to the ground to trigger
# IRQs
touch.irq(trigger=Pin.IRQ_FALLING, handler=pin_cb)
t1.init(mode=Timer.PERIODIC, period=5000, callback=lambda t:t1_cb(t)) I hope it is useful. |
Beta Was this translation helpful? Give feedback.
-
Hi, To se the big picture : Br /Stefan |
Beta Was this translation helpful? Give feedback.
-
@karfas, why does critical region protection using a variable not work in my example? The sd.put(msg) was executed by the scheduler placed by the timer callback. This means that all possible sd.put(msg) are queqed in the scheduler and run sequentially. The button IRQ callback is called directly, but not before the IRQ has been disabled with touch.irq(handler=None). I don't see a problem here. I tested the script before posting it here. The touch.irq() happened whenever the trigger pin was connected to ground. The timer triggered at a steady rate. There must be contention for the critical region during the test, but I did not experincing problem. Since we are on esp32, I even try I had problems with sd.write(). After some time the script hangs at fw.write(msg) without any error message, this is with the default slot1 sdmmc. With slot2, SPI sdcard interface I saw the following errors:
I don't know what's causing the problem. Bad sdcard, bad connections, bad programming? @Fisherman48 said that his system worked fine after one day of testing. I am interested in a different way of doing things. I admire people who try something out of the ordinary. Sure, you might make a big mistake and feel like a fool, but that is how you learn. I am sure everyone wants a simple and clean solution. But sometimes it is good to be out of your comfort zone. We grow by doing something different. Just my 2 cents. |
Beta Was this translation helpful? Give feedback.
-
Using IRQ's with switches is a disaster because of contact bounce. This stuff is so much easier with from mqtt_as import MQTTClient
from mqtt_local import config # WiFi credentials, broker IP etc
import asyncio
from primitives import ESwitch
async def messages(client):
async for topic, msg, retained in client.queue:
print(f'Topic: "{topic.decode()}" Message: "{msg.decode()}" Retained: {retained}')
# Handle incoming message: your code here
async def switch_handler():
es = ESwitch(Pin(0, Pin.IN, Pin.PULL_UP))
while True:
es.close.clear()
await es.close.wait()
# Handle switch closure: your code here
# Deal with WiFi outages
async def up(client):
while True:
await client.up.wait()
client.up.clear()
await client.subscribe('foo_topic', 1) # This is where you subscribe to a topic
async def main(client): # Main program
try:
await client.connect()
except OSError:
print('Connection failed.')
else:
asyncio.create_task(up(client))
asyncio.create_task(messages(client))
await switch_handler()
# Define configuration
config['keepalive'] = 120
config["queue_len"] = 1 # Use event interface with default queue
client = MQTTClient(config) # Set up client.
try:
asyncio.run(main(client))
finally:
client.close()
asyncio.new_event_loop() The "handle incoming message" and "handle switch closure" sections would embody the application code. These are not truly concurrent so don't need any special precautions unless they compete for the same resource. They can access files or hardware devices. |
Beta Was this translation helpful? Give feedback.
-
See my thoughts on future development. I know the maintainers have discussed this but it is quite a big job. |
Beta Was this translation helpful? Give feedback.
See my thoughts on future development.
I know the maintainers have discussed this but it is quite a big job.