SENG 466 Software for Embedded and Mechatronics Systems Project 1...

29
SENG 466 Software for Embedded and Mechatronics Systems Project 1 Report Team Members: Jackson Hong Derek Jacoby Riley Nold

Transcript of SENG 466 Software for Embedded and Mechatronics Systems Project 1...

Page 1: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

SENG 466 Software for Embedded and Mechatronics Systems

Project 1 Report

Team Members:

Jackson Hong

Derek Jacoby

Riley Nold

Page 2: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

i

Table of Contents List of Tables and Figures ......................................................................................................................................... ii

Introduction and Problem Statement ..................................................................................................................... 1

Collaboration Tools ................................................................................................................................................. 1

AVR for Eclipse ..................................................................................................................................................... 1

RXTX and Target Management ............................................................................................................................ 2

Subversive ........................................................................................................................................................... 3

Test Scenario ........................................................................................................................................................... 4

Hardware Setup ....................................................................................................................................................... 5

The Base Station .................................................................................................................................................. 6

The Remote Station ............................................................................................................................................. 7

Project Components ................................................................................................................................................ 8

Motor Wiring and Control ................................................................................................................................... 8

Servo Motor Control .......................................................................................................................................... 10

nRF24L01+ Radio Transceiver ........................................................................................................................... 15

Joystick .............................................................................................................................................................. 17

MaxSonar-EZ1 Sonar Sensor ............................................................................................................................. 20

The Hardware Basics ..................................................................................................................................... 20

Obtaining the Sensor Data ............................................................................................................................ 20

How It Works ................................................................................................................................................. 21

The Code Structure ........................................................................................................................................ 21

Problems and Solutions ................................................................................................................................. 23

BlinkM RGB LED ................................................................................................................................................. 23

The Hardware Basics ..................................................................................................................................... 23

How It Works ................................................................................................................................................. 23

The Code Structure ........................................................................................................................................ 24

Reference .............................................................................................................................................................. 26

Page 3: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

ii

List of Tables and Figures Table 1 - Seeeduino Base Station Pin Assignment .................................................................................................. 6

Table 2 - Seeeduino Remote Station Pin Assignment ............................................................................................. 7

Table 3 - Radio Transceiver Connections .............................................................................................................. 15

Figure 1 - AVR for Eclipse......................................................................................................................................... 2

Figure 2 - Terminal Within Eclipse ........................................................................................................................... 3

Figure 3 - SVN Integration in Eclipse ....................................................................................................................... 4

Figure 4 - Overall Setup ........................................................................................................................................... 5

Figure 5 - Base Station ............................................................................................................................................. 6

Figure 6 - Remote Station ........................................................................................................................................ 7

Figure 7 - Solarbotics L298 Motor Controller .......................................................................................................... 8

Figure 8 - Neutral Pulse Width .............................................................................................................................. 13

Figure 9 - Minimum Servo Pulse Width ................................................................................................................. 14

Figure 10 - Maximum Servo Pulse Width .............................................................................................................. 14

Figure 11 - Joystick Pins ......................................................................................................................................... 19

Figure 12 - MaxSonar-EZ1 Sonar Sensor ............................................................................................................... 20

Figure 13 - BlinkM RGB LED ................................................................................................................................... 23

Page 4: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

1

Introduction and Problem Statement The goal of the first project is to demonstrate mastery over the individual component parts of the final

hovercraft solution. These parts include the following:

Seeeduino microcontrollers

Motor and servo motor outputs via the L298 motor controller

joystick and button inputs

I2C LED

Sonar input sensor for distance measurement

Radio connection for a control station and a remote station

In this first demonstration of functionality, the parts will be assembled on a non-moving platform so some of

the sensors and motor outputs will be put to somewhat different uses than will be expected in the final

hovercraft.

In addition, this first project is where we put together the support tools to allow us to develop as a team. In our

case, we have decided upon the following:

Google calendar to coordinate availability

Google code as a source code repository to allow backup and change tracking

Eclipse as a development environment

Serial.print as the primary debugging mechanism, with the output viewable in Eclipse

This report will detail the steps we have taken in each of these areas to create a test platform for the

technologies and teamwork that we will require to execute on the final project.

The component parts were each worked on individually, and then the final phase was to integrate them into a

test scenario. Subsequent sections will detail the initial implementations of each section and then the

integration work.

Collaboration Tools The final project of this course involves a rather complicated design in both hardware and software. As a result,

collaboration tools are needed to synchronize the work from everybody. In the end, we decided to use the free

SVN provided by Google Code and Eclipse to help us in the development of the project.

Because we decided to use Eclipse as our development platform and Google SVN to manage our source code, it

is ideal to have Eclipse coordinate everything. Also, the serial terminal window is very useful for debugging

purposes; however, having to rely on Tetra Term is very inconvenient and a much better solution would be to

have Eclipse connect to the serial port so everything can be done within Eclipse without the need of outside

tools.

AVR for Eclipse Eclipse is a very modern IDE with many features. With this AVR for Eclipse plug-in, the user is set free of the

hassle with makefiles. And with a simple click, it will download the HEX dump to the microcontroller.

Page 5: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

2

Figure 1 - AVR for Eclipse

Eclipse Update Site: http://avr-eclipse.sourceforge.net/updatesite/.

RXTX and Target Management RXTX is a Java library, using a native implementation (via JNI), providing serial and parallel communication for

