Standard API for drivers?

Discuss development of drivers for external hardware and components, such as LCD screens, sensors, motor drivers, etc.
Target audience: Users and developers of drivers.
User avatar
platforma
Posts: 258
Joined: Thu May 28, 2015 5:08 pm
Location: Japan

Re: Standard API for drivers?

Post by platforma » Thu May 12, 2016 10:34 am

Perhaps adding an iconic example of a simple driver and it's interface would be a good idea!

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: Standard API for drivers?

Post by deshipu » Thu May 12, 2016 10:49 am

platforma wrote:Perhaps adding an iconic example of a simple driver and it's interface would be a good idea!
Great idea, please go for it! (yes, I'm being manipulative here ;-) )

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: Standard API for drivers?

Post by deshipu » Thu May 19, 2016 9:07 am

I'm now writing a driver for a distance sensor (VL6180), and it, as many sensors, requires a small delay between starting the measurement and reading the result. The usual way in the brave and wild world of Arduino to deal with this situation is to simply insert a random delay in there and move on. But of course this can be disastrous to any code that needs to do multiple things in a loop, especially when you use yield to implement co-routines, etc. So I was thinking about a better approach.

One simple way, and in fact one that directly translates into the sensor's i2c calls, is to have two separate methods, one for starting the measurement, and one for reading it, and clearly document that there has to be a delay between them. You can even have a third method that includes the delay, for people who just don't care.

But I was thinking, why don't we use the power of Python, and, in particular, a pattern that has been used in its standard library already (for instance, in https://docs.python.org/2/library/conte ... extmanager). That is, still have a single method, but instead of a function make it an iterator generator, and put yield in place of the delay. Then there is just a single function, but the user still has freedom as to how to implement the delay. It would look something like this:

Code: Select all

def measure():
    start_measurement()
    yield
    return read_measurement()
    
measurement = measure()
time.sleep_us(10)
result = next(measurement)
What do you think?

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

Re: Standard API for drivers?

Post by pythoncoder » Thu May 19, 2016 9:37 am

It's a neat idea but the implementation needs improvement. measure() is a generator function and the line

Code: Select all

measurement = measure()
creates a generator without running it. The line

Code: Select all

result = next(measurement)
will result in a StopIteration error without yielding a value. Try something on these lines:

Code: Select all

def rats():
    print('Got here') # do the measurement
    yield
    yield 'Result' # fetch the result
which produces the following at the REPL:

Code: Select all

>>> a = rats()
>>> next(a)
Got here
>>> next(a)
'Result'
>>> 
Note the lack of output from a = rats()
Peter Hinch
Index to my micropython libraries.

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: Standard API for drivers?

Post by deshipu » Thu May 19, 2016 11:54 am

Hmm... seems my generator-fu is a little bit rusty. I have to check how that @contectmanager decorator is doing it with a single yield...

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: Standard API for drivers?

Post by deshipu » Thu May 19, 2016 11:59 am

OK, after some experimenting (sorry I didn't do that before posting, I though I remember the behavior correctly), I conclude that the way this works is too magical and un-intuitive. I guess the right solution would be to use futures/promises/deferreds here, but that's not in stdlib.

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: Standard API for drivers?

Post by deshipu » Thu May 19, 2016 12:09 pm

By the way, anybody knows how expensive closures are? I would expect them to be rather costly, as they have to keep all the context and all. But something like this could look nice:

Code: Select all

def measure():
    start_measurement()
    def result():
        return read_measurement()
    return result
    
measurement = measure()
time.delay_us(10)
result = measurement()
I could even record the time when the measurement was started, and throw an exception if you try to check too soon!
Hmm... actually...

Code: Select all

class Sensor:
    def measure(self):
        self.start_measurement()
        return self.read_measurement
 
would work too, and without the closure.

MCHobby
Posts: 52
Joined: Mon Jan 26, 2015 2:05 pm
Contact:

Re: Standard API for drivers?

Post by MCHobby » Sat Jun 27, 2020 1:27 pm

Hi,
I would like to share some concept I'm using when writing driver.
  • API should also been Plateform Agnostic, so the __init__() should receive the required bus (and address when appropriate).
  • API should use machine.I2C, machine.SPI, ... in priority.
  • Driver should always include code sample , detailled readme (as manual) an comprehensive wiring example (Markdown is great).

Code: Select all

from machine import I2C
from trackball import Trackball
import time

i2c = I2C(2) # Y9=scl, Y10=sda or Pyboard-Uno-R3 (I2C over pin 13)

# initialise le trackball
trackball = Trackball( i2c )
Here how I do conduct the work... not perfect but available for the mass.
https://github.com/mchobby/esp8266-upy

I do also like the example is to previous response. KISS concept... like it

Post Reply