├── .npmrc
├── .gitignore
├── .npmignore
├── doc
├── led.png
├── hc-sr04.png
├── hc-sr04-two-pin.png
├── continuous-servo.png
└── implementation-notes.md
├── .jshintrc
├── example
├── led.js
├── built-in-led.js
├── led-pulse.js
├── continuous-servo.js
├── button.js
├── hc-sr04-proximity.js
├── led-brightness.js
├── led-button.js
├── servo.js
├── hc-sr04-proximity-two-pin.js
├── digital-write-performance.js
└── i2c-accelerometer.js
├── package.json
├── LICENSE
├── test
├── lcd-stress-test.js
└── enable-pull-up-pull-down.js
├── lib
├── controller.js
├── pins.js
├── range-finder.js
└── pi-io.js
├── History.md
└── README.md
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | node_modules
3 |
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | *~
2 | .jshintrc
3 | .travis.yml
4 | node_modules/
5 |
6 |
--------------------------------------------------------------------------------
/doc/led.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fivdi/pi-io/HEAD/doc/led.png
--------------------------------------------------------------------------------
/doc/hc-sr04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fivdi/pi-io/HEAD/doc/hc-sr04.png
--------------------------------------------------------------------------------
/doc/hc-sr04-two-pin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fivdi/pi-io/HEAD/doc/hc-sr04-two-pin.png
--------------------------------------------------------------------------------
/doc/continuous-servo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fivdi/pi-io/HEAD/doc/continuous-servo.png
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "curly": true,
3 | "esversion": 6,
4 | "eqeqeq": true,
5 | "latedef": false,
6 | "noarg": true,
7 | "node": true,
8 | "quotmark": "single",
9 | "strict": "global",
10 | "undef": true,
11 | "varstmt": true
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/example/led.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const led = new five.Led('GPIO17');
12 |
13 | led.blink(500);
14 | });
15 |
16 |
--------------------------------------------------------------------------------
/example/built-in-led.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const led = new five.Led('LED0');
12 |
13 | led.blink(500);
14 | });
15 |
16 |
--------------------------------------------------------------------------------
/example/led-pulse.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const led = new five.Led('GPIO17');
12 |
13 | led.pulse(1000);
14 | });
15 |
16 |
--------------------------------------------------------------------------------
/example/continuous-servo.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const servo = new five.Servo({
12 | pin: 'GPIO27',
13 | type: 'continuous'
14 | });
15 |
16 | servo.cw(0.8);
17 | });
18 |
19 |
--------------------------------------------------------------------------------
/example/button.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const button = new five.Button('GPIO4');
12 |
13 | button.on('down', function() {
14 | console.log('down');
15 | });
16 |
17 | button.on('up', function() {
18 | console.log('up');
19 | });
20 | });
21 |
22 |
--------------------------------------------------------------------------------
/example/hc-sr04-proximity.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const proximity = new five.Proximity({
12 | controller: 'HCSR04',
13 | pin: 'GPIO25'
14 | });
15 |
16 | proximity.on('data', function() {
17 | console.log('cm: ', this.cm);
18 | });
19 | });
20 |
21 |
--------------------------------------------------------------------------------
/example/led-brightness.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const led = new five.Led('GPIO17');
12 |
13 | (function next() {
14 | led.brightness(20);
15 | setTimeout(function () {
16 | led.brightness(255);
17 | setTimeout(next, 1000);
18 | }, 1000);
19 | }());
20 | });
21 |
22 |
--------------------------------------------------------------------------------
/example/led-button.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const led = new five.Led('GPIO17');
12 | const button = new five.Button('GPIO4');
13 |
14 | button.on('down', function() {
15 | led.on();
16 | });
17 |
18 | button.on('up', function() {
19 | led.off();
20 | });
21 | });
22 |
23 |
--------------------------------------------------------------------------------
/example/servo.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const servo = new five.Servo({
12 | pin: 'P1-19',
13 | pwmRange: [600, 2300]
14 | });
15 |
16 | (function next() {
17 | servo.to(0);
18 | setTimeout(function () {
19 | servo.to(180);
20 | setTimeout(next, 2000);
21 | }, 2000);
22 | }());
23 | });
24 |
25 |
--------------------------------------------------------------------------------
/example/hc-sr04-proximity-two-pin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const proximity = new five.Proximity({
12 | controller: PiIO.HCSR04, // Custom controller
13 | triggerPin: 'GPIO23',
14 | echoPin: 'GPIO24'
15 | });
16 |
17 | proximity.on('change', function() {
18 | console.log('cm: ', this.cm);
19 | });
20 | });
21 |
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pi-io",
3 | "version": "1.1.1",
4 | "description": "Raspberry Pi IO Plugin for Johnny-Five",
5 | "main": "lib/pi-io.js",
6 | "directories": {
7 | "example": "example"
8 | },
9 | "scripts": {
10 | "lint": "jshint lib/*.js example/*.js test/*.js"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/fivdi/pi-io.git"
15 | },
16 | "dependencies": {
17 | "linux-io": "^1.1.1",
18 | "pigpio": "^3.3.1"
19 | },
20 | "devDependencies": {
21 | "jshint": "^2.12.0"
22 | },
23 | "keywords": [
24 | "raspberry-pi",
25 | "johnny-five"
26 | ],
27 | "author": "fivdi",
28 | "license": "MIT"
29 | }
30 |
--------------------------------------------------------------------------------
/example/digital-write-performance.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const pin = board.io.normalize('GPIO17');
12 | let writesPerSecond;
13 | let time;
14 | let i;
15 |
16 | this.pinMode(pin, five.Pin.OUTPUT);
17 |
18 | time = process.hrtime();
19 |
20 | for (i = 1; i <= 1e7; i += 1) {
21 | this.digitalWrite(pin, i & 1);
22 | }
23 |
24 | time = process.hrtime(time);
25 | writesPerSecond = Math.floor(i / (time[0] + time[1] / 1E9));
26 |
27 | console.log('writes per second', writesPerSecond);
28 | });
29 |
30 |
--------------------------------------------------------------------------------
/example/i2c-accelerometer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const five = require('johnny-five');
4 | const PiIO = require('..');
5 |
6 | const board = new five.Board({
7 | io: new PiIO()
8 | });
9 |
10 | board.on('ready', function() {
11 | const accelerometer = new five.Accelerometer({
12 | controller: 'ADXL345'
13 | });
14 |
15 | accelerometer.on('change', function() {
16 | console.log('accelerometer');
17 | console.log(' x : ', this.x);
18 | console.log(' y : ', this.y);
19 | console.log(' z : ', this.z);
20 | console.log(' pitch : ', this.pitch);
21 | console.log(' roll : ', this.roll);
22 | console.log(' acceleration : ', this.acceleration);
23 | console.log(' inclination : ', this.inclination);
24 | console.log(' orientation : ', this.orientation);
25 | console.log('--------------------------------------');
26 | });
27 | });
28 |
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Brian Cooke
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/test/lcd-stress-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is an LCD stress test to determine whether or not there are timing
4 | // issues when using an LCD with pi-io. The test continuously fills the LCD
5 | // with each character as quickly as it can. After the LCD is filled with the
6 | // character '5' the program pauses for one second and then continues.
7 | //
8 | // Initially this test failed and the following issue was created:
9 | // https://github.com/rwaldron/johnny-five/issues/1295
10 | // This issue has since been resolved.
11 |
12 | const five = require('johnny-five');
13 | const PiIO = require('..');
14 |
15 | const board = new five.Board({
16 | io: new PiIO()
17 | });
18 |
19 | board.on('ready', function() {
20 | const lcd = new five.LCD({
21 | // LCD pin name RS EN DB4 DB5 DB6 DB7
22 | pins: ['GPIO13', 'GPIO19', 'GPIO16', 'GPIO26', 'GPIO20', 'GPIO21'],
23 | rows: 4,
24 | cols: 20
25 | });
26 |
27 | let charCode = 0;
28 |
29 | (function fillDisplay() {
30 | lcd.cursor(0, 0).print(
31 | new Array(20 * 4 + 1).join(String.fromCharCode(charCode))
32 | );
33 | charCode = (charCode + 1) & 0xff;
34 | setTimeout(fillDisplay, charCode === 0x36 ? 1000 : 0);
35 | }());
36 | });
37 |
38 |
--------------------------------------------------------------------------------
/test/enable-pull-up-pull-down.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // In order for this test program to function correctly nothing should be
4 | // connected to GPIO6.
5 |
6 | const assert = require('assert');
7 | const five = require('johnny-five');
8 | const PiIO = require('..');
9 |
10 | const board = new five.Board({
11 | io: new PiIO(),
12 | repl: false,
13 | debug: false
14 | });
15 |
16 | board.on('ready', function() {
17 | let callbacks = 0;
18 |
19 | this.pinMode('GPIO6', this.MODES.INPUT);
20 |
21 | this.digitalRead('GPIO6', function (value) {
22 | callbacks += 1;
23 |
24 | if (callbacks === 1) {
25 | assert(value === 1, 'expected GPIO6 to have a value of 1');
26 |
27 | // Enable pull-down. Because nothing is connected to GPIO6 this should
28 | // pull GPIO6 low and result in the digitalRead callback being called
29 | // with a value of 0.
30 | this.digitalWrite('GPIO6', 0);
31 | } else if (callbacks === 2) {
32 | assert(value === 0, 'expected GPIO6 to have a value of 0');
33 |
34 | process.exit();
35 | }
36 | });
37 |
38 | // Enable pull-up. Because nothing is connected to GPIO6 this should pull
39 | // GPIO6 high and result in the digitalRead callback being called with a
40 | // value of 1.
41 | this.digitalWrite('GPIO6', 1);
42 | });
43 |
44 |
--------------------------------------------------------------------------------
/doc/implementation-notes.md:
--------------------------------------------------------------------------------
1 | # Implementation Notes
2 |
3 | linux-io supports the following features:
4 |
5 | * Digital IO
6 | * Implementation based on the [GPIO sysfs interface](https://www.kernel.org/doc/Documentation/gpio/sysfs.txt) using [onoff](https://github.com/fivdi/onoff)
7 | * I2C
8 | * Implementation based on the [/dev interface](https://www.kernel.org/doc/Documentation/i2c/dev-interface) using [i2c-bus](https://github.com/fivdi/i2c-bus)
9 | * Built-in LEDs
10 | * Implementation based on the [LED sysfs interface](https://www.kernel.org/doc/Documentation/leds/leds-class.txt) using [led.js](https://github.com/fivdi/linux-io/blob/master/lib/led.js)
11 |
12 | pi-io extends and overrides linux-io to provide the following features:
13 |
14 | * Digital IO
15 | * The default implementation provided by linux-io is overridden to provide a more performant implementation based on the [pigpio C library](https://github.com/joan2937/pigpio) using the [pigpio C library wrapper](https://github.com/fivdi/pigpio)
16 | * PWM and Servo pulses on any number of GPIOs simultaneously
17 | * Implementation based on the [pigpio C library](https://github.com/joan2937/pigpio) using the [pigpio C library wrapper](https://github.com/fivdi/pigpio)
18 | * Ping
19 | * Implementation based on the [pigpio C library](https://github.com/joan2937/pigpio) using the [pigpio C library wrapper](https://github.com/fivdi/pigpio)
20 |
21 |
--------------------------------------------------------------------------------
/lib/controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const RangeFinder = require('./range-finder');
4 |
5 | // Copied from https://github.com/rwaldron/johnny-five/blob/v0.11.1/lib/fn.js#L6-L22
6 | function toFixed(number, digits) {
7 | // Guard against error when number is null or undefined
8 | // Cast result as number
9 | return +(number || 0).toFixed(digits);
10 | }
11 |
12 | // Based on https://github.com/rwaldron/johnny-five/blob/v0.11.1/lib/proximity.js#L170-L212
13 | module.exports.HCSR04 = {
14 | initialize: {
15 | value: function(opts, dataHandler) {
16 | const msToNextRead = 65;
17 |
18 | const rangeFinder = new RangeFinder(
19 | this.io._pins[this.io.normalize(opts.triggerPin)].gpioNo,
20 | this.io._pins[this.io.normalize(opts.echoPin)].gpioNo
21 | );
22 |
23 | const read = function() {
24 | // If an attempt is made to measure proximity with two or more HC-SR04
25 | // sensors concurrently the sound pulses from the different sensors
26 | // can interfere with each other. The lock here prevents this from
27 | // happening.
28 | this.io._pingReadLock(function(release) {
29 | // Note that the pingRead callback does not have an err argument. If
30 | // pingRead can't measure proximity it calls the callback with the
31 | // microseconds argument set to 0.
32 | rangeFinder.pingRead(function(microseconds) {
33 | dataHandler(microseconds);
34 |
35 | setTimeout(read, msToNextRead);
36 |
37 | release();
38 | }.bind(this));
39 | }.bind(this));
40 | }.bind(this);
41 |
42 | read();
43 | }
44 | },
45 | toCm: {
46 | value: function(raw) {
47 | return toFixed(raw / 29.1 / 2, 3);
48 | }
49 | }
50 | };
51 |
52 |
--------------------------------------------------------------------------------
/lib/pins.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = [
4 | /* P1_1 - 3V3 */
5 | /* P1_2 - 5V */
6 | { ids: ['P1-3', 'GPIO2'], gpioNo: 2, modes: [0, 1, 3, 4] },
7 | /* P1_4 - 5V */
8 | { ids: ['P1-5', 'GPIO3'], gpioNo: 3, modes: [0, 1, 3, 4] },
9 | /* P1_6 - Ground */
10 | { ids: ['P1-7', 'GPIO4'], gpioNo: 4, modes: [0, 1, 3, 4] },
11 | { ids: ['P1-8', 'GPIO14'], gpioNo: 14, modes: [0, 1, 3, 4] },
12 | /* P1_9 - Ground */
13 | { ids: ['P1-10', 'GPIO15'], gpioNo: 15, modes: [0, 1, 3, 4] },
14 | { ids: ['P1-11', 'GPIO17'], gpioNo: 17, modes: [0, 1, 3, 4] },
15 | { ids: ['P1-12', 'GPIO18'], gpioNo: 18, modes: [0, 1, 3, 4] },
16 | { ids: ['P1-13', 'GPIO27'], gpioNo: 27, modes: [0, 1, 3, 4] },
17 | /* P1_14 - Ground */
18 | { ids: ['P1-15', 'GPIO22'], gpioNo: 22, modes: [0, 1, 3, 4] },
19 | { ids: ['P1-16', 'GPIO23'], gpioNo: 23, modes: [0, 1, 3, 4] },
20 | /* P1_17 - 3V3 */
21 | { ids: ['P1-18', 'GPIO24'], gpioNo: 24, modes: [0, 1, 3, 4] },
22 | { ids: ['P1-19', 'GPIO10'], gpioNo: 10, modes: [0, 1, 3, 4] },
23 | /* P1_20 - Ground */
24 | { ids: ['P1-21', 'GPIO9'], gpioNo: 9, modes: [0, 1, 3, 4] },
25 | { ids: ['P1-22', 'GPIO25'], gpioNo: 25, modes: [0, 1, 3, 4] },
26 | { ids: ['P1-23', 'GPIO11'], gpioNo: 11, modes: [0, 1, 3, 4] },
27 | { ids: ['P1-24', 'GPIO8'], gpioNo: 8, modes: [0, 1, 3, 4] },
28 | /* P1_25 - Ground */
29 | { ids: ['P1-26', 'GPIO7'], gpioNo: 7, modes: [0, 1, 3, 4] },
30 | /* P1_27 - ID_SD */
31 | /* P1_28 - ID_SC */
32 | { ids: ['P1-29', 'GPIO5'], gpioNo: 5, modes: [0, 1, 3, 4] },
33 | /* P1_30 - Ground */
34 | { ids: ['P1-31', 'GPIO6'], gpioNo: 6, modes: [0, 1, 3, 4] },
35 | { ids: ['P1-32', 'GPIO12'], gpioNo: 12, modes: [0, 1, 3, 4] },
36 | { ids: ['P1-33', 'GPIO13'], gpioNo: 13, modes: [0, 1, 3, 4] },
37 | /* P1_34 - Ground */
38 | { ids: ['P1-35', 'GPIO19'], gpioNo: 19, modes: [0, 1, 3, 4] },
39 | { ids: ['P1-36', 'GPIO16'], gpioNo: 16, modes: [0, 1, 3, 4] },
40 | { ids: ['P1-37', 'GPIO26'], gpioNo: 26, modes: [0, 1, 3, 4] },
41 | { ids: ['P1-38', 'GPIO20'], gpioNo: 20, modes: [0, 1, 3, 4] },
42 | /* P1_39 - Ground */
43 | { ids: ['P1-40', 'GPIO21'], gpioNo: 21, modes: [0, 1, 3, 4] },
44 |
45 | { ids: ['LED0'], ledPath: '/sys/class/leds/led0', modes: [1] }
46 | ];
47 |
48 |
--------------------------------------------------------------------------------
/History.md:
--------------------------------------------------------------------------------
1 | 1.1.1 / May 01 2021
2 | ===================
3 |
4 | * update dependencies
5 |
6 | 1.1.0 / Apr 25 2020
7 | ===================
8 |
9 | * update dependencies (linux-io v1.1.0, pigpio v3.1.1, jshint v2.11.0)
10 |
11 | 1.0.2 / Sep 07 2019
12 | ===================
13 |
14 | * update dependencies (linux-io v1.0.4, pigpio v2.0.0)
15 |
16 | 1.0.1 / May 01 2019
17 | ===================
18 |
19 | * allow servowrite to support degrees or microseconds
20 | * improve installation instructions
21 |
22 | 1.0.0 / Mar 30 2019
23 | ===================
24 |
25 | * update dependencies (linux-io v1.0.3, pigpio v1.2.2)
26 | * lint with jshint
27 |
28 | 0.5.1 / May 21 2018
29 | ===================
30 |
31 | * update dependencies
32 |
33 | 0.5.0 / Jul 16 2017
34 | ===================
35 |
36 | * prevent a non-responding hc-sr04 from blocking other hc-sr04s
37 | * update dependencies
38 |
39 | 0.4.0 / Jul 08 2017
40 | ===================
41 |
42 | * add support for using hc-sr04 sensors with separate trigger and echo pins
43 |
44 | 0.3.0 / Jul 02 2017
45 | ===================
46 |
47 | * add support for enabling pull-up and pull-down resistors
48 | * update dependencies
49 | * improve hc-sr04 timeout detection
50 | * use lower resistor values for hc-sr04
51 | * document hc-sr04 issues
52 |
53 | 0.2.2 / Jun 18 2017
54 | ===================
55 |
56 | * update dependencies
57 | * improve documentation
58 | * disable pull-up and pull-down resistors when a new Gpio is created
59 |
60 | 0.2.1 / May 29 2017
61 | ===================
62 |
63 | * add name property
64 | * update dependencies
65 |
66 | 0.2.0 / Apr 22 2017
67 | ===================
68 |
69 | * update dependencies
70 | * add support for pingRead
71 | * wait 100ms for the pigpio C library to stabilize
72 |
73 | 0.1.2 / Apr 22 2017
74 | ===================
75 |
76 | * update dependencies
77 | * use pigpio mode constants when setting mode
78 |
79 | 0.1.1 / Feb 26 2017
80 | ===================
81 |
82 | * lcd stress test
83 |
84 | 0.1.0 / Feb 25 2017
85 | ===================
86 |
87 | * built-in led support
88 | * add support for all gpios on p1 header
89 | * documentation
90 |
91 | 0.0.1 / Feb 22 2017
92 | ===================
93 |
94 | * simplify overriding of pinMode for digital io
95 | * put pin information in separate file
96 |
97 | 0.0.0 / Feb 20 2017
98 | ===================
99 |
100 | * initial release
101 |
102 |
--------------------------------------------------------------------------------
/lib/range-finder.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Gpio = require('pigpio').Gpio;
4 |
5 | function RangeFinder(triggerGpioNo, echoGpioNo) {
6 | if (!(this instanceof RangeFinder)) {
7 | return new RangeFinder(triggerGpioNo, echoGpioNo);
8 | }
9 |
10 | if (triggerGpioNo === echoGpioNo || typeof(echoGpioNo) !== 'number') {
11 | this._singlePin = true;
12 | echoGpioNo = triggerGpioNo;
13 | } else {
14 | this._singlePin = false;
15 | }
16 |
17 | this._triggerGpio = new Gpio(triggerGpioNo, {
18 | mode: Gpio.OUTPUT,
19 | pullUpDown: Gpio.PUD_OFF,
20 | alert: this._singlePin
21 | });
22 |
23 | if (this._singlePin) {
24 | this._echoGpio = this._triggerGpio;
25 | } else {
26 | this._echoGpio = new Gpio(echoGpioNo, {
27 | mode: Gpio.INPUT,
28 | pullUpDown: Gpio.PUD_OFF,
29 | alert: true
30 | });
31 | }
32 |
33 | this._callback = undefined;
34 | this._startTick = undefined;
35 | this._ignoreAlerts = false;
36 | this._timeout = undefined;
37 |
38 | this._triggerGpio.digitalWrite(0);
39 |
40 | // If a single pin is used for both trigger and echo there will be alert
41 | // events for trigger pulses and echo pulses. Trigger pulses are ignored.
42 | this._echoGpio.on('alert', function (level, tick) {
43 | let endTick;
44 | let diff;
45 |
46 | if (this._ignoreAlerts) {
47 | return;
48 | }
49 |
50 | // level === 0 implies falling edge of pulse.
51 | // level === 1 implies rising edge of pulse.
52 | if (level === 1) {
53 | this._startTick = tick;
54 | } else if (this._startTick === undefined) {
55 | // The rising edge of a pulse was not detected. Ignore alerts and
56 | // allow the 250ms timeout started in pingRead to occur.
57 | this._ignoreAlerts = true;
58 | } else {
59 | endTick = tick;
60 |
61 | // Determine pulse width in microseconds (unsigned 32 bit arithmetic.)
62 | diff = (endTick >> 0) - (this._startTick >> 0);
63 |
64 | // Ignore short pulses less than 20 microseconds wide.
65 | // Short pulses are usually trigger pulses.
66 | if (diff > 20) {
67 | if (diff > 40000) {
68 | // Assume the hc-sr04 timed out. If the hc-sr04 sends a pulse and
69 | // can't detect it, it will timeout after approx. 200000
70 | // microseconds. To recover, ignore alerts and allow the 250ms
71 | // timeout started in pingRead to occur.
72 | this._ignoreAlerts = true;
73 | } else {
74 | clearTimeout(this._timeout);
75 | this._callback(diff);
76 | }
77 | }
78 | }
79 | }.bind(this));
80 | }
81 |
82 | RangeFinder.prototype.pingRead = function (callback) {
83 | this._callback = callback;
84 | this._startTick = undefined;
85 | this._ignoreAlerts = false;
86 |
87 | if (this._singlePin) {
88 | this._triggerGpio.mode(Gpio.OUTPUT);
89 | }
90 |
91 | this._triggerGpio.trigger(10, 1);
92 |
93 | if (this._singlePin) {
94 | this._triggerGpio.mode(Gpio.INPUT);
95 | }
96 |
97 | this._timeout = setTimeout(function() {
98 | if (this._singlePin) {
99 | // Some hc-sr04 sensors appear to hang and don't timeout after 200000
100 | // microseconds. Setting the pin-mode to output appears to force the
101 | // timeout to occur.
102 | this._triggerGpio.mode(Gpio.OUTPUT);
103 |
104 | setTimeout(function () {
105 | this._callback(0);
106 | }.bind(this), 100);
107 | } else {
108 | this._callback(0);
109 | }
110 | }.bind(this), 250);
111 | };
112 |
113 | module.exports = RangeFinder;
114 |
115 |
--------------------------------------------------------------------------------
/lib/pi-io.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const LinuxIO = require('linux-io');
4 | const pigpio = require('pigpio');
5 | const Gpio = pigpio.Gpio;
6 | const util = require('util');
7 | const pins = require('./pins');
8 | const controller = require('./controller');
9 | const RangeFinder = require('./range-finder');
10 |
11 | function PiIO() {
12 | LinuxIO.call(this, {
13 | name: 'Pi-IO',
14 | pins: pins,
15 | defaultI2cBus: 1,
16 | defaultLed: 'LED0'
17 | });
18 |
19 | pigpio.initialize();
20 |
21 | // After explicitly calling pigpio.initialize wait 100ms for pigpio to become
22 | // ready for usage. This delay is necessary for alerts to function correctly.
23 | // See https://github.com/joan2937/pigpio/issues/127
24 | // Update: This issue was fixed with pigpio V63 in May 2017. When Raspbian is
25 | // updated to include pigpio V63 the timeout here can be removed.
26 | setTimeout(function () {
27 | this.emit('connect');
28 | this.emit('ready');
29 | }.bind(this), 100);
30 | }
31 | util.inherits(PiIO, LinuxIO);
32 |
33 | PiIO.prototype._pinPigpioMode = function(pinData, pigpioMode) {
34 | if (!pinData.gpio) {
35 | pinData.gpio = new Gpio(pinData.gpioNo, {
36 | mode: pigpioMode,
37 | pullUpDown : Gpio.PUD_OFF
38 | });
39 | } else {
40 | pinData.gpio.mode(pigpioMode);
41 | }
42 | };
43 |
44 | PiIO.prototype._pinModeInput = function(pinData) {
45 | this._pinPigpioMode(pinData, Gpio.INPUT);
46 | };
47 |
48 | PiIO.prototype._pinModeOutput = function(pinData) {
49 | this._pinPigpioMode(pinData, Gpio.OUTPUT);
50 | };
51 |
52 | PiIO.prototype._pinModePwm = function(pinData) {
53 | this._pinPigpioMode(pinData, Gpio.OUTPUT);
54 | };
55 |
56 | PiIO.prototype._pinModeServo = function(pinData) {
57 | this._pinPigpioMode(pinData, Gpio.OUTPUT);
58 |
59 | pinData.servoConfig = {
60 | min: 600, // min must be >= 544
61 | max: 2400
62 | };
63 | };
64 |
65 | PiIO.prototype._enablePullUpResistor = function(pinData) {
66 | pinData.gpio.pullUpDown(Gpio.PUD_UP);
67 | };
68 |
69 | PiIO.prototype._enablePullDownResistor = function(pinData) {
70 | pinData.gpio.pullUpDown(Gpio.PUD_DOWN);
71 | };
72 |
73 | PiIO.prototype._digitalReadSync = function(pinData) {
74 | return pinData.gpio.digitalRead();
75 | };
76 |
77 | PiIO.prototype._digitalWriteSync = function(pinData, value) {
78 | pinData.gpio.digitalWrite(value);
79 | };
80 |
81 | PiIO.prototype._pwmWriteSync = function(pinData, value) {
82 | pinData.gpio.pwmWrite(value >> 0);
83 | };
84 |
85 | PiIO.prototype._servoWriteSync = function(pinData, value) {
86 | const min = pinData.servoConfig.min;
87 | const max = pinData.servoConfig.max;
88 | let dutyCycle;
89 |
90 | // By the time flow of control reaches this point of execution one of the
91 | // following is true.
92 | // For degrees:
93 | // 0 <= value && value <= 180
94 | // For microseconds:
95 | // pinData.servoConfig.min <= value && value <= pinData.servoConfig.max
96 |
97 | // value < 544 implies degrees
98 | // value >= 544 implies microseconds
99 | // 544 is a magic number from the arduino servo library
100 | if (value < 544) {
101 | dutyCycle = (min + (value / 180) * (max - min));
102 | } else {
103 | dutyCycle = value;
104 | }
105 |
106 | pinData.gpio.servoWrite(dutyCycle >> 0);
107 | };
108 |
109 | PiIO.prototype._pingRead = function(pinData, callback) {
110 | if (!pinData.custom.rangeFinder) {
111 | pinData.custom.rangeFinder = new RangeFinder(pinData.gpioNo);
112 | }
113 |
114 | pinData.custom.rangeFinder.pingRead(callback);
115 | };
116 |
117 | PiIO.HCSR04 = controller.HCSR04;
118 |
119 | module.exports = PiIO;
120 |
121 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pi-IO
2 |
3 | A proof of concept to demonstrate that it's feasible to implement a Raspberry
4 | Pi IO Plugin for Johnny-Five using
5 | [Linux-IO](https://github.com/fivdi/linux-io).
6 |
7 | Tested on a Raspberry Pi 4 Model B with Node.js v16.0.0. Should function with
8 | Node.js v10 or higher.
9 |
10 | ## Installation
11 |
12 | #### Step 1 - Install the pigpio C library
13 |
14 | The [pigpio C library](https://github.com/joan2937/pigpio) is a prerequisite
15 | for pi-io.
16 |
17 | Run the following command to determine which version of the pigpio C library
18 | is installed:
19 |
20 | ```
21 | pigpiod -v
22 | ```
23 |
24 | For the Raspberry Pi Zero, 1, 2 and 3 V41 or higher of the pigpio C library is
25 | required. For the Raspberry Pi 4 V69 or higher is required.
26 |
27 | If the pigpio C library is not installed or if the installed version is too
28 | old, the latest version can be installed with the following commands:
29 |
30 | ```
31 | sudo apt-get update
32 | sudo apt-get install pigpio
33 | ```
34 |
35 | Alternative installation instructions for the pigpio C library can be found
36 | [here](http://abyz.me.uk/rpi/pigpio/download.html).
37 |
38 | **Warning:** The pigpio C library contains a number of utilities. One of these
39 | utilities is pigpiod which launches the pigpio C library as a daemon. This
40 | utility should not be used as the pi-io uses the pigpio C library directly.
41 |
42 | #### Step 2 - Install the pi-io Node.js package
43 |
44 | ```
45 | npm install pi-io
46 | ```
47 |
48 | ## Johnny-Five Features Supported
49 |
50 | Feature | Support
51 | :--- | :---
52 | Analog Read | no
53 | Digital Read | yes
54 | Digital Write | yes
55 | PWM | yes
56 | Servo | yes
57 | I2C | yes
58 | One Wire | no
59 | Stepper | no
60 | Serial/UART | no
61 | DAC | no
62 | Ping | yes *)
63 |
64 | *) Ping was tested with three HC-SR04 proximity sensors with varying degrees
65 | of success. One sensor functioned as expected. The second didn't timeout as
66 | it should but it's was possible to workaround the issue with software. The
67 | third sensor didn't function. The issues may be related to the fact that a
68 | single pin is used for both trigger and echo.
69 |
70 | To workaround these issues a controller was implemented that allows separate
71 | pins to be used for trigger and echo. The same three HC-SR04 proximity sensors
72 | functioned well with this controller.
73 |
74 | ## Supported Pins
75 |
76 | Pin ID | Supported Modes | Comments
77 | :--- | :--- | :---
78 | P1-3 or GPIO2 | INPUT, OUTPUT, PWM, SERVO | Alternatively I2C SDA1
79 | P1-5 or GPIO3 | INPUT, OUTPUT, PWM, SERVO | Alternatively I2C SCL1
80 | P1-7 or GPIO4 | INPUT, OUTPUT, PWM, SERVO |
81 | P1-8 or GPIO14 | INPUT, OUTPUT, PWM, SERVO |
82 | P1-10 or GPIO15 | INPUT, OUTPUT, PWM, SERVO |
83 | P1-11 or GPIO17 | INPUT, OUTPUT, PWM, SERVO |
84 | P1-12 or GPIO18 | INPUT, OUTPUT, PWM, SERVO |
85 | P1-13 or GPIO27 | INPUT, OUTPUT, PWM, SERVO |
86 | P1-15 or GPIO22 | INPUT, OUTPUT, PWM, SERVO |
87 | P1-16 or GPIO23 | INPUT, OUTPUT, PWM, SERVO |
88 | P1-18 or GPIO24 | INPUT, OUTPUT, PWM, SERVO |
89 | P1-19 or GPIO10 | INPUT, OUTPUT, PWM, SERVO |
90 | P1-21 or GPIO9 | INPUT, OUTPUT, PWM, SERVO |
91 | P1-22 or GPIO25 | INPUT, OUTPUT, PWM, SERVO |
92 | P1-23 or GPIO11 | INPUT, OUTPUT, PWM, SERVO |
93 | P1-24 or GPIO8 | INPUT, OUTPUT, PWM, SERVO |
94 | P1-26 or GPIO7 | INPUT, OUTPUT, PWM, SERVO |
95 | P1-29 or GPIO5 | INPUT, OUTPUT, PWM, SERVO |
96 | P1-31 or GPIO6 | INPUT, OUTPUT, PWM, SERVO |
97 | P1-32 or GPIO12 | INPUT, OUTPUT, PWM, SERVO |
98 | P1-33 or GPIO13 | INPUT, OUTPUT, PWM, SERVO |
99 | P1-35 or GPIO19 | INPUT, OUTPUT, PWM, SERVO |
100 | P1-36 or GPIO16 | INPUT, OUTPUT, PWM, SERVO |
101 | P1-37 or GPIO26 | INPUT, OUTPUT, PWM, SERVO |
102 | P1-38 or GPIO20 | INPUT, OUTPUT, PWM, SERVO |
103 | P1-40 or GPIO21 | INPUT, OUTPUT, PWM, SERVO |
104 | LED0 | OUTPUT | Built-in LED
105 |
106 | ## Usage
107 |
108 | ### Pluse an LED connected to GPIO17
109 |
110 |
111 |
112 | ```js
113 | var five = require('johnny-five');
114 | var PiIO = require('pi-io');
115 |
116 | var board = new five.Board({
117 | io: new PiIO()
118 | });
119 |
120 | board.on('ready', function() {
121 | var led = new five.Led('GPIO17');
122 |
123 | led.pulse(1000);
124 | });
125 | ```
126 |
127 | ### Rotate a continuous servo connected to GPIO27 clockwise
128 |
129 |
130 |
131 | ```js
132 | var five = require('johnny-five');
133 | var PiIO = require('pi-io');
134 |
135 | var board = new five.Board({
136 | io: new PiIO()
137 | });
138 |
139 | board.on('ready', function() {
140 | var servo = new five.Servo({
141 | pin: 'GPIO27',
142 | type: "continuous"
143 | });
144 |
145 | servo.cw(0.8);
146 | });
147 | ```
148 |
149 | ### Measure proximity with a HC-SR04 using separate pins for trigger and echo
150 |
151 |
152 |
153 | ```js
154 | var five = require('johnny-five');
155 | var PiIO = require('pi-io');
156 |
157 | var board = new five.Board({
158 | io: new PiIO()
159 | });
160 |
161 | board.on('ready', function() {
162 | var proximity = new five.Proximity({
163 | controller: PiIO.HCSR04, // Custom controller
164 | triggerPin: 'GPIO23',
165 | echoPin: 'GPIO24'
166 | });
167 |
168 | proximity.on("change", function() {
169 | console.log("cm: ", this.cm);
170 | });
171 | });
172 | ```
173 |
174 | ### Measure proximity with a HC-SR04 connected to GPIO25
175 |
176 | Note that this circuit uses a single pin for trigger and echo. Whether or not
177 | this functions correctly appears to be highly dependent on the particular
178 | HC-SR04 sensor being used. The circuit shown above that uses separate pins for
179 | trigger and echo is far more reliable.
180 |
181 |
182 |
183 | ```js
184 | var five = require('johnny-five');
185 | var PiIO = require('pi-io');
186 |
187 | var board = new five.Board({
188 | io: new PiIO()
189 | });
190 |
191 | board.on('ready', function() {
192 | var proximity = new five.Proximity({
193 | controller: 'HCSR04',
194 | pin: 'GPIO25'
195 | });
196 |
197 | proximity.on("data", function() {
198 | console.log("cm: ", this.cm);
199 | });
200 | });
201 | ```
202 |
203 | ## Examples
204 |
205 | Additional examples can be found in the
206 | [example directory](https://github.com/fivdi/pi-io/tree/master/example).
207 |
208 |
--------------------------------------------------------------------------------