the Java Development Toolkit (JDK). Along with Target Management, which creates data models and

frameworks to configure and manage remote (mainframe down to embedded) systems, their connections, and

their services, allows the user to have a serial console terminal directly from within Eclipse.

Page 6: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

3

Figure 2 - Terminal Within Eclipse

The advantage of having the ability to see serial port communication from within Eclipse is that it helps with

the development flow as well as debugging the software. After all, using the Serial.println() function from the

Arduino library remains the primary debugging method we employ in this project.

Eclipse Update Site: http://rxtx.qbang.org/eclipse/

Eclipse Update Site: http://download.eclipse.org/dsdp/tm/updates/3.0

Subversive The Subversive provides Subversion (SVN) integration for Eclipse. The Subversive plug-in gives the user the

ability to work with the SVN version control system from the Eclipse workbench. This is a must have for this

project because each member is responsible for different sections of the code so a source control system is

crucial.

Page 7: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

4

Figure 3 - SVN Integration in Eclipse

Eclipse Update Site: http://community.polarion.com/projects/subversive/download/eclipse/2.0/update-site/

Test Scenario In order to show that we can use each component in an integrated scenario, we need to define some actions.

First, the components will be broken into a base unit and a remote unit. The sonar will be placed with the

remote unit for greatest realism with respect to our final project hovercraft scenario.

The base unit consists of a Seeeduino microcontroller that accepts the joystick input, including button presses.

On the output side, the I2C LED will be placed on the base unit. The remote unit consists of the second

Seeeduino, a motor output via the L298, a servo motor output through the L298, and the sonar input.

When the joystick (actually a flight controller) is turned to the right or left, the base unit Seeeduino will relay

those commands to the remote unit Seeeduino and the servo motor will move to the right or left. When the

lever on the flight controller is moved up the motor attached to the remote Seeeduino will move from stopped

up through full speed. The motor will have a fan blade attached so that this motion is apparent. When a button

on the flight controller is pressed, the LED onboard the remote Seeeduino will be lit and it will go out when the

button is released.

Normally, the I2C LED on the control unit is green. When an object (a hand, for instance) comes close to the

sonar sensor the LED will reduce the intensity of green.

Page 8: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

5

Hardware Setup

Figure 4 - Overall Setup

On the left hand side is the base unit; on the right is the remote unit. The yellow battery on top of the flight

controller is driving the motors. The seeduinos at this point are driven from the USB port of the attached

laptops.

Page 9: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

6

The Base Station

Figure 5 - Base Station

The base station setup is fairly simple. The red board is the Seeeduino. On the white breadboard is an I2C LED

and a radio which are wired to the Seeeduino. Also wired through the breadboard is the flight controller (the

black connector at the bottom of the image is the cable from the flight controller.)

Table 1 - Seeeduino Base Station Pin Assignment

Seedunio Pin Destination

Analog 5 Y-Axis

Analog 6 X-Axis

Analog 7 Button

19 Radio IRQ

42 Radio CSN

43 Radio CE

48 Radio Vcc

50 Radio MISO

51 Radio MOSI

52 Radio SCK

Gnd Radio Gnd

I2C Vcc LED Vcc

I2C SCL (21) C on the LED

20 D on the LED

Page 10: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

7

The Remote Station

Figure 6 - Remote Station

The remote station setup is slightly more complicated. The yellow battery on top of the flight controller powers

the L298 motor controller and the servo motor. The aluminum heat sink next to the yellow battery is attached

to the L298 to prevent overheating. On the corner of the desk on the other side of the battery you can see the

fan attached to our motor. The white breadboard contains the sonar and the radio. Attached to the bottom

right of the breadboard are the connections between the Seeeduino, the power supply, and the servo motor.

Table 2 - Seeeduino Remote Station Pin Assignment

Seeeduino Pin Destination

7 Servo Data Line

11 Motor Forward

12 Motor Reverse

PE7 Sonar Receive

PE2 Sonar Transmit

19 Radio IRQ

42 Radio CSN

43 Radio CE

48 Radio Vcc

50 Radio MISO

51 Radio MOSI

52 Radio SCK

Gnd Radio Gnd

Page 11: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

8

Project Components

Motor Wiring and Control

This section is concerned only with the standard DC motor that is driving the fan in this project; the servo

motor will be handled in a later section.

The Arduino libraries make simple motor control very easy. They hide all of the details of pulse width

modulation and allow one to simply do an analog write to the pin that is controlling the motor. The only thing

to make sure of, really, is that the pins you want to use the base library functions with are not attached to a

timer that you are using for something else. We had to move our motor output pins a couple of times as we

added the sonar and servo motor to the system so that we could keep our control systems appropriately

separated.

Here is a picture of the Solarbotics L298 motor controller (without the heat sink shown in the first picture in

this report):

Figure 7 - Solarbotics L298 Motor Controller

Since we were using only a single motor for this initial project, we used only one side of the L298. When we

add a second motor in the next phase of our hovercraft project we will use a similar control strategy, but will

wire it to the other side of the L298.

The three large screw connectors on the front of the board are for attaching power and ground, the centre of

the 3 connectors is a +5V regulated output for wiring to the control circuit for each side.

The three small header connectors on the front of the board are for wiring power (from the +5 volt regulated

supply) and two motor input connections from the Seeeduino (one for forward, one for reverse.)

