Proposal: Controlling general-purposal IO pins (GPIO)

412 views
Skip to first unread message

Chris Jones

unread,
Sep 25, 2014, 3:35:26 PM9/25/14
to dev-w...@lists.mozilla.org
... cue the "GPIO on the web platform?? wat" comments ... OK, can we move
on now? :)

This is a very early-stage document that I'm floating around for early
feedback and collection of requirements/use cases that are omitted here.

What is GPIO?
``````````````````````
Hobbyist development boards like the Raspberry Pi and Beaglebone Black
include pins that aren't assigned a specific purpose when the board is
developed. (Hence, "general purpose".) Hobbyists use these GPIO pins to
control a dizzying variety of electronics, from simple LEDs to robotics.
If you took a basic microelectronics class, you may have programmed a
microcontroller to make LEDs blink and buzzers buzz when a button was
pressed: that's GPIO.

Why a GPIO API?
``````````````````````````
There's been interest in bringing the web platform to hobbyist devices in
the past few years. In particular, Firefox OS has been ported to run on
the Raspberry Pi in the past, and a new effort is underway. The use cases
for web code on these devices encompass GPIO, just as the use cases do for
programmers working in C/C++/python/... on those boards. Programmers using
a GPIO API in a web-OS-environment like Firefox OS could see some distinct
advantages: (i) their code doesn't need to run as "root" to use GPIO; (ii)
the runtime can enforce exclusive access to pins; (iii) their pin
configurations and operations can be sanity-checked.

By definition, some of the capabilities of a GPIO API overlap with other
APIs, for example controlling lights or reading input from certain kinds of
sensors. However, the higher-level APIs are designed for a static board
configuration, whereas GPIO is intended for a dynamic user-designed
configuration. I see room in the future for building GPIO configuration
"under" higher-level APIs, so that I could f.e. instantiate a high-level
thermal sensor instance from a low-level dynamic GPIO configuration. But
for now I'm going to wave my hands and say "extensible web" and not discuss
this further here ;).

GPIO capabilities and use cases
````````````````````````````````````````````````
Because GPIO pins are general-purpose by definition, the set of use cases
is unbounded. But I'll provide some examples with each.

o Digital output: set a pin's output to "high" (+V) or "low" (ground).
Example: turn an LED on and off.
o Digital input: check if a pin's input is high or low. Example: check if
a switch is open or closed.
o Digital-input interrupt: fired when a digital input either changes,
changes low->high, or changes high->low. Example: be notified when a push
button has been pressed down.
o Analog input (ADC): read an analog input voltage between ground and a
reference voltage (with a resolution determined by hardware). Example:
read a thermistor.
o Analog output (DAC): output an analog voltage between ground and a
reference voltage (resolution determined by hardware). Example: play audio
through a speaker.
o Pulse-width modulation (PWM): output a digital square wave with a
specified duty cycle and angle. The frequency of the square wave is often
determined by hardware and is usually 500-1000 Hz. Example: control a
servo motor.

Not all devices have all these capabilities. Further, different pins
usually have different capabilities. A common configuration is

o All pins capable of digital input/output
o Subset of pins capable of raising digital-input interrupts
o Subset of pins capable of analog input
o Subset of pins capable of *hardware* PWM. (PWM can be emulated by
software on any digital-output pin).

Security
````````````
GPIO access is kind of an odd duck. The interface is low-level and
exceptionally dangerous; buggy code could even set fire to peripheral
hardware in extreme cases. But the primary use case is power-user
hobbyists who are aware of these risks and want to run code to control
hardware they've built. (Remember, the "competition" in this space is
python scripts and C code running as root.) For now, I'll wave my hands
and say GPIO should be a "certified" permission and we can worry about
developer ergononomics down the road (though it is important).

Strawman API
``````````````````````
There's not much "design space" for GPIO because the capabilities are so
basic. So the APIs used on different platforms generally look about the
same. This strawman is an attempt to follow those conventions in a "webby"
way. The following is pseudocode, not any literal *IDL.

interface Gpio { // navigator.gpio
readonly attribute Map<string, GpioPin> pins; //[1]

[throws]
Promise requestPins(GpioPin[] pins); //[2]

void releasePins(GpioPins[] pins); //[2]
};

interface GpioPin : DOMEventTarget { //[3]
readonly attribute string id; //[4]
readonly attribute Set<string> aliases; //[4]

readonly attribute Set<string> capabilities; //[5]

[throws]
void writeDigital(bool value); //[6]
[throws]
bool readDigital();
[throws]
float readAnalog();
[throws]
void pwm(float duty, float angle=0, float freq=undefined);

[throws]
void writeAnalog(float value); //[7]

readonly attribute int? readAnalogResolutionBits; //[8]
readonly attribute int? pwmFrequencyHz;
readonly attribute int? writeAnalogResolutionBits;
};

Notes

[1] The intention is to enumerate available pins. The set of available
pins are known at UA startup time, so there's no problem with enumerating
them synchronously. The pin objects are mapped by GpioPin.id and all
GpioPin.aliases; see note [4] below. That means more than one map entry
may point at the same GpioPin.

