node-robot
A node.js framework for programming robots. The framework centers around scheduling interruptable sequences of tasks which can get interrupted by things like sensor events. There are adapters for communicating with the Lego Mindstorms EV3 bricks over Bluetooth included, however, you can use any communication adapter you'd like (eg johnny-five).
This started as a rewrite of ev3-Nodejs-bluetooth-Api.
Installation
To install, just run npm install node-robot
.
Examples
See the examples/
directory for some examples of programs
Scheduler
The library comes with a full featured task scheduler. The general pattern is as follows:
- Schedule a sequences of tasks
- Reschedule a sequences once the scheduler is empty
- Interrupt scheduled tasks when sensors are triggered
Doing this is relatively straightforward and can be done using any robot communication library you want:
var Scheduler = Scheduler; var scheduler = ; scheduler var seq = scheduler seq; mySensor;
wait()
and conditional checking
note that the wait()
function will call the condition function every 20 ms by default to see if it's true. You can change this interval like so:
//call every 5 ms instead;
Interrupting sequences
A common situation is when you get an on("change") event from a sensor or something similar, and you want to respond to it by stopping all currently running sequences. To do this, you can use the built in interrupt feature:
scheduler
Scheduler events
There are a few different events that you can use from the scheduler
scheduler scheduler
Sequence events
You can also listen for events on sequences:
var sequence = scheduler; sequence sequence sequence sequence sequence;
Note that these don't return this
since they're standard EventEmitter
events. In other words, you can't do on("event").schedule();
Starting and stopping the scheduler
You can stop and start the scheduler like so:
schedulerstart;scheduleractive == true //true if there are tasks scheduled which are currently runningschedulerstarted == true //truescheduler; //won't interrupt the currently running taskschedulerstarted == false //true
EV3 Adapter & Connecting to the EV3 Brick
The adapter is what the other APIs use to communicate with the EV3 brick. It maintains the serial port connection and does some other protocol-related tasks.
It takes a single argument: the path to the tty device on your machine.
I haven't tested this method, but on Windows you can navigate to Device Manager and open the list of Ports. There you can right-click on the device you wish to assign a port number. After right-clicking, click Properties. There should be a tab called Port Settings. In here, you should find a setting to assign a port number. You can pass that port number (eg COM1) to the adapter.
On OSX, this device should be created automatically (look in /dev/
for the relevant device, it should follow the format /dev/tty.<brick name>-SerialPort
)
On Linux, you need to pair/connect to the device using whichever bluetooth GUI/command line tool you prefer, then running the following:
sudo rfcomm bind /dev/rfcomm0 <device address>sudo chmod /dev/rfcomm0 777 #allow access to the serial port for all users, should be ok for 99% of cases #when you're done and want to remove the device, run this: sudo rfcomm release /dev/rfcomm0
The device address will look like something like 00:16:53:3F:92:CC
and you can copy and paste it from whichever tool you used to pair with the brick.
Note that after pairing & setting up the serial port, the device will appear as disconnected. The computer only connects to the brick when the serial port is opened, so this is completely normal.
Usage is straightforward, just make sure you don't do anything with it until the on("ready")
event is fired:
var robot = ;var adapter = "/dev/rfcomm0" adapter;
EV3 Sensors API
The EV3 sensors API gives you a nice API for getting sensor data and regularly reads the sensor's data for you using a read loop. The frequency that the sensor data is fetched is continuous; as soon as we get a result back, we ask for a new value from the sensor.
All sensor classes have the following API:
var robot = ;var adapter = "/dev/rfcomm0"//make a new TouchSensor on input port 1var sensor = adapter 1; sensor; //note that these values will initially be set to null before on("ready") or on("data") firesensorvalue //the last immediate value read from the sensorsensoraverageValue //a running average of the sensor value using past readings //use this to change the number of readings used to calculate averageValue (default: 10)sensoraverageValueSampleSet = 20; sensor sensor
The following sensors are available:
var sensors = ev3sensors;var touchSensor = sensorstouchSensorvalue == true //will be a boolean // RINTENSITY and AINTENSITY (Reflected Light Intensity and Ambient Light Intensity modes) also supported// those modes will return a number instead of a colorvar colorSensor = sensors//values for this sensor can be compared using the ColorSensor.colors constants, eg:colorSensorvalue == ColorSensorcolorsBLACK//Avail colors: NULL, BLACK, BLUE, GREEN, YELLOW, RED, WHITE, BROWN var infraSensor = adapter portinfraSensorvalue > 0 //will be an integer
Turning off the read loop (not recommended)
By default the sensors API will continually read the sensor reading from the EV3. If you don't want this behavior, you can turn this off by passing in true
as the fourth argument of any of the sensors:
var sensor = adapter 1 null true; //set up manual reading //manually read a sensorsensor
Note that averageValue will only be caluclated with the values the library fetches for each individual read() call, so it won't be very useful unless you're calling read() at regular intervals in your code. You can still change the number of samples used to calculate the average by setting sensor.averageValueSampleSet
.
Also note that things like sensor.value, sensor.averageValue, and the sensor.on("change") event will not be available with the read loop disabled.
EV3 Motors API
Get access to the motors API like so:
var robot = ;var adapter = "/dev/rfcomm0"var motors = adapter;
get()
Get the state of the motors like so:
motors // -> 0motors // -> [0, 100]motors // -> {A: 0, B: 100, C: 100, D: 0}
set()
Set the state of the motors like so:
motors;motors;motors;motors;