The two screw connectors on the left side of the L298 are wired to the motor itself.

In our bouncing around trying to find a set of PWM pins whose timer we didn't want to use for other purposes,

we eventually ended up driving the motor from PWM pins 11 and 12 which are on timer 1. As we add more

devices in the next phase of our project we may end up moving these again since there is really no reason to be

tying up a 16-bit timer for motor PWM and we could as easily use the 8-bit timer.

Page 12: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

9

Setup of the motor pins is very simple, assign a variable to hold the pin numbers (11 and 12) and then set the

pinMode to OUTPUT. Even this step is not strictly necessary since the PWM pins are set to output by default in

the Arduino init function, but since we were having problems with that function (to be described in the next

section) we've decided to explicitly set the pinMode for all the pins we use.

After setup, the values for the motor direction and speed are coming from the base station to the remote

station over the radio. Both the position of the flight controller axis controlling the motor and the axis

controlling the servo are sent as a string to the radio receive handler on the remote station, there they are

processed. The motor control code looks like this:

//set fan position

if (fanPos < 660) {

fanVal = map(fanPos, 120, 660, 0, 255);

d = 1;

fanVal = 255 - fanVal;

}

if (fanPos > 720) {

fanVal = map(fanPos, 720, 1023, 0, 255);

d = 2;

}

switch (d) {

case 1:

analogWrite(motor1Pin1, fanVal);

analogWrite(motor1Pin2, 0);

break;

case 2:

analogWrite(motor1Pin1, 0);

analogWrite(motor1Pin2, fanVal);

break;

default:

analogWrite(motor1Pin1, 0);

analogWrite(motor1Pin2, 0);

servoVal = 3000;

}

The mid-position of the forward/backward axis of the flight controller is centered on 690. Since these changes

slightly each time the controller is used, we elected to leave a space of 30 on either side of the center position

before beginning to operate the motor. The default value in the switch statement sets both channels to zero,

stopping the motor.

If the flight controller is pushed forward beyond this zone, values greater than 720, then map the values in that

range to a value between 0 and 255 and set the switch variable, d, to 2. If it's pulled back lower than 660 then

map that to a value between 0 and 255 and set the switch variable to 1. Note that pulled all the way back

should be the fastest reverse speed, so the fanVal is inverted (255-fanVal) to set this value properly. The switch

statement merely writes the appropriate speed value to the proper output pin to drive the motor.

Under the covers, what is going on here is that the Arduino libraries take care of the proper pulse width

modulation to proportionally drive the motor at a duty cycle related to the value of the output pin. All of this is

Page 13: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

10

hidden from the end user of the Arduino libraries, but the mechanism will be discussed in the next section on

the servo motors.

Finally, the L298 is in the middle of the wiring from the Seeeduino to the motor so that sufficient power can be

delivered to the motor to drive it – the output pins do not have the current capacity to drive the motor

directly.

Servo Motor Control Arduino libraries exist to drive the servo motor, but we were not able to get them to function properly. In

addition, other groups had problems with the built in libraries being disrupted by use of the motor and sonar.

So we elected to abandon the built-in Arduino libraries and write the control code directly using timer 4.

First, since we've just discussed the related code in the rxhandler for motors, let's look at the rxhandler for the

servo:

if (servoPos < 300) {

servoVal = map(servoPos, 0, 300, 1800, 3000);

}

if (servoPos > 350) {

servoVal = map(servoPos, 350, 920, 3000, 4200);

}

if ((servoPos > 300) & (servoPos < 350)) {

servoVal = 3000;

}

servoSet(servoVal);

The flight controller we are using has a rather compressed and un-centered range for this axis. At rest it reads a

value of approximately 323. Similar to the rationale for the motor, we've left a bit of room on either side

before we begin driving the servo. The highest value of the flight controller is only 920, so that's what we've

used to map the position, in this case, to a value between 1800 and 4200. As we'll see in a few moments, this

corresponds to a range of .9 ms to 2.1 ms which the servo datasheet lists as the rated range of our servo.

ServoSet is a function in servo.cpp that sets the register value for the timer.

Let's look at the code for setting up and controlling the servo motor:

static unsigned int servoValue = 2397;

static unsigned char sreg;

void servoInit() {

/*

* set non-inverted PCM mode (COM4B1 set, COM4B0 unset),

* mode 14 (fast PWM, TOP in ICR4)

* set prescale to 64 - CS 40, 41 set, 42 unset

*/

TCCR4A = 0b100010; // COM4B1, COM4B0, COM4C1, COM4C0, WGM41, and WGM40

TCCR4B = 0b11010; // WGM43, WGM42, CS42, CS41, and CS40

/*

* initialize the servo pin as an output:

Page 14: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

11

* pinMode(ServoPin, OUTPUT);

*/

DDRH |= _BV(DDH4);

/*

* Set top to 40000 (which will be 20ms with the prescaler)

*/

sreg = SREG;

cli();

ICR4 = 40000;

SREG = sreg;

/*

* the time it should be up for no movement is 3054, this

* is just an initial value. it gets set to servoValue the

* first time the interrupt is hit.

*/

sreg = SREG;

cli();

OCR4B = (unsigned int) 1500;

SREG = sreg;

return;

}