[2] requestPins()/releasePins() are intended to "lock" a set of pins for a
particular requestee. Multiple code modules (perhaps across applications,
though I don't have use cases for that in mind) may wish to request pins.
This interface allows them to not step on each other and sanity-check
allocations. Note that a hobbyist must decide on a pin allocation before
running an application that uses GPIO, because the hobbyist has to set up
the hardware to match. requestPins() guards against software bugs that
mismatch the hardware design. (This interface also allows implementors to
optimize pin access in certain ways.)

[3] I propose exposing digital-input interrupts through DOM events, so
applications can listen for them with |addEventListener(event)|. Here's a
list of strawman event names
* "digitalchange" --- digital input level changed.
* "digitalup" --- input level changed low->high
* "digitaldown" --- output level changed high->low
If a particular interrupt type isn't supported by the hardware, UAs could
either be forced to emulate them through polling, or be allowed to throw an
exception. I don't have a strong opinion on that right now. (I lean
towards requiring emulation.)

[4] Pins are documented by board manufacturers using a set of identifiers.
Often there are multiple names for the same pin. For example: on the
Raspberry Pi, there is a pin that's called pin "10" by the RPi SoC
(Broadcom 2835). The RPi documentation calls this pin "GPIO15"; that's the
pin's primary name. This pin also has the alias "UART0_RXD". So with this
strawman interface,
pin.id = "GPIO15"
pin.aliases = { "10", "UART0_RXD" }
and then |navigator.gpio[id]| for id in { "GPIO15", "10", "UART0_RXD" }
returns a reference to the same GpioPin.

[5] Let programmers write sanity checks like |"readAnalog" in
pin.capabilities|. Also lets developers probe capabilities without having
to look up external UA and board docs (get capabilities "from the horse's
mouth"). I don't know what the current style is for this kind of
capability-checking interface, so alternative suggestions welcome.

[6] These are the fairly standard GPIO access APIs you'll find on a variety
of boards. Typing is open to discussion. A few specific notes
* writeDigital(bool value) --- some systems let you use symbols/strings
like "high" or "low". I don't have a strong opinion on whether to allow
that. writeDigital(1) / writeDigital(0) are pretty clear on their own IMO.
* float readAnalog() --- return a value in the range [ 0.0, 1.0 ].
* void pwm(float duty) --- duty cycle is value in the range [ 0.0 1.0 ].
Calling one of these functions on a pin that doesn't support that
capability would throw an exception.

[7] The Arduino API uses writeAnalog() to refer to PWM. I'm also not
familiar with a hobbyist board that has an integrated DAC (though I'm sure
one exists). So I'd be happy to drop this interface for a v1 API.

[8] Let programmers see hardware capabilities. Similar in vein to [5].
Attributes that don't make sense for a given pin, for example
|readAnalogResolutionBits| on a pin that's not capable of analog input,
would not be present.

Issues
``````````
Most GPIO APIs provide a function to explicitly set a pin's mode, for
example |pinMode(INPUT)|. I'd like mode setting to happen implicitly if
possible. An alternative is to make the pin mode one of the parameters to
|requestPins()|, and then lock the pin to that mode. It doesn't really
make sense to change a pin mode from input to output in a live circuit; my
guess is that's usually programmer error and it would be nice for the
runtime to flag those. But most hardware is capable of "live" mode
switches, so perhaps this low-level API should quietly allow them.

Some GPIO use cases work "better" when the system can respond with low
latency. Hitting real-time constraints will never be possible in this
setup, but I propose we keep WebWorker threads in mind for this API. It
might be useful for some applications to do GPIO on a separate,
lower-latency worker thread.

Many people who've read this far are going to be asking, "what about SPI /
i2c / RS232?" They're definitely interesting for this class of
applications, but let's discuss them separately.

Implementation notes
````````````````````````````````
I'm confident that these APIs can be efficiently implemented on the boards
I'm familiar with. Happy to follow up on implementation questions.

Comments and questions welcome.

Cheers,
Chris

Lars Knudsen

unread,
Sep 25, 2014, 3:44:36 PM9/25/14
to Chris Jones, dev-webapi
Hi,

Although I am normally in favor of "the browser should do all", this
particular case - to me - seems better solved with e.g. node.js and
socket.io in the background of the dedicated system. The reason being
(mostly) that if you have GPIO capabilities, they are (currently) very
custom and not mobile nor desktop.

I'd like to be challenged on that though. So hit me :)

Br
Lars / www.empiriKit.com
> _______________________________________________
> dev-webapi mailing list
> dev-w...@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-webapi
>

Chris Jones

unread,
Sep 25, 2014, 3:55:22 PM9/25/14
to Lars Knudsen, dev-webapi
On Thu, Sep 25, 2014 at 12:44 PM, Lars Knudsen <lar...@gmail.com> wrote:

> Although I am normally in favor of "the browser should do all", this
> particular case - to me - seems better solved with e.g. node.js and
> socket.io in the background of the dedicated system.
>
​ I certainly understand that sentiment. But you're really talking about
implementation, not API necessarily. I think a positive outcome of this
discussion would be
- hobbyists interested in GPIO from "web apps" reach ~consensus on basic
API
- implementors all say "uh-uh, Ich don't think so"​ to UA-native
implementations
- hobbyists patch together implementations of common API with polyfill
frontends and duck-tape-and-rubber-band backends (I've already written one
of these, fwiw)

There are some advantages of UA-native implementation that I touched on in
my proposal. But of course, if folks feel like this is the wrong forum for
this discussion, so be it :).​

