ENGR 466 Final Report: Robot Drummer

73
ENGR 466: Integrated Design Project Course Instructor: Daniela Constantinescu, Mechanical Engineering Department Client: George Tzanetakis, Computer Science Department Final Design Report for a Robotic Drummer Group Members: Neil MacMillan, V00182450 Matthew Loisel, V00194616 Daniel Partridge, V00334811

description

This is our final report for ENGR 466, the integrated mechatronics design course at UVic. We built a robotic drummer that can be mounted over a drum and strike the drum in response to MIDI commands. Go here for more information:http://nrqm.pbworks.com/Robot-Drummer

Transcript of ENGR 466 Final Report: Robot Drummer

Page 1: ENGR 466 Final Report: Robot Drummer

ENGR 466: Integrated Design Project Course

Instructor:

Daniela Constantinescu, Mechanical Engineering Department

Client:

George Tzanetakis, Computer Science Department

Final Design Report for a Robotic Drummer

Group Members:

Neil MacMillan, V00182450

Matthew Loisel, V00194616

Daniel Partridge, V00334811

Page 2: ENGR 466 Final Report: Robot Drummer

P a g e | i

Contents List of Tables ................................................................................................................................... iii

List of Figures .................................................................................................................................. iii

Project Overview: ............................................................................................................................ 1

Major Design Criteria: ..................................................................................................................... 1

Secondary Goals: ............................................................................................................................. 1

Mechanical Design .......................................................................................................................... 2

Workspace Envelope ................................................................................................................... 2

Vertical Displacement ............................................................................................................. 2

Rotary Displacement ............................................................................................................... 3

Output Frequency ....................................................................................................................... 4

Output Force ............................................................................................................................... 4

Design .......................................................................................................................................... 6

Electrical Design .............................................................................................................................. 7

Overall Design ............................................................................................................................. 7

Electrical Components ................................................................................................................ 7

Power Supply .......................................................................................................................... 7

Microcontroller ....................................................................................................................... 8

Solenoid and Transistor Driver ............................................................................................. 10

Stepper Motor and Driver ..................................................................................................... 11

MIDI Connector and Interface Board .................................................................................... 12

Circuit Layout, Mounting, and Wiring ....................................................................................... 13

Software Implementation ............................................................................................................. 15

The MIDI Protocol ..................................................................................................................... 15

Note On Command ............................................................................................................... 15

Pitch Bend Command ........................................................................................................... 16

Serial Protocol ....................................................................................................................... 18

Command Stream ................................................................................................................. 18

Firmware Implementation ........................................................................................................ 20

Page 3: ENGR 466 Final Report: Robot Drummer

P a g e | ii

Activity Diagram .................................................................................................................... 21

Object Design ........................................................................................................................ 23

Sequence Diagrams ............................................................................................................... 24

Workstation Software ............................................................................................................... 27

DrummerBot Test ................................................................................................................. 27

Music Analysis ....................................................................................................................... 28

References .................................................................................................................................... 30

Appendix A: Pontiac Coil Solenoid ................................................................................................ 32

Appendix B: Stepper Motor Torque vs. Speed Specification ........................................................ 33

Appendix C: Part Drawings ........................................................................................................... 34

Appendix D: Code Listings ............................................................................................................. 44

Page 4: ENGR 466 Final Report: Robot Drummer

P a g e | iii

List of Tables Table 1: Force Calculations ............................................................................................................. 5

Table 1 - Power Supply Pin Connections ........................................................................................ 8

Table 2 - Microcontroller Pin Connections ..................................................................................... 9

Table 3 - Stepper Motor Wiring .................................................................................................... 11

Table 4 - Base Unit to Top Unit Wiring ......................................................................................... 14

Table 5 - MIDI Commands ............................................................................................................. 15

List of Figures Figure 1 - Vertical Displacement of Drumstick End ........................................................................ 3

Figure 2 - Horizontal Displacement of Drumstick End .................................................................... 4

Figure 3 - Force vs. Stroke Length for 1-1/4" Solenoid ................................................................... 5

Figure 4 - 466Bot Located Near Sample 50 cm Drum..................................................................... 6

Figure 5 - System Block Diagram .................................................................................................... 7

Figure 6 - Power Supply Pin Configuration ..................................................................................... 7

Figure 7 - Arduino Duemilanove Microcontroller Board ................................................................ 9

Figure 8 - Power MOSFET and Solenoid Circuit ............................................................................ 10

Figure 9 - Stepper Motor Driver Circuit ........................................................................................ 11

Figure 10 - MIDI Interface Circuitry and Connector ..................................................................... 12

Figure 11 - Approximate Circuitry Layout ..................................................................................... 13

Figure 12 - Picture of Circuitry Layout .......................................................................................... 13

Figure 13 - Base Unit to Top Unit Wiring Connector .................................................................... 14

Figure 14 - The "Note On" Command ........................................................................................... 16

Figure 15 - The "Pitch Bend" Command ....................................................................................... 17

Figure 16 - Firmware Activity Diagram ......................................................................................... 21

Figure 17 - Class Diagram .............................................................................................................. 23

Figure 18 - Sequence Diagram for Explicit Commands ................................................................. 24

Figure 19 - Sequence Diagram for Implicit Commands ................................................................ 25

Figure 20 - Sequence Diagram for Determining if the Ring Buffer Contains an Implicit Command

....................................................................................................................................................... 26

Figure 21 - The DrummerBot Test Console After Sending a Note On and Pitch Bend Command 28

Figure 22 - Accompany Data Flow Diagram .................................................................................. 29

Figure 23 - Solenoid Data Sheet .................................................................................................... 32

Figure 24 - Torque vs. Speed specifications from Digikey ............................................................ 33

Page 5: ENGR 466 Final Report: Robot Drummer

P a g e | 1

Project Overview: As described by George Tzanetakis this project is to design and build a robotic drummer. This

robot is to be capable of holding a variety of drumsticks, translating radially along the

drumhead and striking with a variety of forces to create louder or softer sounds. The project is

motivated by the design of Ajay Kapur, a recent grad student, who created a similar device to

provide percussion backup to his work on a sitar. The minimum required by George is a robot

arm capable of handling a drumstick and hitting a variety of positions on a drumhead with

varying force. Further development of multiple robots capable of hitting different drums or

cymbals would be appreciated and considered an additional benefit.

Major Design Criteria: In order of importance as we understand from meetings with George Tzanetakis the design

must be:

Robust – since the device is to be used as a teaching tool the design must be able to

withstand being handled by multiple users and capable of working properly after being

stored and shipped.

Repositionable – often times this device will be taken off campus for use in

demonstrations at schools and other institutes, for this the robot has to be light weight,

easy to setup and use.

Replicable – to make an adequate teaching instrument many copies of this device will

be created, to ease future manufacturing the device has to be simple, and require

minimal manufacturing time.

Secondary Goals: To meet the major design criteria, secondary goals have been added for consideration. These

objectives are to:

Create a manipulator capable of holding a variety of drumsticks.

Design a robot able to move vertically and able to move the end of a drumstick

to various location on a single drum.

Explore different methods of actuations including solenoid and DC motor based

designs.

Implement the ability to stand alone or link with other robots for greater

flexibility.

Explore the possibility of using wireless communication.

Page 6: ENGR 466 Final Report: Robot Drummer

P a g e | 2

Provide full drawing set, bill of materials and specifications with procedures.

Mechanical Design The design used for this project is based on the Trimpin Hammer used by Ajay Kapur in the

MahaDeviBot. There are a number of reasons why this design was chosen over the three other

alternatives. These advantages include:

Large workspace envelope

High output frequency

Greater range of output force than other options

Robust design

Workspace Envelope

The workspace envelope is defined as the three-dimensional space within which physical work

is done; it is determined by functional arm reach which, in turn, is influenced by the direction of

reach and the nature of the task being performed. The envelope for the 466Bot is determined

by the drumstick’s vertical displacement from the upper rest position to the drumhead and the

horizontal position of the end of the drumstick relative to the drumhead. The vertical and

horizontal displacements are controlled using a solenoid and a stepper motor respectively.

Vertical Displacement

The vertical displacement is controlled using a Pontiac Coil, Inc. pull-type solenoid. The solenoid

chosen for this purpose is the 1-1/4” 24VDC solenoid shown in Appendix A. This solenoid was

chosen due to its large stroke length and high force capabilities. Using a 15 inch drumstick

(standard drumsticks range in length from 13-17 inches) protruding 13 inches from the pivot,

with the solenoid attached 2 inches from the pivot, the vertical displacement of the drumstick

end can be calculated as 8.13 inches:

The following figure shows the range of the vertical displacements capable of being achieved

using this solenoid.

Page 7: ENGR 466 Final Report: Robot Drummer

P a g e | 3

Figure 1 - Vertical Displacement of Drumstick End

In Figure 1 - Vertical Displacement of Drumstick End the two blue lines represent the limits of

the vertical workspace envelope.

Rotary Displacement

Striking a drum at different locations on the drum head produces different sounds. To produce

a greater range of sounds the 466Bot will use a stepper motor mounted to the base to position

the end of the drumstick over the drum head. With drums ranging in diameter from 10cm to

50cm, a stepper motor with a resolution of 3.6 degrees per step was chosen as it allows the

robot the ability to hit a variety of locations while still being able to translate fast enough to

reduce lag between the input from the controller and the vertical actuation of the solenoid.

The motor used for the 466Bot has a maximum speed of 450 steps per second. A 39.6 degree

rotation, which covers the entire envelope for a 50 cm diameter drum, would require 11 steps

and take approximately 24ms. Although this delay seems large, research has shown that

different sounds less than 50ms apart are not discernable as distinct. The figure below shows

the 466Bot and a model of a 50cm drum. The blue lines represent the envelope achievable

using the 3.6 degree stepper motor.

Page 8: ENGR 466 Final Report: Robot Drummer

P a g e | 4

Figure 2 - Horizontal Displacement of Drumstick End

Output Frequency

Using the data collected by Ajay Kapur in designing and building the MahaDeviBot the projected

maximum frequency of the 466Bot is 18Hz. According to research by Harvard University’s

Division of Engineering and Applied Sciences an expert drummer can achieve a frequency of

40Hz [10]. This is achieved using both arms each equipped with a drumstick and a technique

known as “drumstick bounce” whereby a drumstick is loosely held in the hand and allowed to

strike the drum two or three times per movement of the wrist. Since the 466Bot will have one

arm drumming at a frequency of 18Hz, it will almost be able to match in speed one arm of an

expert drummer. Two 466Bots will be able to produce the output of an expert drummer, if they

are programmed correctly.

Output Force