void servoSet(unsigned int value) {

servoValue = value;

sreg = SREG;

cli();

OCR4B = servoValue;

SREG = sreg;

Serial.print(servoValue);

Serial.println();

return;

}

Let's look at the setup of the timer. Chapter 17 of the datasheet is the bible for understanding what is going on

here. First, let's look at our waveform generation bits, WGM40, WGM41, WGM42, WGM43. The table to refer

to is on page 148 of the datasheet. We want “fast-PWM” mode. This means that we want a pulse width

modulation mode, and we don't care too much about the shape of the waveform. The other PWM modes try

to control the phase and frequency of the PWM pulses in ways that we don't really need. In addition, since we

want to control the overall clock frequency we want to set our own TOP value. This can be done either by

setting the OCR4A register or the ICR4 register. Since we may want to control more than two servos at some

point in the future, I've chosen to store this TOP value in ICR4. This means that we want to set mode 14, or

WGM41, WGM42, and WGM43 set, and WGM40 unset.

Next, let's take care of the directionality of the pulse. We want to be normally low, with the pin set high for the

first short part of the cycle. This is non-inverted mode, so we set COM4B1 to allow the pulse to be output on

the pin, and unset COM4B0 so that we do not invert the signal. (See page 160 of the datasheet.)

Next, let's set the prescale value. If we operate unscaled our clock frequency is 16MHz, but since we want a

frequency of 50Hz on our output pin (equivalent to a 20ms pulse frequency as specified by our servo

Page 15: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

12

datasheet), we could not store a large enough value in a 16-bit register to accomplish that at the full clock

speed. The smallest prescale value we can use to accomplish this is 8, so we set bit CS41 and unset CS40 and

CS42. This comes from the table on page 162 of the ATMEL1280 datasheet.

After setting the control register values, set the data direction register to output. This can be done either

through the Arduino libraries by setting the pinMode to OUTPUT, or directly on the DDRH register by setting

the DDH4 bit using the command DDRH |= _BV(DDH4). (Just to decipher some of the pin naming – we are

driving our servo motor from PWM pin 7, which corresponds to pin PH4 on the Seeeduino schematic. This

happens to be on port H, so we are setting the DDH4 bit of the port H data direction register.)

It's come time to start populating our data registers. It's worth noting that the ordering of these operations is

critical. The Arduino libraries, in the init() function, set the timer4 control registers to 8-bit fast PWM mode.

This masks out the high byte of the data registers. There is a section of the datasheet that goes into great detail

on how to set the contents of a 16-bit register using the 8-bit data bus, and I initially thought that we were

falling afoul of this somehow. In actuality the AVR compiler takes care of the complexities of setting the

register, and the high byte was simply being masked by the Arduino setup of the register. In summary, do all of

your controls register setup BEFORE you attempt to write to any of your data registers. Saving the status

register and calling cli(); and then restoring the status register after populating the data register is a bit of

paranoia left over from the investigation of this ordering problem. But it's probably good practice to do in any

case. If an interrupt comes in the middle of a set of the 16 bit register (since it takes two operations on the 8-

bit data bus) then it's theoretically possible that the register will get set to an improper value. The cli(); call

suspends interrupt processing until we are done setting the register.

The servo datasheet specifies an interval of 20msecs between pulses, and the width of the pulse determines

the position of the servo motor. The width is to be .9 ms to 2.1 ms with the servo position centered at 1.5 ms.

An interval of 20 ms corresponds to a frequency of 50Hz. From the Atmel 1280 datasheet, we find the

following formula for determining the frequency of a motor control signal:

𝐹𝑟𝑒𝑞𝑢𝑒𝑛𝑐𝑦𝑃𝑊𝑀 =𝑓𝐶𝑙𝑜𝑐𝑘 𝐼/𝑂

𝑁 × (1 + 𝑇𝑂𝑃)

The clock I/O is 16 MHz, N is the pre-scaler value (8 in our case), and TOP is the value of the TOP register (ICR4

in our case). Rearranging this we find that a TOP value of 40000 will give us a PWM frequency of 50Hz, so we

set our ICR4 register to 40000.

Next, we want to set an output compare register to a value that will leave the servo in the neutral position.

This has been previously specified as 1.5 ms. Since we can see from the previous calculation that one counter

increment corresponds to 2usecs, this means that we want to set out output compare to 3000 to maintain a

neutral position at the centre of travel. This value is stored in OCR4B.

So at this point, we have a pulse with a width of 1.5msecs being generated every 20msecs. This appears as

follows on the logic analyzer:

Page 16: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

13

Figure 8 - Neutral Pulse Width

Going back to our mapping functions, we can see the minimum and maximum positions, at .9msec and 2.1

msec respectively, should be found at each extreme of the flight controller travel. This can be verified on the

logic analyzer as shown in the following images:

Page 17: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

14

Figure 9 - Minimum Servo Pulse Width

Figure 10 - Maximum Servo Pulse Width

Before using the logic analyzer to confirm pulse width values, considerable difficulties were caused by

improperly setting the output compare register. If the servo motor is driven with a pulse width outside of it's

Page 18: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

15

rated range, the small plastic gears can easily strip. On the smaller HS55 servo motors, the electronics can also

become overdriven and be damaged. In fact, some careful examination of 3 failed HS55 servo motors showed

that all three had electrical issues and two of the three had stripped gears. On the larger HS81, the electronics

seem more prone to survive, but the gears still strip very easily. New gear sets are available for the HS81 from

