Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 6536

MicroPython • Re: PicoW MicroPython project: I added a bunch of debug lines to trace a hang and it somehow fixed my crashes

$
0
0
@JoeBobby

As this is the second post where you allude to some sort of argument I refute that this. As you would have read the case for using the treading module in MP is dubious because the Micropython team have not provided any documentation and there has been posts by those who have got errors when using this module. When your original post appeared to indicate the threading module could not even be used in a simple way I had a quick go with a quick hack and found it would work if your simple example was coded correctly (though do not to take the quick hack as a good example to base your code on)

As I already thought I would have look at the threading module sometime as I had not had any problem with some small example play programs I had done in the past ,I thought now was a good time to see if it was worth having a further look to see if it was usable for any more serious work. I did wonder how many of the problems that had been previously posted were due to the lack of documentation on the way to use the module and how much was due to it being a buggy module. To that end I sort to elicited some code examples that highlighted bugs. gmx was kind enough to give an example, albeit a rather contrived one, that led me to think I would go on to experiment some more. I did post a tease to gmx on his continued remarks on using C as not being applicable to seeking to determine if threading with micropython could be used, but if you construe this as an argument then I apologise for a post that missed the mark.

I do not have any preference for using mp or C or to argue about the merits of each. If you want to you can find adequate guidance on the relative merits of using either language. But I suggest that you may find using mp for prototyping and working through your design may prove helpful even if you go on to develop your production code in c. You may find it adequate just to speed up the slower parts of your mp code by using the viper decorator to speed up a critical bit of code, or perhaps create a module in c just for the bit of code where you find a bottleneck. Or perhaps you will find a suitable refactoring of your mp code where you are finding the bottleneck is all that is required. Or indeed you may prefer to code it all in C/C++

Whilst I occasionally make a post where I think I may help out, I've already indicated I do readily do so the help out with some AI generated code. Even more so I do not wish to give assistance for nascent commercial product. But best wishes for your adventure and I hope it succeeds.

On my attempts to see if I can use the mp threading module then, albeit based on minimal test code snippets, I find it to be worthwhile for a consideration in a hobby project. In my perusal of the module I noted that there was an update made last July that may have solved some of the previous issues. When using the module I took the tack of ensuring the lock acquire and release methods were used in the same way one would use interrupt service routines, just for the very minimal intervention during a read/write of a data item. I also made use of the asyncio ThreadSafeFlag. I found some excellent classes created by Peter Hinch, see https://github.com/peterhinch/micropyth ... threadsafe where there are examples of using threadsafe queues, events and messages that can be used when utilising the second core on a pico.

For one of my test snippets I took some code from an existing project I had that ran a ili9341 in touch screen mode, and a temperature sensor. I put a modified version on core0 and put a simple web server running on core1 and that worked. Other small tests, ensuring I made use of the threading module lock acquire / release also worked out well.

Obviously there is a limitation with the threading module on the pico in that its only designed to run one additional thread on core1 and the code takes the form of running a function. Also I noted that asyncio should only be used on core0 and running tasks both on core0 and core1 would be unlikely to succeed. (I did not try this, but this comes from write ups about micropython asyncio.)

When desiring to have many concurrent task working then using asyncio is the way to go. So what would one consider a suitable candidate to run on core1. I thought maybe a function that had some longer running calculations might be a good candidate to offload onto core1. I constructed a short test to this end, though my example is admittedly not exactly a heavy computational task, but it does show the use of the threadsafe message class as found in the link given above and I post it here as maybe folk are not aware of these excellent libraries.

The essence of the code example is that on core0 some asyncio tasks run, and one of the tasks is awaiting for the threadsafe message flag to be set from core1. The message module allows both the setting of the flag and the passing of data. The task then takes action on the data received and then clears the flag ready for it to be set again. Should the function on core1 attempt to set the message flag whilst its already set (i.e it has not been cleared) then only the latest 'set' will be used when the flag has eventually been released which was exactly the process I required for this test example. I hope that all makes sense. Ofcourse the Peter Hinch threadsafe library would need to be loaded onto the pico.