The force with which the drumstick will strike the drum is determined by three things: the

length of the solenoid actuation; the moment arm for both the drumstick end and the solenoid;

and the strength of the spring mounted behind the pivot.

Solenoids contain a ferrous core surrounded by copper windings. When a current passes

though the windings a magnetic field is produced and applies a force to the core. As the

solenoid moves, the core retracts, and less of the core is within the windings; this results in less

force being applied to the core. The data sheet in Appendix A and the graph in Figure 3 - Force

Page 9: ENGR 466 Final Report: Robot Drummer

P a g e | 5

vs. Stroke Length for 1-1/4" Solenoid show the force capabilities for various positions of the

solenoid.

Figure 3 - Force vs. Stroke Length for 1-1/4" Solenoid

The solenoid applies a force to the mechanism, which revolves around a pin joint, causing the

drumstick to rotate around the pin joint as well. The force applied by the solenoid causes the

end of the drumstick to accelerate until it strikes the drum, at which point the velocity of the

drumstick decreases and applies a force to the drum.

The spring, which repositions the drumstick between actuation, also applies a moment about

the pin joint counter to that of the solenoid. Since this reduces the force at which the drumstick

strikes the drum the spring constant for the spring used is small. Calculations for determining

the spring used are shown below.

Based on the data obtained in determining the force requirements for the preliminary design

report, the range of forces required to produce audible sounds are:

Table 1: Force Calculations

Voltage (V) Force(N) Force (oz)

Soft Hit 0.044 0.45 1.6 Hard Hit 2.233 22.66 81.5

The following calculations show the spring constant required for a 1-1/4” compression of the

solenoid, with the drumstick applying a “soft hit” as defined in the preliminary report. The

lengths for the solenoid, spring and drumstick moment arms are defined by the geometry of

the 466Bot.

Page 10: ENGR 466 Final Report: Robot Drummer

P a g e | 6

Design

Since one of the preliminary requirements of the project was that the robot be easy to

manufacture, the mechanical parts have been designed for simplicity. This will aid in future

replication of the design for educational purposes and demonstrations. The following figure

shows the assembly model, produced using SolidWorks 2008. Individual part drawings are

included in Appendix C along with a labelled assembly drawing of the robot. Assembly

procedures will be included in the final report along with estimated machining times.

Figure 4 - 466Bot Located Near Sample 50 cm Drum

Not shown in the image is the stand for positioning and orienting the robot, as these stands

contain universal mounts. This makes the robot capable of mounting to any standard drum kit

stand.

Page 11: ENGR 466 Final Report: Robot Drummer

P a g e | 7

Electrical Design

Overall Design

The overall system design is shown below in Figure 5. The control of the system is based around

the microcontroller. The microcontroller receives MIDI encoded serial data from a computer or

from a MIDI enabled musical instrument. It then uses these MIDI data to control the solenoid

and stepper motors through the transistor driver and stepper motor driver respectively.

MCU

Solenoid

Drum StickStepper

Motor

Stepper

Motor Driver

Musical

Instrument

Transistor

Driver

MIDI

Interface

Circuitry

Computer

Figure 5 - System Block Diagram

Electrical Components

Power Supply

The power supply used was the GLC75DG power supply from Condor electronics. It features

four set DC output lines of +5.1V at up to 8A, +24V at up to 2A, -12V at up to 1A, and +12V at up

to 1A. The configuration of the input and output pins is shown below in Figure 6 and the

function and connection of each pin is listed in Table 2.

5 3 1 13 12 11 10 9 8 7 6 5 4 3 2 1

Figure 6 - Power Supply Pin Configuration

Page 12: ENGR 466 Final Report: Robot Drummer

P a g e | 8

The power supply documentation states that it is necessary that a constant current draw of

0.2A be present on the 5.1V output and 0.25A be present on the 24V output for proper

rectification of all outputs. This would correspond to a 25.5Ω, 1.02W minimum resistor on the

5.1V output and a 96Ω, 6W minimum resistor on the 24V output. Through testing though, it

was determined that a single 50Ω resistor across the 5.1V output is enough to result in proper

rectification. A 51Ω, 1W resistor was selected and placed between pins 3 and 6 of the power

supply output.

Table 2 - Power Supply Pin Connections

Pin Input Pin Output Connection

1 AC Ground 1 +5.1V NC 3 AC Neutral 2 +5.1V MIDI interface board 5 AC Line 3 +5.1V Power resistor 4 GRND NC 5 GRND MIDI interface board 6 GRND Power resistor 7 GRND Solenoid 8 +24V Solenoid 9 +24V NC 10 POWERFAIL NC 11 -12V NC 12 GRND Stepper motor driver/MCU 13 +12V Stepper motor driver/MCU

Microcontroller

The microcontroller development board used was the Arduino Duemilanove board shown

below in Figure 7. The microcontroller is powered using the Vin and Gnd input pins from the

12V output of the power supply. The board needs 7-12V to be input into the internal regulator

to produce the necessary 5V for the rest of the board. Through testing it was found that the

Arduino operated at 3.3V when powered by the external power input, and 5 V when powered

by the USB connector. At 3.3V the PWM signal generated by the Arduino was insufficient to

switch the transistor on and off; the 5V levels were needed. 5.1V was not enough to power the

board and it was necessary in this case for the USB connector to be connected for the

microcontroller to be able to output enough voltage on the PWM line to switch the transistor

on and off.

Page 13: ENGR 466 Final Report: Robot Drummer

P a g e | 9

Figure 7 - Arduino Duemilanove Microcontroller Board

The pin connections for the microcontroller are listed below in Table 3. During testing, a

problem arose where code could not be properly loaded onto the microcontroller from the USB

connector. Upon further investigation this was determined to be because the MIDI input (see

MIDI section) is normally high and is connected to the same serial receive line as the USB input.

To correct this, a single-pole-double-throw slider switch was connected between the MIDI input

signal and the serial receive pin (Rx) to be able to disconnect two lines when loading code

through the USB connector.

The outputs to the stepper motor driver are connected to the Arduino’s analog input port. The

Arduino’s analog pins are really general I/O pins that happen to be connected to the ATmega’s

analog to digital converter, so they can function as digital outputs. As digital pins they are

referred to in software as pins 14-19, even though on the board they are labelled analog 0-5.

Table 3 - Microcontroller Pin Connections

Pin Connection

0 (Rx) MIDI interface board output 5 (PWM)

Transistor gate – pin 1

14 K CMD L298 - EN0 15 K CMD L298 - EN1 16 K CMD L298 - L1 17 K CMD L298 - L2 18 K CMD L298 - L3 19 K CMD L298 - L4

Page 14: ENGR 466 Final Report: Robot Drummer

P a g e | 10

Solenoid and Transistor Driver

The activation of the drumstick is achieved through the use of the L90 series solenoid F0464A.

The solenoid is switched through the use of the IRFZ40 power MOSFET which is connected as

shown in Figure 8. The transistor is driven by a pulse width modulation (PWM) signal from the

microcontroller. The use of PWM allows for easy variation of the amount of power transferred

to the solenoid through the modification of the signal duty cycle and reduction of the overall

power consumption.

F0464A

PWM5

24V

IRFZ40

10k

1

2

3

Figure 8 - Power MOSFET and Solenoid Circuit

The solenoid was mounted directly to the custom aluminum chassis. A high-power diode was

placed between 24V and the MOSFET drain to prevent damage to the transistor and power

supply due to reverse currents that can occur when the solenoid is switched. The transistor was

mounted on a small piece of prototyping board to create a simple stand for the transistor and

to allow for easier soldering of the three lines connected to the transistor. A 10kΩ resistor was

placed between the PWM5 output of the MCU and the gate of the transistor to ensure current

limiting.

Page 15: ENGR 466 Final Report: Robot Drummer

P a g e | 11

Stepper Motor and Driver

To rotate the drum stick to hit different parts of the drumhead, the 42M100B2U unipolar

stepper motor was implemented. The stepper motor was driven by the K CMD L298 stepper

motor driver board. The board came in a kit and had to be soldered together using easy to

follow instructions included with the kit. The K CMD L298 board accepts four control lines and

two enable lines from the MCU and converts those signals to the necessary 12V signals to drive

the stepper motor. The board has four LEDs on it that indicate the current line that is active

(M1+, M1-, M2+, or M2-) and alternate flashing when the stepper motor coils are actuated in

the correct order. The K CMD L298 board was connected to the stepper motor and the MCU as

shown in Figure 9.

M 42M100B2U

K CMD L298

M2-

M1+

M1-

I2

EN1

I3

I4

M2+I1

EN0

* / *

1

2

3

4

5

619

18

15

17

1614

12V

Figure 9 - Stepper Motor Driver Circuit

The wiring for the stepper motor is described below in Table 4. The wires embedded into the

stepper motor were soldered and shrink-wrapped onto longer wires that ran down to the base

(see wiring section).

Table 4 - Stepper Motor Wiring

Line Colour

M1+ yellow M1- orange M2+ brown M2- black Ground red Ground green

Page 16: ENGR 466 Final Report: Robot Drummer

P a g e | 12

The stepper motor originally selected for this project ended up not being able to produce

enough torque to rotate the solenoid and drumstick chassis. The current stepper motor only

drew approximately 0.16A of the total 1A available to it which limits the amount of torque it

can produce. Stepper motors that draw more current and can produce more torque are

currently being investigated to replace the existing stepper motor.

MIDI Connector and Interface Board

The MIDI protocol is a normally-high serial protocol based on a current loop that exists between

pins 4 and 5 of the MIDI connector. The current loop runs through the input pins (1 and 2) of

the PC900 opto-isolator as shown below in Figure 10 and switches the input LED on and off. Pin

4 is held at a constant 5V in the MIDI out device while pin 5 is 5V when a 0 is being sent and

switches to 0V when a 1 is being sent. Thus, a current of 5 mA corresponds to logic 0 and a

current of 0 mA corresponds to logic 1.

RxPC900

300

5V

220

1

2

3

45

1

2

4

5

6

Figure 10 - MIDI Interface Circuitry and Connector

The opto-isolator converts the current signal to a voltage signal. As described in the

Microcontroller section, a single-pole-double-throw slider switch was connected between the

output of the opto-isolator and the serial receive pin of the microcontroller to be able to

disconnect the MIDI input line from the receive pin when loading code through the USB

connector. The downwards (towards the middle of the layout) position of the switch enables

MIDI input while the upwards (towards the edge of the layout) position enables code loading

through USB.

Page 17: ENGR 466 Final Report: Robot Drummer

P a g e | 13

Circuit Layout, Mounting, and Wiring

To accommodate the desire to have an open design, the various control circuitry and interface

circuitry was mounted on top of the power supply. To allow for proper air flow out of the

power supply and to prevent short circuits, the various components were mounted on plastic

or metal spacers or washers. The circuit layout was based around having any external ports on

the opposite side of the power supply as the power pins. The approximate circuit layout is

shown below in Figure 11. This layout was implemented as shown in Figure 12.

Stepper Motor

Driver

MIDI Interface CircuitryMIDI

Connector

MCU

Power Pins

Power

MOSFET

Switch

Figure 11 - Approximate Circuitry Layout

Figure 12 - Picture of Circuitry Layout

Page 18: ENGR 466 Final Report: Robot Drummer

P a g e | 14

Long lengths of wire were run from the top unit (chassis with solenoid and stepper motor)

down to the base unit (power supply and control boards) which were long enough to run from

the top of a standard cymbal stand down to the bottom with extra slack. In total, seven lines

were run: two for the solenoid and five for the stepper motor. Closer to the base unit, a

connector was inserted so that the base unit could be detached from the top unit. The pin

configuration for the connector is shown below in Figure 13 and the functions and colours of

the wires run into and out of the connector are listed in Table 5.

1 2 3

4 5 6

7 8 9

Figure 13 - Base Unit to Top Unit Wiring Connector

Table 5 - Base Unit to Top Unit Wiring

Pin Base Unit Device Top Unit Device Line Wire Colour

1 power supply solenoid +24V red 2 MOSFET solenoid MOSFET drain grey 3 K CMD L298 stepper motor M1+ orange 4 K CMD L298 stepper motor M1- green 5 K CMD L298 stepper motor M2+ blue 6 K CMD L298 stepper motor M2- white 7 K CMD L298 stepper motor ground black 8 NC NC - - 9 NC NC - -

Page 19: ENGR 466 Final Report: Robot Drummer

P a g e | 15

Software Implementation

The MIDI Protocol

The robot is controlled using the Musical Instrument Digital Interface (MIDI) protocol. MIDI is a

common digital music format that represents music as a sequence of commands, as opposed to

a sequence of analog data samples. Each MIDI command is an 8-bit byte, and can be followed

by several 8-bit data bytes. Command bytes always have a 1 as their most significant bit, and

data bytes always have a 0 as their most significant bit. As such, data bytes can actually only

hold 7 bits of information, for a maximum range of 0 to 127. The following musical commands

are defined by the MIDI protocol:

Table 6 - MIDI Commands

Byte Command Meaning

0x80 Note Off Stop playing a note. 0x90 Note On Start playing a note. 0xA0 Aftertouch Change a note’s velocity while it’s playing. 0xB0 Control Change Change the value of a controller (e.g. a button or

slider). 0xC0 Patch Change Change the instrument that the MIDI device is

simulating. 0xD0 Channel

Pressure Adjust the velocity of all notes on the given channel.

0xE0 Pitch Bend Adjust a note pitch by small increments.

The command bytes in the table all have their lower nibbles set to 0. In MIDI, these four bits

contain the number of the channel to which the command is addressed. There are also several

non-musical system messages represented by bytes 0xF0 to 0xFF.

The robot’s firmware implements a subset of this protocol. Specifically, it responds to the

“note on” and “pitch bend” commands. It ignores all other commands, and ignores channel

numbers.

Note On Command

The “note on” command indicates that a MIDI device should start playing a note. It has two

parameter bytes. The note byte selects which of 128 notes to start playing, with middle C

defined as note 60. The velocity byte determines the note’s velocity, which usually corresponds

to the volume at which the note is played.

Page 20: ENGR 466 Final Report: Robot Drummer

P a g e | 16

Note On

(0x90)

note

(0 – 127)

velocity

(0 – 127)

0 8 16 24

Figure 14 - The "Note On" Command

The drummer bot interprets the “note on” command as an instruction to strike a drum. The

robot can control up to three different drums, so the note parameter determines which drum

to strike: if all three drums are being used, then every third note controls the same drum. For

example, notes 0, 3, 6, and so on all control the bass drum. Notes 1, 4, 7, ... control the main

drum, notes 2, 5, 8, ... control the hi-hat cymbal, etc. If there are fewer drums, then each drum

can listen to more notes. For example, if there is one drum then the drum can listen for every

note. Currently this setting must be hard-coded into the firmware. The “note on” command’s

velocity parameter controls how hard the drum is struck.

Normally a “note on” command must be followed by a “note off” command on the same note.

With drums, it does not make sense for a note to be held on for an arbitrarily long period of

time; rather, the drummer bot itself should control how long it takes to strike the drum, since it

is a property of the robot and its position relative to the drum. The strike time is not something

that the MIDI controller has enough information to determine. In fact, the robot uses a

hardcoded strike time of 50 milliseconds. Another option is to allow the user to adjust the

strike time, but variations in the strike velocity have a greater impact in the drum stick’s

accuracy than the strike time, so there is not much value in allowing the user to adjust the

strike time.

Most MIDI controllers will still send “note off” commands to the drummer, for example when

the user releases a key on a MIDI keyboard. The robot ignores “note off” commands. Some

controllers send “note on” commands with the velocity set to 0 as a substitute for “note off”

commands. The robot will ignore “note on” commands with 0 velocity so as to behave

consistently between different MIDI controllers. Another option is for the robot to handle

“note off” commands as well as “note on” commands with 0 velocity, and to terminate the

strike when either of those are received. That would create an extra variable that the user

would need to control though, which would make the robot more difficult to use.

Pitch Bend Command

The “pitch bend” command controls fractional steps in a note’s pitch. “Pitch bend” commands

are generated when the user moves the pitch wheel, a common component on MIDI

controllers. For example, if an instrument is playing a D note, the “pitch bend” command can

Page 21: ENGR 466 Final Report: Robot Drummer

P a g e | 17

be used to transpose the note to any of about 16,000 points between C and E. The pitch bend

is applied to all notes being played by the instrument.

Pitch Bend

(0xE0)

pitch_msb

(0 – 127)

pitch_lsb

(0 – 127)

0 8 16 24

Figure 15 - The "Pitch Bend" Command

“Pitch bend” has one 14-bit argument to set the pitch. The argument is sent as two 8-bit bytes

with each byte’s most significant bit set to 0 (because they are data bytes). To get the final

pitch value, the bytes must be converted to 7-bit bytes and concatenated. The following C++

code concatenates the 8-bit bytes into a 14-bit integer (stored as a 16-bit integer, since 14-bit

integers are not usually supported natively in firmware). It assumes that the “pitch_msb”

variable is the most significant byte, and “pitch_lsb” is the least significant byte:

uint16_t pitch = pitch_msb;

pitch = (pitch << 7) | pitch_lsb;

A pitch value of 0x2000 is considered the centre value, and does not transpose the note. Pitch

values between 0x0000 and 0x2000 transpose the note down, and pitch values between

0x2000 and 0x4000 transpose the note up.

The robot interprets the pitch bend command as an instruction to change its lateral position on

the drum by rotating its stepper motor. The pitches between 0x2000 and 0x4000 are divided

into 4 ranges, each of which correspond to a position up to 4 steps to the right of centre on the

drum. The firmware keeps track of the value of the latest “pitch bend” command, and if the

robot’s lateral position does not match the position corresponding to the pitch value, it cycles

the stepper motor until the positions match.

There is a dead zone on the border between each range, to keep the stepper motor from

oscillating when the pitch value is near the border. For example, the transition from pitch

0x27FF to 0x2800 is a border: the drum will be in its centre position up to 0x27FF, and then

when the pitch becomes 0x2800 the drum will move one position to its right. If the user is

holding the pitch wheel such that the pitch value is switching back and forth between 0x27FF

and 0x2800 quickly, then the stepper motor will move back and forth erratically, which is

undesirable behaviour. Instead, the robot will not move a step right until the pitch value

reaches 0x2840. Once it is in that position, it will not move back until the pitch value goes

down to 0x27C0.

Page 22: ENGR 466 Final Report: Robot Drummer

P a g e | 18

Serial Protocol

The MIDI protocol is a simplex Universal Asynchronous Receive/Transmit (UART), or serial,

protocol that operates at 31250 bits per second. The optoisolator described in the Electrical

Design section above allows the MIDI receiver to receive serial data at whatever levels it can

use most easily—in this case, 0 to 5 volt levels.

The serial baud rate is configured on the ATmega328P by setting the UART Baud Rate Register

(UBRR) to a certain scaling value. The value in UBRR scales the AVR’s oscillator frequency down

to the clock rate needed to generate the serial waveform. One problem that one can

encounter is that for baud rates that do not divide into the oscillator frequency evenly, the

UART module cannot generate a precise sampling frequency. Another way to look at it is that if

the ideal scaling factor is a rational number and not an integer, then it cannot be represented

precisely in the 8-bit UBRR register. This approximation of the scaling factor leads to framing

errors when the transmitter and receiver go out of synch.

MIDI’s baud rate of 31250 is an unusual speed in typical UART applications, so it is worth

examining how accurately the Arduino can receive serial data at that speed. Atmel’s datasheet

for the ATmega328P gives the following equation for determining the UBRR value in terms of

the oscillator frequency and the desired baud rate:

The Arduino uses a 16 MHz clock, so the UBRR value for MIDI is calculated like this:

Since the UBRR value is precisely 31, the Arduino can receive a 31250 baud signal with no risk of

framing errors. In fact, any microcontroller whose oscillator frequency is a multiple of 500,000

can receive the MIDI signal with perfect precision, which might be why such a strange baud rate

was selected for the protocol.

Command Stream

A MIDI controller sends a stream of commands over its serial connection to the receiver. Each

command consists of a command byte followed by some data bytes. Both of the commands

that the robot listens for have two-byte arguments, so the relevant command packets are each

3 bytes. The firmware polls its UART receiver and copies any bytes it receives into a 3-byte ring

buffer. When the buffer is full and the buffer head is a command byte, then in general the

buffer contains a full command packet, which can be removed from the buffer and processed

by the appropriate command handler. There are two exceptions to this rule.

Page 23: ENGR 466 Final Report: Robot Drummer

P a g e | 19

Implicit Commands

Often, when a MIDI controller sends a command multiples times in a row, it will send the

command byte only for the first command packet and leave the command implicit for the

remaining packets. This is done to reduce the amount of traffic on the serial connection, which

can be an important factor in the responsiveness of the instruments that the device is

controlling. For example when the user turns on a note while turning the pitch wheel, the

following sequence of data might be sent over the serial connection:

0xE0 // “pitch bend”

0x2000 // “set pitch to middle position”

0x2001 // “increment pitch”

0x2002 // “increment pitch”

0x906040 // “note on for note 0x60 with velocity 0x40”

0xE0 // “pitch bend”

0x2003 // “increment pitch”

0x2004 // “increment pitch”

...

In this example, the command packets that set the pitch to 0x2001, 0x2002, and 0x2004 all lack

explicit command bytes. The command is implied to the receiver by the fact that the last

command byte it received was the “pitch bend” command.

The firmware solves this problem by checking the ring buffer’s size every time it receives a byte.

If the buffer contains exactly two bytes and both of them are data (in other words, neither is an

explicit command), then the firmware treats them as arguments to an implicit command.

Recall that data bytes have their most significant bits set to 0, whereas explicit command bytes

have their most significant bit set to 1. This is safe because both of the commands that the

robot can handle have arguments that are exactly two bytes. All the other commands are

ignored, so it doesn’t matter if their arguments are treated incorrectly as implicit commands.

Odd-Sized Arguments

Most MIDI commands are followed by exactly 2 argument bytes, but not all of them. If the

firmware receives a MIDI command that doesn’t have exactly 2 argument bytes, then following

the rule described above might cause a problem. For example, the “patch change” command

only has one 1-byte argument. Consider the case when the firmware receives a “patch change”

command followed by a “note on” command. Once the “note on” is received, the ring buffer

looks like this:

Page 24: ENGR 466 Final Report: Robot Drummer

P a g e | 20

0xC0 // “patch change” 0x40 // “change to instrument 0x40 (argument byte)” 0x90 // “note on”

In this case, the general rule will result in the three bytes being removed from the buffer,

packaged into a “patch change” message, and then ignored since the firmware does not handle

the “patch change” command. This results in the “note on” command, which should be

handled by the firmware, being dropped. Even worse, further “note on” commands will likely

be implicit, but will be interpreted as “patch change” commands since the initial explicit “note

on” command byte was dropped.

The firmware solves this problem by making sure that what it assumes to be arguments are

really data bytes and not command bytes. If either of the argument bytes is a command, then

it adds both arguments back to the ring buffer so that the command will be processed once its

full set of arguments has been received.

Firmware Implementation

The firmware is written in C++ targeting the Arduino platform. C++ was chosen because the

Arduino core library is partially written in C++, so components such as the HardwareSerial class

are unavailable in C.

C is usually used in favour of the object oriented C++ for embedded system development. This

is because C programs have lower program memory requirements, it is easier to avoid dynamic

memory allocation in C, and object-oriented programming often leads to overengineering in

firmware development.

The ATmega processors used in the Arduino have plenty of program memory available, though:

as of this writing, the firmware compiles to a 10 KB binary, which fills about 30% of the

ATmega328P’s program memory.

Avoiding dynamic memory allocation is important for several reasons. The main reason is for

memory safety: allocating objects on the heap creates a risk of memory leaks, which is very

dangerous in a low-memory environment (the ATmega328P provides 2 KB of data memory).

Even if there are no memory leaks, it is still easy to allocate too much memory on the heap

through normal program operation, especially if memory is allocated in loops. Accessing the

heap is also slower than allocating objects statically or on the stack. The firmware avoids the

heap by instantiating objects statically, and by making extensive use of the singleton design

pattern to reduce the number of objects that can be instantiated. Accessing a statically

instantiated object in C++ is not significantly slower than accessing a static structure in C. Thus,

the firmware is small enough and runs fast enough despite being written in C++.

Page 25: ENGR 466 Final Report: Robot Drummer

P a g e | 21

Activity Diagram

The activity diagram below shows an overview of the algorithm that the firmware executes.

There are three main subsystems: the Midi class, the Stepper class, and the Solenoid class. The

program’s main loop repeatedly checks the MIDI subsystem, the stepper motor subsystem, and

the solenoid subsystems. The only asynchronous input to the system is the MIDI UART

connection, which is handled by the Arduino core library and polled during the CheckMidi state.

CheckMidi

CheckSolenoid

CheckStepper

[Command

Ready]

[No

Command]

Assemble Packet

Send Command to Listener

[Strike

Timed Out]

[NoteOn

Command

Received]

Deactivate PWM

Activate PWM

[Otherwise]

[Turn

Complete]

[PitchBend

Command

Received]

Stop Turning

Calculate Turn

[Otherwise]

Start Turning

Start Timer

Figure 16 - Firmware Activity Diagram

Page 26: ENGR 466 Final Report: Robot Drummer

P a g e | 22

The check states are designed for minimal latency so that each iteration of the main loop is

executed as quickly as possible. All timing is done using the Arduino core library’s millis()

function, which returns the number of milliseconds that have elapsed since the timer started. A

delay can be inserted into a subsystem by storing its start time, and continually checking the

result of the millis() function until the desired number of milliseconds have elapsed. This

technique allows the firmware to continue to check all of its subsystems while one of the

subsystems is executing a delay, which makes the system highly concurrent.

The millis() function returns a 32-bit value, but the firmware uses 8- and 16-bit variables. To

avoid direct comparisons between the return value of millis() and stored times, the firmware

defines a function called millis16(), which just samples the millis() function and returns the least

significant 16 bits of that value.

Page 27: ENGR 466 Final Report: Robot Drummer

P a g e | 23

Object Design

The class diagram below shows the objects that are defined in the firmware (not including

those defined in the Arduino core library) and how they relate to one another.

-Midi()

+GetInstance() : Midi*

+CheckMidi()

+RegisterListener(in listener : MidiListener*)

-listeners : MidiListener* [NUM_COMMANDS]

-buffer : ThreeByteRing

-last_command : uint8_t

-last_channel : uint8_t

Midi

#MidiNoteOnListener()

+DoNoteOn(in note : uint8_t, in velocity : uint8_t)

MidiNoteOnListener

#MidiPitchBendListener()

+DoPitchBend(in pitch : int16_t)

MidiPitchBendListener

#MidiListener(in command : MIDI_COMMAND)

+GetCommandType() : MIDI_COMMAND

+ProcessCommand(in param : midi_param_t*)

-command : MIDI_COMMAND

MidiListener

+NOTE_OFF = 0

+NOTE_ON = 1

+AFTERTOUCH = 2

+CONTROLLER = 3

+PATCH_CHANGE = 4

+CHANNEL_PRESSURE = 5

+PITCH_BEND = 6

«enumeration»

MIDI_COMMAND

+note_on : note_on_t

+pitch_bend : pitch_bend_t

«union»

midi_param_t

+note : uint8_t

+velocity : uint8_t

«struct»

note_on_t

+pitch : uint16_t

«struct»

pitch_bend_t

«uses»

-Solenoid(in type : DRUM_TYPE)

+GetInstance(in type : DRUM_TYPE) : Solenoid*

#DoNoteOn(in note : uint8_t, in velocity : uint8_t)

+CheckSolenoid()

-type : DRUM_TYPE

-max_duty_cycle : uint8_t

-duty_cycle : uint8_t

-pwm_pin : uint8_t

-note_divisor : uint8_t

-strike_start_time : uint16_t

-strike_timeout : uint8_t

Solenoid

-Stepper()

+GetInstance() : Stepper*

-CycleCCW()

-CycleCW()

-OutputCycleCode()

-TransferToState(in new_state : STEPPER_STATE)

#DoPitchBend(in pitch : int16_t)

+CheckStepper()

-current_cycle_start_time : uint16_t

-current_pitch : uint16_t

-current_position : uint8_t

-destination_position : uint8_t

-max_positions : uint8_t

-state : STEPPER_STATE

Stepper

+CYCLE1 = 0x05

+CYCLE2 = 0x09

+CYCLE3 = 0x12

+CYCLE4 = 0x22

«enumeration»

STEPPER_STATE

+ThreeByteRing()

+Add(in value : uint8_t)

+Peek() : uint8_t

+Remove() : uint8_t

+Size() : uint8_t

-ring : uint8_t[3]

-size : uint8_t = 0

-head_index : int8_t = -1

-tail_index : int8_t = -1

ThreeByteRing

«uses»

+MAIN = 0

+BASS = 1

+HIHAT = 2

«enumeration»

DRUM_TYPE

Figure 17 - Class Diagram

Page 28: ENGR 466 Final Report: Robot Drummer

P a g e | 24

Sequence Diagrams

The following diagrams show the operation sequence for the CheckMidi task when the robot

receives explicit and implicit commands.

:Midi :ThreeByteRing

Add(byte)

param:midi_param_t

<<create(byte1, byte2)>>

Remove() x3

new_command, byte1, byte2

:MidiListener

ProcessCommand(param)

:MidiNoteOnListener :MidiPitchBendListener

DoNoteOn

DoPitchBend

new_command

is NOTE_ON

new_command

is PITCH_BEND

buffer_head is

a command

byte

Peek()

buffer_head

CheckMidi()

Figure 18 - Sequence Diagram for Explicit Commands

Page 29: ENGR 466 Final Report: Robot Drummer

P a g e | 25

:Midi :ThreeByteRing

Add(byte)

implicit_command = BufferContainsImplicitCommand()

param:midi_param_t

<<create(byte1, byte2)>>

Remove() x2

byte1, byte2

:MidiListener

ProcessCommand(param)

:MidiNoteOnListener :MidiPitchBendListener

DoNoteOn

DoPitchBend

last_command

is NOTE_ON

last_command

is PITCH_BEND

implicit_command

is true

CheckMidi()

Figure 19 - Sequence Diagram for Implicit Commands

The diagram in Figure 20 shows the operation sequence for the

BufferContainsImplicitCommand function used in Figure 19.

Page 30: ENGR 466 Final Report: Robot Drummer

P a g e | 26

:Midi :ThreeByteRing

Size()

size

Remove()

byte1

Remove()

byte2

byte1 is data AND

byte2 is data

Add(byte1)

Add(byte2)

byte1 is command OR

byte2 is command

BufferContainsImplicitCommand()

false

size is not 2

true

false

Figure 20 - Sequence Diagram for Determining if the Ring Buffer Contains an Implicit Command

Page 31: ENGR 466 Final Report: Robot Drummer

P a g e | 27

Workstation Software

The robot can accept MIDI input over the Arduino’s built-in USB port, not just its MIDI

connector. The Arduino includes an FTDI FT232R USB-UART converter chip, which allows the

device to appear as a serial port on the host computer. This allows programs running on the

host computer to send data to the robot over USB without having to configure the robot as a

USB client. In fact, the USB and MIDI inputs are seamlessly interchangeable, since they are both

UART protocols and are both connected to the same serial receive pin on the microcontroller.

As long as the program running on the workstation generates a valid MIDI command stream,

the firmware cannot tell the difference between USB input and MIDI input.

One problem is that the USB host can introduce significant transmission latency. USB does not

use interrupts, so the USB host must poll for data to send or receive. It can take up to 16 ms for

information to be transmitted over USB, which may have a negative impact on the robot’s

responsiveness. This problem will be solved if it appears.

This project includes two programs that can send MIDI data to the robot over USB.

DrummerBot Test

This program is a simple interface that generates a MIDI command stream in response to

control input, and provides a serial console to display text output from the firmware. It has

three buttons to generate three different notes: one for the bass drum, one for the main drum,

and one for the hi-hat cymbal, of which only the main drum is implemented in this project. The

program also has a slider to simulate pitch bend commands. DrummerBot Test produces

explicit and implicit MIDI commands correctly.

The pitch bend is only transmitted once every 10 ms even if it changes more often, which may

not simulate a MIDI instrument correctly. This is necessary because if data are transmitted too

quickly then the robot cannot handle them correctly, and the MIDI stream gets corrupted. The

actual MIDI keyboard used to test the robot also limits how often it sends pitch bend

commands, and this is likely not a problem that can be fixed.

The program includes a thread that polls the serial port for incoming data, and prints any data it

receives to the serial console. This is useful for doing print debugging in the firmware. The

robot can even receive MIDI data from its MIDI port while sending debug messages to its USB

port, since the MIDI port is receive-only. Normally the firmware does not include print

debugging statements, since the inclusion of such code adds about 2 KB to the program

memory requirements, requires a considerable amount of stack space, and is very slow.

Page 32: ENGR 466 Final Report: Robot Drummer

P a g e | 28

Figure 21 - The DrummerBot Test Console After Sending a Note On and Pitch Bend Command

Music Analysis

The music analyser program, called Accompany, analyses musical data, determines when a

drum should be struck in time with the music, and outputs the appropriate MIDI data over the

serial port. This program uses the client’s Marsyas music analysis library to read a source of

music such as a recording or live input.

A simple approach to the music analysis problem has been completed as of this report.

Marsyas comes with a program called mudbox, which is used for experimenting with Marsyas’

functionality. Mudbox now includes an experiment called robot_peak_onset that takes an

audio input, processes it through several systems and passes it on to the computer’s primary

audio output (e.g. the sound card). The processing systems detect peaks in the audio data, and

output in three different ways at each peak:

1. A short sequence of noise is sent to the right speaker. 2. A MIDI command is sent to the MIDI port specified on the mudbox command line (this

does not currently work on Windows). 3. A string (“Peak!!!”) is sent to mudbox’s standard output.

The robot is not able to handle the MIDI command stream that the mudbox experiment

generates. Often it misses beats that other MIDI devices respond to correctly when they are

Page 33: ENGR 466 Final Report: Robot Drummer

P a g e | 29

connected to the command stream. This problem was avoided by parsing the standard output

from mudbox and using the code developed for sending MIDI commands from the

DrummerBot Test program to send appropriate MIDI commands each time mudbox outputs a

newline. This makes the robot behave as expected, but it is not currently clear why the robot is

unable to respond correctly to mudbox’s MIDI output.

Marsyas is unable to have MP3 data as a direct input; compressed audio must be converted to

raw WAV data before being input to mudbox. This can be accomplished using LAME, which

includes an audio decoder. Figure 22 shows the data flow from an MP3 file to the MIDI output

to the serial port:

LAMEInput.mp3 Temp.wav

Mudbox

robot_peak_on

set

Mudbox

stdoutAccompany

Serial

Port

Figure 22 - Accompany Data Flow Diagram

Page 34: ENGR 466 Final Report: Robot Drummer

P a g e | 30

References A. Kapur, Trimpin, E. Singer, A. Suleman, G. Tzanetakis, “A COMPARISON OF SOLENOID-BASED

STRATEGIES FOR ROBOTIC DRUMMING”, 2007. *Online+.Available:

http://www.mistic.ece.uvic.ca/ people/ajay/karmetik/pubs/

2007_icmc_mahadevibot.pdf.[Accessed: May 22,2009]

“Tama Imperialstar 5-Piece Standard Drum Set with Cymbals”. *Online+.Available:

http://www.musiciansfriend.com/document?base_pid=491127&

cpd=0OEY&doc_id=99371&index=1. [Accessed: May22, 2009]

“Bass Drum”, May 2009. [Online].Available: http://en.wikipedia.org/wiki/Kick_drum. [Accessed:

May 22, 2009]

“Shaker (percussion)”, May 2009. *Online+.Available: http://en.wikipedia.org/wiki/Shaker_

(percussion). [Accessed: May 22, 2009]

“Products: Tripod Heads |TheCameraStore.com”, 2009. *Online+.Available:

http://www.thecamera store.com/ search/products/results/taxonomy%253A31.393.

[Accessed: May 22, 2009]

“http://media.digikey.com/pdf/Data%20Sheets/Pontiac%20Coil%20Inc%20PDFs/L-90.pdf”,

2009. [Online].Available:

http://media.digikey.com/pdf/Data%20Sheets/Pontiac%20Coil%20Inc%20PDFs/L-90.pdf.

[Accessed: May 22, 2009]

“http://www.harmony-central.com/ProductImages/Large/000026707.jpg”, 2009. *Online+.

Available: http://www.harmony-central.com/ProductImages/Large/000026707.jpg.

[Accessed May 22, 2009]

“http://media.digikey.com/pdf/Data%20Sheets/Portescap%20Danaher%20PDFs/42M100B2U.p

df”, 2009.

[Online].Available:http://media.digikey.com/pdf/Data%20Sheets/Portescap%20Danaher

%20PDFs /42M100B2U.pdf. [Accessed: June 22, 2009]

“Multi-track recording for musicians”, 2009. *Online+. Available:

http://books.google.ca/books?id

=ByJG1iwUHBAC&pg=PA48&lpg=PA48&dq=smallest+delay+capable+of+hearing+between

+sounds&source=bl&ots=PkjwHLvDGz&sig=lPYU7sdsQbBKD_--

Page 35: ENGR 466 Final Report: Robot Drummer

P a g e | 31

xfXHs9D0v4U&hl=en&ei=OMlCSpyCLIz8sgOgpcTLDw&sa

=X&oi=book_result&ct=result&resnum=2. pp 49. [Accessed: June 24, 2009]

“http://biorobotics.harvard.edu/pubs/drumroll.pdf”, 1997. *Online+. Available:

http://biorobotics.harvard.edu/pubs/drumroll.pdf. [Accessed June 21, 2009]

“MIDI SPEC”. *Online+. Available: http://www.popeye-x.com/tech/midi_spec.htm [Accessed

June 10, 2009]

“ATmega48PA/ATmega88PA/ATmega168PA/ATmega328P Hardware Specification”, May, 2009.

[Online]. Available:

http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf. [Accessed July 7,

2009]

“Effect of USB buffer size and the Latency on Data Throughput”, 2006. *Online+. Available:

http://www.ftdichip.com/Support/Knowledgebase/index.html?an232beffectbuffsizeandl

atency.htm. [Accessed July 20, 2009]

“LAME MP3 Encoder”, 2008. [Online]. Available: http://lame.sourceforge.net/ [Accessed

August 4, 2009]

Page 36: ENGR 466 Final Report: Robot Drummer

P a g e | 32

Appendix A: Pontiac Coil Solenoid

Figure 23 - Solenoid Data Sheet

Page 37: ENGR 466 Final Report: Robot Drummer

P a g e | 33

Appendix B: Stepper Motor Torque vs. Speed Specification

Figure 24 - Torque vs. Speed specifications from Digikey

Page 38: ENGR 466 Final Report: Robot Drummer

P a g e | 34

Appendix C: Part Drawings

Page 39: ENGR 466 Final Report: Robot Drummer

P a g e | 35

Page 40: ENGR 466 Final Report: Robot Drummer

P a g e | 36

Page 41: ENGR 466 Final Report: Robot Drummer

P a g e | 37

Page 42: ENGR 466 Final Report: Robot Drummer

P a g e | 38

Page 43: ENGR 466 Final Report: Robot Drummer

P a g e | 39

Page 44: ENGR 466 Final Report: Robot Drummer

P a g e | 40

Page 45: ENGR 466 Final Report: Robot Drummer

P a g e | 41

Page 46: ENGR 466 Final Report: Robot Drummer

P a g e | 42

Page 47: ENGR 466 Final Report: Robot Drummer

P a g e | 43

Page 48: ENGR 466 Final Report: Robot Drummer

P a g e | 44

Appendix D: Code Listings /*

* main.c

*

* Created on: 4-Jun-2009

* Author: nrqm

*/

#include "arduino/WProgram.h"

#include "midi/midi.h"

#include "stepper/stepper.h"

#include "solenoid/solenoid.h"

/**

* Function for printing debug statements to the serial port.

*/

void print(const char* str)

uint8_t i;

for (i=0; str[i] != 0; i++)

Serial.write(str[i]);

/**

* The Arduino millis function returns a 32-bit integer for some reason, and that

compares unfavourably

* with the 16-bit integers we're using, so we want to use a 16-bit timer.

*/

uint16_t millis16()

return millis() & 0xFFFF;

// This is invoked when a pure virtual function is called. See this:

// http://ccgi.rowley.co.uk/support/faq.php?do=article&articleid=127

extern "C" void __cxa_pure_virtual()

digitalWrite(13, HIGH); // turn the LED on

cli();

while (1); // infinite uninterruptible loop

void setup()

sei();

void loop()

for (;;)

Midi::GetInstance()->CheckMidi();

Solenoid::GetInstance(MAIN)->CheckSolenoid();

Solenoid::GetInstance(BASS)->CheckSolenoid();

Solenoid::GetInstance(HI_HAT)->CheckSolenoid();

Stepper::GetInstance()->CheckStepper();

Page 49: ENGR 466 Final Report: Robot Drummer

P a g e | 45

// This is the default main function used for Arduino sketches.

int main(void)

init();

setup();

for (;;)

loop();

return 0;

Page 50: ENGR 466 Final Report: Robot Drummer

P a g e | 46

/*

* midi.h

*

* Created on: 27-Jun-2009

* Author: nrqm

*/

#ifndef MIDI_H_

#define MIDI_H_

#include <stdlib.h>

#include <avr/io.h>

#include "ThreeByteRing.h"

/**

* The commands defined explicitly in the MIDI protocol.

* There are a bunch of other status bytes (system common and system real time

messages) that are not

* covered here. Currently this program only handles the NOTE_ON and PITCH_BEND

messages.

*/

typedef enum _mcmd

NOTE_OFF, /// turn a note off.

NOTE_ON, /// turn a note on.

AFTERTOUCH, /// apply aftertouch to a note (e.g. changes

in velocity after it is turned on).

CONTROLLER, /// set the value for some controller (switch,

knob, etc.).

PATCH_CHANGE, /// change the program/patch that the device is

running (e.g. change instrument).

CHANNEL_PRESSURE, /// similar to aftertouch, but changes velocity for

all notes on channel.

PITCH_BEND, /// change a note's pitch with fine

resolution, e.g. in response to a pitch wheel.

MIDI_COMMAND;

/// The total number of MIDI commands.

static const uint8_t num_commands = 7;

/// The maximum number of objects that can listen for a given MIDI command.

static const uint8_t max_listeners = 3;

/**

* The parameters to a note on command. See MidiNoteOnListener::DoNoteOn.

*/

typedef struct _no

uint8_t note;

uint8_t velocity;

note_on_t;

/**

* The parameter to a pitch bend command. See MidiPitchBendListener::DoPitchBend.

*/

typedef struct _pb

uint16_t pitch;

pitch_bend_t;

/**

* General 2-byte parameter to a MIDI note on or pitch bend command.

Page 51: ENGR 466 Final Report: Robot Drummer

P a g e | 47

*/

typedef union _param

note_on_t note_on;

pitch_bend_t pitch_bend;

midi_param_t;

class MidiListener;

/**

* A class to handle a subset of the MIDI protocol. To use this class, register

objects derived from the

* listener classes defined below for the note on (MidiNoteOnListener) and pitch bend

(MidiPitchBendListener)

* commands. Then repeatedly call Midi::CheckMidi to check for commands and pass them

on to the listeners.

*/

class Midi

private:

/// Array of command listeners; has room for three listeners for each of the

commands.

MidiListener* listeners[num_commands][max_listeners];

/// The last command byte that was received, masked with 0xF0 (command nibble).

uint8_t last_command;

/// The last command byte that was received, masked with 0x0F (channel nibble).

uint8_t last_channel;

/// A 3-byte ring buffer to store the last three bytes received over serial.

ThreeByteRing buffer;

/// Constructor; do not instantiate this class, use GetInstance instead.

Midi();

/**

* If there are precisely two data bytes in the ring buffer, then this returns

1; otherwise, it returns 0.

* A MIDI data byte is an 8-bit value with the MSb cleared (in contrast, MIDI

command bytes have the MSb set).

* This is useful because MIDI commands are often sent without repeating the

command byte. For example,

* if a controller sends the PITCH_BEND message several times in a row with no

other messages interrupting

* the stream, it might send the PITCH_BEND command byte once followed by

several pairs of data bytes.

* Controllers do this to reduce the amount of data that need to be

transmitted. Some controllers send the

* command byte explicitly in every message.

*

* If this function returns 1, then the buffer contains a new command message

with the command byte left

* implied by the command byte that was received most recently. Some commands

don't have two-byte

* arguments, so this isn't a general method, but both the commands that this

class handles (note on and pitch

* bend) have two-byte arguments.

*

* If the ring buffer has size 2, this function empties the buffer temporarily,

but it will return the buffer

* to its original apparent state before returning (the buffer's internal state

may be changed).

*/

uint8_t BufferContainsImplicitCommand();

public:

/**

Page 52: ENGR 466 Final Report: Robot Drummer

P a g e | 48

* Retrieve the singleton instance of the Midi class.

*/

static Midi* GetInstance();

/**

* Register a command listener object. The object's appropriate method will be

called when the

* Midi class receives the object's corresponding command. For example, if a

note on message is

* received, then the note on listeners' DoNoteOn method is called (if at least

one

* MidiNoteOnListener object has been registered). Up to max_listeners

listeners of each command

* type can be registered.

*/

void RegisterListener(MidiListener* listener);

/**

* Build the command packet from the argument bytes and pass it on to the

command's listeners

* \param byte1 The first argument byte that was received.

* \param byte2 The second argument byte that was received.

*/

void NotifyListeners(uint8_t byte1, uint8_t byte2);

/**

* Read in a single byte from the serial buffer and store it. Once a 3-byte

MIDI message has been

* completely received, pass it on to the appropriate listener. This should be

called from the

* program's main loop.

*/

uint8_t CheckMidi();

;

/**

* Abstract class representing an object that can listen for a MIDI command. A class

derived from

* MidiListener can be registered with the Midi object as a command listener using

RegisterListener.

*/

class MidiListener

private:

MIDI_COMMAND command;

protected:

/**

* Constructor.

*/

MidiListener(MIDI_COMMAND cmd);

public:

/**

* Get the type of MIDI command that this object is listening to.

*/

MIDI_COMMAND GetCommandType();

/**

* Pass on a command to the listener. This should only be called by the Midi

class in response to

* a 3-byte command message being received. This function will call the

appropriate function in

* the concrete listener class (e.g. DoNoteOn, DoPitchBend).

*/

void ProcessCommand(midi_param_t* param);

;

/**

Page 53: ENGR 466 Final Report: Robot Drummer

P a g e | 49

* Abstract class representing an object that can listen for MIDI note on commands. A

class derived

* from MidiNoteOnListener can be registered with the Midi object as a listener for

note on commands.

*/

class MidiNoteOnListener : public MidiListener

protected:

/**

* Constructor.

*/

MidiNoteOnListener();

public:

/**

* This method is called by the Midi class when it receives a note on message.

* \param note The note byte that was sent as an argument with the MIDI note on

message (0x00 - 0x7F).

* \param velocity The velocity byte that was sent as an argument with the MIDI

note on message (0x00 - 0x7F).

*/

virtual void DoNoteOn(uint8_t note, uint8_t velocity) = 0;

;

/**

* Abstract class representing an object that can listen for MIDI pitch bend commands.

A class derived

* from MidiPitchBendListener can be registered with the Midi object as a listener for

pitch bend commands.

*/

class MidiPitchBendListener : public MidiListener

protected:

/**

* Constructor.

*/

MidiPitchBendListener();

public:

/**

* This method is called by the Midi class when it receives a pitch bend

message.

* \param pitch The pitch bend value that was sent as an argument with the MIDI

pitch bend message

* (0x0000 - 0x4000).

*/

virtual void DoPitchBend(int16_t pitch) = 0;

;

#endif

Page 54: ENGR 466 Final Report: Robot Drummer

P a g e | 50

/*

* midi.c

*

* Created on: 28-Jun-2009

* Author: nrqm

*/

#include "midi.h"

#include "../arduino/WProgram.h"

#include "../arduino/HardwareSerial.h"

#define IS_COMMAND_MSK 0x80 // if this bit is set, then the byte is a command

#define COMMAND_MSK 0xF0 // masks out the command nibble of the command byte

#define CHANNEL_MSK 0x0F // masks out the channel nibble of the command byte

#define NOTE_OFF_MSK 0x80

#define NOTE_ON_MSK 0x90

#define AFTERTOUCH_MSK 0xA0

#define CONTROLLER_MSK 0xB0

#define PATCH_CHNG_MSK 0xC0

#define CHAN_PRESS_MSK 0xD0

#define PITCH_BEND_MSK 0xE0

extern void print(const char* str);

Midi::Midi() :

last_command(NOTE_OFF_MSK)

uint8_t i;

uint8_t j;

for (i = 0; i < num_commands; i++)

for (j = 0; j < max_listeners; j++)

listeners[i][j] = NULL;

Serial.begin(31250);

Midi* Midi::GetInstance()

static Midi instance = Midi();

return &instance;

void Midi::RegisterListener(MidiListener* listener)

uint8_t i;

// num_commands is the number of unique MIDI commands that the protocol

defines; defined in midi.h

if (listener->GetCommandType() >= 0 && listener->GetCommandType() <

num_commands)

// Find a free space in the listener's command chain.

for (i = 0; i < max_listeners; i++)

if (this->listeners[listener->GetCommandType()][i] == NULL)

Page 55: ENGR 466 Final Report: Robot Drummer

P a g e | 51

break;

// Make sure that the space is really free (and not just the last space),

and set it to the listener object.

if (this->listeners[listener->GetCommandType()][i] == NULL)

this->listeners[listener->GetCommandType()][i] = listener;

uint8_t Midi::BufferContainsImplicitCommand()

uint8_t byte1 = 0;

uint8_t byte2 = 0;

uint8_t result = 0;

if (buffer.Size() != 2)

return 0;

byte1 = buffer.Remove();

byte2 = buffer.Remove();

if ((byte1 & IS_COMMAND_MSK) != 0 || (byte2 & IS_COMMAND_MSK) != 0)

// Then one of the bytes was a command, and we don't have a valid

argument.

result = 0;

else

// Then both the bytes are data bytes, sent without a command byte (i.e.

the last command,

// if it was NOTE_ON or PITCH_BEND, is implied).

result = 1;

buffer.Add(byte1);

buffer.Add(byte2);

return result;

void Midi::NotifyListeners(uint8_t byte1, uint8_t byte2)

midi_param_t param;

uint8_t i = 0;

switch (last_command)

case NOTE_ON_MSK:

param.note_on.note = byte1;

param.note_on.velocity = byte2;

for (i = 0; i < max_listeners; i++)

if (listeners[NOTE_ON][i] != NULL)

listeners[NOTE_ON][i]->ProcessCommand(&param);

break;

case PITCH_BEND_MSK:

param.pitch_bend.pitch = byte1; // put byte1 into the 16 bit int

so that it can be shifted

Page 56: ENGR 466 Final Report: Robot Drummer

P a g e | 52

param.pitch_bend.pitch = (param.pitch_bend.pitch << 7) | byte2;

// arguments are 7 bits!

for (i = 0; i < max_listeners; i++)

if (listeners[PITCH_BEND][i] != NULL)

listeners[PITCH_BEND][i]->ProcessCommand(&param);

break;

default:

break;

uint8_t Midi::CheckMidi()

uint8_t byte1 = 0;

uint8_t byte2 = 0;

uint8_t command_is_implicit = 0;

if (Serial.available() == 0)

return 0;

buffer.Add(Serial.read());

command_is_implicit = BufferContainsImplicitCommand();

if (!command_is_implicit && (buffer.Size() != 3 || (buffer.Peek() &

IS_COMMAND_MSK) == 0))

// If the buffer doesn't contain a complete implicit command, and the

head of the ring isn't an

// explicit command byte (or it is an explicit command byte and the ring

isn't full yet) then do nothing.

return 0;

// At this point the buffer probably contains a complete command message. If

the command byte is explicitly

// included in the message, then it has to be removed from the buffer before

the argument bytes can

// be accessed. If the command is implicit, then the argument bytes are at the

head of the ring.

if (!command_is_implicit)

last_command = buffer.Peek() & COMMAND_MSK; // extract the

command nibble without losing the byte

last_channel = buffer.Remove() & CHANNEL_MSK; // extract the channel

nibble and remove the byte

byte1 = buffer.Remove(); // remove the next

two bytes, which will be arguments

byte2 = buffer.Remove(); // buffer should

now be empty

// make sure that the argument bytes aren't commands (could happen if a command

with 0 or 1 parameter bytes

// is received)

if ((byte1 & IS_COMMAND_MSK) != 0 || (byte2 & IS_COMMAND_MSK) != 0)

Page 57: ENGR 466 Final Report: Robot Drummer

P a g e | 53

buffer.Add(byte1);

buffer.Add(byte2);

return 0;

NotifyListeners(byte1, byte2);

return 1;

MidiListener::MidiListener(MIDI_COMMAND cmd) :

command(cmd)

Midi::GetInstance()->RegisterListener(this);

MIDI_COMMAND MidiListener::GetCommandType()

return command;

void MidiListener::ProcessCommand(midi_param_t* param)

switch (command)

case NOTE_ON:

((MidiNoteOnListener*)this)->DoNoteOn(param->note_on.note, param-

>note_on.velocity);

break;

case PITCH_BEND:

((MidiPitchBendListener*)this)->DoPitchBend(param->pitch_bend.pitch);

break;

default:

break;

MidiNoteOnListener::MidiNoteOnListener() :

MidiListener(NOTE_ON)

MidiPitchBendListener::MidiPitchBendListener() :

MidiListener(PITCH_BEND)

Page 58: ENGR 466 Final Report: Robot Drummer

P a g e | 54

/*

* ThreeByteRing.h

*

* Created on: 29-Jun-2009

* Author: nrqm

*/

#ifndef THREEBYTERING_H_

#define THREEBYTERING_H_

#include <avr/io.h>

class ThreeByteRing

private:

uint8_t ring[3];

uint8_t size;

int8_t head_index;

int8_t tail_index;

public:

ThreeByteRing();

/**

* Add an integer to the ring buffer. If the buffer is full, then the oldest

byte is dropped from the

* buffer head.

*/

void Add(uint8_t value);

/**

* Remove the byte from the buffer head.

*/

uint8_t Remove();

/**

* Retrieve the byte at the buffer head without removing it.

*/

uint8_t Peek();

/**

* Get the number of bytes in the buffer (0 to 3).

*/

uint8_t Size();

;

#endif /* THREEBYTERING_H_ */

Page 59: ENGR 466 Final Report: Robot Drummer

P a g e | 55

/*

* ThreeByteRing.cpp

*

* Created on: 29-Jun-2009

* Author: nrqm

*/

#include "ThreeByteRing.h"

#include <avr/io.h>

ThreeByteRing::ThreeByteRing() :

size(0),

head_index(-1),

tail_index(-1)

void ThreeByteRing::Add(uint8_t value)

if (head_index == -1 || tail_index == -1)

// The ring is empty

head_index = 0;

tail_index = 0;

else

// Increment the tail. If it exceeds the bounds of the ring, wrap the

tail around to the front.

// If it hits the head, increment the head (and wrap it around if

necessary).

tail_index++;

if (tail_index == 3)

tail_index = 0;

if (tail_index == head_index)

head_index++;

if (head_index == 3)

head_index = 0;

ring[tail_index] = value;

// update the size

if (size == 3)

size = 1;

else

size++;

uint8_t ThreeByteRing::Remove()

uint8_t retval;

Page 60: ENGR 466 Final Report: Robot Drummer

P a g e | 56

if (head_index == -1 || tail_index == -1)

// The ring is empty. TODO: error?

return 0;

retval = ring[head_index];

if (head_index == tail_index)

// There was just one item in the ring, so removing it will leave an

empty ring.

head_index = -1;

tail_index = -1;

// Increment the head, and wrap it around if it exceeds the bounds of the ring.

head_index++;

if (head_index == 3)

head_index = 0;

// update the size

size--;

return retval;

uint8_t ThreeByteRing::Peek()

if (head_index == -1 || tail_index == -1)

// TODO: error?

return 0;

return ring[head_index];

uint8_t ThreeByteRing::Size()

return size;

Page 61: ENGR 466 Final Report: Robot Drummer

P a g e | 57

/*

* solenoid.h

*

* Created on: 8-Jul-2009

* Author: nrqm

*/

#ifndef SOLENOID_H_

#define SOLENOID_H_

#include "../midi/midi.h"

/**

* The type of drum that a solenoid is to strike. Currently only the main drum is

used.

*/

typedef enum _dt

MAIN=0,

BASS=1,

HI_HAT=2,

DRUM_TYPE;

/**

* Represents the solenoid that, when activated with a PWM signal, strikes a drum

(directly or with a drum stick).

*/

class Solenoid : public MidiNoteOnListener

private:

/// The type of drum this solenoid is to strike (bass, main, or hi-hat)

DRUM_TYPE type;

/// The maximum duty cycle that can be applied to the solenoid to avoid

damaging the drum.

uint8_t max_duty_cycle;

/// The current PWM duty cycle being applied to the solenoid.

uint8_t duty_cycle;

/// The Arduino pin on which the solenoid's PWM signal is output.

uint8_t pwm_pin;

/// The modulus (mod 3) of the note value (the parameter to DoNoteOn) to which

this solenoid responds.

/// For example, note % 3 == note_divisor ---> this solenoid is triggered.

uint8_t note_divisor;

/// Stores the timer value when a solenoid strike starts (in ms).

uint16_t strike_start_time;

/// The maximum number of ms that a strike can last (the actual strike time is

controlled by the pot).

/// Must be less than 0xFF.

const uint8_t strike_timeout;

/**

* Constructor.

* \param type The type of drum that this solenoid is to strike (bass, main, or

hi-hat). This determines

* the maximum duty cycle that can be applied to the solenoid, the

Arduino pins used for I/O, and

* the range of MIDI notes that the solenoid responds to.

*/

Solenoid(DRUM_TYPE type);

Page 62: ENGR 466 Final Report: Robot Drummer

P a g e | 58

protected:

/**

* Respond to a MIDI note on command by activating this solenoid to strike its

drum.

* \param note The note (0-127) that was sent over MIDI. This determines which

drum is to be struck.

* \param velocity The force (0-127) with which to strike the drum. This is

proportional to the PWM

* duty cycle that is sent to the solenoid.

*/

void DoNoteOn(uint8_t note, uint8_t velocity);

public:

/**

* Get the Solenoid instance for a type of drum.

*/

static Solenoid* GetInstance(DRUM_TYPE type);

/**

* Check to see if PWM needs to be turned off. This should be called from the

application main loop

* as often as possible.

*/

void CheckSolenoid();

;

#endif /* SOLENOID_H_ */

Page 63: ENGR 466 Final Report: Robot Drummer

P a g e | 59

/*

* solenoid.cpp

*

* Created on: 8-Jul-2009

* Author: nrqm

*/

#include "../arduino/WProgram.h"

#include "solenoid.h"

#define BASS_PWM_PIN 3 // PWM output pin for the bass drum.

#define MAIN_PWM_PIN 5 // PWM output pin for the main drum.

#define HIHAT_PWM_PIN 6 // PWM output pin for the hi-hat cymbal.

#define BASS_POT_PIN 0

#define MAIN_POT_PIN 2

#define HIHAT_POT_PIN 4

#define MAX_DUTY_CYCLE 0xFF

extern void print(const char* str);

extern uint16_t millis16();

Solenoid::Solenoid(DRUM_TYPE type) :

type(type),

duty_cycle(0),

strike_start_time(0),

strike_timeout(50)

switch (type)

case BASS:

max_duty_cycle = MAX_DUTY_CYCLE;

note_divisor=0;

pwm_pin = BASS_PWM_PIN;

break;

case MAIN:

max_duty_cycle = MAX_DUTY_CYCLE;

note_divisor=1;

pwm_pin = MAIN_PWM_PIN;

break;

case HI_HAT:

max_duty_cycle = MAX_DUTY_CYCLE;

note_divisor=2;

pwm_pin = HIHAT_PWM_PIN;

break;

default:

break;

pinMode(pwm_pin, OUTPUT);

analogWrite(pwm_pin, 0);

Solenoid* Solenoid::GetInstance(DRUM_TYPE type)

static Solenoid bass = Solenoid(BASS);

static Solenoid main = Solenoid(MAIN);

static Solenoid hihat = Solenoid(HI_HAT);

switch (type)

Page 64: ENGR 466 Final Report: Robot Drummer

P a g e | 60

case BASS:

return &bass;

case MAIN:

return &main;

case HI_HAT:

return &hihat;

default:

return NULL;

void Solenoid::DoNoteOn(uint8_t note, uint8_t velocity)

static const uint8_t scale_factor = 0xFF; // this multiplies the velocity

so that we can do precise integer division

uint16_t velocity_ratio = 0;

uint16_t scaled_duty_cycle = 0;

if (note % 3 != note_divisor)

// This solenoid isn't listening for the note, so ignore the message.

return;

if (velocity == 0)

// Some controllers send "note on" commands with velocity 0 in place of

of "note off" commands. We want to

// ignore those so as not to terminate the strike prematurely when the

note is release on such a device.

return;

// Calculate the duty cycle that corresponds to the given velocity, by taking

the ratio of the velocity

// to the maximum velocity, and multiplying that by the maximum duty cycle.

The operations are done on scaled

// 16-bit integers so that data aren't aliased by integer division.

velocity_ratio = (velocity * scale_factor) / 0x7F; // max velocity is

0x7F

scaled_duty_cycle = (velocity_ratio * max_duty_cycle);

duty_cycle = scaled_duty_cycle / scale_factor;

// start timer for timeout

strike_start_time = millis16();

analogWrite(pwm_pin, duty_cycle);

void Solenoid::CheckSolenoid()

// check to see if the PWM needs to be turned off.

if (duty_cycle != 0 && (millis16() - strike_start_time > strike_timeout))

duty_cycle = 0;

analogWrite(pwm_pin, duty_cycle);

Page 65: ENGR 466 Final Report: Robot Drummer

P a g e | 61

/*

* stepper.h

*

* Created on: 7-Jul-2009

* Author: nrqm

*/

#ifndef STEPPER_H_

#define STEPPER_H_

#include "avr/io.h"

#include "../arduino/WProgram.h"

#include "../midi/midi.h"

/**

* The stepper motor object.

*/

class Stepper : public MidiPitchBendListener

//private:

public:

