Awesome
IO Plugins
(This document is a work in progress)
An IO Plugin is any class whose instances implement a Firmata compatible interface. For an in-depth case study of creating an IO plugin, read about the design and creation of the Galileo-IO Plugin here.
IO Plugins are used extensively by Johnny-Five to communicate with non-Arduino based hardware but may also be used independently if desired.
Available IO Plugins
The following platform IO Plugins are currently available:
<!--extract-start:ioplugins-->- Arduino StandardFirmata
- BeagleBone Black
- Blend Micro
- BoardIO (Generic IO Plugin class to make your own!)
- C.H.I.P.
- Electric Imp
- Intel Galileo, Edison
- Galileo-IO
- Edison-IO (This is an alias module to Galileo-IO)
- LightBlue Bean
- Linino One
- LinuxIO (Extensible Linux IO Plugin class to make your own!)
- Particle Core & Particle Photon
- pcDuino
- Pinoccio
- Raspberry Pi
- RemoteIO (Wrapper to remote control another IO class)
- Tessel 2
Minimum Plugin Class Requirements
The plugin must...
- Be a constructor function that defines a prototype object
- Be a subclass of EventEmitter
- Initialize instances that must...
- asynchronously emit a "connect" event when the connection to a physical device has been made.
- asynchronously emit a "ready" event when the handshake to the physical device is complete.
- include a property named
isReady
whose initial value isfalse
.isReady
must be set totrue
in the same or previous execution turn as the the "ready" event is emitted.- The process of establishing a connection and becoming "ready" is irrelevant to this document's purposes.
- include a readonly property named
MODES
whose value is a frozen object containing the following property/values:{ INPUT: 0, OUTPUT: 1, ANALOG: 2, PWM: 3, SERVO: 4 }
- include a readonly property named
SERIAL_PORT_IDs
whose value is a frozen object containing key/value pairs that represent a platform independent serial port identification. - include a readonly property named
pins
whose value is an array of pin configuration objects. The indices of thepins
array must correspond to the pin address integer value, eg. on an Arduino UNO digital pin 0 is at index 0 and analog pin 0 is index 14. See mock-pins.js for a complete example.- each pin configuration object must contain the following properties and values:
supportedModes
: an array of modes supported by this pin, eg.[0, 1, 2]
representsINPUT
,OUTPUT
,ANALOG
. (Common analog read capable pins)[0, 1, 4]
representsINPUT
,OUTPUT
,SERVO
. (Common digital I/O capable pins)[0, 1, 3, 4]
representsINPUT
,OUTPUT
,PWM
,SERVO
. (Common digital I/O and PWM capable pins)
mode
: the current mode this pin is set to.value
: the current value of this pin- INPUT mode: property updated via the read loop
- OUTPUT mode: property updated via *Write methods
report
: 1 if reporting, 0 if not reportinganalogChannel
: corresponding analogPin index (127 if none), eg.analogChannel: 0
isA0
whose index is14
in thepins
array.
- each pin configuration object must contain the following properties and values:
- include a readonly property named
analogPins
whose value is an array of pin indices that correspond to the analog pin indices in thepins
array. - include a readonly property named
HIGH
whose value corresponds to the logic high value used by the IO plugin, e.g.1
- include a readonly property named
LOW
whose value corresponds to the logic low value used by the IO plugin, e.g.0
- If an essential IO feature is not implemented or cannot be implemented, the method must throw. For example, the Raspberry Pi does not support analog inputs, if user code calls through to an
analogRead
, the program must throw as an irrefutable means of indicating non-support. - If a non-essential IO feature is not implemented or cannot be implemented, the method must accept the expected arguments and indicate successful completion. For example, if it receives a callback, that callback must be called asynchronously.
Minimum API Requirements
pinMode(pin, mode)
- Set the mode of a specified pin to one of:
INPUT: 0
OUTPUT: 1
ANALOG: 2
PWM: 3
SERVO: 4
Writing
Data writing operations must be executed in order of instruction.
analogWrite(pin, value)
pwmWrite(pin, value) (to supercede analogWrite
)
- Accept an 8 bit
value
(0-255) that is written to the specifiedpin
.
digitalWrite(pin, value)
- Accept a
value
(0 or 1) that is written to the specifiedpin
.
i2cWrite(address, inBytes)
- Write the array of
inBytes
to the specifiedaddress
.
i2cWrite(address, register, inBytes)
- Write the array of
inBytes
to the specifiedaddress
andregister
.
i2cWriteReg(address, register, value)
- Write the single
value
to the specifiedaddress
andregister
.
serialWrite(portId, inBytes)
- Write the array of
inBytes
to the specifiedportId
.
servoWrite(pin, value)
- Accept a
value
that is written to the specifiedpin
. If the value is between 0 and 180, it is assumed to be a servo arm position in degrees. If the value is between 180 and 544, it is also assumed to be in degrees and is truncated to 180. If the value is greater than or equal to 544, it is assumed to be a duty cycle in microseconds.
Reading
All new data read processes must be asynchronous. The following methods must not block the execution process, by any means necessary. The following methods must not return the value of the new data read process.
analogRead(pin, handler)
- Initiate a new data reading process for
pin
- The recommended new data reading frequency is greater than or equal to 200Hz. Read cycles may reduce to 50Hz per platform capability, but no less.
- Invoke
handler
for all new data reads, with a single argument which is the presentvalue
read from thepin
. - A corresponding
analog-read-${pin}
event is created and emitted for all new data reads, with a single argument which is the presentvalue
read from thepin
(This can be used to invokehandler
).
digitalRead(pin, handler)
- Initiate a new data reading process for
pin
- The recommended new data reading frequency is greater than or equal to 200Hz. Read cycles may reduce to 50Hz per platform capability, but no less.
- Invoke
handler
for all new data reads in which the data has changed from the previous data, with a single argument which is the presentvalue
read from thepin
. - A corresponding
digital-read-${pin}
event is created and emitted for all new data reads in which the data has changed from the previous data, with a single argument which is the presentvalue
read from thepin
(This can be used to invokehandler
).
i2cRead(address, register, bytesToRead, handler)
- Initiate a new data reading process for
address
andregister
, requesting the specified number ofbytesToRead
. - The recommended new data reading frequency is greater than or equal to 100Hz. Read cycles may reduce to 50Hz per platform capability, but no less.
- Invoke
handler
for all new data reads, with a single argument which is an array containing the bytes read. - A corresponding
i2c-reply-${address}-${register}
event is created and emitted for all new data reads, with a single argument which is an array containing the bytes read. (This can be used to invokehandler
).
i2cRead(address, bytesToRead, handler)
- Initiate a new data reading process for
address
, requesting the specified number ofbytesToRead
. - The recommended new data reading frequency is greater than or equal to 100Hz. Read cycles may reduce to 50Hz per platform capability, but no less.
- Invoke
handler
for all new data reads, with a single argument which is an array containing the bytes read. - A corresponding
i2c-reply-${address}
event is created and emitted for all new data reads, with a single argument which is an array containing the bytes read. (This can be used to invokehandler
).
i2cReadOnce(address, register, bytesToRead, handler)
- Initiate a new data reading process for
address
andregister
, for the specified number ofbytesToRead
. - This new data read occurs only once.
- Invoke
handler
with a single argument which is an array containing the bytes read. - A corresponding
i2c-reply-${address}-${register}
"once" event is created and emitted, with a single argument which is an array containing the bytes read. (This can be used to invokehandler
).
i2cReadOnce(address, bytesToRead, handler)
- Initiate a new data reading process for
address
, requesting the specified number ofbytesToRead
. - This new data read occurs only once.
- Invoke
handler
for all new data reads, with a single argument which is an array containing the bytes read. - A corresponding
i2c-reply-${address}
event is created and emitted, with a single argument which is an array containing the bytes read. (This can be used to invokehandler
).
pingRead(settings, handler)
This method is defined solely to handle the needs of HCSR04
(and similar) components.
-
No pin mode specified
-
Create a single
data
event, invokinghandler
. -
settings
is an object that includes the following properties and corresponding values:Property Description Default Required pin The pin number/name attached to the servo Yes value HIGH
orLOW
HIGH
No pulseOut Time µs for pulseIn
timeout ✭5 No - ✭ Only used by Firmata.js/PingFirmata, other platforms may safely ignore, for example: https://github.com/rwaldron/particle-io/blob/master/lib/particle.js#L544-L565
-
handler
is called with a duration value, inmicroseconds
, which is the result of take a pulsed measurement as required byHCSR04
(and similar) devices.
serialRead(portId, handler)
serialRead(portId[, maxBytesToRead], handler)
- Initiate a new data reading process for
portId
, optionally capping the read to the specifiedmaxBytesToRead
. - The recommended new data reading frequency is greater than or equal to 100Hz. Read cycles may reduce to 50Hz per platform capability, but no less.
- Invoke
handler
for all new data reads, with a single argument which is an array containing the bytes read. - A corresponding
serial-data-${portId}
event is created and emitted for all new data reads, with a single argument which is an array containing the bytes read. (This can be used to invokehandler
).
Configuring
i2cConfig(options)
-
options
is an object that MUST include, at very least, the following properties and corresponding values:Property Description address The address of the I2C component -
options
may include any of the common configuration properties:Property Description bus The I2C bus port The I2C bus port delay Time µs between setting a register and reading the bus -
i2cConfig will always be called once by every I2C component controller class in Johnny-Five. This means that any setup necessary for a given platform's I2C peripheral capabilities should be done here.
- Examples:
- Firmata.js will negotiate a default register read in µs and a default value for the
stopTX
flag. - Tessel-IO will negotiate the
bus
to use.
- Firmata.js will negotiate a default register read in µs and a default value for the
- Examples:
-
All options specified by a user program in the instantiation of a component will be forwarded to i2cConfig.
serialConfig(options)
-
Must be called to configure a serial/uart port
-
options
is an object that MUST include, at very least, the following properties and corresponding values:Property Description portId Some value that identifies the serial/uart port to configure -
options
may include any of the common configuration properties:Property Description baud The baud rate rxPin The RX pin txPin The TX pin
All options specified by a user program in the instantiation of a component will be forwarded to serialConfig.
servoConfig(options)
-
May be called as an alternative to calling
pinMode(pin, SERVO)
. -
options
is an object that MUST include, at very least, the following properties and corresponding values:Property Description Default Required pin The pin number/name attached to the servo Yes min The minimum PWM pulse time in microseconds ✭, ✭✭ Yes max The maximum PWM pulse time in microseconds ✭, ✭✭✭ Yes -
✭ This is platform dependent and should be tested thoroughly with as many different servos as possible.
-
✭✭ Approximiately between 400 and 600
-
✭✭✭ Approximately between 2200 and 2400
servoConfig(pin, min, max)
- See #servoConfig(options)
- This alternate MUST be supported
IO Control
These additions are still pending.
serialStop(portId)
- Stop continuous reading of the specified serial
portId
. - This does not close the port, it stops reading it but keeps the port open.
serialClose(portId)
- Close the specified serial
portId
.
serialFlush(portId)
- Flush the specified serial
portId
. For hardware serial, this waits for the transmission of outgoing serial data to complete. For software serial, this removes any buffered incoming serial data.
Special Method Definitions
normalize(pin)
- Define a special method that Johnny-Five will call when normalizing the pin value.
// Examples:
var io = new IOPlugin();
// The board might want to map "A*" pins to their integer value,
// eg. Arduino allows user code to write "A0" for pin 14:
io.normalize("A0"); // 14
Special Property Definitions
defaultLed
- This is the pin address for the board's default, built-in led.
name
- A printable version of the name of the IO plugin being used, e.g. "Raspi IO"
TODO
- Define pluggable transports, for example: replacing node-serialport with socket.io-serialport and similar.