Get access to the new Intel® IoT Developer Kit, a complete hardware and software solution that allows developers to create exciting new solutions with the Intel® Galileo and Intel® Edison boards. Visit the Intel® Developer Zone for IoT.
Introduction
Programmable robotic kits are attainable in many forms these days whether it is from a hobby store or online site like dfrobot.com. There are many different platforms, programming languages, and tools that you can learn. Dfrobot* created a tank robot platform called Devastator that contains the Romeo* controller board. This board was modified for use with the Intel® Edison compute module to bring more capability to the kit with an increased number of I/O’s, integrated WiFi, USB Host, servo control, and increased processing power. The kit can be programmed using the Arduino* IDE and a USB connection out of the box.
This article describes another method of programming the robot using the Intel® XDK to program the robot over WiFi, Node.js*, and the MRAA library. In particular, the article will discuss about the tools used, the Romeo controller board, mapping peripheral pins, creating an Intel XDK project, and the implementation of the sensor & actuator components for the robot.
The article will conclude by tying together the concepts discussed with a prototype for the robot to operate autonomously and avoid collisions.
Getting started with the robotics platform
For an introduction and getting started with the Devastator robotics platform please see the link below:
https://software.intel.com/en-us/articles/overview-of-intel-edison-based-robotics-platform
Tools Used
Node.js*
Node.js* is a lightweight JavaScript runtime with a non-blocking I/O model that has a large ecosystem of open source libraries. You can learn more about Node.js from the link below.
https://nodejs.org/en/about/
MRAA Library
MRAA is a Linux based open source low level C peripheral library that has bindings for C++, Python, Node.js, and Java languages. This gives a developer flexibility in choosing a familiar language when developing an IoT application. The library supports a variety of x86 and ARM platforms and has a common API. Intel® Edison comes with the MRAA libraries pre-installed on its Linux image and the Intel XDK can manage updating the libraries to the latest. To learn more about MRAA, please see the links below.
https://github.com/intel-iot-devkit/mraa
http://iotdk.intel.com/docs/master/mraa/edison.html
Intel XDK
The Intel XDK is a full IDE for developing, programming, and debugging an Intel® Edison IoT application over a WiFi connection. It comes with the Node.js environment setup and is MRAA ready. The tool is free and can be downloaded from the link below.
https://software.intel.com/en-us/intel-xdk
Romeo Intel® Edison Controller Board
Reviewing the schematic helps in understanding the key components in the system and how you can best map the available peripherals to the sensors and actuators used by the robot. The robot uses a variety of peripherals to accomplish its tasks. Below is a listing of component to peripheral pin mapping for the robot. An additional mapping is made from the physical pin to the MRAA pin. This pin mapping is what we will use in the code when initializing the peripherals. It should also be noted that there are level translators on the board. These pins are also indicated in the table below to aid in correlation when viewing the schematic.
Component:
| Peripheral:
| Intel® Edison Pin:
| Translated Pin:
| MRAA Pin:
|
Passive Infrared Sensor
| Digital Input
| GPIO43
| D11
| 38
|
Tilt Servo
| PWM
| PWM1
| D5
| 14
|
Pan Servo
| PWM
| PWM0
| D3
| 20
|
Ultrasonic Sensor
| UART TX
UART RX
| GP131
GP130
| D1/TX
D0/RX
| UART0
|
Left LED
| Digital Output
| GPIO48
| D7
| 33
|
Right LED
| Digital Output
| GPIO41
| D10
| 51
|
Buzzer
| PWM
| PWM2
| D6
| 0
|
Brushed DC Motors
| I2C SCL
I2C SDA
| SDA1
SCL1
| I2C1_SDA
I2C1_SDL
| I2C0
|
The MRAA pin mapping table for Intel® Edison can be found at the link below.
http://iotdk.intel.com/docs/master/mraa/edison.html
The Romeo schematic can be downloaded from the link below
https://github.com/Arduinolibrary/DFRobot_Intel_Edison/blob/master/Romeo%20for%20Edison%20Controller%20Schematic.pdf?raw=true
Creating an Intel XDK Project
Now that we have discussed the tools used and studied the schematic, we are ready to create a new project in the Intel XDK and start developing code for the robot.
To create a new project:
Click New Project Button
Click Blank Iot Node.js Template
Give Name->Click Create
Connect to the board
Click Upgrade XDK daemon and Update libraries on board
Sensor & Actuator Components Implementation
Now that we have a new project created and have connected to the board, we are ready to write some code for interfacing to the LEDs, infrared sensor, servo, buzzer, ultrasonic sensor, and brushed dc motor components.
MRAA Initialization
Initializing the MRAA library in the project is done with the code below.
var m = require("mraa");
Indication LEDs
Two indication LEDs are mounted on the front of the robot. For example, these can be used for indication of robot direction, object detection & distance.
Initializing the LEDs is done by creating 2 MRAA GPIO objects for MRAA pins 33 and 51. The pins are both set to an OUTPUT with the dir()
function with a LOW value by default.
var leftLED = new m.Gpio(33);
var rightLED = new m.Gpio(51);
leftLED.dir(m.DIR_OUT_LOW);
rightLED.dir(m.DIR_OUT_LOW);
Typical LED functions are to turn on, off, and toggle the LEDs. This can be accomplished with the code below shown for the right LED using the GPIO write()
and read()
functions.
rightLED.write(1);
rightLED.write(0);
rightLED.write(rightLED.read()^1);
Passive Infrared Sensor (PIR)
The passive infrared sensor is mounted on the rear of the tank robot and can give indication of movement while the tank is moving backwards. The sensor gives a HIGH indication if there is motion detected or a LOW indication is there is no motion.
Initialization for this sensor is similar to the LED that it uses a GPIO object for MRAA pin 38, but the sensor is configured as an INPUT using the dir()
function.
var pirMotionSensor = new m.Gpio(38);
pirMotionSensor.dir(m.DIR_IN);
Detecting motion is also simple by polling the sensor with the function below. Polling the sensor is done using the GPIO read()
function. If motion is detected the function returns TRUE, if no motion is detected the function returns FALSE.
function isMotionDetected() {
if (pirMotionSensor.read())
return true;
else
return false;
}
Servos
The tank contains two servos. The first servo is used for panning an ultrasonic sensor and camera. Panning will allow the tank to look left and right and determine what is in its proximity. The second servo is used for tilting the camera angle up or down. Standard servo interfaces expect a pulse periodically at least every 20ms or 50Hz. The pulse width determines the servo position and is between 1ms-2ms to configure the servo position. For example a 1ms pulse width moves the servo to the 0 degree position, a 1.5ms pulse width moves the servo to the 90 degree neutral position, and a 2ms pulse moves the servo to the 180 degree position. Implementing this is easily accomplished using the pulse width modulation (PWM) peripheral.
Initializing the servos is done by creating 2 MRAA PWM objects using MRAA pin 14 for tilt and MRAA pin 20 for pan. The servos period is configured by using the PWM period_us()
function that sets up the period in microseconds. After the peripheral is initialized the servos are centered to the neutral position by calling the panCenter()
and tiltCenter()
functions.
var tiltServo = new m.Pwm(14);
var panServo = new m.Pwm(20);
tiltServo.period_us(10000);
panServo.period_us(10000);
panCenter();
tiltCenter();
Moving the pan servo is accomplished below with functions to panRight()
, panLeft()
, and panCenter()
. The functions configure the pulse width of the PWM peripheral by calling the pulsewidth_us()
function and then turning on the peripheral by calling the enable(true)
function. A short delay is called with the sleep()
function to allow the servo to move and then the peripheral is disabled by calling the enable(false)
function.
function panRight() {
panServo.enable(false);
panServo.pulsewidth_us(1000);
panServo.enable(true);
sleep(200);
panServo.enable(false);
}
function panLeft() {
panServo.enable(false);
panServo.pulsewidth_us(2000);
panServo.enable(true);
sleep(200);
panServo.enable(false);
}
function panCenter() {
panServo.enable(false);
panServo.pulsewidth_us(1500);
panServo.enable(true);
sleep(200);
panServo.enable(false);
}
Moving the tilt servo up or down is a similar task to what we did with the pan servo except that the angles used are different after experimentation to determine what is practical. The functions for tiltUp()
, tiltDown()
, and tiltCenter()
are listed below.
function tiltUp() {
tiltServo.enable(false);
tiltServo.pulsewidth_us(1250);
tiltServo.enable(true);
sleep(200);
tiltServo.enable(false);
}
function tiltDown() {
tiltServo.enable(false);
tiltServo.pulsewidth_us(1750);
tiltServo.enable(true);
sleep(200);
tiltServo.enable(false);
}
function tiltCenter() {
tiltServo.enable(false);
tiltServo.pulsewidth_us(1500);
tiltServo.enable(true);
sleep(200);
tiltServo.enable(false);
}
Buzzer
The tank contains a buzzer that can be used for audible indication of events. For example, as the tank gets close to an object it could sound different notes from the c major scale to indicate its proximity. The buzzer also uses the PWM peripheral so initialization and usage will be similar to what we did with the servos. It is a little different mindset than the servos because we are using the PWM peripheral as a simple digital to analog converter (DAC).
Initializing the buzzer is done with a MRAA PWM object on MRAA pin 0. The amplitude is configured with the write()
function for configuring the PWM duty cycle and is initialized to 0. The peripheral is disabled by calling the enable(false)
function.
var buzzer = new m.Pwm(0);
buzzer.write(0.0);
buzzer.enable(false);
Creating sounds with the buzzer is accomplished by setting the sound frequency using the period_us()
function and then enabling the sound by calling the enable(true)
function. Below is an example function for playing one octave of the C major scale that could be used as an indication tone when the robot is initialized. A simple 125ms timer loop that plays a note and increments to the next note over the octave.
var state2=0;
var handle2=setInterval(notes,125);
function notes() {
switch (state2){
case 0: buzzer.enable(false); buzzer.period_us(3831); buzzer.enable(true); state2++; break;
case 1: buzzer.enable(false); buzzer.period_us(3412); buzzer.enable(true); state2++; break;
case 2: buzzer.enable(false); buzzer.period_us(3039); buzzer.enable(true); state2++; break;
case 3: buzzer.enable(false); buzzer.period_us(2865); buzzer.enable(true); state2++; break;
case 4: buzzer.enable(false); buzzer.period_us(2551); buzzer.enable(true); state2++; break;
case 5: buzzer.enable(false); buzzer.period_us(2272); buzzer.enable(true); state2++; break;
case 6: buzzer.enable(false);buzzer.period_us(2028); buzzer.enable(true);state2++; break;
case 7: buzzer.enable(false);buzzer.period_us(1912); buzzer.enable(true);state2++; break;
default: clearInterval(handle2); state2=0; buzzer.enable(false); break;
}
}
Ultrasonic Sensor
The ultrasonic sensor is used on the robot to determine its distance to an object. It is mounted on the front of the robot and can be panned left and right to look around using the pan servo mentioned earlier. The sensor has 3 different interfaces for gathering the distance data. There is a PWM output, analog output, or UART interface. You can learn more about this sensor at the wiki below which contains useful information about the sensor, its different interfaces, and command protocol.
https://www.dfrobot.com/wiki/index.php/URM37_V4.0_Ultrasonic_Sensor_(SKU:SEN0001)#Introduction
For our Node.js program the UART interface is used with this sensor to read the distance data. The sensors TXD pin 9 is connected to Intel® Edison RX GP130 and the sensors RXD pin 8 is connected to Intel® Edison TX GP131.
Initializing the UART is done by creating a MRAA UART object. The baud rate is set to 9600 bps using the setBaudRate()
function, the data mode is setup using the setMode()
, and flow control is setup using the setFlowControl()
function. In addition, the command buffer is setup for reading the sensor data. The command buffer byte 0 contains the command code, bytes 1&2 are dummy bytes, and byte 3 is a checksum of the packet.
var u = new m.Uart(0);
u.setBaudRate(9600);
u.setMode(8,0,1);
u.setFlowcontrol(false, false);
sleep(200);
var command = new Buffer(4);
command[0] = 0x22;
command[1] = 0x00;
command[2] = 0x00;
command[3] = 0x22;
Getting the distance data from the sensor involves sending a command packet out the UART and then receiving the response packet. The function below determines if an object is close given a threshold and returns TRUE or FALSE. The command packet was setup in the initialization above and is sent out the UART by calling the write()
function. The response packet is received after a delay and then calling the read()
function. The response packet buffer byte 0 is a dummy byte, bytes 1 & 2 are the high and low bytes respectively corresponding to the distance data in cm, and byte 3 is the checksum. To determine if the data is valid the checksum is analyzed and then the data is compared to a threshold passed in.
function isObjectClose(threshold) {
var rxBuf;
var result;
u.write(command);
sleep(200);
rxBuf = u.read(4);
sleep(200);
if (rxBuf[3] == (rxBuf[0]+rxBuf[1]+rxBuf[2])) {
result = (rxBuf[1]<<8) | rxBuf[2];
if (result < threshold)
return true;
else
return false;
}
else
return true;
}
Brushed DC Motors
The tank is able to move in a forward, backward, left, or right direction. The robot contains two brushed dc motors that are mounted across from each other on the right and left side of the tank chassis. After reviewing the schematic, you will see that the Romeo board contains a full bridge motor control driver that is connected to an Atmega8* microcontroller. The interface used by Intel® Edison to communicate to the microcontroller is I2C. It is useful to review the code implemented in this microcontroller to learn about the I2C slave address that is programmed and the command interface for controlling the motors. The Intel® Edison is the I2C master and the microcontroller is the I2C slave at address 0x4.
You can find more information about the microcontroller code at the link below:
https://github.com/ouki-wang/remeo4edison/blob/master/NG/NG.ino
See the table below to determine the correlation between tank direction and motor direction
Direction:
| Left Motor
| Right Motor
|
Forward
| Counter-Clockwise
| Counter-Clockwise
|
Backward
| Clockwise
| Clockwise
|
Left
| Counter-Clockwise
| Clockwise
|
Right
| Clockwise
| Counter-Clockwise
|
Initializing the I2C peripheral is done by creating a new MRAA I2C object. The slave address is set by calling the address()
function. The command buffer is also initialized and contains a header at bytes 0 & 1. The remaining bytes in the command packet are discussed below.
var x = new m.I2c(0);
x.address(4);
var buf = new Buffer(5);
buf[0] = 0x55;
buf[1] = 0xaa;
The function below moves the tank in a forward direction given the 8-bit speed value passed to the function. The function sets up I2C commands to setup the left motor direction, right motor direction, left motor speed, and right motor speed. The remaining command bytes in the command buffer are the command code at byte 2 for doing either a motor direction command or motor speed command, command argument at byte 3 for setting the motor direction value or motor speed value, and a checksum at byte 4. The I2C command is sent by calling the write()
function.
function tankForward(speed) {
if (speed > 0xFF)
speed = 0xFF;
buf[2] = 0xB1;
buf[3] = 0x1;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xB2;
buf[3] = 0x1;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xC1;
buf[3] = speed;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xC2;
buf[3] = speed;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
}
Additional functions are listed below and follow a similar method for moving the tank backwards, left, right, and stop.
function tankBackward(speed) {
if (speed > 0xFF)
speed = 0xFF;
buf[2] = 0xB1;
buf[3] = 0x0;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xB2;
buf[3] = 0x0;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xC1;
buf[3] = speed;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xC2;
buf[3] = speed;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
sleep(2000);
}
function tankRight() {
buf[2] = 0xB1;
buf[3] = 0x0;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xB2;
buf[3] = 0x1;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xC1;
buf[3] = 0x90;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xC2;
buf[3] = 0x90;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
sleep(2000);
}
function tankLeft() {
buf[2] = 0xB1;
buf[3] = 0x1;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xB2;
buf[3] = 0x0;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xC1;
buf[3] = 0xC0;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xC2;
buf[3] = 0xC0;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
sleep(2000);
}
function tankStop() {
buf[2] = 0xC1;
buf[3] = 0x0;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
buf[2] = 0xC2;
buf[3] = 0x0;
buf[4] = (buf[0]+buf[1]+buf[2]+buf[3]) & 0xFF;
x.write(buf);
}
Autonomous Robot
Now that we have built a foundation of functions using the MRAA library for interfacing to the sensors and actuators, we can start to tie it together by creating a state machine that allows the robot to move autonomously and avoid collisions. The concept is that the tank will try and move forward until it detects that an object is close. When an object is close it will attempt to move backwards if there is not motion behind it. The tank will attempt to go left or right to avoid the object and then attempt to go forward again. Please see the flow chart diagrams below that describe movement in the forward, backward, left, and right states and the implementation that follows.
Flow Chart Diagrams
Implementation
var state=0;
var turnDirection=0;
while(1) {
switch (state) {
case 0:
panCenter();
if (isObjectClose(10)) {
state=1;
}
else {
tankForward(0x7F);
}
break;
case 1:
tankStop();
while (isMotionDetected()) {sleep(100); }
tankBackward(0x7F);
tankStop();
state=2^turnDirection;
break;
case 2:
panRight();
if (isObjectClose(10)) {
state=1;
}
else {
tankRight();
tankStop();
turnDirection ^=1;
state=0;
}
break;
case 3:
panLeft();
if (isObjectClose(10)) {
state=1;
}
else {
tankLeft();
tankStop();
turnDirection ^=1;
state=0;
}
break;
default:
tankStop();
break;
}
}
Summary
Using the Intel XDK, we showed how to program a robot wirelessly over WiFi. We discussed how to take a schematic and map out the peripherals & components for use with the MRAA library. We discussed the sensor & actuator components and how to implement the functionality using Node.js and the MRAA library. We concluded by creating an autonomous robot concept with flow charts and implementation.
About the author
Mike Rylee is a Software Engineer at Intel Corporation with a background in developing embedded systems and apps for Android*, Windows*, iOS*, and Mac*. He currently works on Internet of Things projects.
Notices:
No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by this document.
Intel disclaims all express and implied warranties, including without limitation, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement, as well as any warranty arising from course of performance, course of dealing, or usage in trade.
This document contains information on products, services and/or processes in development. All information provided here is subject to change without notice. Contact your Intel representative to obtain the latest forecast, schedule, specifications and roadmaps.
The products and services described may contain defects or errors known as errata which may cause deviations from published specifications. Current characterized errata are available on request.
Copies of documents which have an order number and are referenced in this document may be obtained by calling 1-800-548-4725 or by visiting www.intel.com/design/literature.htm.
Intel, the Intel logo, and Intel RealSense are trademarks of Intel Corporation in the U.S. and/or other countries.
*Other names and brands may be claimed as the property of others
**This sample source code is released under the Intel Sample Source Code License Agreement Like SubscribeAdd new commentFlag as spam.
© 2016 Intel Corporation.
You may know us for our processors. But we do so much more. Intel invents at the boundaries of technology to make amazing experiences possible for business and society, and for every person on Earth.
Harnessing the capability of the cloud, the ubiquity of the Internet of Things, the latest advances in memory and programmable solutions, and the promise of always-on 5G connectivity, Intel is disrupting industries and solving global challenges. Leading on policy, diversity, inclusion, education and sustainability, we create value for our stockholders, customers and society.