Contributors

Overview

Writing software is like gardening - the flowers are beautiful but you're going to spend a lot of time weeding! Except instead of weeding, its keeping up to date with new frameworks, operating systems and dependancies. It's a ton of work! So why not make it easy on your self with automation?

In this guide we'll go through how to use GitHub (a free service for storing source code) + Doxygen ( a free documentation standard/parser) + TravisCI.org (a free service for continuous integration/testing) To automatically document and test your code.

In specific, we'll be showing how to automate wide-range compilation tests for an Arduino library to make sure that updates to the Arduino IDE, language, and board support packages don't end up breaking builds.

In general, the same steps can be used for any codebase, including every-day source code projects - Arduino and otherwise! (But, it's really good for Arduino because we have scripts that do a lot of work for you already written)

This guide doesn't teach you how to write code, or how to write a library. But it will show you how to make your existing libraries live long fruitful lives as they age!

Hey, there's paid services that do this for you!

Yep, this guide is how to do it using only free services for open-source software

Hey, there's other *better* documentation systems and CI services!

Yep, this guide is how to do it using only the techniques I know how to use

Hey, you can do this yourself using hand-built artisnal servers and crontabs!

Yep, this guide is how to do it without having to host anything on your own server

Repository

Overall Structure

In this guide we'll watch as I transform Adafruit_TSL2561, one of our first libraries, spruce it up and automate it. This guide doesn't talk about how to write code - just how to make it fancy!

We'll be using the 'classic' Arduino library structure. In this case, we make a new repo, with no spaces or dashes, on GitHub: Adafruit_TSL2561 and git clone it into our ArduinoSketches/libraries folder. This makes it easy to test in Arduino and commit in the same folder

Inside the folder you'll need these 4 parts:

Adafruit_TSL2561.cpp - the main cpp file that contains your code

Adafruit_TSL2561.h - the main header file that has macros, enums, classes, etc!

When it renders for now it will say Build Unknown - we'll get that going later!

Create Empty gh-pages Branch

Rather than storing the documentation in your master branch, cluttering your repo, we'll have TravisCI put the files into the gh-pages branch. GitHub can be informed that documentation is there and it will host the webpages for you, it's very slick!

Make sure you've committed and pushed all your code before you continue, since you will be deleting from a branch and it can be scary!

Optional (For Adafruit Only!) Add Adafruit Librarians group

Doxygen

About Doxygen

Doxygen is a somewhat ancient but popular method for creating documentation for C and C++ project (it also supports other languages but its great at C/C++). What's great about Doxygen is that the documentation lives with the source code in the same file so you are less likely to get out of sync

Generate documentation from source code

Doxygen is the de facto standard tool for generating documentation from annotated C++ sources, but it also supports other popular programming languages such as C, Objective-C, C#, PHP, Java, Python, IDL (Corba, Microsoft, and UNO/OpenOffice flavors), Fortran, VHDL, Tcl, and to some extent D.

Doxygen can help you in three ways:

It can generate an on-line documentation browser (in HTML) and/or an off-line reference manual (in ) from a set of documented source files. There is also support for generating output in RTF (MS-Word), PostScript, hyperlinked PDF, compressed HTML, and Unix man pages. The documentation is extracted directly from the sources, which makes it much easier to keep the documentation consistent with the source code.

You can configure doxygen to extract the code structure from undocumented source files. This is very useful to quickly find your way in large source distributions. Doxygen can also visualize the relations between the various elements by means of include dependency graphs, inheritance diagrams, and collaboration diagrams, which are all generated automatically.

You can also use doxygen for creating normal documentation (as I did for the doxygen user manual and web-site).

Doxygen is developed under Mac OS X and Linux, but is set-up to be highly portable. As a result, it runs on most other Unix flavors as well. Furthermore, executables for Windows are available.

Doxygen runs on the source code itself, and create HTML output that we can then point people to when they want detailed information about the library.

To keep our lives easier, instead of running Doxygen every commit, we'll use Travis CI to keep the documentation up to date. Every commit on GitHub will trigger TravisCI to call Doxygen on the source code, verify that documentation exists for each function, and will refresh the website pages.

However, to get started, we'll want to run Doxygen locally so we can make sure our files are in good shape before committing them (otherwise we'll have to run Travis over and over until we get it right, which takes a lot longer than fixing it all locally)

Step 1) Install Doxygen

Doxygen runs in the command line, and is available for Mac, Win and Linux. On Mac/Linux we suggest using brew or apt-get or whatever package manager you have

For Windows, visit the downloads page and scroll down to download the installer, then run it to install. Or if you have something like MSYS2, use the package manager.

