Johnny-Five Robotics and Hardware Programming Framework
repository·main·Indexed 12 days ago
https://github.com/rwaldron/johnny-fiveAn open-source JavaScript robotics and IoT programming framework for Node.js using the Firmata protocol. It provides consistent APIs to control hardware including Arduino (all models), Raspberry Pi, Beagle Bone, Intel Galileo & Edison, and more. Version 2.1.0 supports a wide range of components such as LEDs, servos, motors, and accelerometers (ADXL335, ADXL345, LIS3DH, MMA8452, MPU6050) through robust, composable APIs.
What's inside Johnny-Five
- Johnny-Five is an open-source, Firmata Protocol-based IoT and robotics programming framework for JavaScript. It allows you to control various hardware platforms using Node.js. The framework focuses on providing robust, composable APIs that behave consistently across different hardware, making it easy to integrate with application libraries like Express.js or Socket.io, Bluetooth controllers, and other IoT frameworks.
Browse Johnny-Five component examples by category
mainExamples are organized by component type to help you find specific implementation patterns:
- Board: Initialization, cleanup, sampling intervals, and port specification.
- LED: Blinking, fading, pulsing, RGB control, and matrix/digit displays.
- Servo: Continuous, multi-turn, and animation examples.
- Motor: DC motors, H-Bridges, and driver-specific implementations (e.g., PCA9685, L298).
- Sensors: Accelerometers, Gyros, Proximity (HC-SR04, LIDAR), Motion, and Environmental sensors (Temperature, Humidity, Barometer).
- Input: Buttons, Switches, Keypads, Joysticks, and IR/Motion sensors.
- Display: LCD (I2C and standard) and LED Matrices.
- Specialized Kits: Examples for Lego EVShield, Intel Edison + Grove, and TinkerKit.
Hardware Support and IO Plugins
mainJohnny-Five is a Firmata Protocol based framework.
- Arduino: Supports all Arduino models natively.
- Non-Arduino Platforms: For platforms like Raspberry Pi, Beagle Bone, Intel Galileo, etc., use IO Plugins. These plugins allow Johnny-Five to communicate with non-Arduino hardware using the appropriate protocols.
Supported Hardware and IO Plugins
mainJohnny-Five supports a wide range of hardware including Arduino (all models), Electric Imp, Beagle Bone, Intel Galileo & Edison, Linino One, Pinoccio, pcDuino3, Raspberry Pi, Particle/Spark Core & Photon, Tessel 2, and TI Launchpad.
For non-Arduino based projects, you can use IO Plugins. These plugins allow Johnny-Five code to communicate with non-Arduino hardware by translating the commands into the specific language the platform understands.
Implement a Navigator for BOE Bot using continuous servos
mainThe
Navigatorclass is a custom abstraction designed for the BOE Bot (or similar differential drive robots) usingjohnny-fivecontinuous servos. It handles the inverse relationship between left and right servos (since they are mounted oppositely) and provides high-level movement commands.Initialization
To create a
Navigatorinstance, pass an options object specifying the pins for the right and left servos.Note: BOE Bot continuous servos are calibrated to stop at
90°. You can specify a customcentervalue in the options.Movement API
forward(speed): Moves the bot forward.speedis a value from0(stopped) to5(fastest).reverse(speed): Moves the bot in reverse.speedis a value from0to5.stop(): Stops all movement.left(): Performs a turn to the left.right(): Performs a turn to the right.pivot(which, time): Performs a pivot turn.whichaccepts strings likeforward-right,forward-left,reverse-right, orreverse-left.timeis the duration of the pivot in milliseconds.move(right, left): Low-level method to set specific servo positions directly.
var five = require("johnny-five"); // Assuming a Board is ready... var bot = new Navigator({ right: 10, left: 11, center: 90 }); bot.forward(3); bot.pivot("forward-left", 500); bot.stop();How Navigator handles cooperative servo motion
mainThe
Navigatoris designed for robots where servos are mounted in opposition (e.g., on opposite sides of a chassis). Because of this mounting, a command to move "forward" requires the left and right servos to receive opposing values.The
Navigatormanages this mapping internally. When you callforward(), it calculates the necessary opposing values for theleftandrightservos based on thecentervalue provided during initialization. This allows the developer to think in terms of robot direction (forward, reverse, left, right) rather than individual motor polarity.Configure GPS using Hardware Serial
mainWhen using the
GPSclass with an Arduino or similar microcontroller, you must upload theStandardFirmataPlusfirmware to your board.To use a hardware serial port instead of a software serial port, pass the specific hardware serial ID to the
portoption in theGPSconstructor. You can access these IDs viaboard.io.SERIAL_PORT_IDs.const { Board, GPS } = require("johnny-five"); const board = new Board(); board.on("ready", () => { // Explicitly setting HW_SERIAL1 for the port const gps = new GPS({ port: board.io.SERIAL_PORT_IDs.HW_SERIAL1 }); // Listen for position changes gps.on("change", position => { const { latitude, longitude } = position; console.log("GPS Position:"); console.log(" latitude : ", latitude); console.log(" longitude : ", longitude); console.log("--------------------------------------"); }); });Run the Grove Compass Edison example
mainTo run the specific example for the Intel Edison and Grove Compass setup, use the following command from your terminal:
node eg/grove-compass-edison.jsImplement a Line Follower Robot
mainThis guide demonstrates how to build a line-following robot using a reflectance array (like the Pololu QTR-8A) and continuous servos for wheels.
Core Logic
- Reflectance Array: Use
five.IR.Reflect.Collectionto read sensor data. The sensors detect light/dark transitions to determine the line's position. - Calibration: Use
eyes.calibrateUntil()to capture ambient light and dark values. This data can be persisted to a file (e.g.,.calibration) usingeyes.calibrationand reloaded viaeyes.loadCalibration()to avoid repeated manual calibration. - Driving Rules: Map the sensor's
linevalue (emitted via thelineevent) to specific motor speeds and directions. - Continuous Servos: Control the wheels using
five.Servo.Continuous. Use methods like.stop(),.cw(speed), or.ccw(speed)to adjust movement.
Implementation Example
var five = require("johnny-five"); var board = new five.Board(); board.on("ready", function() { var eyes = new five.IR.Reflect.Collection({ emitter: 13, pins: ["A0", "A1", "A2", "A3", "A4", "A5"], freq: 20 }); var wheels = { left: new five.Servo.Continuous(10), right: new five.Servo.Continuous(9) }; // Calibration logic eyes.calibrateUntil(function() { // Return true when calibration is finished return false; }); // Driving logic eyes.on("line", function(err, line) { // 'line' value is used to determine movement rules if (line < 1000) { wheels.left.cw(0.01); wheels.right.ccw(0.07); } // ... additional rules }); });// This is an example of a line following robot. It uses a // Pololu QTR-8A reflectance array to read a line on my // counter drawn with electrical tape. You can see the // bot in action here: https://www.youtube.com/watch?v=i6n4CwqQer0 var fs = require("fs"); var five = require("johnny-five"); var board = new five.Board(); // Setup Standard input. We use this to let the bot know that we"ve finished // calibrating var stdin = process.stdin; stdin.setRawMode(true); stdin.resume(); var calibrationFile = ".calibration"; // VERY simple driving rules. It uses a mapping from the line value that comes // from the Reflectance Array to the left and right wheel of the bot.n// This can be made much better, it is a good start. var drivingRules = { 0: { left: { dir: "cw", speed: 0.01 }, right: { dir: "ccw", speed: 0.07 } }, 1000: { left: { dir: "cw", speed: 0.02 }, right: { dir: "ccw", speed: 0.05 } }, 2000: { left: { dir: "cw", speed: 0.04 }, right: { dir: "ccw", speed: 0.05 } }, 2500: { left: { dir: "cw", speed: 0.05 }, right: { dir: "ccw", speed: 0.05 } }, 3000: { left: { dir: "cw", speed: 0.05 }, right: { dir: "ccw", speed: 0.04 } }, 4000: { left: { dir: "cw", speed: 0.05 }, right: { dir: "ccw", speed: 0.02 } }, 5001: { left: { dir: "cw", speed: 0.07 }, right: { dir: "ccw", speed: 0.01 } } }; board.on("ready", function() { // Create an instance of the reflectance array. var eyes = new five.IR.Reflect.Collection({ emitter: 13, pins: ["A0", "A1", "A2", "A3", "A4", "A5"], freq: 20 }); // These are the continuous servos that control the wheels var wheels = { left: new five.Servo.Continuous(10), right: new five.Servo.Continuous(9) }; // Make the eyes and wheels available in the REPL UI this.repl.inject({ eyes: eyes, wheels: wheels }); // When the bot starts up, enable the IR emitters and tell the wheels // to stop. Calibrate the device. When complete, drive. function init() { eyes.enable(); wheels.left.stop(); wheels.right.stop(); calibrate(drive); } // Calibrate the bot. If the calibration has been persisted, use it. // If not, calibrate the bot until the user presses a key. Move the sensor // over light and dark regions several times. Persist the calibration data // to a file so it doesn"t need to do it again next time. function calibrate(whenComplete) { var savedCalibration, calibrating = true; if (fs.existsSync(calibrationFile)) { eyes.loadCalibration(JSON.parse(fs.readFileSync(calibrationFile))); whenComplete(); return; } console.log("Calibrating. Press a key..."); eyes.calibrateUntil(function() { return !calibrating; }); stdin.once("keypress", function() { calibrating = false; console.log("Done:", eyes.calibration); fs.writeFile(calibrationFile, JSON.stringify(eyes.calibration)); whenComplete(); }); } // Drive the bot. Every time a line value event comes in, figure out which // rule to follow from the rules mapping. Tell the left and right wheels // which direction and how fast to spin. function drive() { eyes.on("line", function(err, line) { var rule; var threshold = Object.keys(drivingRules).find(function(r) { return line <= parseInt(r); }); if (!threshold) { console.log("Could not find threshold for " + line); } rule = drivingRules[threshold]; wheels.left[rule.left.dir](rule.left.speed); wheels.right[rule.right.dir](rule.right.speed); }); } // Start the bot init(); });- Reflectance Array: Use
Install Johnny-Five and pcduino-io for pcDuino
mainTo use the
pcduino-iolibrary, you must first have Node.js (version 0.10.x or better) and npm installed on your pcDuino. Once the environment is set up, install the required packages using npm:npm install johnny-five pcduino-ioConfigure the DHT22_I2C_NANO_BACKPACK controller
mainTheDHT22_I2C_NANO_BACKPACKcontroller is used for DHT22 hygrometers connected via an I2C Nano Backpack. This setup requires specific firmware installed on the hardware. You can find the required firmware source here: I2C Backpack Firmware.Initialize a Board and wait for the 'ready' event
mainTo use Johnny-Five, you must first instantiate a
Boardobject. Because communication with the hardware takes time, you cannot access pins or components immediately after instantiation. You must listen for thereadyevent emitted by the board instance. Once thereadyevent fires, the hardware is connected and the pins are accessible for component initialization.const { Board, Led } = require("johnny-five"); const board = new Board(); // The board's pins will not be accessible until // the board has reported that it is ready board.on("ready", () => { console.log("Ready!"); const led = new Led(13); led.blink(500); });