Error when using two cores #12391
-
Hi all, been trying to use multicore processing for running a webserver and checking for button input simultaneously, the problem is that things are getting a little confusing when using classes to share data between threads (I'm a begginer), the code is quite long but any help would be greatly appreciated. Error:
Code:
|
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 6 replies
-
Hi, yues concurrency in embedded code can be tricky to get working! Just for a start, personally I try to avoid threads in micropython. Keeping objects in sync between threads can have a fair bit of complexity; and it's worth nothing that the rp2 only support 1 extra thread as it's implemented as code running on the second core.
I see you're already using the pin interrupt for the switch so you could be just printing / handling the button press in the interrupt handler (which by default will be a soft hander anyway iirc) so possibly wouldn't need the thread anyway. That being said, that's not what you asked here, and your code with thread is certainly technically a valid course of action and interesting to learn about threads vs irq vs asyncio!
Luckily this exception messate points the the function that's triggered it! I see you've defined def thread_function(bt: buttonThread):
while True:
if bt.button_presses != bt.old_presses:
print(bt.button_presses)
bt.old_presses = bt.button_presses
...
_thread.start_new_thread(thread_function, (buttonThread,))` Note the extra
This one's a bit trickier as you have to figure out where it comes from. When there's no stack trace / location identified it usually means the error has been raised by some "internal" functionality, eg an interrupt handler. Also, I see in your main you've got
This is an infinite loop that would block the rest of the main code from ever running? |
Beta Was this translation helpful? Give feedback.
-
@ropellanda When beginners come to MicroPython they read about the amazing features such as interrupts, threading, multi-core programming, hard and soft timers. With a mental "whoopee" the temptation is to wade in, thinking "I've got a fuzzbox and I'm gonna use it". Alas these are all advanced programming tools which the experienced user draws on when the normal approach is inadequate. The normal approach - as you have discovered - is Multi-core programming is the hardest technique to get right for reasons discussed here. The techniques are well worth learning, but a little reading can save a lot of frustrating bug hunting. |
Beta Was this translation helpful? Give feedback.
-
Do not be discouraged, concurrency is not easy. It takes time to learn and master. The level of mastery is related to the amount of effort you put in. Some useful examples can be found in the test scripts at https://github.com/micropython/micropython/tree/master/tests/thread. It is better to start slowly and understand the basic principles. It is easier to think of the various concurrent functions as long-running tasks. A task is "normally" a function that never returns, i.e. has a The rpi-pico can multitask with two threads with two cores, whereas the esp32 can multitask with 25 threads on one core. Using the task definition above, rpi-pico can run 2 tasks, while esp32 can run 25 tasks. However, on rpi-pico the 2 tasks are on 2 different cores. Whereas on esp32 the tasks share the same CPU (core1) in what we call time-sharing. On esp32, the RTOS will preemptively take the CPU away from a running task to allow other tasks to run. Although rpi-pico can run 2 tasks on 2 separate cores, other resources such as variables, input/output streams, GPIOs are shared between the 2 tasks. Here is a simple two-task example. If you're interested, you can give it a shot. 'z.py': import time
from x import NS
def test():
while True:
if not NS.mutex.locked():
NS.mutex.acquire() # will block
NS.z_cnt += 1
print('Message from z, x count is', NS.x_cnt)
NS.mutex.release()
time.sleep(5) 'x.py': # usage: import x
import _thread
import time
def run_z():
import z # z refer to x.NS
z.test()
def test():
while True:
if not NS.mutex.locked():
NS.mutex.acquire() # this is blocking
NS.x_cnt += 1
print('Message from x, z count is', NS.z_cnt)
NS.mutex.release()
time.sleep(4)
def main():
new_thread = _thread.start_new_thread(run_z, ())
test()
class NS:mutex=_thread.allocate_lock();x_cnt=0;z_cnt=0
print("write x.main() at REPL to start z loop on core 1 and x loop on core 0")
print("<ctrl+c> will abort x loop")
print("write x.test() at REPL to start x loop again")
print("<ctrl+c><ctrl+d> will soft-reset") On rpi-pico, the "z task" will run on core1 and the "x task" will run on core0. This is not the case on esp32. We will get similar results from rpi-pico or esp32 boards. I hope this is useful to someone. |
Beta Was this translation helpful? Give feedback.
Hi, yues concurrency in embedded code can be tricky to get working!
Just for a start, personally I try to avoid threads in micropython. Keeping objects in sync between threads can have a fair bit of complexity; and it's worth nothing that the rp2 only support 1 extra thread as it's implemented as code running on the second core.
Once using asyncio for an application (which I always use) I find it's best to try to keep everything in async unless its timing is particularly critical at which point I'll add Timer exceptions for time critical stuff.
Something that might be worth looking at are these couple of example asyncio pushbutton reading classes, both with debouncing: