Skip to content

Project management, & IDEs revisited

11 November, 2012

Since writing ‘Setting up your Teensy development environment’ I’ve developed some better ways of handling all the source code that a Teensy-based hardware project can produce.

1 – Separate your source code into different files.

This is my workbench testing box, as of this morning. It is a combination of the Gear Lights tutorial, yesterday’s guide to using encoders, and a general-purpose radio tuner called OmniTune, all driven by one Teensy++ 2.0 board and making use of less than half of its pins.

And this is the source code in the main sketch file, TestBox.ino:

#include 
#include 
#include 

#include "EncoderLCD.h"
#include "OmniTune.h"
#include "DialController.h"
#include "GearLights.h"

void setup() {
  setupEncoderLCD();
  setupGearLights();
}

void loop() {
  FlightSim.update();

  loopEncoderLCD();
  loopGearLights();
}

All the work has been split up into four header files, making the code much more manageable. These are the files:

GearLights, more or less exactly the code from the last Gear Panel tutorial. It lights the six gear lights based on X-Plane’s state and moves the gear handle according to the switch position.

EncoderLCD, which handles input and output using the LCD and encoders. It parses the input through the encoders, and passes it to either OmniTune or Dial Controller, depending on which is selected. Also it calls the ‘update display’ function of the selected module.

OmniTune, the all-purpose radio tuner, where you select a radio channel, see its frequency on the screen, and alter it with the encoders.

Dial Controller, based on ‘Using encoders with X-Plane‘ article, using two encoders (one coarse, one fine) for easy and precise control of various dials in the cockpit.

To turn GearLights into a header, this was remarkably easy.

  • copy GearLights.ino to the TestBox project folder
  • rename setup() and loop() to setupGearLights() and loopGearLights(),
  • rename GearLights.ino to GearLights.h
  • #include it in the TestBox.ino source file
  • call setupGearLights() and loopGearLights() from TestBox.ino’s setup() and loop() functions.

Voila! GearLights is in my TestBox project, but out of the way. I could hammer away at the encoder-wrangling bits without needing to scroll through the ‘frozen’ GearLights code, and then use its pretty lights as a sanity check when testing, to help me realise I’d forgotten about FlightSim.update() again.

It was a bit more effort to separate the input-parsing code from my old OmniTune project, and move it into EncoderLCD.h. I then needed to alter the dial-controlling code to use the same interface as OmniTune. The end result is I have four source files, each with limited to only doing one or two things, and a clear interface between them to add new modes and capabilities to the display (an etch-a-sketch mode comes to mind!). Furthermore, in the OmniTune and Dial Controller files, the input-handling and data-displaying bits are completely separate functions. It’s good to separate stuff out like this – I could replace the LCD-drawing code with 8-segment LEDs and not need to change the input-handling code at all, for example.

I have no idea if this is  a ‘good’ way of separating a project out. All the code is in the global namespace, and it’s necessary to #include the files in the right order, and there’s probably a much smarter way of doing this using classes or something. (I’d be delighted to hear suggestions and criticism!)  But it works, and it’s a sight better than the One Monolithic Source Code File approach.

The source code is available from my GitHub page for the curious.

One caveat: the Arduino IDE does some truly horrible things to your source code when you press ‘compile’. You can’t include Arduino library files (Encoder.h, LiquidCrystalFast.h etc) from any file except your root .ino file – that’s why they’re polluting TestBox.ino, which you might notice hasn’t got any encoders or LCDs. The conventional way of doing things is to include Encoder.h from the file that uses encoders, but there are philosophical reasons why the Arduino IDE won’t let you do that.

2. Use a sane IDE to develop your code

I’ve touched on this before, but I’ve learned a lot more since the dark old days of two weeks ago. The Arduino IDE is geared for beginners, and fills that role very well – it doesn’t do much that Windows Notepad doesn’t do. But it gets very cumbersome very rapidly as projects become complex. That’s why I use Qt Creator to write my code, and only use the Arduino IDE to compile it.

Before, I spouted some nonsense about copy-pasting from a generic Qt project into the Arduino IDE. There must have been something funny in the water that week. A much better way is to have a Qt project for each Arduino project, containing all the source files, with all the Arduino libraries included in the Qt .pro file. This way, you edit your code in Qt, hit ‘save’, switch to Arduino IDE, hit ‘upload’, switch to X-Plane, and test.

This is the project view for my TestBox project:

Snapshot of my project folder in Qt Creator

All the source code (one .ino file, and indefinite headers) are in the project as  ‘Headers’. (It doesn’t seem to like having source files with an .ino extension). There are still many INCLUDEPATHs for all the Arduino libraries, but I’ve added the usb_flightsim/usb_api.h file from the bowels of the Arduino directory to the .pro file as a header, too. This means we just need to #include "usb_api.h" in a file to get proper syntax highlighting for the FlightSimObjects in Qt Creator, and we don’t need to remove that line before compiling. Unlike the very long and awkward #include I recommended last week, this one doesn’t make the Arduino IDE choke.