/**

* The members of this enumeration have values matching the pin outputs for

each cycle.

*/

/*typedef enum _sts

CYCLE1=EN0 | L1,

CYCLE2=EN0 | L2,

CYCLE3=EN1 | L3,

CYCLE4=EN1 | L4,

STEPPER_STATE;*/

typedef enum _sts

CYCLE1=0,

CYCLE2=1,

CYCLE3=2,

CYCLE4=3,

STEPPER_STATE;

/// The cycle that the stepper currently has charged. There are four cycles to

a step.

STEPPER_STATE state;

/// The maximum number of positions that the stepper can traverse (positions

are one step apart).

uint8_t max_positions;

/// The drumstick's current position. 0 is the centre (default), max_positions

is the rightmost edge.

uint8_t current_position;

/// The position where the drumstick is supposed to be. If this differs from

current_position, the stepper turns.

uint8_t destination_position;

/// The Timer::Now value when the current stepper cycle started (if the stepper

is turning).

uint16_t current_cycle_start_time;

/// The pitch bend value that matches the current destination position.

int16_t current_pitch;

/**

* Constructor. This is a singleton, so get the instance using

Stepper::GetInstance.

*/

Stepper();

Page 66: ENGR 466 Final Report: Robot Drummer

P a g e | 62

/**

* Switch the stepper output to a new cycle. This doesn't enforce the state

diagram.

*/

void TransitionToState(STEPPER_STATE new_state);

/**

* Output the appropriate pattern for the current cycle to the output pins.

*/

void OutputCycleCode();

/**

* Traverse through the stepper state diagram such that the stepper moves a

step counterclockwise.

*/

void CycleCCW();

/**

* Traverse through the stepper state diagram such that the stepper moves a

step clockwise.

*/

void CycleCW();

protected:

/**

* Handle a MIDI pitch bend command. This is called through the MidiListener

base class when a new pitch

* bend message comes in.

*/

void DoPitchBend(int16_t new_pitch); // TODO: will this actually override

the public version?

public:

/**

* Retrieve the instance of this singleton class.

*/

static Stepper* GetInstance();

/**

* Check to see if the stepper state needs to be changed. This should be

called from the program's main loop

* as often as possible.

*/

void CheckStepper();

;

#endif /* STEPPER_H_ */

Page 67: ENGR 466 Final Report: Robot Drummer

P a g e | 63

/*

* stepper.cpp

*

* Created on: 7-Jul-2009

* Author: nrqm

*/

#include "../arduino/WProgram.h"

#include "stepper.h"

/*

* The Arduino pins on which output patterns are sent to the stepper motor driver

*/

#define EN0 14 // The documentation says to combine the enable lines

on one output

#define EN1 15 // pin, but I've had trouble doing that with other

3.3V boards.

#define L1 16

#define L2 17

#define L3 18

#define L4 19

#define CYCLE_DELAY_MS 20 // The delay between cycle transitions, in

milliseconds

#define PITCH_STEP 0x800 // The difference in pitch bend value between

positions

#define DEAD_ZONE 0x40

extern void print(const char* str);

extern uint16_t millis16();

Stepper::Stepper() :

MidiPitchBendListener(),

state(CYCLE1),

max_positions(4),

current_position(0),

destination_position(0),

current_cycle_start_time(0),

current_pitch(0x2000)

pinMode(EN0, OUTPUT);

pinMode(EN1, OUTPUT);

pinMode(L1, OUTPUT);

pinMode(L2, OUTPUT);

pinMode(L3, OUTPUT);

pinMode(L4, OUTPUT);

OutputCycleCode();

Stepper* Stepper::GetInstance()

static Stepper instance = Stepper();

return &instance;

void Stepper::TransitionToState(STEPPER_STATE new_state)

// Output the bit pattern for the new cycle to the stepper motor driver. The 6

least-significant bits

Page 68: ENGR 466 Final Report: Robot Drummer

P a g e | 64

// are set to 0 so that the new state can be masked in (the MSbs are left

unchanged).

state = new_state;

OutputCycleCode();

current_cycle_start_time = millis16();

void Stepper::OutputCycleCode()

switch (state)

case CYCLE1:

digitalWrite(EN0, HIGH);

digitalWrite(EN1, LOW);

digitalWrite(L1, HIGH);

digitalWrite(L2, LOW);

digitalWrite(L3, LOW);

digitalWrite(L4, LOW);

break;

case CYCLE2:

digitalWrite(EN0, HIGH);

digitalWrite(EN1, LOW);

digitalWrite(L1, LOW);

digitalWrite(L2, HIGH);

digitalWrite(L3, LOW);

digitalWrite(L4, LOW);

break;

case CYCLE3:

digitalWrite(EN0, LOW);

digitalWrite(EN1, HIGH);

digitalWrite(L1, LOW);

digitalWrite(L2, LOW);

digitalWrite(L3, HIGH);

digitalWrite(L4, LOW);

break;

case CYCLE4:

digitalWrite(EN0, LOW);

digitalWrite(EN1, HIGH);

digitalWrite(L1, LOW);

digitalWrite(L2, LOW);

digitalWrite(L3, LOW);

digitalWrite(L4, HIGH);

break;

void Stepper::CycleCCW()

switch (state)

case CYCLE4:

TransitionToState(CYCLE2);

break;

case CYCLE2:

TransitionToState(CYCLE3);

break;

case CYCLE3:

TransitionToState(CYCLE1);

break;

case CYCLE1:

TransitionToState(CYCLE4);

current_position--; // Step has completed.

break;

Page 69: ENGR 466 Final Report: Robot Drummer

P a g e | 65

default:

break;

void Stepper::CycleCW()

switch (state)

case CYCLE1:

TransitionToState(CYCLE3);

break;

case CYCLE3:

TransitionToState(CYCLE2);

break;

case CYCLE2:

TransitionToState(CYCLE4);

break;

case CYCLE4:

TransitionToState(CYCLE1);

current_position++; // Step has completed

break;

default:

break;

void Stepper::DoPitchBend(int16_t new_pitch)

int16_t old_pitch = current_pitch;

int8_t pitch_diff = 0;

if (new_pitch < current_pitch - DEAD_ZONE)

// move a step or more to the left

current_pitch -= ((current_pitch - new_pitch)/PITCH_STEP + 1) *

PITCH_STEP;

else if (new_pitch > current_pitch + PITCH_STEP + DEAD_ZONE)

// move a step or more to the right

// multiplying by PITCH_STEP/PITCH_STEP masks off some bits to make

current_pitch an integer

// multiple of PITCH_STEP (in this case 1 is not the multiplicative

identity!!!?!?!?!)

current_pitch += ((new_pitch - current_pitch) / PITCH_STEP) * PITCH_STEP;

else

return;

if (current_pitch < 0x2000)

// don't allow it to go left past the centre position.

current_pitch = 0x2000;

destination_position = 0;

return;

if (old_pitch != current_pitch)

// calculate the new position.

Page 70: ENGR 466 Final Report: Robot Drummer

P a g e | 66

pitch_diff = (current_pitch - old_pitch) / PITCH_STEP;

if (current_position + pitch_diff > max_positions)

// integer underflow case

destination_position = 0;

else

destination_position += pitch_diff;

void Stepper::CheckStepper()

uint16_t now = millis16();

if (now - current_cycle_start_time > CYCLE_DELAY_MS)

// If the cycle timer has expired, then move to the next cycle.

if (current_position < destination_position)

// Then the stick is to the left of its destination, and needs to

move clockwise.

CycleCW();

else if (current_position > destination_position)

// Then the stick is to the right of its destination, and needs to

move counterclockwise.

CycleCCW();

else

// It's all good, the stepper's already there!

Page 71: ENGR 466 Final Report: Robot Drummer

P a g e | 67

/*

* MidiInterface.cs

*

* Created on: 12-Aug-2009

* Author: nrqm

*/

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.IO.Ports;

using System.Threading;

namespace MidiInterface

public enum MidiCommand

NoteOn,

PitchBend,

/// <summary>

/// A class for sending MIDI commands to a serial port.

/// </summary>

public class Midi

//TODO: support writing MIDI data to a file?

private MidiCommand lastCommand;

private SerialPort port;

private Mutex serialMutex;

/// <summary>

/// Constructor.

/// </summary>

/// <param name="comPort">The COM port that is to be used as MIDI output (e.g.

"COM7")</param>

public Midi(string comPort)

lastCommand = MidiCommand.NoteOn;

port = new SerialPort(comPort, 31250, Parity.None, 8, StopBits.One);

serialMutex = new Mutex();

/// <summary>

/// Read the instantaneous contents of the serial receive buffer.

/// </summary>

/// <returns>A string containing the contents of the serial buffer. The

string is empty if there is

/// nothing in the buffer or the serial port is closed.</returns>

public string Receive()

string retval = "";

if (port.IsOpen)

serialMutex.WaitOne();

retval = port.ReadExisting();

serialMutex.ReleaseMutex();

return retval;

Page 72: ENGR 466 Final Report: Robot Drummer

P a g e | 68

/// <summary>

/// Send some data to the serial port. If the serial port is closed, nothing

happens.

/// </summary>

/// <param name="bytes">The sequence of bytes to send over the serial

port.</param>

/// <param name="len">The number of bytes in the array.</param>

public void Send(byte[] bytes, int len)

serialMutex.WaitOne();

if (port.IsOpen)

port.Write(bytes, 0, len);

serialMutex.ReleaseMutex();

/// <summary>

/// Send a MIDI note on command to the serial port.

/// </summary>

/// <param name="note">The note to send. (0 to 127)</param>

/// <param name="velocity">The velocity with which to strike the note. (0 to

127)</param>

public void SendNote(byte note, byte velocity)

int i = 0;

byte[] bytes = new byte[3];

if (lastCommand != MidiCommand.NoteOn)

bytes[i] = 0x90; // note on

lastCommand = MidiCommand.NoteOn;

i++;

bytes[i] = note;

i++;

bytes[i] = velocity;

i++;

Send(bytes, i);

/// <summary>

/// Get or set the last command that was sent. This controls whether an

explicit command byte is sent.

/// </summary>

public MidiCommand LastCommand

get

return lastCommand;

set

lastCommand = value;

/// <summary>

/// Get whether the MIDI interface is connected.

Page 73: ENGR 466 Final Report: Robot Drummer

P a g e | 69

/// </summary>

public bool IsConnected

get

return port.IsOpen;

/// <summary>

/// Disconnect the MIDI interface.

/// </summary>

public void Disconnect()

port.Close();

/// <summary>

/// Connect to the MIDI interface.

/// </summary>

public void Connect()

port.Open();