RFC: Hardware API: finalising machine.Pin

C programming, build, interpreter/VM.
Target audience: MicroPython Developers.
tannewt
Posts: 51
Joined: Thu Aug 25, 2016 2:43 am

Re: RFC: Hardware API: finalising machine.Pin

Post by tannewt » Sun Oct 23, 2016 10:22 pm

deshipu wrote:@tannewt Actually, after a moment of thought, you already have your DigitalIO objects in the current machine API -- they are called Pin.
Sure but my original idea I was getting at was to split Pin as direct IO from Pin as a resource for a peripheral. Doesn't it make sense to split the class that does raw IO to a pin from the object that other's use as a reference? Whether there is a single DigitalIO or one for in and one for out matters less to me.

pfalcon
Posts: 1155
Joined: Fri Feb 28, 2014 2:05 pm

Re: RFC: Hardware API: finalising machine.Pin

Post by pfalcon » Sun Oct 23, 2016 11:00 pm

deshipu wrote:
pfalcon wrote: So, how do we want to deal with it? The current answer is "oops, we didn't think about it!"
I like how you have here answered your own question about why we need to revisit the hardware api spec.
Sorry, I don't agree - majority of things were well enough though out, and there're only few things which are worth revisiting.
I have never really had a problem that would require me to use a pin with an inverted signal. A simple "not" usually solved any issues. So I don't feel like there is enough information gathered to make a decision about this at the moment.
So MicroPython has always been about being able to write portable apps, i.e. portability of user apps among devices it runs on. Hardware API has the same goal. And without supporting inverted signals it's more or less hard, definitely not elegant. So, consider that we want to write an application which will light an LED for 0.1s, and then turn off for 0.9. The main, portable application would look like:

Code: Select all

import time
import app_config

while true:
    LED.value(1)
    time.sleep_ms(100)
    LED.value(0)
    time.sleep_ms(900)
This is 100% portable app which doesn't require any changes to run on any board (so, can be posted on PyPI and install from it via upip). The only change which is needed to be changes from board to board is an application config file, here app_config.py.

For one board, it would look like:

Code: Select all

from machine import Pin

LED = Pin("LED0", Pin.OUT)
For another e.g.:

Code: Select all

import machine
from machine import Pin

LED = Pin((machine.PORT_Z, 5), Pin.OUT)
And here's caveat: if for second board the signal which drives an LED is actually, the application won't work as expected. Yes, perhaps for this simple case one could add: boolean LED_inverted variable to app_config.py, but what if we're talking about an application with couple of dozen of input and outputs? Tracking "inverted" status explicitly and patching code to support it is quite cumbersome and not elegant. It would be different if that was just property of an object:

Code: Select all

LED = Pin((machine.PORT_Z, 5), Pin.OUT, inverted=True)
Note that I already mentioned that "inverted" is not a property of an (abstract) Pin object, it's property of a Signal object. But as I mentioned too, we can't afford to spawn so many entities, nor users would find it's nice to use construct like

Code: Select all

LED = Signal(Pin((machine.PORT_Z, 5), Pin.OUT), inverted=True)
So, the talk is about merging "normalized" Pin class and "normalized" Signal class into MicroPython Pin class, "denormalized", but optimized for usability.