In this guide we'll be using version 1.8.13 - if its something much older you may get errors

Step 2) Grab our Doxyfile

Doxygen needs a 'setup' file called Doxyfile. There's two ways to go about this step.

If you are not going to be using our Travis CI auto-doxy-runner or if you have needs for a custom Doxyfile you should run doxygen -g from within the library folder, which will create a Doxyfile. Then edit it to your hearts desire and commit it to your GitHub repo

However, if you are planning to use TravisCI to auto-generate Doxygen and you don't have any big changes you want to make to our setup we recommend using our default Doxyfile, and not committing it. Our TravisCI will automatically grab the Doxyfile for you, which means we can update it from time to time as doxygen updates and changes the config file. This is what we'll assume you're doing and continue as-is!

OK its a little annoying to do but eventually you'll get to zero output from Doxygen. Actually, because my compiled doxygen binary didn't come with CLANG support, I do get two warnings, but you can ignore these.

Preview Documentation

OK now you can look in the folder and you'll see that in addition to the Doxyfile, you also have a sqlite3.db and an html folder.

Don't commit these to your git repo! Instead, open up the html folder and double-click the index.html

You will now be able to browse the documentation. You can see the nice front page you set up in the .cpp file

Click the Classes button

And you can locate your object, click on that...

Now you can see the details of your class documentation! Check it over for typos, corrections, or other stuff you want to add. Re-run doxygen as desired until you get it to a good spot

Commit Documented Code

You are very nearly done!

Commit and push your updated code to GitHub

Do not commit the Doxygen file (unless you changed it), the html folder, or the sqlite db. Only your fully updated and documented files!

OK you are ready for the next, amazing step...automation!

Doxygen Tips

Main .cpp File Header

This fancy file header from KTown has multiple @ tags that will make a very nice 'front page' for your documentation and contains useful information! Copy and paste the whole chunk, then edit as necessary:

@file - change this to the name of the file the header is at the top of!

@mainpage - This will be the name of the documentation page, so make it short but descriptive

@section intro_sec Introduction - Your intro. Longer than the header

@section dependencies Dependencies - What other libraries or hardware are required

Main .h File Header

/*!
* @file Adafruit_FXOS8700.h
*
* This is part of Adafruit's FXOS8700 driver for the Arduino platform. It is
* designed specifically to work with the Adafruit FXOS8700 breakout:
* https://www.adafruit.com/products/3463
*
* These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
* to interface with the breakout.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* Written by Kevin "KTOWN" Townsend for Adafruit Industries.
*
* BSD license, all text here must be included in any redistribution.
*
*/

/*!
* @file Adafruit_FXOS8700.h
*
* This is part of Adafruit's FXOS8700 driver for the Arduino platform. It is
* designed specifically to work with the Adafruit FXOS8700 breakout:
* https://www.adafruit.com/products/3463
*
* These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
* to interface with the breakout.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* Written by Kevin "KTOWN" Townsend for Adafruit Industries.
*
* BSD license, all text here must be included in any redistribution.
*
*/

If you don't see any global variables, enums or typdefs in your documentation, you probably forgot to add the @file tag to your file. Without this tag, it won't parse global values properly.

Documenting Functions

The most work will be in documenting functions. Here's another lovely example from KTown we can reference:

/**************************************************************************/
/*!
@brief Gets the most recent sensor events.
This function reads from both the accelerometer and the
magnetometer in one call, and is a deviation from the standard
Adafruit_Sensor API, but is provided as a convenience since most
AHRS algorithms require sensor samples to be as close in time as
possible.
@param accelEvent
A reference to the sensors_event_t instances where the
accelerometer data should be written.
@param magEvent
A reference to the sensors_event_t instances where the
magnetometer data should be written.
@return True if the event read was successful, otherwise false.
*/
/**************************************************************************/
bool Adafruit_FXOS8700::getEvent(sensors_event_t* accelEvent, sensors_event_t* magEvent)

/**************************************************************************/
/*!
@brief Gets the most recent sensor events.
This function reads from both the accelerometer and the
magnetometer in one call, and is a deviation from the standard
Adafruit_Sensor API, but is provided as a convenience since most
AHRS algorithms require sensor samples to be as close in time as
possible.
@param accelEvent
A reference to the sensors_event_t instances where the
accelerometer data should be written.
@param magEvent
A reference to the sensors_event_t instances where the
magnetometer data should be written.
@return True if the event read was successful, otherwise false.
*/
/**************************************************************************/
bool Adafruit_FXOS8700::getEvent(sensors_event_t* accelEvent, sensors_event_t* magEvent)

