Suggestion - how to make micropython much simpler

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
thorwald
Posts: 15
Joined: Wed Mar 19, 2014 2:38 pm

Suggestion - how to make micropython much simpler

Post by thorwald » Thu May 14, 2015 4:45 pm

One thing that is still a bit undeveloped with embedded platforms for beginners(arduino/mbed/micropython) is concurrency and non-blocking libraries - and it makes it harder for beginners to write code, because of timing issues. This is especially true when you want to mix 2 projects from 2 different sources.

At the moment there all kinds of ideas how to solve these issues ,and they surely improve usability a great deal , for example the asynchrouns library by pfalcon. But they're rarely used by beginners(at least in the arduino).

So, what could be the simplest concurrency abstraction for beginners ?

What about software "chips"/"Shields" ? You basically just instantiate them(power them up), send them commands, and check if they have new data to read, and if so - read the data ?

This way requires almost zero learning(unlike all common current common concurrency methods) , because as part of learning to use embedded platform users already learn the concept behind chips/shields.

So user code should look something like this:

c = KeyboardScannerChip(scan_inputs, scan_outputs, speed) ; \\key scanner is a concurrent module

c.change_speed(3);

c.key_available(){
data = read_key();
}

Simple example, i know , but this may as well be a complex machine with accurate timing abstracted by this block.

This i believe is where micropython can really shine. Why ?

1. Currently many choose arduino over micropython because of more libraries. by requiring almost zero learning, it might have a decent chance to be preferred by the beginners , and significantly increase the level of abstraction and code reuse in the community - way beyond the levels of the arduino - And encourage faster library creation.

2. Python has better and more usable abstraction tools as a language(for example introspection and decorators), i believe those could apply well to making the tasks of writing such libraries much easier.

3. Having more memory on the platform could surely help.


Of course the hard part is how to implement this - which i don't have good ideas to how .

So what do you think ?

cloudformdesign
Posts: 35
Joined: Wed Mar 11, 2015 7:48 pm

Re: Suggestion - how to make micropython much simpler

Post by cloudformdesign » Thu May 14, 2015 5:43 pm

I would think you could just implement your "chips" library using the asyncio library and it could work like this.

If the uasyncio library is anything like the stanard one, you just add it to the event loop and wallah! It will do the "asynchronous io" for you.

Also, why is the example code written in C?

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

Re: Suggestion - how to make micropython much simpler

Post by pythoncoder » Sat May 16, 2015 7:05 am

@ thorwald Many of the Micropython device drivers already work in the way you suggest and are designed to enable the user to opt for blocking or non-blocking I/O. Look at the drivers for the nRF24L01 radio or the MPU9150 inertial navigation unit just to give examples I've recently used. What you propose is standard practice.
Peter Hinch
Index to my micropython libraries.

thorwald
Posts: 15
Joined: Wed Mar 19, 2014 2:38 pm

Re: Suggestion - how to make micropython much simpler

Post by thorwald » Sat May 16, 2015 12:13 pm

Thanks pythoncoder - i'll have a look.

Turbinenreiter
Posts: 288
Joined: Sun May 04, 2014 8:54 am

Re: Suggestion - how to make micropython much simpler

Post by Turbinenreiter » Sat May 16, 2015 2:26 pm

I battled this problem in my BMP180 library. Maybe that sensor and library are a good practical starting point to figure out how to do this well. The way I implemented it works, but I think there is a lot of room to grow. I.e. there should be blocking functions alongside the non-blocking, for when time of read is critical.

thorwald
Posts: 15
Joined: Wed Mar 19, 2014 2:38 pm

Re: Suggestion - how to make micropython much simpler

Post by thorwald » Sat May 16, 2015 5:45 pm

Turbinenreiter , Maybe the ideal way , if possible , is when writing a module, you just write something according to some specifications , and mostly automatically you'll get blocking functions, and non-blocking functions , both arranged in the optimal form for the user ?

So for example :

class KeypadDriver(Driver):

def __init__ (... ):

@input
def get_scan_speed(speed):

@output
def return_key():
# do something
return last_read_key

And than micropython will automatically generate both blocking and non-blocking interfaces ? For example , things returned from return_key will be automatically put inside a queue. If you use a non-blocking function you'll have a way to to check the queue and read items - while if you use the blocking function you'll wait until something in queue and read it ?

Like:

k = KeypadDriver(..)

#blocking
data = k.return_key_blocking()

#non-blocking
if k.return_key_availble():
data = k.return_key_read()

When return_key_blocking and return_key_read are automatically generated?

This way , writing drivers could be very easy - just follow a few easy rules , while driver users can get the optimal driver ?

BTW editor doesn't give tabs, so code is a bit messy.

Turbinenreiter
Posts: 288
Joined: Sun May 04, 2014 8:54 am

Re: Suggestion - how to make micropython much simpler

Post by Turbinenreiter » Sat May 16, 2015 7:32 pm

Code: Select all

'''
example-sensor.py
A generic sample class to figure out best approach for specification to get
librarys with good support for non-blocking and standard interface.
Sebastian Plamauer
'''
import time

delay = 2   # Sensor read delay time

