#Programming with Hardware Pins for Kinoma Element
This document describes how to program Kinoma Element's hardware pins, and serves as a handy reference to the pin types supported by Kinoma Element.
##About the Pins
Kinoma Element has 16 hardware pins. These pins implement Digital Input/Output, Analog Input, Pulse Width Modulation (PWM), Serial Input/Output, and I2C.
The pins are divided into left and right groups of eight pins each. These pins are user-configurable, using either the Front Pins app in Kinoma Code or the Pins module in an application. All pins operate at 3.3 volts.
Figure 1. Kinoma Element Pins
##Introducing BLL Modules
All hardware pin programming is done in JavaScript. The code for each hardware module (sensor, LED, button, motor, and so on) is contained in a JavaScript module called a BLL. BLLs communicate directly with hardware pins. The application communicates with the BLL using the Pins module.
Note: "BLL" stands for Blinking Light Library, but a BLL is not limited to blinking an LED; a BLL can be used to interact with all kinds of hardware modules.
Developers either implement their own BLL to support the hardware modules they have connected to Kinoma Element or use an existing BLL implementation. Sample BLL implementations for common hardware modules are available in our collection of sample apps on GitHub. In addition, the Pins module includes built-in BLLs for each hardware protocol, and you can also find their source code on GitHub; they are the files with names of the form bll_x.js
, where x
is the name of the relevant hardware protocol (for example, bll_Analog.js
and bll_PWM.js
).
Applications first configure the BLLs they will use, and then issue single or repeating commands to the BLL. The Pins module is used to send configuration and commands from the application to BLLs. (See also the document Using the Pins Module to Interact with Sensors on Kinoma Element.)
The call to Pins.configure
lists the BLLs the application uses together with the pins that each BLL uses to communicate to its hardware module.
The following code configures the hardware pins to work with BLLs named buttonBLL
and ledBLL
. The BLL code is stored in files named buttonBLL.js
and ledBLL.js
. The second argument of Pins.configure
is a callback function that is invoked whether or not the configuration is successful.
Pins.configure({
greenButton: {
require: "buttonBLL",
pins: {
button: { pin: 1 },
power: { pin: 2 },
ground: { pin: 3 },
}
},
redLED: {
require: "ledBLL",
pins: {
led: { pin: 8 },
ground: { pin: 9 },
}
}
}, success => {
if (success)
trace("Configured pins.\n");
else
trace("Failed to configure pins.\n");
});
Applications assign a unique name to each hardware module in the configuration. In the preceding example, the button BLL is named greenButton
and the LED BLL is named redLED
. The application uses these names to send commands to the corresponding hardware module.
The require
and pins
properties are defined by the application. The value of the require
property is the name of the BLL file corresponding to the hardware module. The names of the properties inside the pins
objects (in this example, led
, button
, power
, and ground
) are defined by each individual BLL.
While not evident from the application code, the button BLL uses a Digital Input pin, and the LED BLL uses a Digital Output pin. By convention, the application code specifies the pin numbering and the BLL defines the type of pins (Digital Input, Digital Output, PWM, Serial, and so on) that are used. The next section covers how to specify the pin type.
The LED BLL is bound to pins 8 and 9 and the button BLL to pins 1 through 3, as defined in the pins
object.
Once the pins are configured, an application can invoke BLL functions using the Pins.invoke
function.
Pins.invoke("/redLED/write", 1); // Turn LED on
Pins.invoke("/redLED/write", 0); // Turn LED off
Applications retrieve values from BLLs in a similar way: calling Pins.invoke
with the name of the target hardware module and the name of the command in the path. Also passed in is a callback function, which will be called with the value returned.
Pins.invoke("/greenButton/get", value => {
trace("Button value: " + value + "\n");
});
Note: The format of the value returned from the BLL is determined by the author of the BLL. In the preceding example, the type of the returned value is
Number
, but it may be any valid JavaScript object.
Applications establish repeated polling of a hardware module at a specified interval using the Pins.repeat
function. The following example polls the button at 50-millisecond intervals.
Pins.repeat("/greenButton/get", 50, value => {
trace("Button value: " + value + "\n");
});
If you would like to take a closer look at the Pins module for Kinoma Element, you can find its source codeon GitHub.
As mentioned earlier, a BLL is a JavaScript module that communicates directly with hardware pins. The BLL is configured by the application, which communicates with the BLL exclusively using the Pins module.
As illustrated in the simple example below, all BLLs must export the following:
-
A
pins
object that defines the types of pins it uses -
A
configure
function that initializes each of the objects by calling the object'sinit
function -
A
close
function that is called automatically when the host application exits and that typically closes the objects used to communicate with pins
You can define and export additional functions required for working with the module to interact with the sensors.
Here are the pins
exports corresponding to ledBLL
and buttonBLL
(respectively) as introduced earlier:
exports.pins = {
led: { type: "Digital", direction: "output" },
ground: { type: "Ground" }
};
exports.pins = {
button: { type: "Digital", direction: "input" }
power: { type: "Power" },
ground: { type: "Ground" }
};
When Pins.configure
is called, the configuration data provided by the application (highlighted in green
below) is merged with the properties of the pins
object exported by the BLL (highlighted in blue
), to arrive at the following complete configurations:
redLED_configuration = {
led: { type: "Digital", pin: 8, direction: "output" },
ground: { type: "Ground", pin: 9 }
}
greenButton_configuration = {
button: { type: "Digital", pin: 1, direction: "input" },
power: { type: "Power", pin: 2 },
ground: { type: "Ground", pin: 3 }
}
The appropriate BLLs are then loaded using the require
function, and JavaScript objects to communicate with the hardware pins are instantiated. In this example, two Digital pin objects are instantiated: one configured as an input and the other as an output. The input is bound to pin 1 and the output to pin 8. These JavaScript objects are assigned to the module using the property names given by the BLL (led
and button
in this example).
Once the objects are instantiated and bound, the configure
function of each BLL is called. The configure
function must initialize each of the objects by calling the object's init
function, and it can do any additional work required by the BLL. Note that Power and Ground pins do not need to be initialized.
exports.configure = function() {
this.led.init();
}
exports.configure = function() {
this.button.init();
}
The BLL also defines a close
function, which is called automatically when the host application exits. The close
function typically closes the objects used to communicate with pins, as follows:
exports.close = function() {
this.led.close();
}
exports.close = function() {
this.button.close();
}
The BLL author may define additional functions required for working with the module. Here are sample read
and write
functions for the led
and button
objects:
exports.read = function() {
return this.button.read();
}
exports.write = function(value) {
this.led.write(value ? 1 : 0);
}
This section describes the pins data format that an application uses to configure a BLL. For each of the pin types that are programmatically supported by Kinoma Element--Digital
, Analog
, PWM
, Serial
, and I2C
--the following reference details are provided:
- Pins Object -- The full pin configuration data, specified in the
pins
object with properties that define the type of pin and the pin number used. Data inblue
is defined by the BLL; data ingreen
, by the application.
- Functions, Values -- The functions and value properties supported by the pin type.
The object for each pin type has an init
function that reserves any hardware resources required by the object, and a close
function to release the resources used by the object. The object will not operate properly until the init
function is called.
Note: This reference section refers to chunks, but use of chunks has been deprecated.
###Digital
####Pins Object
{type: "Digital", pin: 2, direction: "input"};
{type: "Digital", pin: 3, direction: "output"};
####Functions
#####digital.read()
Returns 0 or 1, or undefined
if the read fails. This function is used to retrieve either the value of an input pin or the value being sent by an output pin.
#####digital.write(value)
value
must be 0 or 1. This property can be read or written to change the direction.
####Values
#####digital.direction
Value is the string input
or output
.
###Analog
####Pins Object
{type: "Analog", pin: 3};
Analog is available only on pins 1 through 8.
####Functions
#####analog.read()
Returns a floating-point value from 0 to 1.0
###PWM
####Pins Object
{type: "PWM", pin: 9};
PWM is available only on pins 9 through 16. All PWMs can be active simultaneously.
####Functions
#####pwm.write(value)
Sets the duty cycle (percentage of time spent "high") to value
(a floating-point value from 0 to 1.0). The frequency is 50 Hz (period 20 ms).
#####pwm.write(value, period)
Sets the PWM output pulse width to value
and the period (cycle duration, or 1/frequency) to period
, both specified in milliseconds. Pulse width and period can also be specified as an array, as in pwm.write([width, period])
. The minimum pulse width is .00002 ms (20 ns). The maximum frequency is 5 kHz--that is, a period of .2 ms, or 200 µs. Very long periods (greater than one hour) are supported.
#####Examples
this.pwm.write(0.5); // 50% duty cycle
this.pwm.write(0.5, 5); // 0.5 ms pulse width, 5 ms period (10% @ 200 Hz)
this.pwm.write([0.5, 5]); // Same as preceding example
###Serial
####Pins Object
{type: "Serial", rx: 1, tx: 2, baud: 38400};
Serial RX and TX pins must be used in adjacent pairs; the options are 1-2, 5-6, and 12-11.
If the serial device is read-only or write-only, the unused pin property, rx
or tx
, may be excluded from the pin configuration data.
####Functions
#####serial.read(format)
Reads data from the serial input. Pass one of the following strings for the format
parameter:
-
String
to return a string. The data read into a string must be valid UTF-8 data, otherwise an exception is thrown. -
ArrayBuffer
to return an array buffer.
#####serial.read(format, maximumBytes)
Same as serial.read(format)
but reads no more than maximumBytes
. This call does not block, so only immediately available bytes are returned.
#####serial.read(format, maximumBytes, msToWait)
Same as serial.read(format, maximumBytes)
but waits up to msToWait
milliseconds for maximumBytes
to arrive
#####serial.write(value)
Writes the specified value to the output. value
can be a number, string, array, or array buffer.
###I2C
####Pins Object
{type: "I2C", sda: 13, clock: 14, address: 0x36};
I2C pins must be used in adjacent pairs; the options are 13-14 and 15-16.
The address
property is the I2C slave address. The data sheet for some I2C devices specifies different slave addresses for reading and writing, differing only in the least significant bit; if this is the case, use the most significant seven bits for the slave address here.
####Functions
#####i2c.readByte()
Reads one byte
#####i2c.readBlock(count, format)
Reads the number of bytes specified by count
. Pass a string for the format
parameter: Chunk
to return the data in a chunk, or Array
to return an array of integer character codes from 0 to 255.
#####i2c.readByteDataSMB(register)
Reads one byte from the specified register
#####i2c.readWordDataSMB(register)
Reads two bytes from the specified register
#####i2c.readBlockDataSMB(register, count, format)
Reads count
bytes starting at the specified register. Pass a string for the format
parameter: Chunk
to return the data in a chunk, or Array
to return an array of integer character codes from 0 to 255.
#####i2c.writeByte(value)
Writes one byte.
#####i2c.writeBlock(value...)
Writes the specified value(s), which are treated in the same way as by serial.write
.
#####i2c.writeByteDataSMB(register, value)
Writes one byte to the specified register
#####i2c.writeWordDataSMB(register, value)
Writes two bytes to the specified register
#####i2c.writeBlockDataSMB(register, value...)
Writes up to 32 bytes starting at the specified register. The value(s) are treated in the same way as serial.write
.
#####i2c.writeQuickSMB(value)
Sends the low bit of value
using the I2C write_quick
command
#####i2c.processCallSMB(register, value)
Writes two bytes to specified register; after the write completes, reads two bytes and returns the resulting data word.