├── .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 | --------------------------------------------------------------------------------