If you set the Arduino IDE to ‘use external editor’, it will automatically reload your source code whenever you save it in Qt. Write your code in the comfort and elegance of Qt’s editing environment, hit ‘save’, switch over to Arduino, press ‘upload’, and hope you don’t get an error code!

My current favourite Qt Creator feature is how holding down Ctrl makes your code into links. Ctrl-click on a function call and it jumps you to its definition. Ctrl-click on a filename in an #include statement and it opens that file. It makes for very fluid navigation of your code, especially if you’ve split your project into multiple files.

The .pro file is on GitHub with the rest of my TestBox project, but for completeness I’ll copy the contents here. You should see a ‘show source’ link here. Look for the <> icon on the mouseover menu; it lets you copy source code from WordPress pages very easily. It should be obvious how to alter this for your own use!

HEADERS += \
    GearLights.h \
    OmniTune.h \
    EncoderLCD.h \
    DialController.h \
    TestBox.ino

INCLUDEPATH += \
../../xteensy/SimObjectsDev/ \
../_arduino-1.0.1/libraries/AccelStepper \
../_arduino-1.0.1/libraries/AltSoftSerial \
../_arduino-1.0.1/libraries/ArdOSC \
../_arduino-1.0.1/libraries/Bounce \
../_arduino-1.0.1/libraries/CapSense \
../_arduino-1.0.1/libraries/DmxSimple \
../_arduino-1.0.1/libraries/DogLcd \
../_arduino-1.0.1/libraries/EEPROM \
../_arduino-1.0.1/libraries/Encoder \
../_arduino-1.0.1/libraries/Ethernet \
../_arduino-1.0.1/libraries/Firmata \
../_arduino-1.0.1/libraries/FlexiTimer2 \
../_arduino-1.0.1/libraries/FreqCount \
../_arduino-1.0.1/libraries/FreqMeasure \
../_arduino-1.0.1/libraries/FrequencyTimer2 \
../_arduino-1.0.1/libraries/IRremote \
../_arduino-1.0.1/libraries/Keypad \
../_arduino-1.0.1/libraries/ks0108 \
../_arduino-1.0.1/libraries/LedControl \
../_arduino-1.0.1/libraries/LedDisplay \
../_arduino-1.0.1/libraries/LiquidCrystal \
../_arduino-1.0.1/libraries/LiquidCrystalFast \
../_arduino-1.0.1/libraries/list.txt \
../_arduino-1.0.1/libraries/LowPower \
../_arduino-1.0.1/libraries/Metro \
../_arduino-1.0.1/libraries/MIDI \
../_arduino-1.0.1/libraries/MsTimer2 \
../_arduino-1.0.1/libraries/NewSoftSerial \
../_arduino-1.0.1/libraries/OneWire \
../_arduino-1.0.1/libraries/Ping \
../_arduino-1.0.1/libraries/PS2Keyboard \
../_arduino-1.0.1/libraries/PWMServo \
../_arduino-1.0.1/libraries/SD \
../_arduino-1.0.1/libraries/Servo \
../_arduino-1.0.1/libraries/ShiftPWM \
../_arduino-1.0.1/libraries/SoftPWM \
../_arduino-1.0.1/libraries/SoftwareSerial \
../_arduino-1.0.1/libraries/SPI \
../_arduino-1.0.1/libraries/ST7565 \
../_arduino-1.0.1/libraries/Stepper \
../_arduino-1.0.1/libraries/TimerOne \
../_arduino-1.0.1/libraries/TinyGPS \
../_arduino-1.0.1/libraries/TimerThree \
../_arduino-1.0.1/libraries/Tlc5940 \
../_arduino-1.0.1/libraries/VirtualWire \
../_arduino-1.0.1/libraries/Wire \
../_arduino-1.0.1/libraries/x10 \
../_arduino-1.0.1/libraries/XBee \

HEADERS += \
../_arduino-1.0.1/hardware/teensy/cores/usb_flightsim/usb_api.h

SOURCES +=

From → Guides

2 Comments
  1. Andrew Powell permalink

    Hi, on the breadboard image showing the components for the test box there are two components next to the pots that are simply wired to ground and a pin on the teensy. Can you identify what these two components are?

    • Hi Andrew,

      I can’t find the diagram, but I’m pretty sure those are push-to-make switches next to rotary encoders. The reality is I’m using rotary encoders with integrated switches (you click down on the shaft to activate the switch) but Fritzing didn’t have a component for this.

Leave a reply to Andrew Powell Cancel reply