Ruff Driver Programing Concepts
Drivers are key components of Ruff. They constitute the foundation for controlling hardware using software.
Ports
Ports are used to interact with data between devices such as pins and buses. Ruff creates an abstraction layer that integrates some standard industry ports.
Before you start building a driver, make sure the port type is supported by Ruff.
Ruff supports the following ports:
Input/Output(driver.json)
The Ruff driver contains a description file, driver.json
, that defines input and output as in the following example:{
"inputs": {
"btnGpio": {
"type" : "gpio"
}
}
}
Here btnGpio
is an ID. Since a button requires a GPIO port as its input, we define type
as gpio
.
Some devices may require additional arguments. We define these in args
:{
"inputs": {
"btnGpio": {
"type": "gpio",
"args": {
"direction": "in"
}
}
}
}
If the device is a board or a bus type, we need to add outputs in addition to inputs. Notice how this is done in the following example, which shows the conversion of I2C to GPIO:{
"inputs": {
"i2c": {
"type": "i2c",
"args": {
"address": 35
}
}
},
"outputs": {
"ch0": {
"type": "gpio"
},
"ch1": {
"type": "gpio"
},
"ch2": {
"type": "gpio"
},
"ch3": {
"type": "gpio"
},
"ch4": {
"type": "gpio"
},
"ch5": {
"type": "gpio"
},
"ch6": {
"type": "gpio"
},
"ch7": {
"type": "gpio"
}
}
}
Driver Development
To illustrate the driver development process, let’s consider the creation of a button driver:var driver = require('ruff-driver');
var Gpio = require('gpio');
module.exports = driver({
attach: function(inputs) {
var _this = this;
this.gpio = inputs.getRequired('btnGpio');
this.gpio.setDirection(Gpio.IN);
this.gpio.setEdge(Gpio.EDGE_BOTH);
this.gpio.on('interrupt', function() {
var data = _this.gpio.read();
if (data === 0) {
_this.emit('pressed');
} else {
_this.emit('released');
}
});
},
events: {
presse : 'message when button is presse',
release : 'message when button is release'
},
});
ruff-driver
needs to be required, as in the above example, in order to create adriver
method. We have declared a few methods for app developers.
There are a few names reserved by framework and a few methods we should be aware of.
attach
attach
will be invoked when the system starts. It will take one config parameter as inputs.attach: function(inputs) {
}
We can use inputs
to get the config parameter:inputs.getRequired('btnGpio');
getRequired
returns a required parameter and will result in an error if not configured. You may use getOptional
to get a non-required parameter.inputs.getOptional('optionalArg', defaultValue);
The name used here is identical to the name of parameter we set up in driver.json
.
detach
detach
invokes when the system stops. Any resource to be released will be released.detach: function() {
}
detach
is an optional method. By default Ruff will detach all resources.
Events
A driver may need to declare some events. Ruff’s driver development is based on an asynchronous I/O model which is event-driven. Normally events are generated from interruption. In the above example, when an interruption occurs, the attach
and detach
methods will trigger presse
and release
.events: {
presse : 'message when button is pressed',
release : 'message when button is released'
}
Custom Function
In addition to events, we can define custom functions to control the hardware subjectively instead of its being triggered, as with initiation of the LED turn-on.
Custom functions are declared within exports
:var driver = require('ruff-driver');
module.exports = driver({
attach: function(inputs) {
...
},
events: {
...
},
exports: {
customizedFunction: function() {
...
}
}
});
We saw an example of defining a custom function in getting started. In that instance, the custom function readValue
was defined in the driver.
Export Device
Sometimes you may need to convert an I2C to a GPIO or other type of device. In such cases, you need a function to handle the conversion. In our programming model, you should write your code within getDevice
.var driver = require('ruff-driver');
module.exports = driver({
attach: function(inputs) {
...
},
getDevice: function(name, options) {
}
});
getDevice
has two parameters:
name
: name of the device you want to export.options
: user-defined options.
Dependency Injection
Dependency injection is a common software design model.
Ruff uses dependency injection to allow driver developers to code towards built-in API instead of worrying about how devices are connected. This makes driver development and testing much easier.
An example of how to get dependencies(config arguments) appears above in attach
.