Draft Report
Copyright © 2014-2016 the Contributors to the Web GPIO API Specification, published by the Browsers and Robotics Community Group under the W3C Community Final Specification Agreement (FSA). A human-readable summary is available.
This specification defines an API to enable web applications to access GPIO embedded in the underlying device.
This specification was published by the Browsers and Robotics Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.
This section is non-normative.
The Web GPIO API provides interfaces that web applications can access GPIO embedded in the underlying devices.
Using this API, web applications can set GPIO direction (input or output), read GPIO value, write GPIO value, watch hardware interrupts, and so on.
This API is intended to be used under the mechanism which maintains the safety of the device by which this API operates and users using it. Note that this API is not intended to be used by web applications running on a general web browser without such considerations. As a mechanism for such consideration, there may be packaged applications running on a browser runtime or web applications running on a browser in some kind of dedicated devices.
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in the normative parts of this document are to be interpreted as described in RFC2119. For readability, these words do not appear in all uppercase letters in this document. [RFC2119]
The EventTarget
, Event
, and EventInit
interfaces are defined in the Web IDL specification. [DOM4]
The DOMExceptions
interfaces is defined in the Web IDL specification. [WEBIDL]
The Promise
is defined in ECMA-262 6th Edition. [ES6]
This section is non-normative.
The following examples show common GPIO usage in JavaScript. The variable gpio is a GPIOAccess
object in the examples blow.
This example shows how to request access to the set of GPIOs on the underlying system.
var gpio = null; // global GPIOAccess object navigator.requestGPIOAccess().then( function(gpioAccess) { console.log("GPIO ready!"); gpio = gpioAccess; // store in the global }, function(error) { console.log("Failed to get GPIO access: " + error.message); } );
This example shows how to list the GPIO ports available on the underlying operating system.
// get a GPIOPortMap object var ports = gpio.ports; // show the detailed information for each port ports.values(function(port) { console.log("* Port " + port.portNumber); console.log(" - Port name : " + port.portName); console.log(" - PIN name : " + port.pinName); });
This example shows how to get a GPIO port.
var port = null; // global GPIOPort object // get the GPIOPort object representing the GPIO port named "gpio18". gpio.ports.get(18).then( function(gpioPort) { port = gpioPort; // store in the global }, function(error) { console.log("Failed to get GPIO port: " + error.message); } );
This example shows how to activate the specified GPIO port and read the value.
// export (activate) the port. port.export("in").then(exportSuccess, gpioError); // successfully exported function exportSuccess() { window.setInterval(function() { port.read().then(readSuccess, gpioError); }, 1000); } // the value successfully read function readSuccess(value) { console.log(port.portName + ": " + value); } // Show an error function gpioError(error) { console.log("Error: " + error.message + "(" + port.portName + ")"); }
This example shows how to listen to changes of a GPIO port values. In this example, the exported GPIO port will be unexported in 60 seconds.
// export (activate) the port port.export("in").then(exportSuccess, gpioError); // successfully exported function exportSuccess() { // set an event handler port.onchange = valueChanged; } // the value changed function valueChanged(event) { var port = event.port; // GPIOPort object var value = event.value; // current value console.log(port.portName + ": " + value); } // Show an error function gpioError(error) { console.log("Error: " + error.message + "(" + port.portName + ")"); } // unexport (deactivate) the port in 60 seconds window.setTimeout(function() { if(port.exported === true) { port.unexport(); } }, 60000);
This example shows how to listen to changes of multiple GPIO port values as a whole. In this example, 3 GPIO ports are listened. When the value of the GPIO port "gpio25" changes to 1, all of the exported GPIO port will be unexported.
// set a listener for all of the exported GPIO ports gpio.onchange = valueChanged; // get some GPIOPort objects var targets = [ port18, port23, port25 ]; // export (activate) the ports targets.forEach(function(port) { port.export("in").then(exportSuccess, gpioError); }); // successfully exported function exportSuccess() { console.log(port.portName + " is exported."); } // the value changed function valueChanged(event) { var port = event.port; // GPIOPort object var value = event.value; // current value console.log(port.portName + ": " + value); // if the value of the gpio25 was changed to 1, unexport all of the exported GPIO ports if(port.portNumber === 25 && value === 1) { gpio.unexportAll(unexportSuccess, gpioError); } } // successfully unexported function unexportSuccess() { console.log("This process has been terminated."); } // Show an error function gpioError(error) { console.log("Error: " + error.message + "(" + port.portName + ")"); }
This example shows how to write a value to the specified GPIO port. This example writes 0 or 1 one after the other to the GPIO port "gpio18" at a second interval.
// export (activate) the port. port.export("out").then(writeValue, gpioError); // the value to be written var v = 0; // successfully exported and write a value function writeValue() { v = v ? 0 : 1; port.write(v).then(writeSuccess, gpioError); } // the value successfully written function writeSuccess(value) { console.log(port.portName + " was set to " + value); window.setTimeout(writeValue, 1000); } // Show an error function gpioError(error) { console.log("Error: " + error.message + "(" + port.portName + ")"); }
requestGPIOAccess()
methodpartial interface Navigator { Promise<GPIOAccess> requestGPIOAccess (); };
The requestGPIOAccess()
method must be visible on the Navigator
object if the script's global object is the Window
object, or on the WorkerNavigator
object if the script's global object is the WorkerUtils
. [HTML5] [WEBWORKERS]
The requestGPIOAccess()
method is used to get a GPIOAccess
object. The requestGPIOAccess()
method is invoked, the user agent must run the steps as follows:
Let promise be a new Promise
object and resolver be its associated resolver.
Return promise and run the following steps asynchronously.
If the underlying operating system does not support GPIO access, then jump to the step labeled failure below.
Create a GPIOPortMap
object, then let map be the newly created GPIOPortMap
.
Find all of the GPIO ports available on the underlying operating system. For each of the GPIO port, create a GPIOPort
object for the GPIO port, then add it to the map.
How can the UA find the GPIO ports available on the underlying operating system?
Create a GPIOAccess
object, then set the ports
attribute to the map.
Let access to be the newly created GPIOAccess
at the previous step.
success: Call resolver's accept(value)
method with access as value argument. Abort these steps.
failure: Let error be a new DOMExceptions
. This must be of type "NotSupportedError
". Then call resolver's reject(value)
method with error as value argument.
GPIOAccess
interfaceinterface GPIOAccess : EventTarget { readonly attribute GPIOPortMap ports; Promise unexportAll(); attribute EventHandler onchange; };
The ports
attribute must return the GPIOPortMap
object representing all of the GPIO ports available on the underlying operating system.
The unexportAll()
method is used to release all of the GPIO ports exported by this API. The unexportAll()
method is invoked, the user agent must run the steps as follows:
Let promise be a new Promise
object and resolver be its associated resolver.
Return promise and run the following steps asynchronously.
Let list be the list of the GPIOPort
object whose exported
attribute is true.
For each of the GPIOPort
object in the list, release (unexport) the related GPIO port, then set the exported
attribute to false.
Wait until the previous step for all of the GPIOPort
in the list finishes.
If all of the exported GPIO port are released successfully, jump to the step labeled success below. Otherwise, jump to the step labeled failure below.
success: Call resolver's accept()
method without any argument. Abort these steps.
failure: Let error be a new DOMExceptions
. This must be of type "OperationError
". Then call resolver's reject(value)
method with error as value argument.
Is unexportAll necessary?
Should GPIOAccess have an EventTarget interface?
The onchange
attribute is a event handler invoked when the value of one of the exported GPIO ports changes (i.e. the value changes from 1 to 0 or from 0 to 1). Whenever the event handler is to be invoked, the user agent must run the following steps:
Let port be the GPIOPort
object.
Let port value be the value of the GPIO port corresponding to the GPIOPort
.
Let event be a newly constructed GPIOChangeEvent
, with the value
attribute set to the port value, and the port
attribute set to the port.
Fire an event named change
at the GPIOAccess
object, using the event as the event object.
Is onChange needed although there is an EventTarget interface?
Before the change
event is fired on the GPIOAccess
object, another change
event is fired on the GPIOPort
object. Therefore, the event handler set on the GPIOAccess
is invoked after the event handler set on the GPIOPort
object is invoked.
GPIOPortMap
interfaceinterface GPIOPortMap { attribute readonly unsigned short size; function keys (void function (portNumber)); function values (void function (GPIOPort)); GPIOPort get (unsigned short key); };
The size
attributes is the number of GPIO ports available on the underlying operating system at the current time.
The keys()
method is an iterator for keys. The values()
method is an iterator for values.
The get()
method is a getter for a GPIOPort
. When the get()
method is invoked, the GPIO port number must be passed as the first argument. If the first argument is not specified, or it is not valid as a port number, or it does not represent any GPIO port available on the underlying operating system, the get()
method must return null. Otherwise, the get()
method must return the GPIOPort
corresponding to the specified port number.
This interface is used to represent all the currently available GPIO ports as a MapClass-like interface. This interface also act as an associative array:
// Iterate for values ports.keys( function( portNumber ) { var port = ports.get(portNumber); console.log(port.pinName + ", " + port.portNumber); }); // or you could express as: ports.values( function( port ) { console.log(port.pinName + ", " + port.portNumber); }); // or you could express as: for (port in ports) { console.log(port.pinName + ", " + port.portNumber); }
This should be Iterable
GPIOPort
interfaceinterface GPIOPort : EventTarget { readonly attribute unsigned short portNumber; readonly attribute DOMString portName; readonly attribute DOMString pinName; readonly attribute DirectionMode direction; readonly attribute boolean exported; Promise export(DirectionMode direction); Promise unexport(); Promise read(); Promise write(unsigned short value); attribute EventHandler onchange; }; enum DirectionMode { "", "in", "out" }
The GPIOPort
interface represents a GPIO port assigned to a physical GPIO pin.
The portNumber
attribute must return the GPIO port number assigned to the GPIOPort
object.
The portName
attribute must return the name of the GPIO port. If the name is unknown, the portName
attribute must return an empty string.
The pinName
attribute must return the name of the GPIO pin. If the name is unknown, the pinName
attribute must return an empty string.
The value to portNumber
attribute is a port number assigned to a physical pin by the underlying operating system. This value is based on the name of the GPIO port. Note that this value is not based on the name of the physical pin.
In most of operating systems, the name of the physical pin is different from the name of the GPIO port. For example, the pin name "P1-12" on the Raspberry Pi is mapped to "gpio18" on Raspbian Linux. In this case, the value of the portNumber
attribute is 18, the value of the portName
is a string "gpio18", and the value of the pinName
attribute is a string "P1-12".
Are portName and pinName needed? May portMap have such a functionality?
The direction
attribute must return either an empty string, "in
", or "out
". This value is initially an empty string. This value is set to "in
", or "out
" when the export()
method is invoked and successfully completed based on the argument passed to the method.
The exported
attribute gives whether the GPIO port has been exported or not. If the GPIO port has been exported, the exported
attribute must return true, otherwise false.
The export()
method
activate the related GPIO port. When the export()
method is invoked, the user agent must run the steps as follows:
Let promise be a new Promise
object and resolver be its associated resolver.
Return promise and run the following steps asynchronously.
If the value of the exported
attribute is true, jump to the step labeled success below.
Let direction be the value of the first argument passed to the method.
If direction is neither "in
" nor "out
", jump to the step labeled failure below.
Activate the related GPIO port in the specified direction mode ("in
" or "out
"). If succeeded, set the exported
attribute to true, then jump to the step labeled success below. Otherwise, jump to the step labeled failure below.
success: Call resolver's accept()
method without any argument. Abort these steps.
failure: Let error be a new DOMExceptions
. This must be of type "InvalidAccessError
" if direction was invalid (i.e. neither "in
" nor "out
"), "SecurityError
" if this operation was denied by the operating system because of some kind of security reason, "OperationError
" if this operation was failed because of any reasons other than security reason. Then call resolver's reject(value)
method with error as value argument.
The unexport()
method deactivates the related GPIO port. When the unexport()
method is invoked, the user agent must run the steps as follows:
Let promise be a new Promise
object and resolver be its associated resolver.
Return promise and run the following steps asynchronously.
If the value of the exported
attribute is not true, jump to the step labeled success below.
Deactivate the related GPIO port. If succeeded, set the exported
attribute to false, then jump to the step labeled success below. Otherwise, jump to the step labeled failure below.
success: Call resolver's accept()
method without any argument. Abort these steps.
failure: Let error be a new DOMExceptions
. This must be of type "OperationError
". Then call resolver's reject(value)
method with error as value argument.
Is unexport necessary?
The read()
method reads the value from the related GPIO port. When the read()
method is invoked, the user agent must run the steps as follows:
Let promise be a new Promise
object and resolver be its associated resolver.
Return promise and run the following steps asynchronously.
If the value of the exported
attribute is not true, jump to the step labeled failure below.
If the value of the direction
attribute is not "in
", jump to the step labeled failure below.
Read the value of the related GPIO port. If succeeded, let port value be the value of the related GPIO port. Otherwise, jump to the step labeled failure below.
success: Call resolver's accept(value)
method with port value as value argument. Abort these steps.
failure: Let error be a new DOMExceptions
. This must be of type "InvalidAccessError
" if the value of the exported
attribute is not true or the value of the direction
attribute is not "in
", "OperationError
" otherwise . Then call resolver's reject(value)
method with error as value argument.
The write()
method writes the value passed as the first argument to the related GPIO port. The value must be numeric 0 or 1. When the write()
method is invoked, the user agent must run the steps as follows:
Let promise be a new Promise
object and resolver be its associated resolver.
Return promise and run the following steps asynchronously.
If the value of the exported
attribute is not true, jump to the step labeled failure below.
If the value of the direction
attribute is not "out
", jump to the step labeled failure below.
Let port value be the value of the first argument passed to this method.
If port value is neither numeric 0 nor 1, jump to the step labeled failure below.
write port value to the related GPIO port. If succeeded, jump to the step labeled success below. Otherwise, jump to the step labeled failure below.
success: Call resolver's accept(value)
method with port value as value argument. Abort these steps.
failure: Let error be a new DOMExceptions
. This must be of type "InvalidAccessError
" if the value of the exported
attribute is not true or if the value of the direction
attribute is not "out
" or if port value is neither 0 nor 1, "OperationError
" otherwise . Then call resolver's reject(value)
method with error as value argument.
The onchange
attribute is a event handler invoked when the value of the GPIO port corresponding to the GPIOPort
object changes (i.e. the value changes from 1 to 0 or from 0 to 1). Whenever the event handler is to be invoked, the user agent must run the following steps:
Let port be the GPIOPort
object.
Let port value be the value of the GPIO port corresponding to the GPIOPort
.
Let event be a newly constructed GPIOChangeEvent
, with the value
attribute set to port value, and the port
attribute set to port.
Fire an event named change
at the GPIOPort
object, using the event as the event object.
After the change
event is fired on the GPIOPort
object, another change
event will be fired on the GPIOAccess
object. Therefore, the event handler set on the GPIOPort
is invoked before the event handler set on the GPIOAccess
object is invoked.
GPIOChangeEvent
interfaceWhen a value of a GPIO port changes, if the onchange
of the GPIOPort
object is set to a function, the GPIOChangeEvent
object is passed to the event handler function as the first argument . If the onchange
attribute of
[Constructor(DOMString type, optional GPIOChangeEventInit eventInitDict)] interface GPIOChangeEvent : Event { readonly attribute unsign short value; readonly attribute GPIOPort port; };
The value
attribute must return the value of the GPIO port corresponding to the GPIOPort
object where the change
event was fired..
The port
attribute must return the GPIOPort
object where the change
event was fired.
Is GPIOChangeEventInit needed?
GPIOChangeEventInit
interfacedictionary GPIOChangeEventInit : EventInit { unsign short value; GPIOPort port; };
The value
attribute is a value of the related GPIO port.
TBD