Robotshop.ca for $5.45 per set under part number RB-Tig-01. (Please note that not all of the damaged servos I

examined were damaged by me, although I do take full responsibility for one of the HS55 servos and one of the

HS81 servos. Three new gear sets for the HS81 are on the way...)

Use of the logic analyzer to examine pulse widths is consequently highly encouraged before connecting your

servos. Setting it up is fairly simple – load the control program from the website listed on the front of the

analyzer, plug it into a USB port, connect the grey wire to a ground pin on the Arduino, the black wire to the

servo output pin, and start sampling. Taking 1 million samples at a 1 MHz rate seemed to work well in our case.

One final issue that is worth mentioning is the timing code here it is possible to achieve timing to within 2

microseconds. The servo motor actually uses larger time slices than that, it appears, and a variation of 1-2

microseconds in timing of a pulse is able to push the servo over an internal boundary which causes jitter. This 1

microsecond difference is visible in the logic analyzer if you zoom in. Initially, the center value was set as 3054

counter ticks, a leftover value from the old servo which was not centered quite right. At this value the new

servo had quite a pronounced jitter as the logic analyzer showed variation from 1524 microseconds to 1525

and rarely 1526 microseconds. This 1524 to 1525 microsecond boundary was apparently the edge of a more

pronounced movement within the servo motor and when the center value was reset to 3000 counter ticks the

center-position-jitter went away entirely.

nRF24L01+ Radio Transceiver There are three aspects to the nRF24L01 radio that deserve discussion. The first, and simplest, is the wiring of

the radio. Although it is the simplest part of getting the radio working, there are still several problems that can

ensue. Second, we'll briefly discuss the radio driver that Neil, the TA, has written. There are a couple of things

in the driver code that need changing if you change the pins that the radio sits on. Finally, the radio client will

be discussed.

The radio is wired to the Seeeduino SPI pins (50-52), these are hardwired to the Arduino's SPI module and

should generally not be changed. The other pins are configurable in order to avoid conflicts with other devices,

though. The following table shows the connections as wired in our implementation:

Table 3 - Radio Transceiver Connections

Seeeduino Pins Radio Module

19 Radio IRQ

42 Radio CSN

43 Radio CE

48 Radio Vcc

50 Radio MISO

51 Radio MOSI

52 Radio SCK

Gnd Radio Gnd

The CSN pin was originally wired to pin 53 in the reference implementation on the AT90, but Neil moved it to

pin 7 in his driver. We've chosen to move it to pin 42, mostly for ease of wiring, but also to avoid conflicting

Page 19: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

16

with the servo code previously described. Similarly the CE connection has been moved to pin 43 so as not to

conflict with the PWM outputs.

An interesting problem occurred with the CE pin on the base station radio. We were getting thirty or forty

packets through the radio and then it would inexplicably die. The number of packets was variable, but the

problem always occurred within a minute or two of starting the base station. We finally determined that the CE

pin was not well wired and the intermittent connection was causing the radio to go into a state which was only

recoverable by resetting it.

Another pin that caused wiring difficulties was the IRQ. The main problem here is the renaming of pins that

takes place between the Arduino documentation and the Atmel1280 datasheet. The Arduino naming really

makes very little sense, but on some earlier version of the board the interrupts were probably named

sequentially. The first two interrupts (0 and 1) are pretty easy to find on pins 2 and 3, but the remainders are

renumbered as described on the attach interrupt page of the Arduino documentation: numbers 2 (pin 21), 3

(pin 20), 4 (pin 19), and 5 (pin 18).

We've chosen to put the radio on interrupt 4, pin 19. The Atmel 1280 documentation and seeeduino schematic

all refer to int4 as being on pin PE4, or PWM2, but Arduino has renumbered this to be int0. If you use the

Arduino library attach interrupt function to attach the interrupt you must use their numbering scheme! If the

interrupt is not wired properly then it will hang during the radio init code on the attach interrupt call. This

wiring took some work to figure out initially.

Finally, the Vcc connection for the radio has been placed onto digital pin 48. This allows the radio to be reset

through software at the start of setup. This is to enable the radio to be reset at the same time as the software

is reset in case the radio has found itself in an unstable state and stopped working. (Note the highest current

draw for the radio is listed as 12.3 mA which is far less than the 40mA maximum current per pin allowable in

the Atmel1280 datasheet, although the working load lists 20mA as the recommended maximum.)

To make these changes, there are a couple of places in radio.cpp in the radio driver code that must be

changed. First, the pins except for the interrupt are defined at the top of radio.cpp:

#define CE_PIN 43

#define CSN_PIN 42

#define VCC_PIN 48

To change the IRQ, you must go change the hard-coded value in Radio_Init:

// Enable radio interrupt. This interrupt is triggered when data are

received and when a transmission completes.

/*DDRE &= ~_BV(PORTE7);

EICRB |= _BV(ISC71);

EICRB &= ~_BV(ISC70);

EIMSK |= _BV(INT7);*/

attachInterrupt(4, int0handler, LOW);

Notice Neil's use of Arduino library calls instead of the more cryptic direct register sets that are there for the

AT90.

Page 20: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

17

Finally, in the transmit code that we started from there was a line in the IRQhandler at the bottom that must

be uncommented:

if (status & _BV(RX_DR)) {

pipe_number = (status & 0xE) >> 1;

radio_rxhandler(pipe_number);

}

The radio_rxhandler line was commented out and must be called in order to receive any packets.

If you feel compulsive you can change the name of the IRQ0handler as well to reflect the new IRQ you put the

radio on, but we didn't bother. Also, if there are many radios operating nearby changing the channel the radio

is operating on could help alleviate conflicts, but we didn't see a need to do this yet.

Finally we'll talk about the client code. This is all in radioclient.cpp, in either the base station project or the

remote project. Aside from the message receive handler, the only difference between the two is which is

identified as the rx_addr and which the tx_addr. These should be complements of each other, of course, so

that the radios talk to each other and not some other station. Although the use of Neil's base station during

testing was quite useful.

uint8_t rx_addr[RADIO_ADDRESS_LENGTH] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x88 };

uint8_t tx_addr[RADIO_ADDRESS_LENGTH] = { 0xDE, 0xAD, 0xBE, 0xEF, 0x77 };

As long as the radios are set up to use the same channel, power, and transmit rate, the only code that will

require much attention is the receive message handler. The transmit code is used from Neil's sample virtually

unchanged, and we keep the same packet definition so that all of our messages are passed as strings of up to

20 bytes. One additional thing that we have done is to set messages originating from the base station as

message type 41, and ones from the remote station as message type 42. This allows us to avoid doing any

processing of the automatic reply messages.

On the remote side, the following code identifies a message as coming from the base station and breaks it's

string into two int variables containing the state of the two flight controller axes:

Radio_Receive(&packet);

if (packet.type == MESSAGE && packet.payload.message.messageid == 41) {

sscanf((char*) &packet.payload.message.messagecontent, "%d/%d",

&servoPos, &fanPos);

}

The explicit cast to a pointer to a char helps the system interpret the uint8_t data type of the packet data, and

the two numbers are placed into the appropriate variables. From this point on the code in the rxhandler has

already been discussed in the sections for the motor and servo.

It is worth noting that the Radio_Receive call MUST be made, even if you do not intend to do anything with the

data. If the packet is not picked up then after a few received packets (usually 3) the radio will be unable to

receive anything else until its buffer is cleared by picking up the data already received.

Joystick Our joystick is a Virtual Pilot made by CH Products. This joystick differs from most others in that it has 3 axes

instead of 2. The black cylinder sliding in and out constitutes one axis, the “steering wheel” rotating clockwise

Page 21: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

18

and counter-clockwise is the second axis, while the black throttle is the third. These axes correspond to pins 6,

3, and 11 respectively as seen in Illustration 8. We chose to use the red button on the right, using pin 2, to turn

on the LED on the Seeeduino board when held down. The x-axis (pin 3) is used to control the servo motor

position, while the y-axis (pin 6) is used to control the fan speed and direction.

The joystick pins are connected to the analog inputs of the Seeeduino board, which send the board between 0

and 5 volts depending on the positioning of the axes. These values can then be accessed by calling the

analogRead function. The board will then convert the analog voltage signal to a digital signal by mapping the

voltage to a number between 0 and 1023. However, due to a problem with our joystick our x-axis only registers

values up to 920. We found that while the buttons could be used as a digital input, it was easier to use the pins

as analog inputs. This way whenever a button is pressed down, the analogRead function will return 0, and

when the button is not pressed down the value could have a wide range from 10-700. This avoids the bouncing

problem because low voltage drops will not be read in as a low instead of as a high after the button has been

released.

There were a couple of minor problems concerning the setup of the joystick. First of all, the connector had

most of its wires missing when we got it, so we had to solder the connectors on. The next challenge was

determining which axes belonged to which pins. This was determined by using the Serial interface and printing

out the values we received from the pins when we moved the axes on the joystick. We then had to calibrate

the joystick to find the center of the axes. The values of the axes at their default positions were determined,

and then the minimum and maximum values of the axes were determined.

However, we had a setback when we started getting erratic results from the joystick. This was observed from

the fact that the motors were stuttering when they would normally not have been. After verifying all of our

hardware connections, we determined that the problem was inside of the joystick itself. Therefore, we opened

it up and found that there were faulty connections inside of the joystick. We repaired the fault and everything

went back to working order.

Page 22: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

19

Figure 11 - Joystick Pins

Page 23: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

20

MaxSonar-EZ1 Sonar Sensor

Figure 12 - MaxSonar-EZ1 Sonar Sensor

The Hardware Basics

Because the primary purpose of this project is to get familiar with all the hardware components, a sonar sensor

is a must have. The MaxSonar-EZ1 offers very short to long-range detection and ranging, in an incredibly small

package, with ultra low power consumption. The MaxSonar-EZ1 detects objects from 0-inches, even objects

pressing against the front sensor face, to 254-inches (6.45 meters), and provides sonar range information from

6-inches to 254 inches, with 1-inch resolution. Objects between 0-inches and 6-inches range as 6-inches.

Obtaining the Sensor Data

This sonar sensor offers several ways to return the range value, such as Pulse Width train, analog output or

plain ASCII text. In the first project, pulse width is chosen as the method of getting the sensor data because it

offers very high reliability whereas text parsing is not as easy and A/D conversion is not as accurate.

In order to measure the pulse width, a hardware timer is needed. The microprocessor on the Seeeduino board