The first and last /**************************************************************************/ are not required but we think it looks good.

Brief & Description

Start with @brief and add a short sentence, ending with a . After that you can write as much as you like and it goes into the longer description of the function. KTown did some fun text-justification to make it all line up but if your editor doesn't do that, you can make it one long sentence like so:

@brief Gets the most recent sensor events. This function reads from both the accelerometer and themagnetometer in one call, and is a deviation from the standard Adafruit_Sensor API, but is provided as a convenience since most AHRS algorithms require sensor samples to be as close in time as possible.

Parameters

Each parameter needs a @param line. If you have no parameters to the function, skip this part. The word right after@param must be the same as the name of the parameter (make sure the parameter name in the .cpp file matches the one in .h) If you want you can make the name and the text on separate lines, or just put on one line like:

@param magEvent
A reference to the sensors_event_t instances where the
magnetometer data should be written.

Return Value

Lastly, we need to put in the return value details. You can skip this if your function returns void or is a constructor. Otherwise, put in a @returns with a line explaining what it returns

Macros and Enums

After functions, you'll need to document all your enumerations and macros (#defines)

For enums, you only need a comment beforehand like so, but I like to add a comment per line also:

Optional: Code Snippets and Fixed Width Text Blocks

While you might want to avoid inserting complex code snippets since it becomes a maintenance risk, if you do need to include a code snippet or a block of 'fixed width' text, you can use the @code and @endcode tags, as shown below:

Make sure all of your comments use '//' (and not '/* .. */') inside the code block!

Travis CI

Now that we have fully documented the library, we are ready to add automation. Automation means we don't have to remember to run doxygen, or compile every platform every time. Automation will do this for you after every commit or pull request! You'll make fewer mistakes and get better pulls from the community. We'll be using Travis CI, a very popular and well run Continuous Integration service. You might be wondering - how much is the monthly subscription? Nothing! For open source libraries, you can run travis free of cost at travis-ci.org

(Adafruit pays for the .com version of the service which we use a lot for internal repositories, and we recommend paying for it if you're building anything commercial because it's soooo worth it.)

Sign up for Travis

Use your GitHub login to register for Travis. This is required

Once logged in, you can Sync your account and Search for the repository you want to automate

Flick the switch to turn on Travis for that repository

Nothing will happen quite yet. That's OK! We have to add our Travis config file to the repository

Travis YML

Travis will create a fully new computer image every time and run your shell script instructions to compile the code and document it.

This step will install Arduino on your virtual computer and set up the board types as well, such as Uno, ESP8266, Zero, Due, etc! It's just a shell script so you can check it out at the URL to see what its' doing

Lastly we have two more scripts we run. library_check.sh, at this time, is an empty hook but we hope to add some things like checking format of the library and looking for missing or misformatted lines. You can remove this.

doxy_gen_and_deploy.sh Is the script that will do the doxygen generation and then push the pages to your gh-pages branch

Don't forget you'll need that empty gh-page branch if you want to have the doxygen pages pushed for you, see earlier in the guide!

Edit it as we explain above, in particular edit the PRETTYNAME variable and change the extra-library-installation line as necessary!

Save, commit and push the new .travis.yml file to your master branch

Checking Status

You can now check out the status of your build by visiting http://travis-ci.org

If you have multiple repositories, they'll appear on the left hand side. Yellow means working, green means pass and red means fail.

Failure/Passes

After every commit you may get an email or notification that Travis failed, or if it succeeded after failing you can get one too

Lets go and check out in the build log avialable at https://travis-ci.org/yourgithubaccount/repositoryname

In this case, you can see we failed to compile the sensorapi.ino demo when running the zero test (SAMD21) due to an #include "avr/delay.h" now we know we need to either #ifdef it out or restructure our code.

Travis builds take 5 minutes or so, so you're best off doing fast iterative tests locally. After you've fixed the bug and tested it locally, commit and push!

This time, with any luck, you'll pass the main compilation tests!

However your CI will probably still fail because we cannot 'push' the documentation to gh-pages so the last thing we have to do is give Travis CI permission to push to our repository

OUT OF STOCK NOTIFICATION

YOUR NAME

YOUR EMAIL

You have been successfully subscribed to the Notification List for this product and will therefore receive an e-mail from us when it is back in stock!

For security reasons, an e-mail has been sent to you acknowledging your subscription. Please remember that this subscription will not result in you receiving any e-mail from us about anything other than the restocking of this item.

If, for any reason, you would like to unsubscribe from the Notification List for this product you will find details of how to do so in the e-mail that has just been sent to you!