The function that runs on core1 has sub functions one of which is meant to substitute for a link to a GPS sensor that returns Latitude and Longitude coordinate points. For the demo these coordinates are picked off a list and used to calculate a compass bearing between two coordinates and the distance between them. The calculated data is then passed to the awaiting task on core0 to steer a supposed robot to the correct compass bearing from magnetometer sensor readings. Just a simple and contrived test and an illustration of the threadsafe message whilst using both the pico's cores.

So for me, I've concluded that the use of the threading module is worthy of consideration if I undertake a project that could make use of the second core. But I'm not encouraging others, who may well wish to steer clear whilst this module is marked as experimental and without proper documentation, to take a similar line.

I also think that quite enough from me on this topic. 8-)

Code:

import asyncioimport _threadimport timefrom math import asin, cos, radians, sin, sqrt, atan2, degreesfrom threadsafe import Messagemsg = Message()# ---------------------------------------------------------# compass bearing and distance calculations to run on core1def gps_calcs():    global msg    seq_read = 0    def read_sensor():        """ get GPS lat & lon from GPS sensor """        sensor_data_1 =[(51.506990, -0.088209),(51.507001, -0.088209),(51.507040, -0.088209)]        sensor_data_2 =[(51.508927, -0.087522),(51.508927, -0.087540),(51.508927, -0.087600)]        nonlocal seq_read        point_1 = sensor_data_1[seq_read]        point_2 = sensor_data_2[seq_read]        seq_read += 1        if seq_read > 2: seq_read = 0        return point_1, point_2            def compass_bearing(point1,point2):        # compass bearing from first lat,lon (point1) to second lat, lon (point2)        if (type(point1) != tuple) or (type(point2) != tuple):            raise TypeError("Only tuples are supported as arguments")        lat1 = radians(point1[0])        lat2 = radians(point2[0])        diffLong = radians(point2[1] - point1[1])        x = sin(diffLong) * cos(lat2)        y = cos(lat1) * sin(lat2) - (sin(lat1)                * cos(lat2) * cos(diffLong))        initial_bearing = atan2(x, y)        initial_bearing = degrees(initial_bearing)        return round(((initial_bearing + 360) % 360) ,1)           def distance(point_1, point_2):        # haversine calculation formula        r = 6371  # Earth radius in kilometers        lon_1, lon_2 = radians(point_1[1]), radians(point_2[1])        lat_1, lat_2 = radians(point_1[0]), radians(point_2[0])        h = (sin((lat_2 - lat_1) / 2)**2                + cos(lat_1) * cos(lat_2) * sin((lon_2 - lon_1) / 2)**2)        return (2 * r * asin(sqrt(h)))        while True:        p1, p2 = read_sensor()            msg.set((compass_bearing(p1,p2),distance(p1,p2)))        time.sleep(2)        # --------------------------------------------------------------------async def receive_data(msg):    while True:        response = await msg        print(f'bearing to destination: {response[0]} degrees,  distance:{response[1]*1000:.3f} metres')        await steering(response[0])        msg.clear()async def steering(bearing):    """ steer rover to align to the bearing"""    # get magnetometer heading    # compare to bearing (adj for magnatic declination of location)    # adj steering servo    # waste a bit of time pretending to do the above    for _ in range(10):        await asyncio.sleep(0.01)    print(f'steering {bearing} degrees') async def main():    global msg    asyncio.create_task(receive_data(msg))    await asyncio.sleep(1)    # start core1 function to read gps, cal bearing/distance, and send result    _thread.start_new_thread(gps_calcs,())    while True:        await asyncio.sleep(0)    try:    asyncio.run(main())except KeyboardInterrupt:    print('Ctl C')finally:    print('finally is being actioned')    _thread.exit()    asyncio.new_event_loop()    

Statistics: Posted by SirFico — Fri Dec 13, 2024 4:47 pm



Viewing all articles
Browse latest Browse all 6536

Trending Articles