> The reason being (mostly) that if you have GPIO capabilities, they are
> (currently) very custom and not mobile nor desktop.
>
​I don't quite follow this point. Do you mean the particular GPIO
configuration (# pins, naming, etc.) on a given board, or the capabilities
of GPIOs themselves?​ Also I'm not sure what you mean by "mobile" and
"desktop" in that context (sorry, I've been out of this game for a while ;).

Cheers,
Chris

Axel Hecht

unread,
Sep 25, 2014, 5:38:33 PM9/25/14
to mozilla-d...@lists.mozilla.org
Is this JanOS? https://github.com/janjongboom/janos and his talk at
jsconf.eu.

Only half-snarky comment? It seems that this might be an interesting
thing for people at the JanOS level, but not necessarily on the web level?

Axel

Thomas Daede

unread,
Sep 25, 2014, 6:38:12 PM9/25/14
to dev-w...@lists.mozilla.org
Instead of a rather inflexible "GPIO" API, why not just expose Linux
syscalls so that you can communicate with hardware directly via
/sys/class/gpio or whatever? It would also be neat to be able to mmap()
hardware regions directly, X server style, in some cases. I know you can
at least do the former in node.

Obviously this would be a "certified API" only, intended for hobbyists
who are wanting direct control over their device, and are using a
Firefox OS phone or RPi or whatever almost like a dedicated hardware
peripheral. Or maybe even put the functions behind a compile time flag
that is off by default, so you need to make your own b2g build.

Do such functions already exist for things like the battery API? They
could allow it to be implemented entirely in Javascript.

Chris Jones

unread,
Sep 25, 2014, 6:42:23 PM9/25/14
to Axel Hecht, mozilla-d...@lists.mozilla.org
On Thu, Sep 25, 2014 at 2:37 PM, Axel Hecht <l1...@mozilla.com> wrote:

> Is this JanOS? https://github.com/janjongboom/janos and his talk at
> jsconf.eu.
>
>
​Nope, first I've heard of that.​


> Only half-snarky comment? It seems that this might be an interesting thing
> for people at the JanOS level, but not necessarily on the web level?
>
>
Doesn't sound snarky to me, but I'm not quite sure what you mean by "web
level". There are plenty of interesting use cases for apps that display
HTML/CSS and poke at GPIOs. If by "web level" you mean loading
http://gpio-code.com in a browser and have it poke at your raw machine
pins, then of course that's not a use case, any more than is having
http://dialer.com talk directly to your phone's modem ;) (or bluetooth,
etc.).

Cheers,
Chris​

Chris Jones

unread,
Sep 25, 2014, 9:24:12 PM9/25/14
to Thomas Daede, dev-webapi
On Thu, Sep 25, 2014 at 3:37 PM, Thomas Daede <bztd...@gmail.com> wrote:

> Instead of a rather inflexible "GPIO" API,


​Do you mean inflexible in that the API is specific to GPIO?​


> why not just expose Linux
> syscalls so that you can communicate with hardware directly via
> /sys/class/gpio or whatever?


I understand why this could be attractive from an implementor's​ view --- a
very general-purpose, small-surface-area API that's literally at the OS
level, on which many other things can be built at the "user level". I
think I also see where you're coming from in that, if a programmer is
already tying their code to a particular hardware board by naming specific
pins/whatever, why not assume the kernel too and cut out the middle-man
runtime?

​My problems with this on the specification side are that (1) you would be
forced to specify POSIX semantics for the syscall subset you're referring
to; (2) ​you would have to specify linux sysfs semantics; (3) some of these
primitives (f.e. blocking syscalls) don't map "to the web". And what about
other kernels? (That's not a theoretical problem, even the Raspberry Pi
runs kernels other than linux.)

This gets much fuzzier, but I also don't feel this is the right level of
abstraction. As a programmer, if I want to program GPIOs, then yes I have
to choose a pin configuration for my board, but that's just a detail. I'm
really thinking in terms of primitives like read/writeDigital(),
interrupts, etc. Linux and sysfs details (and other things like pin
mux'ing) just get in my way. If I move to another hardware board, then
sure I have to choose another pin config, but with a higher-level API I can
reuse almost all of my code and all of my development skills. GPIO
programming hasn't really changed since I was a kid, but the hoops I have
to jump through to get at them programmatically sure have.

The counterargument of course is that programmers could just use a library
written over lower-level primitives that abstract those details away. And
you get more mileage out of those lower-level primitives by reusing them in
other contexts. This is an eternal meta-discussion :). In this case, I
listed my spec concerns above, but beyond that all I can argue is that that
approach doesn't "feel" right to me :).


> Obviously this would be a "certified API" only, intended for hobbyists
> who are wanting direct control over their device,


I don't quite understand how you would specify an access policy for sysfs.
If I'm programming robotics, say, I don't want my mistakes to take down the
entire OS by poking the wrong device. I think there's some comfort in
austerity with a more narrowly-tailored API, again at the expense of
general applicability.

Do such functions already exist for things like the battery API? They
> could allow it to be implemented entirely in Javascript.
>

The battery API implementation
​*​
s
​*​
in Gecko ​talk directly to the host OS from C++.
​​
Note, the battery API is implemented for
​kernels other than linux/sysfs (IIRC). I don't know about webkit's
implementation(s?) but I assume it's the same.

Cheers,
Chri
​s

Thomas Daede

unread,
Sep 25, 2014, 10:25:37 PM9/25/14
to Chris Jones, dev-webapi
On 09/25/2014 08:24 PM, Chris Jones wrote:
> ​My problems with this on the specification side are that (1) you would be
> forced to specify POSIX semantics for the syscall subset you're referring
> to; (2) ​you would have to specify linux sysfs semantics; (3) some of these
> primitives (f.e. blocking syscalls) don't map "to the web". And what about
> other kernels? (That's not a theoretical problem, even the Raspberry Pi
> runs kernels other than linux.)

I was specifically thinking of Bonescript, an implementation of
Arduino-like APIs on top of Node.js. Node provides all of the necessary
mappings via the fs module. I took a look and it lacks ioctl, but
apparently what it has was enough for Bonescript's purposes.

Also I see your point with it also being a kernel abstraction layer,
however in practice I don't see this API being implemented for any
platform other than Linux.

> I don't quite understand how you would specify an access policy for sysfs.
> If I'm programming robotics, say, I don't want my mistakes to take down the
> entire OS by poking the wrong device. I think there's some comfort in
> austerity with a more narrowly-tailored API, again at the expense of
> general applicability.

This is a really complicated policy decision that is extremely specific
to the hardware being used. I assume you could set the sysfs policy just
like you would if writing a native application - set the permissions on
the devices files so that only some are accessible via the b2g process.

Anyway, as far as a path forward, I think the first step would be to
implement this API outside of the browser process - make a websocket
based server that implements a RPC mechanism similar to what you have
presented. This method also has the advantage that you can specify
whatever security policies you like in the server, and have b2g isolated
from that. The only downside I can see to this approach is the
performance cost of the inter-process communication - but you also have
a lot of overhead just from your API abstraction, which is why I
suggested the system call interface in the first place. As a bonus, it
means you can run your app over the network, which might be handy for
debugging.

Anyway, I'll make some comments on your proposed API, assuming you go
ahead and implement it out of process.

> Promise requestPins(GpioPin[] pins); //[2]
>
> void releasePins(GpioPins[] pins); //[2]

Why is requesting pins async, but releasing them isn't?

> void writeDigital(bool value); //[6]
> [throws]
> bool readDigital();

Virtually all modern hardware supports pin change interrupts, allowing a
callback function upon the pin changing. Allowing these sort of events
would seem to be the more Javascript-y way to go.

> [throws]
> float readAnalog();

How can this be synchronous? An analog conversion takes time. For that
matter, most ADCs have a ton of configuration for sample rate, sample
depth, hold times, etc. In addition, many of them can run continuously
and generate interrupts upon each reading, which could be translated to
an event in Javascript.

> [throws]
> void pwm(float duty, float angle=0, float freq=undefined);

Hardware PWM often has many clock sources. Are all PWM outputs always
locked in phase? If I request a certain frequency, how close is the
hardware actually supposed to get? Is the PWM compare register
double-buffered? There is also no way for me to queue samples, leaving
me at the mercy of Javascript timing. This issue is present in all of
the APIs.

> [throws]
> void writeAnalog(float value); //[7]
This has many of the same issues as readAnalog.

Another problem with such general and underspecified APIs is that your
application can very easily end up depending on the idiosyncrasies of
your particular implementation, defeating the point of trying to make a
"portable" API in the first place.

Jonas Sicking

unread,
Sep 27, 2014, 6:17:47 PM9/27/14
to Chris Jones, dev-webapi
On Thu, Sep 25, 2014 at 12:35 PM, Chris Jones <jones....@gmail.com> wrote:
>
> Security
> ````````````
> GPIO access is kind of an odd duck. The interface is low-level and
> exceptionally dangerous; buggy code could even set fire to peripheral
> hardware in extreme cases. But the primary use case is power-user
> hobbyists who are aware of these risks and want to run code to control
> hardware they've built. (Remember, the "competition" in this space is
> python scripts and C code running as root.) For now, I'll wave my hands
> and say GPIO should be a "certified" permission and we can worry about
> developer ergononomics down the road (though it is important).

I feel like the security model that we want is similar to what was
discussed for the serial-port API. Which effectively comes down to not
having any prompts of any kind and not require any signing of any
kind, but require the user to take some sufficiently awkward steps in
order to enable the API for a given app/origin that normal users won't
do it.

For Firefox desktop we can simply rely on using the
nsIPermissionManager since there's no way to modify its contents in
the default UI (not even about:config). But there are various addons
out there that enables a user to modify the nsIPermissionManager
arbitrarily.

For FirefoxOS simply making the API certified might be enough. That
means that a user is required to install the app through the app
manager.

> Strawman API
> ``````````````````````
> There's not much "design space" for GPIO because the capabilities are so
> basic. So the APIs used on different platforms generally look about the
> same. This strawman is an attempt to follow those conventions in a "webby"
> way. The following is pseudocode, not any literal *IDL.
>
> interface Gpio { // navigator.gpio
> readonly attribute Map<string, GpioPin> pins; //[1]

Sadly this doesn't quite do what you want. "readonly attribute
Map<...>" doesn't mean that the contents of the Map can't be mutated.
It just means that you can't write "obj.pins = someMap".

Sadly the JS language doesn't have the concept of immutable maps. Not
even Object.freeze will do what you want.

One possible solution would be to create a new interface which
consists of just the reading functions of the Map API.

Another would be to use a plain JS objects whose property names are
the keys that you want to use, and whose values are the GpioPin
objects.

> [throws]
> Promise requestPins(GpioPin[] pins); //[2]

It might be simpler to just take pin ids here. Then return the GpioPin
objects in the Promise?

> void releasePins(GpioPins[] pins); //[2]
> };
>
> interface GpioPin : DOMEventTarget { //[3]
> readonly attribute string id; //[4]
> readonly attribute Set<string> aliases; //[4]
>
> readonly attribute Set<string> capabilities; //[5]

Set has the same problem as Map regarding immutability. Maybe simply
make this an Array (which can be frozen) unless you expect a large
number of capabilities?

> [throws]
> void writeDigital(bool value); //[6]
> [throws]
> bool readDigital();
> [throws]
> float readAnalog();

Should the read functions not be async? Given that you can
synchronously get to the GpioPin object through
navigator.gpio.pins.get(x)?

Alternatively, make navigator.gpio.pins just be an array of pin names,
then require that they be asynchronously requested through
requestPins? This way there's no way of doing a sync read.

> [throws]
> void pwm(float duty, float angle=0, float freq=undefined);
>
> [throws]
> void writeAnalog(float value); //[7]
>
> readonly attribute int? readAnalogResolutionBits; //[8]
> readonly attribute int? pwmFrequencyHz;
> readonly attribute int? writeAnalogResolutionBits;
> };

/ Jonas

Fabrice Desré

unread,
Sep 28, 2014, 12:01:49 PM9/28/14
to Jonas Sicking, Chris Jones, dev-webapi
On 09/27/2014 03:17 PM, Jonas Sicking wrote:

>
> Another would be to use a plain JS objects whose property names are
> the keys that you want to use, and whose values are the GpioPin
> objects.

That's what MozMap<GpioPin> gives you.

Fabrice
--
Fabrice Desr�
b2g team
Mozilla Corporation

Marco Chen

unread,
Sep 29, 2014, 11:27:01 AM9/29/14
to Fabrice Desré, dev-webapi, Chris Jones, Jonas Sicking
Hi all,

I want to note some additional info about usage of GPIOs.
1. While a specific GPIO is used as input pin, normally developers can configure it with a pull-up or pull-down resistor.
2. Except pairs of digital/analog - input/output, some GPIOs can be configured as "alternative function". For example, the PWM is one of the alternative function.
3. Each GPIO can be set into a certain power domain. ex: This GPIO is output 1.8V or 3.3V or others.

So I think at least two set of APIs would be added for pull-up/down configuration and power domain (r/w).

Sincerely yours.

----- 原始郵件 -----

寄件者: "Fabrice Desré" <fab...@mozilla.com>
收件者: "Jonas Sicking" <jo...@sicking.cc>, "Chris Jones" <jones....@gmail.com>
副本: "dev-webapi" <dev-w...@lists.mozilla.org>
寄件備份: 2014 9 月 29 星期一 上午 12:01:49
主旨: Re: Proposal: Controlling general-purposal IO pins (GPIO)

On 09/27/2014 03:17 PM, Jonas Sicking wrote:

>
> Another would be to use a plain JS objects whose property names are
> the keys that you want to use, and whose values are the GpioPin
> objects.

That's what MozMap<GpioPin> gives you.

Fabrice
--
Fabrice Desré
b2g team
Mozilla Corporation

martyn....@gmail.com

unread,
Nov 4, 2014, 3:22:17 PM11/4/14
to mozilla-d...@lists.mozilla.org
The arduino method for setting the internal pullup/down resistors used to be :-
pinMode(2 ,INPUT)
digitalWrite(2, HIGH) .... or LOW

They have fixed that now and we can avoid it totally but it did lead to very funny looking code.

The API suggested in the post looks sensible to me.

It seems you are just going with true / false for digital I/O. I agree with that as its one of the bugbears teaching kids to code on arduino, but arduino must use HIGH / LOW for a reason (do we know what that is?)

I +1 a pullup /down option when setting the mode, wouldn't you want to request the pins with a mode? (You wouldn't want 2 apps to be writing to the same pin but what would be wrong with 2 reading?)

It's not directly related to Pi but I'd specify an analogchange event as well.

And I'd have a debounce time on the digitalchange/up/down events

I'd also request that as much as possible that when we do I2C SPI etc it has the same I/O API calls but the request method is different, obviously there are extra functions as well but the major uses (PiFace and oodles of other I/O addons) tend to be read write etc. and using the same methods would give us an extra layer of device agnosticism.


janjo...@gmail.com

unread,
Dec 9, 2014, 5:10:50 AM12/9/14
to mozilla-d...@lists.mozilla.org
We are intending to implement this (at least in our own fork), because we want to control the GPIO ports (f.e. for LED) on our embedded platform that runs Firefox OS. I think it's definitely something we should add to platform, f.e. to facilitate the LED notification API that was proposed some time ago, better to have a generic GPIO API than making a new one just for controlling the LED. Making it certified only makes sense. Controlling it through normal web apps could happen through f.e. the notification API.

janjo...@gmail.com

unread,
Feb 16, 2015, 9:06:16 AM2/16/15
to mozilla-d...@lists.mozilla.org
So I gave this some more thought, and came up with the following API:

enum GpioMode {
"output",
"input",
"input_pullup",
"input_pulldown"
};

interface mozGpio {
Promise<void> setPinMode(DOMString id, GpioMode mode);

GpioPin getPin(DOMString id);
};

interface GpioPin : EventTarget {
readonly attribute DOMString id;
readonly attribute GpioMode mode;

Promise<void> writeDigital(Boolean value);
Promise<void> writeAnalog(float value);

Promise<Boolean> readDigital();
Promise<float> readAnalog();

attribute EventHandler ondigitalchange;
attribute EventHandler onanalogchange;
};

Here is my reasoning behind this:

1. Pin mode should be mandatory. You know beforehand which pins have which function, and you're not going to hot-swap them while running, just declare them up front.
2. Enumerating pins seems unnecessary. You are aware of the pins that are available, as you were just wiring them.
3. Pins should have the ID that you referenced them with. If the vendor wants to support aliasses in libhardware, that's fine, but don't bother the user with this.
4. Getting the pin is non-promise, but calling read/write/setPinMode on the pin will fail when not initialized or present. In general you'd setPinMode in startup of your application anyway, so if something's wrong you'll find out then. Making this promise-based would make the API way more verbose for very low benefit.
5. Normalizing analog values to 0..1 seems good idea, as presented in the opening post.
6. Maybe we want support for hardware interrupts, but I don't really know how to add it to the API
7. I would like a pragmatic approach to PMW, just do it like Arduino does it (via setAnalog)
8. I didn't see references to the GPIO output voltage in Arduino/RPi/Spark documentation, so I ignored it.

Here's some examples of usage:

function setup() {
var gpio = navigator.mozGpio;
Promise.all([
gpio.setPinMode.bind(gpio, 'D6', 'output'),
gpio.setPinMode.bind(gpio, 'A8', 'output'),
gpio.setPinMode.bind(gpio, 'D19', 'input'),
gpio.setPinMode.bind(gpio, 'A23', 'input_pullup'),
gpio.setPinMode.bind(gpio, 'D24', 'input_pulldown')
]).then(() => {
console.log('Setup done');
});
}

// Writing
yield navigator.mozGpio.getPin('D6').writeDigital(true);
// Normalizes to 0..1, independent of hardware
yield navigator.mozGpio.getPin('A8').writeAnalog(0.73);

// Reading
let val = yield navigator.mozGpio.getPin('D19').readDigital();
navigator.mozGpio.getPin('D19').addEventListener('digitalchange', ev => {
// ev.value is Boolean
console.log(ev.value);
});

// Reading analog
let val = yield navigator.mozGpio.getPin('A23').readAnalog();
navigator.mozGpio.getPin('A23').addEventListener('analogchange', ev => {
// ev.value is normalized 0..1
console.log(ev.value);
});

// Reading pin that did not has setPinMode called
// * PinMode not configured
// * PinMode is input, cannot read values
// * PinMode is output, cannot write values

Thomas Daede

unread,
Feb 17, 2015, 1:26:41 AM2/17/15
to dev-w...@lists.mozilla.org
I have been following the GPIO thread with some interest because it
would be a neat feature to have. However, I am concerned that the API is
getting farther and farther away from the implementation. I think it is
very important that you provide a practical implementation of each API
to consider what it needs to provide.

GPIO on Linux is abstracted to userspace via the /sys/class/gpio
interface. GPIO pins here are numbered, and classified under their
controller chip. As of this writing, I don't believe it supports pullup
or pulldown configuration.

writeDigital returns a Promise, however all it ends up doing is a
write() to a fd. Are you going to use asyncio and use the completion of
the write() as the signal for the promise to complete?

You mention hardware interrupts - Linux does support these via the
userspace gpio interface, but only on certain pins. However, your
eventhandler functions require interrupts to operate. They also need
special setup and a edge triggering mode, which your API does not provide.

What APIs are you going to use for analog and PWM? Linux doesn't have
any standard userspace API for these.

It is my opinion, as I have stated earlier, that a better interface
would be exposing open(), close(), write(), read(), ioctl(), and
possibly mmap() to Javascript. This gives the ability for Javascript to
talk to any character device, such as the GPIO interface. It also allows
the use of custom PWM and ADC interfaces, as well as other hardware
interfaces. For example, it allows I2C and SPI to be used, reconfiguring
the FPGA on a Zynq, etc.

Given that this interface is only going to be used by certified apps
specifically installed by the user onto specialized hardware, I consider
the security risks the same as the abstracted GPIO interface.

janjo...@gmail.com

unread,
Feb 17, 2015, 4:46:36 AM2/17/15
to mozilla-d...@lists.mozilla.org
Hi Thomas,

Thanks for your reply. This proposal was modeled after the APIs that I could find for RPi / Arduino / Spark Core, they abstract on a similar level. Regarding promises/asyncio: in Chrome code we have https://developer.mozilla.org/en-US/docs/JavaScript_OS.File which also returns promises for all of these actions, guess take similar approach, although it'd be implemented in libhardware.

Regarding your proposal: this was my original thought, outlined in https://groups.google.com/forum/#!searchin/mozilla.dev.webapi/A$20WebAPI$20for$20lower$20level$20OS$20related$20tasks$20%28filesystem$2C$20execution%29/mozilla.dev.webapi/t4Xh2EH0rIo/lz3jmyzEFGkJ, but it did not gain any support.

If we can just decide that GPIO / I2C will only work on POSIX then we could do something like above and solve the API problem in userland.

Jan

janjo...@gmail.com

unread,
Feb 17, 2015, 11:18:47 AM2/17/15
to mozilla-d...@lists.mozilla.org
On Tuesday, February 17, 2015 at 7:26:41 AM UTC+1, Thomas Daede wrote:
> I have been following the GPIO thread with some interest because it
> would be a neat feature to have. However, I am concerned that the API is
> getting farther and farther away from the implementation. I think it is
> very important that you provide a practical implementation of each API
> to consider what it needs to provide.

Also we need the write to be a promise, because f.e. on Rpi:

echo 1 > /sys/class/gpio/gpio2/value

will fail with bash: echo: write error: Operation not permitted

When not configured for output.

Mike Habicher

unread,
Feb 17, 2015, 3:30:35 PM2/17/15
to janjo...@gmail.com, mozilla-d...@lists.mozilla.org

On 15-02-16 09:06 AM, janjo...@gmail.com wrote:
1. Pin mode should be mandatory. You know beforehand which pins have which function, and you're not going to hot-swap them while running, just declare them up front.
Keep in mind that function != mode. If I were to write something to bitbang I2C (or some other single-wire bidirectional protocol), I would probably set the pin's output latch to "low", and toggle the pin between "output" (for driving a low) and "input_pullup" (for sending a high, or sensing the state of the 'bus').

(Which assumes the pin isn't an open-collector-only pin, or doesn't have some other way of turning off the high-side drive.)

--m.

Thomas Daede

unread,
Feb 17, 2015, 8:49:23 PM2/17/15
to dev-w...@lists.mozilla.org
On 02/17/2015 12:30 PM, Mike Habicher wrote:
>
> On 15-02-16 09:06 AM, janjo...@gmail.com wrote:
>> 1. Pin mode should be mandatory. You know beforehand which pins have which function, and you're not going to hot-swap them while running, just declare them up front.
> Keep in mind that function != mode. If I were to write something to
> bitbang I2C (or some other single-wire bidirectional protocol), I would
> probably set the pin's output latch to "low", and toggle the pin between
> "output" (for driving a low) and "input_pullup" (for sending a high, or
> sensing the state of the 'bus').

In general, swapping modes like this can be glitchy and most GPIO
controllers have an open drain mode for this purpose. Also, the Linux
GPIO API doesn't handle this at all, so I don't know how you are going
to implement it.

I am still very much in favor of the system call API. I'm going to
address a couple of concerns from the earlier discussion I was linked to.

- B2G is currently implemented only on top of Linux. I can't think of
any non Linux examples that even have a GPIO API. If they do have a GPIO
API, it's almost certainly via the same character device syscalls that
Linux uses, just with some different names.

- Devices without standard Linux APIs can be used with a custom kernel
driver providing a character device. A file descriptor based API means
that the API does not need to be changed to accommodate these devices.
The less places that need to be touched for this, the better, IMO.

- Certified apps do not have root, whereas the /sys/ interface is
root-only be default. Whichever API you implement, you are going to have
to change the /sys/ permissions to be accessible by the B2G process.
This requires robust path parsing, but this is pretty standard for
seccomp-based sandboxing already used by many projects.

- If you want extra protection for even certified apps, you could add
list of allowed open() paths to the app manifest. This would prevent
accidental accesses or security breaches in the app from affecting other
apps.

- I would much prefer the first interface you proposed to be more
fd-oriented, aka you have to open() before calling write(). This can be
higher performance and has a more 1:1 mapping to the implementation.

janjo...@gmail.com

unread,
Feb 20, 2015, 8:54:00 AM2/20/15
to mozilla-d...@lists.mozilla.org
I went with the approach of exposing low level access to userland code, and then wrote the absolute basics for gpio handling on top of that (does not live in Gecko). Pure hack but I can blink a LED on the rpi :-)

Photo: https://pbs.twimg.com/media/B-SsOmvCYAABDl_.jpg:large
Gecko: https://github.com/jan-os/gecko-dev/tree/rpi
Exec API Doc: http://janos.io/api-reference/exec.html

GPIO API: https://github.com/jan-os/janos/blob/f3e31eb4f5985d8a898dbced40a99a383b598e8b/apps/system/js/api/gpio.js
Usage: https://github.com/jan-os/janos/blob/f3e31eb4f5985d8a898dbced40a99a383b598e8b/apps/system/js/my-app.js

fiedle...@gmail.com

unread,
Feb 25, 2015, 12:43:39 PM2/25/15
to mozilla-d...@lists.mozilla.org
Hi Jan, that looks really nice! Are there any docs how I can flash this on my SD-Card and get it running? :)

janjo...@gmail.com

unread,
Feb 27, 2015, 7:43:35 AM2/27/15
to mozilla-d...@lists.mozilla.org
https://gist.github.com/janjongboom/94d575526a689687a6b8

It can blink LEDs and do push buttons, no PWM or anything.

Exposes navigator.mozOs.openFile() which returns a file descriptor with some methods. It's a super simple wrapper around fopen/fwrite/fread, and goes into C basically straight away.

See: https://github.com/jan-os/gecko-dev/commit/2adce94e0869606f1825107671ac9b5cc83c7ae2#diff-7fb7143f4504e462f2fe61bc4cc423b6L137

janjo...@gmail.com

unread,
Feb 27, 2015, 8:16:03 AM2/27/15
to mozilla-d...@lists.mozilla.org
So another thought. If we have filesystem lib, we can just cross-compile WiringPi with emscripten, map emscripten's filesystem to the new fs API and run the whole thing in userland without us having to do anything!

André Fiedler

unread,
Mar 3, 2015, 1:24:39 PM3/3/15
to mozilla-d...@lists.mozilla.org
Am Freitag, 27. Februar 2015 14:16:03 UTC+1 schrieb janjo...@gmail.com:
> So another thought. If we have filesystem lib, we can just cross-compile WiringPi with emscripten, map emscripten's filesystem to the new fs API and run the whole thing in userland without us having to do anything!

that sounds "dirty". ;) how much from WiringPi do we need to implement?

naoki sekiguchi

unread,
Mar 26, 2015, 5:07:20 AM3/26/15
to mozilla-d...@lists.mozilla.org
Hi all,

We are developing new FirefoxOS embedded board which called "CHIRIMEN" in MozOpenHard Project[1]. The board has some low level I/O (GPIO,I2C, and so on) and FirefoxOS is installed. We have started implementing gecko APIs to control GPIO and I2C for the board, and we would like to start to distribute the board until September hopefully. Some technical information available from https://github.com/MozOpenHard/CHIRIMEN. We will open all of the sources which contain firmware code, electrical schematic, design drawing and so on.

We also launched a W3C CG which called Browser and Robotics community group[2]. We are creating spec drafts about Web GPIO API and Web I2C API in the CG[3]. Please review and comment.

The followings are presentations about MozOpenHard project and FirefoxOS for embedded systems.
http://www.slideshare.net/naokisekiguchi75/20150317-firefox-osstudymtgengver
http://www.slideshare.net/honmamasashi1/fxos-for-embedded-systems-english-version


[1]http://mozopenhard.mozillafactory.org/
MozOpenHard is one of the Mozilla factory project. Mozilla Factory takes openness as a keyword to encourage acts of creation among people. Its idea founded by Mozilla Japan in spring 2012. (Mozilla factory: http://en.mozillafactory.org/)

[2]https://www.w3.org/community/browserobo/

[3]https://github.com/browserobo

Sincerely,
Naoki

janjo...@gmail.com

unread,
Mar 26, 2015, 1:02:31 PM3/26/15
to mozilla-d...@lists.mozilla.org
Hi, I sent this e-mail to Masashi Honma, is he involved in this as well?

"Anyway, this looks a lot like the discussion we have had on the mozilla.dev.webapi mailing list: https://groups.google.com/forum/#!topic/mozilla.dev.webapi/4BC0pBzEC7E. In here we decided against a WebAPI for GPIO access but rather settle for a new low-level API which allows for access of the underlying file system. This gives us the opportunity to implement a GPIO API on top of these new APIs, and thus the GPIO API can be a normal userland library, instead of being part of Gecko. The description of the new API can be found in https://groups.google.com/forum/#!topic/mozilla.dev.webapi/t4Xh2EH0rIo, and the tracking bug is https://bugzilla.mozilla.org/show_bug.cgi?id=1141021, which I plan to start working on this week.

I implemented an earlier version of this API in https://github.com/jan-os/gecko-dev/tree/os-module, and have a build for Raspberry Pi which includes it: https://gist.github.com/janjongboom/94d575526a689687a6b8. The API is here: https://github.com/jan-os/janos/blob/gpio/apps/system/js/api/gpio.js. However this is not going to work exactly like this, f.e. on Raspberry Pi writing / reading over the file descriptors is too slow to do things like PWM or drive servo's. You should rather read/write to /dev/mem. When we have the new API we plan to cross-compile WiringPi through emscripten for FxOS."

If you think this approach makes sense, then we should definitely collaborate on this. We (Telenor) are working in exactly the same space (on the software side). If we have proper low level APIs we don't need to have GPIO/I2C web apis, but can solve this in userland.

Here's some of my latest work: https://github.com/jan-os/gecko-dev/tree/os-module-worker

naoki sekiguchi

unread,
Mar 30, 2015, 6:14:50 AM3/30/15
to mozilla-d...@lists.mozilla.org
Hi Jan,

Thank you for your reply. I am also discussing with Masashi.
I want the API for filesystem operation and would like to try your nice implement.

However, I think that GPIO/I2C APIs also are necessary. Specialized APIs for GPIO/I2C are more easier to understand for developper, and easy to add some specialized functions. Additionally, I think these APIs should be exposed as privileged APIs so that any device users can replace and modify the control programs.

Nonethless, from a practical standpoint, those GPIO/I2C APIs shold be implemented in the filesystem operation based first. So, I will refer your implement.

Thanks,
Naoki

janjo...@gmail.com

unread,
Mar 31, 2015, 12:02:06 PM3/31/15
to mozilla-d...@lists.mozilla.org
Wouldn't it be great if the GPIO/I2C APIs would be userland APIs instead? Then we can update them without having to update Gecko. If that'd not be possible with the filesystem API proposal, because we miss a certain operations we should fill that gap.

As hardware vendor you could just add the gpio/i2c API to your SDK.
Reply all
Reply to author
Forward
0 new messages