Manage Code for Multiple Boards

This section requires embedXcode+.

Managing code for multiples boards and platforms is a real issue. Why start a project from scratch for a new board?

We need to take into account different dimensions:

  • the boards, as Arduino Uno or LaunchPad MSP430,

  • the architectures, as AVR and SAM for Arduino, or MSP430 and LM4F for Energia,

  • the platforms, as Arduino or Energia,

  • and the frameworks, some of them with incompatible releases, as Arduino 0023, 1.0 or 1.5, Wiring.

One platform can handle different architectures, and one architecture includes different boards. For example, the same Arduino platform manages two architectures, AVR and SAM. The Arduino AVR architecture includes many different boards, as the Arduino Uno, Arduino Mega2560, ...

I’ve designed embedXcode with the aim of sharing the same code with different boards and platforms.

This can be done in two ways, MCU-based or IDE-based.

Both are valid from an embedXcode point of view and compatible with all the IDEs.

Write Specific Code for Multiple Platforms

The different platforms share most of the framework in common, except limited but annoying differences. Most of the code is the same, except a small number of lines. So we use pre-processing statements to write code for different platforms.

The most pre-processing used statements are #if #elif #endif and #defined().

One example is the number of the pin for the LED,

// myLED pin number
#if defined(ENERGIA) // All LaunchPads supported by Energia
    myLED = RED_LED;
#elif defined(DIGISPARK) // Digispark specific
    myLED = 1; // assuming model A
#elif defined(MAPLE_IDE) // Maple specific
    myLED = BOARD_LED_PIN;
#elif defined(MPIDE) // MPIDE specific    
    myLED = PIN_LED1;
#elif defined(WIRING) // Wiring specific
    myLED = 15;
#elif defined(ROBOTIS) // Robotis specific
    myLED = BOARD_LED_PIN;
#elif defined(RFDUINO) // RFduino specific
    myLED = 3;
#elif defined(LITTLEROBOTFRIENDS) // LittleRobotFriends specific
    myLED = 10;
#elif defined(SPARK) || defined(PARTICLE) // Particle / Spark specific
    myLED = D7;
#elif defined(PANSTAMP_AVR) // panStamp AVR specific
    myLED = 7;
#elif defined(PANSTAMP_NRG) // panStamp NRG specific
    myLED = ONBOARD_LED;
#elif defined(ESP8266) // ESP8266 specific
    myLED = 16;
#else // Arduino, chipKIT, Teensy specific
    myLED = 13;
#endif

The name of the board is queried to select the right pin number. This example uses the MCU-based approach.

Another example is the function for writing a byte to the I2C bus. The function is send() or write() depending on the platform.

// Write or read according to framework
static void _send(uint8_t ul) { 
#if defined(WIRING) // Wiring specific 
  Wire.write(ui);
#elif defined(ARDUINO) && (ARDUINO >= 100) 
  Wire.write(ui);
#elif defined(ENERGIA)
  Wire.write(ui);
#else
  Wire.send(ui);
#endif
}

This example uses the IDE-based approach, as it queries the IDE.

Use the MCU-Based Approach

The first approach is MCU-based and relies solely on the micro-controller type.

This approach is compatible with the respective IDEs, as no new environment variable is created or required.

In the Arduino case, two frameworks exist so the IDE variable ARDUINO is required for disambiguation.

// Core library for code-sense - MCU-based
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) || defined(__AVR_ATmega168__) // Arduino specific
#include "Arduino.h"
#elif defined(i586) // Galileo specific
#include "Arduino.h"
#elif defined(__32MX320F128H__) || defined(__32MX795F512L__) || defined(__32MX340F512H__) // chipKIT specific
#include "WProgram.h"
#elif defined(__AVR_ATtinyX5__) // Digispark specific
#include "Arduino.h"
#elif defined(__AVR_ATmega644P__) // Wiring specific
#include "Wiring.h"
#elif defined(__MSP430G2452__) || defined(__MSP430G2553__) || defined(__MSP430G2231__) || defined(__MSP430F5529__) || defined(__MSP430FR5739__) || defined(__MSP430F5969__) // LaunchPad MSP430 specific
#include "Energia.h"
#elif defined(__LM4F120H5QR__) || defined(__TM4C123GH6PM__) || defined(__TM4C129XNCZAD__) || defined(__CC3200R1M1RGC__) // LaunchPad LM4F TM4C CC3200 specific
#include "Energia.h"
#elif defined(__MK20DX128__) || defined(__MK20DX256__) // Teensy 3.0 3.1 specific
#include "WProgram.h"
#elif defined(__RFduino__) // RFduino specific
#include "Arduino.h"
#elif defined(MCU_STM32F103RB) || defined(MCU_STM32F103ZE) || defined(MCU_STM32F103CB) || defined(MCU_STM32F103RE) // Maple specific
#include "WProgram.h"
#else // error
#error Platform not defined or not supported
#endif

Use the IDE-Based Approach

The second approach is IDE-based.

Each IDE defines a specific environment variable which includes the boards type it supports, and optionally the framework version.

For example, the Arduino IDE defines ARDUINO=23, ARDUINO=101 or ARDUINO=150, depending on the version installed.

The variable is then passed on to the tool-chain with -D, as -DARDUINO=101 or -DARDUINO=150.

// Core library for code-sense - IDE-based
#if defined(WIRING) // Wiring specific
#include "Wiring.h"
#elif defined(MAPLE_IDE) // Maple specific
#include "WProgram.h"
#elif defined(ROBOTIS) // Robotis specific
#include "libpandora_types.h"
#include "pandora.h"
#elif defined(MPIDE) // chipKIT specific
#include "WProgram.h"
#elif defined(DIGISPARK) // Digispark specific
#include "Arduino.h"
#elif defined(ENERGIA) // LaunchPad specific
#include "Energia.h"
#elif defined(LITTLEROBOTFRIENDS) // LittleRobotFriends specific
#include "LRF.h"
#elif defined(MICRODUINO) // Microduino specific
#include "Arduino.h"
#elif defined(TEENSYDUINO) // Teensy specific
#include "Arduino.h"
#elif defined(REDBEARLAB) // RedBearLab specific
#include "Arduino.h"
#elif defined(RFDUINO) // RFduino specific
#include "Arduino.h"
#elif defined(SPARK) // Spark specific
#include "application.h"
#elif defined(ARDUINO) // Arduino 1.0 and 1.5 specific
#include "Arduino.h"
#else // error
#error Platform not defined
#endif // end IDE

The Arduino, Wiring and Maple IDEs set one single environment variable: ARDUINO=23 or ARDUINO=101, WIRING=100 and MAPLE_IDE, respectively.

The remaining IDEs, Teensy and Energia defines two environment variables, their own on top of the default one: CORE_TEENSY and ARDUINO=101, ENERGIA=10and ARDUINO=101, respectively.

So embedXcode tests ARDUINO after the specific variables.

Insert Pre-Processing Statements from Code Snippet

A code snippet includes all the pre-processing statements for selecting the core libraries. There are two versions: one MCU-based and another IDE-based.

For more information,

To display the code snippets,

  • Click on the right button of the View selector.

The library of code snippets is at the bottom of the pane.

  • Select the area with User labelled on the code snippets.

  • Select the embedXcode #include Core Library snippet.

  • Click and drop to the destination.

The pointer changes for

The cursor appears on the code.

The code is then inserted.