openFrameworks and Arduino
openFrameworks is a set of C++ libraries that provides an easy method of coding audio, video, and graphical components. openFrameworks provides mechanisms to easily connect serial devices and Arduinos to personal computers, making openFrameworks an invaluable tool for Arduino development and a useful next topic for discussion.
openFrameworks can be compared to interlocking plastic construction bricks in that using individual units does not require knowing how to make them. The libraries of openFrameworks are a lot like boxes of construction bricks, allowing creativity to flow without having to code from the ground up and always having a piece that will work. This is done by utilizing C++ object-oriented programming methods, which add abstraction and reusability. The advantage to openFrameworks in a development scene is that you can put together proofs of concept without having to do a lot of low-level coding. Working in openFrameworks also provides working code that can be used as a blueprint to migrate from when a final project goes into production and needs more optimizations.
Incorporating both openFrameworks and Arduino helps create a proof-of-concept environment for hardware and software interaction, which uses a development approach that “work fosters ideas”; an exploratory development style where ideas can be explored without waste. The key to this is reusability: not having to worry about permanently using a resource and having plenty components to play with. The combination of openFrameworks and Arduino is cross compatible on most systems.
The disadvantages to this setup are that it may not be production quality, optimized, reliable, or usable for the masses; things that are arguably less important than sharing and exploration in idea generation. The disadvantages are taken care of when moving away from the proof of concept to a prototype or putting the project into production. For developers, showing an idea is more impressive when that idea is something that can be fully manipulated. Physical models go a long way toward helping ideas to take life and can be easily created with clay, wood, 3D printing, or various other means. Adding openFrameworks and Arduinos to a physical model can, for example, help you create a new game controller design that can be used to play games.
Arduino and openFrameworks comprise a nice tool set to help breathe that extra life into an idea. With its simple code structure, designers, artists, it gives developers the ability to add buttons to make LEDs blink, create controllers to move virtual objects, and make systems that manipulate physical objects. Both Arduino and openFrameworks have vast online communities and a plethora of other documentation, making the knowledge to work and develop with these systems easily available. This chapter focuses on connecting the Arduino to computers via openFrameworks to expand the functionality of the Arduino.
Getting Started
To get started, make sure that the openFrameworks and Arduino software are set up and working, and also make sure there is a compatible Arduino board (e.g., an Uno, Leonardo or Nano) available. To download and install openFrameworks, go to www.openframeworks.cc and follow the setup instructions for your system. openFrameworks requires C++ and is built for integrated development environments (IDEs) such as Code::Blocks (www.codeblocks.org), Visual C++ (www.microsoft.com/express), and Xcode (http://developer.apple.com/xcode/).
The first four examples in this chapter (Listings 3-1 to 3-4) show how to set up serial communications. All the examples are written using Arduino 1.0.1 and openFrameworks version 0071 but have been tested with Arduino 1.5.1r2 and openFrameworks 0073.
Arduino Code
Listing 3-1 shows the code to set up the Arduino, connect to a push button on pin 8, and check if the button is pressed or released and report the change in this state to a serial connection using a character. The code also checks for an incoming character from the serial; a and s signify turning on and off an LED on pin 13, respectively. This passing of characters is important when developing code for openFrameworks to control the Arduino, thus making the Arduino a possible controller for a game, a sensor for a door, and so on.
Listing 3-1. Arduino Sketch That Sets Upthe Arduino
int button = 8 , ledPin = 13; // pin assignments: button on pin 8,LED on pin 13
boolean oldState = 0 , newState = 0; // state change variables
voidsetup() {
pinMode(button, INPUT); ////////////////////////////
pinMode(ledPin,OUTPUT); // set pin I/O types
Serial.begin(9600); // starts serial at baud rate 9600
} // end setup()
voidloop() {
newState = digitalRead(button); // save current button state
if(newState != oldState){ // test for change in button state
if (newState == true) // for button press, send the "h" to serial
Serial.print('h'),
if (newState == false) // for button release, send the "l" to serial
Serial.print('l'),
} // end if(state0 != state1)
oldState = newState; // save new state to old state for comparison
delay(40); // delay for bounce control
} // end void loop()
voidserialEvent() { // called upon incoming serial
switch (Serial.read()){ // determine if serial is one of the required inputs
case 'a': digitalWrite(ledPin, HIGH);
break; // for input of "a", turn on LED
case 's': digitalWrite(ledPin, LOW);
break; // for input of "s", turn off LED
} // end switch (Serial.read())
} // end serialEvent()
Note The serialEvent() function does not work with the Leonardo board. To convert for the Leonardo board change void serialEvent() to if (Serial.available() > 0) and move the loop ending bracket to below the ex void serialEvent() function.
Load Listing 3-1 and hook up the Arduino with a momentary push-button switch and a pull-down resistor hooked to pin 8. The LED set up on pin 13 is optional because the Arduino has one on the board. With the board set up as per Figure 3-1 and plugged in, start the serial monitor in the Arduino IDE and match the baud rate of 9600. When the button is pressed, it causes an h or an l character to be displayed for a high or low state change. Sending the Arduino an a or an s will turn on or off the LED.
Figure 3-1 . Arduino circuit for Listing 3-1
Listed following is a reference of Arduino serial functions and what they are used for. These functions reside in the predefined Serial object. To call any of the serial functions, use Serial. before the name, like so:
Serial.begin(9600);
openFrameworks Setup
With the Arduino code outputting and accepting input from the serial monitor, other programs can be developed using C, C++, Java, or any other computer language from scratch to connect the Arduino to the computer. Coding from scratch, however, can be a bit tedious for a proof-of-concept project. openFrameworks provides a nice prebuilt interface for programming serial with C++ and also adds many other useful tidbits for audio and graphics. The next set of examples will show how the openFrameworks libraries can be used to connect to the Arduino and the sketch from Listing 3-1.
To verify that openFrameworks is working properly and the library is compiled, make a copy of the empty example folder in the openFrameworks distribution; keep the folder in the same directory and rename it to ch3.
Note The examples are located in the openFrameworks examples directory. An empty example to start with is located in the apps/myApps subdirectory of the openFrameworks main directory.
Open the workspace for Code::Blocks or the VC++ project file in the renamed folder, and then compile. Two things should compile: emptyexample and libopenFrameworks. After both parts are compiled successfully, openFrameworks is ready for new code. Coding the examples in this chapter for openFrameworks applications is done in the Code::Blocks project file or the Microsoft Visual Studio solution (not the one named libopenFrameworks, but the one named after the project—e.g., emptyexample.workspace). The files for the examples in this chapter are available for download from www.apress.com/9781430239390 and are contained in three files: main.cpp, testapp.cpp,and testapp.h. These three files can replace the files with the same name that are in the renamed folder src directory.
Caution Moving and compiling projects outside of the openFrameworks apps/examples directory may cause dependencies issues. To solve this, point all the dependencies to the location of the openFrameworks main directory.
Connecting to the Arduino from openFrameworks
Listings 3-2 through 3-4 make up the three files to create and run a basic openFrameworks program to connect to an Arduino to send and receive data without having to use the serial monitor or console, while also providing extra computing power by allowing the Arduino to connect to a C++ program.
Listing 3-2. main.cpp
#include "ofMain.h" // include files
#include "testApp.h" // declarations for the testapp class
#include "ofAppGlutWindow.h" // for using OpenGL and creating windows
int main() {
ofAppGlutWindow window; // sets up an OpenGL window object
ofSetupOpenGL(&window,200,100, OF_WINDOW); //sets window size in pixels
ofRunApp(new testApp()); // create testapp object & enter program loop
} // end int main()
openFrameworks code is set up to be event driven and window based, the same as other graphical-interface programs. The main.cpp file contains the main() function, which is the entry point to the openFrameworks programs. The main() function sets parameters for the window, including the window size and window mode. It is rare to make many changes in main.cpp; most of the time the only thing that will change is the window size.
Listing 3-3. testapp.h
#include "ofMain.h"
class testApp : public ofBaseApp{
public:
void setup(); // for setting initial parameters
void update(); // code in this function is constantly run, events will interrupt
void draw(); // runs after update,this updates & creates the window objects
void mousePressed(int x, int y, int button); // on event function
bool SendSerialMessage; // signals that data needs to be sent
char ledcommand ; // hold what state the LED is in
char Returned; // hold returned char from Arduino
ofSerial serial; // this is the object to handle serial
};// end class testApp : public ofBaseApp
The testApp class inherits common functionality from the ofBaseApp class. This is where the function prototypes are created. Variables that will be used in many functions can be declared here. There is a set of functions that are called when events occur, such as mouse movement or using the keyboard. Note the line where you need to change COM4 to match your Arduino setup.
Listing 3-4. testapp.cpp
#include "testApp.h"
void testApp::setup(){
ofSetVerticalSync(true); // helps to smooth out object drawing
ofBackground(255,255,255); // set background color to an RGB value
serial.setup("COM7", 9600); // change "COM7" to match where the Arduino is
ledcommand = 's'; // set initial state of the LED
serial.writeByte(ledcommand); // tell Arduino of the initial state
SendSerialMessage = false; // nothing to send yet
} // end void testApp::setup()
void testApp::update(){
if (SendSerialMessage) // is there serial information that needs to be sent
serial.writeByte(ledcommand); // tell the Arduino to change LED state
if (serial.available()) // check to see if there is incoming data
Returned = serial.readByte(); // save the incoming data
SendSerialMessage = false; // reset the need to send data to the Arduino
}//end testApp::update
void testApp::draw(){ // defines placement and draws objects in the window
ofFill(); // fills geometry with a solid color
if (Returned == 'h') // is the button on the Arduino being pressed
ofSetColor(0,0,255); // set the first circle color to full blue
else // the button is not pressed or the state is not known
ofSetColor(0,0,127); // set the first circle color to 1/2 blue
ofCircle(50,50, 50); // draw the first circle at last set color
if (ledcommand == 'a') // should the LED be on
ofSetColor(0,255,0); // set color to full green for the second circle
else // LED should be off or not known
ofSetColor(0,127,0); // set color to 1/2 green for the second circle
ofCircle(150,50, 50); // draw the second circle at last set color
} //end void testApp::draw()
void testApp::mousePressed(int x, int y, int button){
SendSerialMessage = true; // inform update function that there is data to send
if(ledcommand == 'a') // if the LED is ON
ledcommand = 's'; // change LED to be OFF
else // if the LED is OFF
ledcommand = 'a'; // change LED to be ON
} //end testApp::mousePressed
Make sure that the Arduino that was set up in Listing 3-1 is plugged into the computer and take note of the port that it is plugged into.
Change the serial.setup(COM4,9600) line in Listing 3-4 to match the Arduino’s connecting point. Once the test app is set to know where the Arduino is, compile the examples. Running the program will open a window frame that looks like Figure 3-2, with the first circle representing the push button and the second circle showing the state of the LED. To change the LED state, click in the window with the mouse.
Figure 3-2 . Example of the running program
openFrameworks Serial Functions
The following reference list is for the openFrameworks serial functions. Most of the functions work just like the Arduino’s counterpart functions. The serial object must be declared before using and calling the openFrameworks serial functions. The serial object comes from the ofSerial class; just as a variable is declared, a serial object is created by using the following:
ofSerial serial;
To use any of the functions, use the name declared for the object—for example, serial.setup();. Here are the functions:
Coding Once Using Firmata and ofArduino
In keeping with the spirit of “work fosters ideas,” working with two different pieces of code (one on the Arduino and one using openFrameworks) is a bit inefficient for exploring ideas, especially when changing things frequently. Luckily, there are items included with the Arduino IDE and openFrameworks (a program for the Arduino and a built-in class for openFrameworks) that make it possible to write single programs that take care of having to separately code the Arduino.
Set up the Arduino board with the components connected as in the schematic in Figure 3-3, and then upload the Standard Firmata sketch. The sketch is located in the Arduino IDE under File Examples Firmata StandardFirmata.
Figure 3-3 . Arduino circuit for Listing 3-5
To verify that Firmata is working on the Arduino, download and run the test app from www.firmata.org/. Select the port to connect to from the drop-down menu, and the app will show all the pins, which have drop-down boxes for pin configuration and buttons for output values, as shown in Figure 3-4.
Note The Leonardo need the Firmata library updated. Instructions and updated library available at www.github.com/soundanalogous/Breakout/wiki/Updating-Firmata-in-Arduino
Figure 3-4 . Firmata testing application
The Firmata test app is especially usefully for testing out component setups that use multiple pins, such as a three- to eight-line MUXs, seven-segment displays , keypads, and servomotors.
Controlling the Arduino with openFrameworks
The code in the next example uses the same main.cpp as Listing 3-2. The header file testapp.h in Listing 3-5 still declares the class function prototypessetup(),update(),draw(), and mousePressed(). The two new function prototypes are set up to mimic the Arduino’s coding structure. The function arduinoSetup() is for initializing pin configurations, and the function arduinoLoop() is the equivalent to loop in Arduino sketches.
Listing 3-5. testapp.hfor the Standard Firmata Sketch Communication
#include "ofMain.h"
#include "ofEvents.h"
class testApp : public ofBaseApp {
public:
void setup();
void update();
void draw();
void mousePressed(int x, int y, int button);
void arduinoSetup(const int & version); // Arduino equivalent setup function
void arduinoLoop(); // Arduino-equivalent loop function
bool ledcommand;
bool pin13; // pin13 data container
bool pin8; // pin8 data container
float analogPin0; // pin8 data container
bool isArduinoSet; // flag to know when Arduino is connected and configured
ofArduino arduino; // the Arduino object
}; // end class testApp : public ofBaseApp
In testapp.cpp of Listing 3-6, the functions arduinoSetup() and arduinoLoop() perform the same functions of an Arduino sketch with openFrameworks on top of the Arduino-style functions. Firmata and the openFrameworks ofArduino class make the serial communication less apparent. By carefully mimicking the same structure as an Arduino sketch, the conversion to an actual Arduino sketch is made simpler if the conversion becomes necessary, as when moving to a more professional setup. Keep in mind it is possible to develop code in openFrameworks that may require more space and computing power than might be available on the Arduino. This is especially important to remember when using Firmata as a tool in making proofs of concept to eventually be used solely on the Arduino.
Note Firmata is capable of using I2C and other communication functionality; however, openFrameworks does not currently support I2C functionality (as of version 0071).
Example 3-6. testapp.cppfor Standard Firmata Communication
#include "testApp.h"
void testApp::setup() {
arduino.connect("COM7"); // remember! change this to the proper port
ofAddListener(arduino.EInitialized, this, &testApp::arduinoSetup);
/*the ofAddListener waits for the Arduino to perform a handshake telling the program that it is ready to be configured and set up. This will call arduinoSetup*/
isArduinoSet = false; // this flag is set false until the Arduino is set up
} // end void testApp::setup()
void testApp::update() {
testApp::arduinoLoop();// perform the Arduino-style code
} // end void testApp::update()
void testApp::draw() { // objects are drawn to the screen in the order called
if (isArduinoSet){ // do not run this code until Arduino is operating
ofFill();
if(pin8 == ARD_HIGH)
ofSetColor(0,0,255);// if button on pin8 pressed, brighten the circle
else
ofSetColor(0,0,127);// blue is dim if button is released
ofCircle(50,50,50); // draw circle at (x,y,radius) in pixels for button
if(pin13 == ARD_HIGH)
ofSetColor(0,255,0); // when LED is on, draw full green
else
ofSetColor(0,127,0);// green is dimmed when LED is off
ofCircle(150,50, 50); // draw circle at (x,y,radius) in pixels for LED
ofSetColor(255,0,0); // set color for analog potentiometer
// draw rectangle with corners at (x1,y1,x2,y2)
ofRect(0, 45 ,(analogPin0*200) , 10); // rectangle is dynamic on the x-axis
// analogPin0 is a percentage multiplied by window width
} // end if (isArduinoSet)
}// end void testApp::draw()
void testApp::mousePressed(int x, int y, int button) {
if(ledcommand == true) // if LED is ON
ledcommand = false ; // flag the LED to turn OFF
else // the LED is OFF
ledcommand = true; // flag the LED to turn ON
}// end testApp::mousePressed
void testApp::arduinoSetup(const int & version) {
ofRemoveListener(arduino.EInitialized, this, &testApp::arduinoSetup);
// there is no need to continue to listen for the Arduino, so clear memory
arduino.sendAnalogPinReporting(0, ARD_ANALOG);// turn on analog pin0
arduino.sendDigitalPinMode(8, ARD_INPUT);// set digital pin8 as input
arduino.sendDigitalPinMode(13, ARD_OUTPUT);// set digital pin13 as output
isArduinoSet = true;// inform the rest of the program that Arduino is ready
}//end void testApp::arduinoSetup(
void testApp::arduinoLoop() {
// do not run this code until Arduino is operating
if (isArduinoSet){
pin8 = arduino.getDigital(8);// digital read pin8
pin13 = arduino.getDigital(13);// digital read pin13 verifying state
analogPin0 = arduino.getAnalog(0)/1023.0; // analog read A0
arduino.sendDigital(13, ledcommand);// digital write new state
}// end if (isArduinoSet)
arduino.update();// get any changes that the Arduino might have
}// end void testApp::arduinoLoop()
When done looking over and compiling the code, plug in the Arduino with the components set up in as Figure 3-3 and the standard Firmata sketch uploaded. When running, the program will open a window with the same size as the prior example. The program will also have the same two circles representing the button and LED, respectively performing the same functions. A red bar is added to the program that will go from side to side, representing the full sweep of the potentiometer.
Note The Arduino may be required to reset, via the reset button, before the listener initializes and recognizes the Arduino. The listener is built into openFrameworks to listen for an Arduino on the connection.
Key Constants Used by ofArduino
ofArduino defines some useful constants for more readable code. The following list is a reference of names and values of the constants. The first part of the constants, ARD, is short for Arduino, and is a reminder that this is dealing with the hardware. The second part is the type—for example, the pin modes or state declarations.
ofArduino Reference of Class Functions
The following list is a reference for the class functions that make up the ofArduino class. The functions that are included in the ofArduino class are used to control and connect to Arduinos that have the standard Firmata sketch loaded. Most of the functions are a direct counterpart of the functions used in the Arduino IDE and work the same way; for example, sendDigital() is the same as digitalWrite(). The functions require an ofArduino object declared before they can be used. You can connect multiple Arduinos to the same computer by declaring separate objects for each Arduino.
Expanding on the Idea
We now have openFrameworks controlling the Arduino, which is running the standard Firmata sketch. The next example illustrates the increase of efficiency that can be gained in development by having Arduino and openFrameworks integrated.
Figure 3-5 . Arduino circuit for the “Expanding on the Idea” example
For the rest of this example, the only changes are going to be to the code.
For the hardware, the potentiometer is going to control the servo, while the brightness of the LED on pin 3 will represent the position of the servo. When the button is pressed, the LED on pin 13 will turn on; at the same time, the servo and the other LED will pause and stop accepting new values from the potentiometer until the button is released.
While openFrameworks is controlling the Arduino, it will simultaneously be displaying a representation of what the hardware is doing. The program will have a window the same size as the two prior examples, with shapes representing the button, the LED, and the potentiometer’s position. The only change to the graphics is that we will dynamically change the color of the bar to represent the new brightness value for the LED, with the color fading from black to full red with the servo and potentiometer’s full swing.
Open the project from Listings 3-5 and 3-6. The same main.cpp will be used without alteration. Within testapp.cpp, the entire mousePressed() function can be removed or commented out, along with its prototype in testapp.h. You can omit the following line from the arduinoLoop() function:
arduino.sendDigital(13, ledcommand);
The last thing to comment out is the variable declaration bool ledcommand; from testapp.h. With the code that is no longer needed out of the way, change the line ofSetColor(255,0,0);, located in the draw() function, to
ofSetColor((analogPin0*255),0,0);
This change takes advantage of the analog percent value to evenly change the color in proportion to the bar.
Add the following code to the arduinoSetup() function below the code line arduino.sendDigitalPinMode(13, ARD_OUTPUT); defining the new componets. Note that the text following the comment delimiters (//) is optional.
arduino.sendDigitalPinMode(3, ARD_PWM); // set pin 3 for PWM
arduino.sendDigitalPinMode(10, ARD_SERVO);// set pin 10 to accept a servo
arduino.sendServoAttach(10);// define servo information as default
isArduinoSet = true;
Listing 3-7 shows the next portion of code to add, which is the last for this example. The code handles the button pause, the servo, and the PWM LED, and gets inserted into the arduinoLoop() function before the ending bracket of the if (isArduinoSet) statement and after analogPin0 = arduino.getAnalog(0)/1023.0;.
Listing 3-7. The End of “Expanding on the Idea” Example
if (pin8 == ARD_HIGH){ // check if button is being pressed
pin13 = true; // flag the draw function to change
arduino.sendDigital(13, ARD_HIGH);// turn on LED
} // end if pin8 == ARD_HIGH)
else {
arduino.sendDigital(13, ARD_LOW);
arduino.sendPwm(3,analogPin0*255);
arduino.sendServo(10, analogPin0*180);
} // end else
With all the code changed and compiled, start the program with the Arduino plugged in. The program should look like Figure 3-6. When the screen is fully drawn, the pause button will have to be pressed to get the servo and the LED to activate.
Figure 3-6 . The look of the final example
The changes that were made make the Arduino act differently without your having to upload a new sketch. Note that the changes now allow the potentiometer to control the sweep of the servo and the brightness of the LED when the potentiometer is swept from maximum to minimum. Also take note of the behavior when the pause is held while the potentiometer is being moved.
openFrameworks has a lot more functionality than described in this chapter. openFrameworks can manipulate sound images or even 3D objects, and the connection to serial devices or Arduinos allows controllers to be built that can control the computer. You can create programs to control the Arduino as well. With all the possible projects that you can create using Arduinos, the extra features that openFrameworks provides may make it hard to decide where to go and what to do. Try out some of the ideas listed below; they came up during a few brainstorming sessions. These ideas should help further your exploration of openFrameworks while providing more experience with the platform.
Summary
This chapter discussed the fundamental steps to integrate openFrameworks with Arduino. As a development tool, openFrameworks may provide the catalyst that can take a good idea to the next step. Its versatility is only increased by providing two great ways to develop: using serial or Firmata. With time and use, most developers will find a preference for one method over the other.
openFrameworks has a lot more functionality than can be covered here, but this chapter should provide you the knowledge and confidence to delve deeper into openFrameworks. Also check out other resources available; the forums at www.arduino.cc/ and www.openframeworks.cc/ are great places to find more information. The examples included with openFrameworks can also provide excellent insight.