comes with several hardware timers of varying size, either 8-bit or 16-bit. For this project, hardware timer 3

was chosen because Timer 1 is used by the Pulse Width Modulation library functions from Arduino and Timer 2

is only 8-bit which is not large enough to hold the range value without overflow. Several macros are made to

make setting up the Timer a little easier; they are shown here:

/*

* Macros to change Timer 3 settings when used for

* input capture and measure the pulse width of

* the PW pin from the sonar

*/

#define SET_RISING_EDGE() (TCCR3B |= _BV(ICES3))

#define SET_FALLING_EDGE() (TCCR3B &= ~(_BV(ICES3)))

#define IS_RISING_EDGE() (TCCR3B & _BV(ICES3))

#define IS_FALLING_EDGE() ~(TCCR3B & _BV(ICES3))

#define SET_IC_ENABLE() (TIMSK3 |= _BV(ICIE3))

#define SET_IC_DISABLE() (TIMSK3 &= ~_BV(ICIE3))

#define CLEAR_IC_FLAG() (TIFR3 |= _BV(ICF3))

Page 24: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

21

How It Works

When the RX pin on the sonar sensor is pulled HIGH for 20 us, a sound wave pulse is fired by the sonar and the

PW pin will go high and then it will go low once the echo is received, which gives an accurate representation of

the distance that is proportional to the width of the pulse on the PW pin with 147 us for every one inch of

distance.

The key in this implementation is to measure both the rising edge and the falling edge of the pulse on the PW

pin accurately. This is achieved by using the Input Capture Unit that comes with the hardware Timer 3 on the

microprocessor.

The Input Capture Unit is capable of monitoring specific input pins for either a rising edge or a falling edge and

it will copy the value of Timer 3 into its own register once the appropriate edge is detected.

As a result, the ISR in the code must be able to monitor the rising edge first, and change the register settings to

look for the falling edge once the rising edge on PW pin is detected.

The Code Structure

The sonar handling code is divided into 2 main functions and an ISR. The sonarInit() function initializes the

sonar such as setting the pins to either INPUT or OUTPUT and wait for 250 ms for the sonar to power up and do

the initial calibration. After that is all done, drive the RX pin low so the sonar does not continuously sending out

sound wave pulses.

static int SonarRX = 4; // Sonar RX pin is connected to pin 4, Pin4 is PE2

static int SonarPW = 9; // Sonar PW pin is connected to pin 9, ICP3 is on PE7

void sonarInit() {

// initialize the pins as inputs:

pinMode(SonarRX, OUTPUT);

pinMode(SonarPW, INPUT);

/*

* Assuming sonarInit() gets called immediately after

* power up, then a period of 250 ms must be passed

* before the RX pin is ready to receive command.

*/

delay(250);

//Disable sonar echo firing when it is first initialized

digitalWrite(SonarRX, LOW);

return;

}

The sonarEcho() function is very simple because it is only responsible of setting up the Input Capture Unit to

look for a rising edge and then driving the RX pin HIGH.

/**

* Set Input Capture to look for a rising edge, clear

* the interrupt flag and then enable Input Capture.

* After that, set RX to HIGH to enable the sonar.

*/

void sonarEcho() {

SET_RISING_EDGE();

Page 25: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

22

CLEAR_IC_FLAG();

SET_IC_ENABLE();

//Enable Sonar

digitalWrite(SonarRX, HIGH);

return;

}

Note that the RX pin only needs to stay high for 20 us for the sonar to fire a sound wave pulse but here the RX

pin set to HIGH and never brought back down to LOW. This is because the ISR is actually responsible of setting

the RX to LOW.

ISR(TIMER3_CAPT_vect)

{

// Disable global interrupt

Disable_Interrupt();

/*

* Once the rising edge of PW is detected, it means

* RX has been staying HIGH long enough. Set it to

* LOW now to disable sonar.

*/

digitalWrite(SonarRX, LOW);

/*

* Reset Timer 3 when the rising edge of PW is

* detected, then change the Input Capture configuration

* to detect the falling edge and clear the interrupt flag.

*/

if (IS_RISING_EDGE()) {

TCNT3 = 0;

SET_FALLING_EDGE();

CLEAR_IC_FLAG();

} else {

/*

* Store the ICR3 value and disable Input Capture

* so it does not interfere with other components.

*/

timerTickCount = ICR3;

SET_RISING_EDGE();

CLEAR_IC_FLAG();

SET_IC_DISABLE();

}

// Enable global interrupt

Enable_Interrupt();

}

Inside this Interrupt Service Routine, the first thing that happens is that interrupts are disabled globally. That is

because the timing measurements must be very accurate so during this period of time, nothing else should be

able to halt the execution of the code. Because the Input Capture Unit is originally set to monitor a rising edge,

it is required to clear the Timer 3 and set the Input Capture Unit to look for a falling edge the next time around.

It is also important to clear the Input Capture flag too. When this ISR gets triggered again, by the falling edge,

the value stored in the ICR3 register is the value of the hardware Timer 3 when the falling edge is detected. It is

also important to note that the global interrupt must be re-enabled before leaving this ISR.

Page 26: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

23

Problems and Solutions

The sonar sensor is easy to use. There is only one problem that caused some confusion when calibrating and

testing the sonar sensor. The sensor is capable of measuring a maximum distance of 256 inches and the