Also some other points:
  • I'm +0 on this idea, because accepting it will kill my loved idea of "close to hardware" Pins. So, I hope there will be enough other votes. But if there won't be enough, some time later I'll likely regret that I didn't +1 it.
  • I strongly dislike idea of merging "unrelated" functionality into one entity. My proposal above is an exceptional case, driven by the idea of making MicroPython friendly to users and not fall behind the competition. (To illustrate my dislike for merging "unrelated" things, I'll be minus something on the idea of turning I2C.start() into I2C.start(addr, rw)).
Awesome MicroPython list
Pycopy - A better MicroPython https://github.com/pfalcon/micropython
MicroPython standard library for all ports and forks - https://github.com/pfalcon/micropython-lib
More up to date docs - http://pycopy.readthedocs.io/

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

Re: RFC: Hardware API: finalising machine.Pin

Post by deshipu » Mon Oct 24, 2016 7:42 am

I think we have a completely different approach to working on requirements for the API. For me it's "obvious" that the way to go about it is to write some code for a real use case, or take existing code for real use cases, analyze it, and see what is missing or could be improved. I like it, because it leads to clean and readable code in practice without too much overhead for "purity" sake. The down side is that you first need to collect the data for real use cases, so you can't do that with the initial design -- and that it inevitably leads to changing the design once you have the data. Like those pesky scientists without any backbone, who change their mind as soon as they see new data.

Your approach, of first setting some design rules, and then growing the API according to those rules by covering all the possible use cases that you can imagine is much more comfortable, because it doesn't require any real world data. It results in a pure and consistent API, and once you learn the rules used, you can easily guess any part of the API that you don't know yet. However, it depends heavily on setting just the right design rules at the start, and on imagining just the right use cases later. Even a very small error can (and will) lead to API that is either bloated or not useful. The solution to it is to have the rules set by a single very experienced person who will take all responsibility.

This is a fundamental difference in the approach, and I doubt we can reconcile it. As you can probably tell from the biased descriptions above, I prefer the first one, even though it is more work and more burden for the community. I have seen it used it the Python community many times, however, and I like the results. (Although there are also some pretty bad examples out there too.)

I'm not sure the two points of view can be easily reconciled, but I will certainly try.

As for your use case for inverted signals, I find it artificial for two reasons -- but this may be just because I have never actually stumbled upon this particular problem. The first reason is, for a LED that is built-in into the board itself, you would probably have a driver, similar to what pyb has for the LEDs and buttons and accelerometer, separate from the machine API, and the inversion would be handled there. Yes, there would be an additional object, but how many LEDs can a board have? In addition, this allows you support boards that have an RGB LED, or boards that have PWM available on their LEDs, in a transparent manner. Second, if you connect a lot of LEDs to the board externally (for instance, as a LED-based display similar to the micro:bit), you will want to create an object for handling them anyways -- not 40 objects for each of the LEDs separately, but a single "Display" object. That object can easily handle inverting the signal if you need that in some setups. I now remember, that I actually had this problem before, and this is exactly the solution I used -- but not for LEDs and ON/OFF signal, but instead for hobby servos and PWM signal.

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

Re: RFC: Hardware API: finalising machine.Pin

Post by Turbinenreiter » Mon Oct 24, 2016 2:00 pm

Trying to conclude on inverting Pins:
@deshipu: -1
@pfalcon: +0
@marfis: -1
@chrismas9: -1

Pro: you can simply invert a Pin when porting from one board to another where i.e. a LED is pulled up or down
Con: it's not actually a function that the Hardware has

Way I see it: machine implements the Hardware. On top of that, i.e. the LED module can provide the 'inverted' flag.
On the other hand, it literally costs us nothing and would in some cases obliterate the need for a i.e. LED module, because you'd only need what's already implemented in Pin.

I'd say when in doubt, stick to machine = Hardware, if the discussed functionality can be delivered by a Python module.

pfalcon
Posts: 1155
Joined: Fri Feb 28, 2014 2:05 pm

Re: RFC: Hardware API: finalising machine.Pin

Post by pfalcon » Mon Oct 24, 2016 3:20 pm

deshipu wrote:I think we have a completely different approach to working on requirements for the API. For me it's "obvious" that the way to go about it is to write some code for a real use case, or take existing code for real use cases, analyze it, and see what is missing or could be improved.
@deshipu, you're hard to communicate with. When there's an abstract choice in discussion, and one choice is obviously productive, and another obviously unproductive (such obvious that a productively oriented person wouldn't even considered the latter), you select the unproductive (you did it twice today on github). Here, the moment I posted a real example, you started to talk about need to consider real examples. The real example has been posted, please consider it.
Your approach, of first setting some design rules, and then growing the API according to those rules by covering all the possible use cases that you can imagine is much more comfortable, because it doesn't require any real world data. It results in a pure and consistent API, and once you learn the rules used, you can easily guess any part of the API that you don't know yet. However, it depends heavily on setting just the right design rules at the start, and on imagining just the right use cases later. Even a very small error can (and will) lead to API that is either bloated or not useful. The solution to it is to have the rules set by a single very experienced person who will take all responsibility.
That's no how design works. It starts with initial requirements, then specification based on that requirements, implementation, then adjustments based on that implements, then many more implementations and further adjustments. You talk about the same things, but I'm not sure you pay enough attention to ordering and meaning of the different stages. For example, initial requirements are hard requirements, serving as Occam's razor for this project, and adjustments are adjustment, not "everything is wrong, let's do everything differently", and "many more implementations" is non-similar, widely spaced implementations, not just "8 ports". Practical testing is very important part of it, and your detailed feedback is much appreciated (I especially appreciate detailed write-up, beyond just "many things feel wrong". To bad I didn't have a chance to review and reply to them before this thread popped up). But please don't underestimate need for stick to the requirements and proper ordering and scoping of the project.
As you can probably tell from the biased descriptions above, I prefer the first one, even though it is more work and more burden for the community.
If so, you may volunteer to write Hardware API for unix port, to serve as a usual foundation for further development of MicroPython and testing.
I'm not sure the two points of view can be easily reconciled, but I will certainly try.
That's really great, because "theoretical" and "practical" approaches should go hand in hand and intertwine, not be opposed to each other.
As for your use case for inverted signals, I find it artificial for two reasons -- but this may be just because I have never actually stumbled upon this particular problem.
MicroPython (and its apps) being portable across boards is the hard initial requirement. (With a usual disclaimer that if BDFL suddenly denounce it, I'll find myself in the "oops" position of "what project did I work on for last 3 years?")
The first reason is, for a LED that is built-in into the board itself, you would probably have a driver, similar to what pyb has for the LEDs and buttons and accelerometer, separate from the machine API, and the inversion would be handled there.
No, no drivers. MicroPython mandates only machine API, and who want to write drivers anyway? Where are you people, why you still haven't written *mandatory* machine API for unix port? Where're drivers for LEDs of my TV, fridge, and washing machine?
Yes, there would be an additional object, but how many LEDs can a board have? In addition, this allows you support boards that have an RGB LED, or boards that have PWM available on their LEDs, in a transparent manner. Second ...
Sorry, but this argument can be reduced to "no matter what hardware API is there, users will be able to what they need - indeed, they simple will have no choice than to hack and mock something". That's indeed true, but the whole talk about Hardware API is how to give as much as possible cleanliness and power into users hands with the minimal size of this API and maximal portability across hardware. If you write that you have faced that issue, then you understand it. And good API allows to do simple things in a simple way, while still allow complex things, and extensibility, to allow to add even more complex and or more convenience on top of it. The above presented is the usecase which can be simple if inversion is supported on Pin level, and no longer simple if a user needs to deal with it (consider if user is a kid or novice; or purist, yeah; or pragmatic, who knows that elsewhere this issue resolved without his need to for him to keep it in mind each time he does something with a Pin).
Awesome MicroPython list
Pycopy - A better MicroPython https://github.com/pfalcon/micropython
MicroPython standard library for all ports and forks - https://github.com/pfalcon/micropython-lib
More up to date docs - http://pycopy.readthedocs.io/

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

Re: RFC: Hardware API: finalising machine.Pin

Post by Turbinenreiter » Mon Oct 24, 2016 4:00 pm

Three pages of discussion for one simple flag - API design is _hard_ :shock: :D

Lysenko
Posts: 62
Joined: Wed Aug 17, 2016 1:21 pm

Re: RFC: Hardware API: finalising machine.Pin

Post by Lysenko » Mon Oct 24, 2016 8:20 pm

Are you trying to define a HW API for a chip or a board?

If the former (which is better for portability) then there should be no assumptions made about what individual pins are connected to. Chips don't have LEDs or buttons.

If the latter (which arguably makes things simpler for a limited set of use cases) then you shouldn't be too concerned about portability: applications that assume specific peripherals are present and talk to them directly aren't portable anyway.

"Code" may be portable so long as it doesn't touch hardware, but whole applications are unlikely to be. How many ESP8622 apps run on a pyboard? My guess would be next to none because if it was implemented on an ESP8622 it probably has a hard dependency on networking.

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

Re: RFC: Hardware API: finalising machine.Pin

Post by Turbinenreiter » Mon Oct 24, 2016 8:44 pm

How many ESP8622 apps run on a pyboard? My guess would be next to none because if it was implemented on an ESP8622 it probably has a hard dependency on networking.
Don't think about apps, think about drivers. When I implement a module for i.e. a sensor I want that to be usable to people across all ports.

I think the consensus is that 'machine' is the microcontrollers API.

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

Re: RFC: Hardware API: finalising machine.Pin

Post by deshipu » Mon Oct 24, 2016 10:42 pm

Here is a very practical reason for the signal inversion not being put on the pin objects: pin objects are singletons. That pretty much makes their state global. If you put anything application-specific in there, you will get conflicts between two parts of your code using a shared resource.

Consider the following (a bit artificial, but simple) example. You have a LED on an ESP8266 board on pin gpio2, which is active low. You decide to wrap your whole program in a try-except cause that upon error sets the pin low to make the LED shine and signal a problem. So far so good. Next, you use in you program a library for, say, transmitting files. That library is written in a portable way, and as one of the configuration parameters it takes a pin for a LED on which it will indicate activity -- but it expects it to be active high. No problem, you just invert the pin's signal and pass it to the library like that. Your error handler just stopped working.

Lysenko
Posts: 62
Joined: Wed Aug 17, 2016 1:21 pm

Re: RFC: Hardware API: finalising machine.Pin

Post by Lysenko » Mon Oct 24, 2016 10:47 pm

Turbinenreiter wrote: Don't think about apps, think about drivers. When I implement a module for i.e. a sensor I want that to be usable to people across all ports.
I think the consensus is that 'machine' is the microcontrollers API.
I was referring to this:

"MicroPython (and its apps) being portable across boards is the hard initial requirement."

...comment earlier in the thread.

machine from the WiPy docs is pretty close to a chip API, but pyb is clearly a board API and bundles in things (the LCD) that aren't even part of the board. I was attempting to clarify which model was being pursued here.

Post Reply