class Sensor():
    '''
    Generic sample class do find a good specification to write librarys with 
    good support for non-blocking functions.
    '''

    def __init__(self):
        '''
        A measurement_ready_at is defined for every type of measurement with
        known delay. If delay is unknown, there has to be a data_ready-register
        on the Sensor.
        '''
        self.measurement_ready_at = time.time()

    def _start_measurement(self):
        '''
        starts the measurement, i.e. by writing to registers of the sensor.
        '''
        print('start')
        self.measurement_ready_at = time.time()+delay

    def _read_measurement(self):
        '''
        reads the measurement, i.e. by reading the registers.
        There is no protection against reading to early.
        '''
        return 125

    def _measurement_ready(self):
        '''
        checks if the measurement is ready, either by checking ready_at or
        using data_ready-register of Sensor.
        '''
        if time.time() > self.measurement_ready_at:
            return True
        else:
            return False

    def get_measurement_blocking(self):
        '''
        simply start measurement and wait for it to finish.
        '''
        self._start_measurement()
        while not self._measurement_ready():
            pass
        return self._read_measurement()

    def get_measurement_non_blocking(self):
        '''
        generator yielding None until finished, then yields measurement.
        '''
        self._start_measurement()
        while not self._measurement_ready():
            yield None
        yield self._read_measurement()
        return

if __name__=='__main__':

    # create Sensor object
    s = Sensor()

    # print measurement blocking
    print(s.get_measurement_blocking())

    # print measurement non-blocking
    g = s.get_measurement_non_blocking()    # start generator
    while True:                             # start loop
        # do stuff
        try:                                # try getting generator respond
            r = next(g)                     # save to r
        except StopIteration:               # when StopIterator (return)
            print(r)                        # print last r - holds measuremnt
            break                           # stop loop
This could be a base class to derive from. You then would have to rewrite all _functions, fitting to your device, i.E:

Code: Select all

import time
import sensor

delay = 3

class testSensor(sensor.Sensor):

    def __init__(self):
        '''
        A measurement_ready_at is defined for every type of measurement with
        known delay. If delay is unknown, there has to be a data_ready-register
        on the Sensor.
        '''
        self.measurement_ready_at = time.time()

    def _start_measurement(self):
        '''
        starts the measurement, i.e. by writing to registers of the sensor.
        '''
        print('start')
        self.measurement_ready_at = time.time()+delay

    def _read_measurement(self):
        '''
        reads the measurement, i.e. by reading the registers.
        There is no protection against reading to early.
        '''
        return 225

    def _measurement_ready(self):
        '''
        checks if the measurement is ready, either by checking ready_at or
        using data_ready-register of Sensor.
        '''
        if time.time() > self.measurement_ready_at:
            return True
        else:
            return False

thorwald
Posts: 15
Joined: Wed Mar 19, 2014 2:38 pm

Re: Suggestion - how to make micropython much simpler

Post by thorwald » Sun May 17, 2015 12:56 am

Turbinenreiter, that's a well written example and it can work well for the sensor.

But what about the more general case ? Let's say our goal is to enable people to combine full systems.

So for example somebody built a hex-robot system that can also sense when it's going outside the house which isn't recommended. That's one system(which need real-time operation).

Another guy built some sensor that can sense if there's a cat before it(don't ask me how) , it was a part of system to feeding cats.

It would be fun to easily combine them into one system - a cat chasing hex-robot that is smart enough not to go outside - while not messing up the real-time performance of them.

Made up example, but the principle holds - mixing systems is very useful and powerful.

And for that we need something more generic than sensors - and i think "chips"(as i put in the beginning of the thread) and the rough idea of how to build them , might be a useful starting point.

stijn
Posts: 735
Joined: Thu Apr 24, 2014 9:13 am

Re: Suggestion - how to make micropython much simpler

Post by stijn » Sun May 17, 2015 7:23 am

Let's say our goal is to enable people to combine full systems.
No matter how generic you make your interfaces, you cannot simply combine two full systems by adding their corresponding software together (at least I have the impression that's what you're after) or in another simple way. Say you have code for chasing cats and code for not leaving the house: in the end both of these pieces are going to need access to the motion system of the robot to make it do whatever it needs to do, so you cannot just combine that because part A would be saying 'go X' while part B would be saying 'go Y'. Instead you'd use a single control loop in which you read from both sensors and then decide what to do based on that input. The more sensors you get, the more complicated thats get and a typical pattern to cope with that is to use a state machine.

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

Re: Suggestion - how to make micropython much simpler

Post by pythoncoder » Sun May 17, 2015 9:17 am

In general if you write a nonblocking driver, providing a blocking interface is trivial. The nonblocking driver provides two interfaces: a device_ready() test and device_use(). device_ready() returns immediately, device_use() returns immediately if it's ready. device_ready() may provide some initialisation to initiate the reading (if it's a sensor). In principle the blocking interface is just

Code: Select all

def device_use_blocking():
	while not device_ready():
		pass
	device_use()
I'm not saying the nonblocking case is necessarily simple but, having developed it, the blocking interface is surely easy to implement.
Peter Hinch
Index to my micropython libraries.

Post Reply