hardware Timer 3 is a 16-bit timer with different clock frequency prescalers such as 8 and 64. Initially, the

prescaler is set at 8 because the microprocessor has a 16MHz crystal. With an 8 prescaler, each timer tick

represents a nice and easy 0.5 us. However, this is not enough because each inch of distance is represented by

a 147 us pulse and in order to measure 256 inches, the timer must be able to handle a pulse of 37632 us, times

that by 2 gives 75264, a number that is just slightly larger than the maximum value that can be represented by

a 16-bit counter, which is 65536.

In order to accommodate this, the clock frequency prescaler of the hardware Timer 3 is changed to 64. This

means that each timer tick is now 4 us. Given that one inch is 147 us, it results in a 147 / 4 = 36.75 us of pulse

width for each tick.

BlinkM RGB LED

Figure 13 - BlinkM RGB LED

The Hardware Basics

BlinkM is a networkable and programmable full-color RGB LED. It is designed to allow the easy addition of

dynamic indicators, displays, and lighting to existing or new projects. BlinkM uses a high quality, high power

RGB LED and a small AVR microcontroller to allow a user to digitally control an RGB LED over a simple I2C

interface.

How It Works

The BlinkM RGB LED is much easier to use than the sonar because it comes with an AVR microcontroller and a

ready to use library of functions. In a sense, Arduino library really makes embedded programming on AVR

microcontrollers a much easier thing to do, but also not good for students to really learn the protocols since

the library functions mask the implementation details.

The BlinkM RGB LED uses an I2C interface to allow the user to control it. It is capable of accepting RGB color

range from 0 to 255 as well as HSB settings from 0 to 255. Since all the library functions are all readily available,

it is only necessary to create the appropriate functions to set the address of the BlinkM LED over the I2C BUS as

well as functions to change its color and fade speed. The fade speed controls how fast the LED changes its

current color to the new color once it receives the command.

Page 27: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

24

The Code Structure /*

* First transmit to the broadcast address, then

* set the device with the given address.

*/

void LEDSetAddress(uint8_t address) {

Wire.beginTransmission(0x00);

Wire.send('A');

Wire.send(address);

Wire.send(0xD0);

Wire.send(0x0D);

Wire.send(address);

Wire.endTransmission();

delay(50);

return;

}

Before setting up an address for the device, the message needs to be broadcasted to the default address of 0,

which is an address that every device on the I2C BUS listens. An ASCII ‘A’ indicates that this is an address

assignment command followed by the actual address the device is assigned to. The 0xD0 and 0x0D arguments

are used as a check against inadvertent address changing. They are only used when first assigning the device an

address on the I2C BUS.

/*

* Set the RGB color of the LED at the given address. It also sets

* the color transition type and whether or not to hold the color

* since if that bit is not set, the LED will reset to default after

* a short period of time.

*/

void LEDSetColor(uint8_t address, uint8_t red, uint8_t green, uint8_t blue,

bool fade, bool holdColor) {

Wire.beginTransmission(address);

if (fade) {

Wire.send('c');

} else {

Wire.send('n');

}

Wire.send(red);

Wire.send(green);

Wire.send(blue);

if (holdColor) {

Wire.send('o');

}

Wire.endTransmission();

delay(50);

return;

}

This LEDSetColor() function takes parameters such as holdColor and fade on top of the address and RGB values. The fade boolean value indicates whether to change to the new color immediately upon receiving the command, or fade to the new color according to the interally set fade speed. Because the BlinkM LED is run by an AVR microcontroller, it will reset to its default behavior after running for an extended period of time. The Wire.send('o') command is used to stop the interal script so the color would stay the same indefinitely. Lastly, a function is created to set the fade speed of the BlinkM LED.

Page 28: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

25

/*

* Set the speed of fading between colors.

* Higher numbers mean fading faster.

* 255 = instant fading

*/

void LEDSetFadeSpeed(uint8_t address, uint8_t fadeSpeed) {

Wire.beginTransmission(address);

Wire.send('f');

Wire.send(fadeSpeed);

Wire.endTransmission();

}

There many other commands available but for the purpose of this project, these three functions are enough.

Page 29: SENG 466 Software for Embedded and Mechatronics Systems Project 1 Reportmcheng/466/spring.2013... · 2014-02-12 · This report will detail the steps we have taken in each of these

26

Reference "ATMega 1280 Datasheet." Atmel. 01 Feburary 2010

<http://www.atmel.com/dyn/resources/prod_documents/doc2549.pdf>.

"BlinkM Datasheet." ThingM. 01 Feburary 2010

<http://thingm.com/fileadmin/thingm/downloads/BlinkM_datasheet.pdf>.

"Compact Motor Driver Kit." Solarbotics. 01 Feburary 2010

<http://www.solarbotics.com/assets/datasheets/solarbotics_l298_compact_motor_driver_kit.pdf>.

"Language Reference." Arduino. 01 Feburary 2010 <http://arduino.cc/en/Reference/HomePage>.

"MaxSonar-EZ1 Datasheet." MaxBotix Inc. 01 Feburary 2010 <http://www.maxbotix.com/uploads/LV-

MaxSonar-EZ1-Datasheet.pdf>.

"nRF24L01+ Product Specification." Nordic Semiconductor. 01 Feburary 2010

<http://www.nordicsemi.com/files/Product/data_sheet/nRF24L01P_Product_Specification_1_0.pdf>.