6. Alternatives to the Arduino IDE – Arduino Software Internals: A Complete Guide to How Your Arduino Language and Hardware Work Together

© Norman Dunbar 2020
N. DunbarArduino Software Internalshttps://doi.org/10.1007/978-1-4842-5790-6_6

6. Alternatives to the Arduino IDE

Norman Dunbar1 
Rawdon, West Yorkshire, UK
A number of alternatives to the Arduino IDE exist. Some are massive overkill such as the Atmel Studio 7 (www.microchip.com/mplab/avr-support/atmel-studio-7), based on Microsoft Visual Studio and which only runs on Windows, and MPLAB-X (www.microchip.com/mplab/mplab-x-ide). Others such as the AVR Eclipse Plugin (http://avr-eclipse.sourceforge.net/wiki/index.php/The_AVR_Eclipse_Plugin) are plugins for the Eclipse IDE, a Java-based IDE on steroids, and again quite large. This chapter looks at two other, smaller, alternatives and the two which I will be investigating here are
  • PlatformIO (https://platformio.org/), which is both a command-line version and can be used to convert your favorite text editor into an IDE to develop Arduino software – if your favorite editor is Atom or VSCode/VSCodium, that is, but fear not. It can also be used to create project files for a number of popular IDEs.

  • The all-new Arduino CLI, (https://github.com/arduino/arduino-cli), which is still in its alpha release status but which is available and surprisingly usable. This is proposed as a compilation replacement for the current Java-based IDE in a forthcoming version, but is specifically designed to be used in make files.

Bear in mind that the latter is in its alpha release status, so is likely to change as time passes.

6.1 PlatformIO

PlatformIO is a system which allows you to write, compile, and upload programs to your Arduino board, either in plain Arduino format – as you are used to in the Arduino IDE – or in plain vanilla AVR C/C++ format, which removes the hand holding that you get from the Arduino IDE and only sets up and runs the code that you write. There is no millis() function, for example – you are on your own.

The PlatformIO package comes in two flavors:
  • PlatformIO Core – Which installs command-line utilities.

  • PlatformIO IDE – Which installs an IDE-style plugin for the Atom editor and also for the Visual Studio Code editor. Other IDE systems, Eclipse or Code::Blocks, for example, don’t have plugins as such, but the pio command can generate project files for those IDEs to allow you to develop AVR code in a familiar IDE environment.

6.1.1 Installing PlatformIO Core

PlatformIO Core runs in a bash shell, but don’t panic. There are IDE versions if you wish to use one; however, I think it’s better to understand what is happening before heading off to a GUI tool – you never know when you will be without a GUI.

This requires Python 2.7 as, currently, no other versions are able to be used. If you have Linux, as I do, the chances are that Python 2.7 is already installed alongside Python 3.

The following instructions apply to Debian-based systems such as Debian itself, Ubuntu, or Linux Mint, which I’m using.
python --version

The response needs to be something like Python 2.7.x; Python3 does not yet work.

If this gives something like python not found, then install it:
sudo apt install python

Don’t worry if you already have Python version 3.x installed. This command shouldn’t overwrite your Python 3 installation as, on all the systems I’ve looked at, it is known as python3 when installing and when executing code written in version 3 syntax.

Next, make sure that pip is installed:
pip --version
The response should print the text pip 9.0.1 from /usr/lib/python2.7/dist-packages (python 2.7) or similar. Look for python 2.7 in the text. If pip is not installed, install it as follows:
sudo apt install python-pip
Then install the setuptools package using pip:
pip install -U setuptools
Now, install platformio:
pip install -U platformio

You might see warnings informing you that the bottle and/or symantic-version wheels cannot be built. Don’t worry about this. As long as you see the following, or something remarkably similar at the end, you are good to go:

Successfully installed bottle-0.12.13 certifi-2018.8.24chardet-3.0.4 click-5.1 colorama-0.3.9 idna-2.7platformio-3.6.0 pyserial-3.4 requests-2.19.1semantic-version-2.6.0 urllib3-1.23

You can see that both bottle and symantic-version were installed, regardless of the errors listed.

6.1.2 Testing PlatformIO Core

The commands pio and platformio are now installed, in my case to /home/norman/.local/bin, and both are actually the same thing. Because I’m lazy, I use the shorter version, pio. Feel free to use the longer version if this appeals to you. Set Up Your Environment

On Linux, in order for your user to upload code to the Arduino, they must be a member of the dialout group. On Debian-based Linux systems, it appears that you may require to be in the group plugdev too if you wish to use an ICSP device. These rules apply to the PlatformIO system as well, so first of all, check that you do indeed have membership of the group(s). You must do this while logged in as your normal account:
In my case, I received the following response:
norman adm dialout cdrom sudo dip plugdev lpadmin sambashare vboxsf
I am already a member of dialout and plugdev as I’ve been using the Arduino IDE and an ICSP device previously. If your account shows that you are not a member of one or the other of the two groups, then run the following commands, as appropriate, making sure to enter the “G” in upper case and substitute your login name where indicated:
sudo usermod -a -G dialout your_user_name
sudo usermod -a -G plugdev your_user_name

This will add you to the desired group when you next log in, so log out and log back in again. This isn’t Windows, so you don’t have to reboot! Once logged in, make sure you are now a member of dialout as mentioned.

Using an ICSP Programming Device

If you intend to use an ICSP device, rather than the Arduino bootloader, then you will need to download the appropriate udev rules file from https://github.com/platformio/platformio-core/blob/develop/scripts/99-platformio-udev.rules and copy it, as root with sudo, to /etc/udev/rules.d so that the programmer will be recognized and permissions given to allow your user (any user actually) to upload programs to the Arduino board.

In my setup, I originally had a few difficulties with the ICSP device, both in the Arduino IDE and using PlatformIO, so I had to edit the downloaded rules file as follows for my USBtiny ICSP device.

The original line for my device was this, all on one line:
# USBtiny
SUBSYSTEMS=="usb", ATTRS{idProduct}=="0c9f",
    ATTRS{idVendor}=="1781", MODE="0666"
I changed it to the following, again, all on one line:
# USBtiny
SUBSYSTEMS=="usb", ATTRS{idProduct}=="0c9f",
    ATTRS{idVendor}=="1781", MODE="0666", GROUP="plugdev"

Setting the group to plugdev allows anyone in that group to use the device. I could have set it to dialout I suppose, but I’m sure that the relevant document outlining the solution to my (long forgotten) problem said to use plugdev.

It is a minor annoyance that if you have to make changes to this file, every time that you compile some code with PlatformIO, it will warn you that the file is missing or out of date and must be reinstalled. This appears in the output for the compilation and not, thankfully, as a pop-up.

Once the file is installed, you may be required to restart the udev service – I didn’t – however, just in case, other systems do require this:
sudo service udev restart
If that gives errors because systemd is not in use, then these commands should suffice:
sudo udevadm control --reload-rules
sudo udevadm trigger

Now, if your Arduino board is connected to the computer, you must unplug it and plug it back in to pick up the changes.

None of this is necessary if you only intend to use the bootloader built in to the Arduino.

If you do intend to use a programmer, then you must be aware that any time you program the Arduino with such a device, the AVR microcontroller will be erased completely and you can no longer use the bootloader as it has been overwritten.

You can, thankfully, restore the bootloader, either with PlatformIO (see in the following) or with the Arduino IDE – just pick your board and programmer in the normal manner and then go to Tools ➤ Burn Bootloader, and it will be restored. You will, of course, have to use the ICSP device to burn the bootloader, for obvious reasons! Set Up a New Project

Find out where PlatformIO expects to find your projects. The default will be displayed using the following command which displays all the current settings:
pio settings get
A more specific command, to display only the projects_dir setting, is the following:
pio settings get projects_dir
On my Linux system, it returns the following:
Name            Value [Default]
projects_dir    /home/norman/Documents/PlatformIO/Projects

In order to save wrapping text around, I’ve trimmed the excess from the preceding output. The output also lists a description of the setting name, which is useful.

If you don’t like the default location, you can easily change it:
mkdir -p ~/SourceCode/PlatformIO/Projects
pio settings set projects_dir ~/SourceCode/PlatformIO/Projects
The result of executing this command is
The new value for the setting has been set!
Name           Value [Default]
projects_dir   /home/norman/SourceCode/PlatformIO/Projects

The previous setting is listed, at the end of the line, in square brackets, but is not shown here.

PlatformIO has the idea of a default location for projects, but strangely, it is not used except in PlatformIO Home which is a browser-based pseudo-IDE, which is discussed later on in Section 6.1.5, “PlatformIO Home.”

When creating new projects, you must be located in the folder or directory where you wish to create the new project. The pio init command creates all its files right where you are currently located – so beware.

Change to the project directory, as listed, and create a new folder to house the project:
cd ~/SourceCode/PlatformIO/Projects
mkdir TestProject
cd TestProject
Determine the name to be used for your specific board. In my case, it’s a Duemilanove:
pio  boards  duemil
This gives me two options:
Platform: atmelavr
ID                   MCU         ...  Name
diecimilaatmega168   ATMEGA168   ...  Arduino Duemilanove ...
diecimilaatmega328   ATMEGA328P  ...  Arduino Duemilanove ...

The output text has a lot more information – but it’s far too wide for the page, so I’ve trimmed it of irrelevant detail.

As I’m using the latter version with the ATmega328P, I need to initialize the new project with the diecimilaatmega328 board name. If you have an Arduino Uno, the process is similar, but has many more results. You should be looking under the Platform: atmelavr for your Uno, which will be called uno.

There is no need to install any tools, compilers, etc. for the various boards as PlatformIO will do this automatically for you if it detects that you are using a board for which no tools yet exist.

Unfortunately, it doesn’t use the existing tools that were installed by the Arduino IDE, so you may end up with two separate versions of the AVR compiler and so on. This is not a major problem and does mean that when you decide to continue writing Arduino code using PlatformIO, instead of the Arduino IDE, you can simply uninstall the Arduino stuff and still be able to compile with PlatformIO. Initialize the Project

The following example sets up a new project for the Duemilanove board which I’m using. You can also create the same project for numerous boards. To show how this can be done, I’ll create the project for an Arduino Uno as well as my Duemilanove.

If you accidentally forget to initialize a second or subsequent board, you can easily do it by making another call to pio init --board with the additional board(s). Those new boards will be added to the current project.

pio init --board diecimilaatmega328 --board uno

After a very short delay, the screen will be filled with useful information about the new project and a list of commands to compile, upload, and clean the project files.

As the messages indicate, some files and directories have been created. These are as follows:
  • platformio.ini is a file that holds all the configuration for the project. Any changes you make to this file will only affect the project in the current directory – but all environments may be involved. The file itself contains settings for all the boards with which the project was initialized. Listing 6-1 shows the default file in full.

  • include is a folder expected to be used for any header files for your project. These are the *.h files.

  • src is a folder where you are expected to save all the C/C++ files that make up the project.

  • lib is a folder where any libraries, private to this project, should be saved or copied. A readme.txt file is also created within this directory explaining how it should be used.

Some commands are displayed as examples of how to build and upload sketches to the board. However, these assume that you will be using the normal Arduino bootloader to do the uploading.

You can edit platformio.ini and change the long-winded name, if necessary, to something more memorable. I changed mine to the following as I couldn’t be bothered to have to type “diecimilaatmega328” all the time (“2009” is what “duemilanove” means in Italian):
platform = atmelavr
board = diecimilaatmega328
framework = arduino

Now I can compile for the Duemilanove with the name 2009 instead. It’s easier to remember and less typing too.

If, like me, you have an ICSP device, then you need to edit the platformio.ini file, or programming the board will not work. In my case, I added a new environment so that I could use either the bootloader or the ICSP device. The new environment is simply a copy of the existing one, with a couple of lines added as shown in Listing 6-1. I did edit the Uno environment as well, but that’s not shown in the listing.
platform = atmelavr
board = diecimilaatmega328
framework = arduino
; Code below added by NDunbar to allow use of
; my USBtiny for programming.
platform = atmelavr
board = diecimilaatmega328
framework = arduino
;uploader = usbtinyisp
upload_protocol = usbtiny
Listing 6-1

Example platformio.ini file

The PlatformIO Documentation at https://docs.platformio.org/en/latest/platforms/atmelavr.html has details of what is required by each known ICSP device. In my case, I have a USBtiny clone, so I used these two lines from Listing 6-1:
;uploader = usbtinyisp
upload_protocol = usbtiny

From version 4.1.0 onward, it appears that the uploader line is no longer required, just the upload_protocol line. I assume (always a bad idea) that the protocol gives PlatformIO all the detail it needs to determine the uploader device.

It does no harm to leave the line in; however, you will receive a warning at upload time if you do. I’ve simply commented mine out with a leading semicolon to disable the warning message.

Also available in recent versions is the ability to extract common lines to a separate section and just add in the specific lines for the environments in your file. This is not shown here.

The command to use to upload a sketch using the programmer is
pio run -e 2009_programmer -t program

You can now run pio run -e 2009 -t upload to upload using the bootloader or the preceding command to use the ICSP device. Be aware that once you have used the programmer, you no longer have a bootloader and cannot then use pio run -t -e 2009 upload to upload. You must always use pio run -t -e 2009_programmer program – until you recreate the bootloader of course.

I suppose we need to create the ubiquitous Blink sketch now? Arduino-Style Projects

As mentioned earlier, PlatformIO allows you to continue using the Arduino Language in your projects. This section will explain how the Blink sketch can be converted and compiled in the new environment.

Create a new file, src/main.cpp (or whatever name you wish – I called mine Blink.cpp), and add the code in Listing 6-2 to it.
#include "Arduino.h"                               ①
// Is the built in LED already named?
#ifndef LED_BUILTIN                                ②
#define LED_BUILTIN 13
// This runs once, in the usual Arduino manner.
void setup()
    // Make sure the LED is an output pin.
void loop()
    // LED on, then wait 1,000 milliSeconds.
    digitalWrite(LED_BUILTIN, HIGH);
    // LED off, then wait another 1,000 milliSeconds.
    digitalWrite(LED_BUILTIN, LOW);
Listing 6-2

Arduino blink sketch

  •     ①    You normally do not need to do this in the Arduino IDE, but in PlatformIO, you must.

  •     ②    This is just a safety check to ensure that the built-in LED has been given a name in the Arduino.h file. In most cases, it has been done, but it’s always best to check and avoid any compilation errors that may arise.

As you see, apart from adding one line, nothing has changed. You can now use your favorite text editor to write code for your Arduino. Compiling Arduino Projects

Compile the preceding code by first making sure that you are located in the directory where the file platformio.ini exists. If you have changed into the src directory to edit the file as in the preceding text, then please change back up one level.

Now run this command:
pio run -e 2009
The -e option relates to the environment (or board) that you wish to compile the code for. As I created two, I need to inform PlatformIO which board I wish to compile for. In this case, it’s the Duemilanove (which I renamed to “2009” ) and not the Uno. If you omit this option, all environments in the platformio.ini file will be compiled, as follows:
pio run
Compile , but do not upload, the code – in case there are errors:
pio run -e 2009
The compilation produced the following (slightly abridged) output:
PLATFORM: Atmel AVR > Arduino Duemilanove ... ATmega328   ①
SYSTEM: ATMEGA328P 16MHz 2KB RAM (30KB Flash)             ②
Library Dependency Finder ... [URL removed for brevity]   ③
Collected 24 compatible libraries                         ④
Scanning dependencies...                                  ⑤
No dependencies
Compiling .pioenvs/2009/src/blink.cpp.o                   ⑥
Archiving .pioenvs/2009/libFrameworkArduinoVariant.a      ⑦
Indexing .pioenvs/2009/libFrameworkArduinoVariant.a
Compiling .pioenvs/2009/FrameworkArduino/CDC.cpp.o
Archiving .pioenvs/2009/libFrameworkArduino.a             ⑧
Indexing .pioenvs/2009/libFrameworkArduino.a
Linking .pioenvs/2009/firmware.elf
Checking size .pioenvs/2009/firmware.elf
Building .pioenvs/2009/firmware.hex                       ⑨
Memory Usage -> http://bit.ly/pio-memory-usage            ➉
DATA:    [          ]   0.4% (used 9 bytes from 2048 bytes)
PROGRAM: [          ]   3.0% (used 928 bytes from 30720 bytes)
============ [SUCCESS] Took 1.06 seconds ============
============ [SUMMARY] ============                       ①
Environment 2009    [SUCCESS]
Environment uno     [SKIP]
============ [SUCCESS] Took 1.06 seconds ============
  •     ①    This line summarizes the platform and board in use. In this case, the platform is Atmel AVR, and the board is a Duemilanove (even though I renamed it to 2009 in the platformio.ini file).

  •     ②    This line summarizes the capacity of the chosen device.

  •     ③    The PlatformIO dependency finder goes hunting for anything that it thinks needs to be included in this compilation. I removed the URL that’s normally listed here to get the line on the page.

  •     ④    This is the number of files and others that the finder thinks are required.

  •     ⑤    The system is looking for any dependencies here. It decided that there were none.

  •     ⑥    This is where my source file got compiled. Your file name should appear here too.

  •     ⑦    Do you recognize these file names? They are all the files that are included by default when you compile an Arduino sketch in the Arduino IDE.

  •     ⑧    All the Arduino code is statically linked to the elf file. This will be converted to a hex file for uploading.

  •     ⑨    The hex file is the one that will be uploaded to the Arduino board. It has only been compiled at present, not yet uploaded.

  •     ➉    The memory usage section shows that this Arduino sketch used 9 bytes of RAM and 928 bytes of flash program memory. This is standard for the default Arduino Blink sketch. The blank spaces between the square brackets here look pointless; however, when you compile large sketches, this shows a histogram of the amounts of Static and Flash RAM used.

  •     ①    The “SUMMARY” shows that this compilation only affected the 2009 environment/board and that the uno was not touched.

On the first compilation of any target device, PlatformIO will download the required toolchain. In my example, it downloaded the gcc-avr compiler toolset. This already exists under my Arduino IDE installation, but is sadly not found or used by PlatformIO. Uploading Arduino Projects
Uploading compiled sketches to an Arduino board is carried out with a bootloader or with an ICSP device. In the former case:
pio run -e 2009 -t upload
Or with an ICSP device:
pio run -e 2009_programmer -t program

Don’t forget that you need to edit the platformio.ini file if you want to use a programmer instead of the bootloader – see preceding text.

You should note that in the absence of an -e option, all environments/boards in the current project will be compiled and uploaded. This is best avoided as you could end up with Uno code uploaded to your Duemilanove, which may not work correctly – it depends on which board is attached to the USB at the time.

You could see something similar to the following when uploading:
Configuring upload protocol...
AVAILABLE: arduino
CURRENT: upload_protocol = arduino
Looking for upload port...
Auto-detected: /dev/ttyUSB0
Uploading .pioenvs/2009/firmware.hex
avrdude: AVR device initialized, ready to accept instructions
Reading | ###################################### | 100% 0.01s
avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: reading input file ".pioenvs/2009/firmware.hex"
avrdude: writing flash (928 bytes):
Writing | ###################################### | 100% 0.51s
avrdude: 928 bytes of flash written
avrdude: verifying flash memory ...
avrdude: load data flash data from input file ...
avrdude: input file ... contains 928 bytes
avrdude: reading on-chip flash data:
Reading | ####################################### | 100% 0.44s
avrdude: verifying ...
avrdude: 928 bytes of flash verified
avrdude: safemode: Fuses OK (E:00, H:00, L:00)
avrdude done. Thank you.
============ [SUCCESS] Took 3.33 seconds ============

It appears that you will always see the following message until you install the requested file or if it has been installed but has been modified from the released file:

Warning! Please install 99-platformio-udev.rules and check that your board’s PID and VID are listed in the rules. http://docs.platformio.org/en/latest/faq.html#platformio-udev-rules

This can be a little irritating, but if anything goes wrong with the upload, at least you have a couple of clues as to what to check. It’s especially irritating as the file is only required for ICSP devices. Using a bootloader only requires that your user be in the dialout group.

You should be able to see the built-in LED flashing away merrily in the usual manner.

So that’s how easy it is to create Arduino-style projects using the command-line versions of the PlatformIO Core code. I admit that it would be nice to have an IDE, even one as simple as the Arduino IDE, so later on, in Section 6.1.4, “PlatformIO in an IDE,” I’ll explain how easy it is to add PlatformIO features to one of a number of existing IDEs.

In the meantime, the next section shows how to create the Blink sketch as a plain AVR C/C++ program.

You can use PlatformIO to import existing Arduino sketches. This is not really possible from the command line, yet. You have to use the pio home command, which is discussed later. If you need to do it manually, then
  • Create a new project in the usual manner.

  • Edit the platform.ini file and make sure that the Arduino framework is listed.

  • Copy the existing Arduino project’s ino file into the src folder.

  • Edit the ino file and add the line #include "Arduino.h" at the top. AVR-Style Projects

The preceding project used the standard Arduino Language and compiled down to a hex file the same size as you would have seen if the Arduino IDE had been used instead. You can, however, go commando and bypass the entire Arduino system, as shown in the following. Remember, however, that it is your responsibility to make all the decisions about ports, pins, and so on.

If you are still in the TestProject directory, change back up one level to the standard location for PlatformIO projects. Then create a new project similar to the previous one. I’m not using an additional uno variant this time, but there’s no reason that you cannot use “uno,” for example.

Remember this is no longer an Arduino board; it’s a plain vanilla Atmel AVR development board – it just happens to look like an Arduino!

You should find a suitable board as follows, specifying the “atmega328” device name:
pio boards atmega328 | grep 16
If you are on Windows, then use this command instead:
pio boards atmega328 | find "16"
There are quite a few occurrences of the text “atmega328,” so the additional filtering with grep or find helps narrow it down to 16 MHz versions. I picked the 328p16m variant, but anything which matches your setup should do:
mkdir TestProjectAVR
cd TestProjectAVR
pio init --board 328p16m
Once again, if you wish to upload using a programmer, then edit the platformio.ini file and add a new environment which is a copy of the one just created, with the additional lines for your particular programmer. Mine will be as per Listing 6-3, with the comments removed for brevity.
platform = atmelavr
board = 328p16m
platform = atmelavr
board = 328p16m
;uploader = usbtinyisp
upload_protocol = usbtiny
Listing 6-3

The new platform.ini file

You can hopefully see that I’ve also deleted the line framework = arduino as this is no longer required for plain AVR programming. Leaving it in will cause all the Arduino files to be compiled regardless of the fact that they are not used.

Yes, the actual physical board is an Arduino of some kind, but it is now being used as an AVR development board instead of an Arduino.

Create src/Blink.cpp containing the code in Listing 6-4.
#include <avr/io.h>           ①
#include <util/delay.h>       ②
int main(void)
    // D13 is actually PortB Pin 5. Configure
    // that pin as an output.
    // This equates to the Arduino setup() function.
    DDRB = (1 << DDB5);                        ③
    // This equates to the Arduino loop() function.
    while (1)
        _delay_ms(1000);                       ④
        // Toggle the LED by writing to PINB.
        PINB = (1 << PINB5);                   ⑤
    return 0;                                  ⑥
Listing 6-4

Another blink sketch

  •     ①    This brings in the correct settings, register names, pin numbers and other definitions for the particular AVR microcontroller in use.

  •     ②    We need this to enable us to call the _delay_ms() function (delay in milliseconds).

  •     ③    This is effectively pinMode(13, OUTPUT). Digital pin 13 is on PORTB and is bit 5 of that port.

  •     ④    This delays for 1 second.

  •     ⑤    Pin toggling is carried out by writing a 1binary to the appropriate bit in the PIN register for the pin to be toggled.

  •     ⑥    We never get here, but because main() is always declared as returning an int, then the compiler complains if we leave this off. There are other ways to silence the compiler, but this is the easiest, in my opinion. Compiling AVR Projects
Compiling an AVR-style program is exactly the same as before:
pio run -e 328p16m
The -e option can be omitted if there is only a single board in the project. The output from the command will be as follows, and no Arduino files will have been included in the compilation. The following has been slightly abridged to fit on the page:
PLATFORM: Atmel AVR > Microduino Core (Atmega328P@16M,5V) ①
SYSTEM: ATMEGA328P 16MHz 2KB RAM (31.50KB Flash)          ②
Library Dependency Finder ...                             ③
Collected 0 compatible libraries                          ④
Scanning dependencies...                                  ⑤
No dependencies
Compiling .pioenvs/328p16m/src/Blink.o                    ⑥
Linking .pioenvs/328p16m/firmware.elf                     ⑦
Checking size .pioenvs/328p16m/firmware.elf
Building .pioenvs/328p16m/firmware.hex                    ⑧
Memory Usage -> http://bit.ly/pio-memory-usage            ⑨
DATA:    [          ]   0.0% (used 0 bytes from 2048 bytes)
PROGRAM: [          ]   0.5% (used 158 bytes from 32256 bytes)
============ [SUCCESS] Took 0.36 seconds ============
  •     ①    This line summarizes the platform and board in use.

  •     ②    This line summarizes the capacity of the chosen device. You may notice that it appears to have an extra 1.5 Kb of flash over the Duemilanove that I used previously. This is using the Uno bootloader which is smaller, by 1.5 Kb.

  •     ③    The PlatformIO dependency finder goes hunting for anything that it thinks needs to be included in this compilation. The URL (not shown) that would normally be on this line is that of the documentation for the Library Dependency Finder. I removed it to fit the page width.

  •     ④    This is the number of libraries that the finder thinks are required. It says nothing else will be required.

  •     ⑤    The system is looking for any dependencies here. It decided that there were none.

  •     ⑥    This is where my source file got compiled. Your file name should appear here too.

  •     ⑦    The code is first compiled into an elf file. This will be converted to a hex file later.

  •     ⑧    The hex file is the one that will be uploaded to the Arduino board. It has only been compiled at present, not yet uploaded.

  •     ⑨    The memory usage section shows that this Arduino sketch used no RAM and only 158 bytes of flash program memory.

In bare-bones AVR code, the standard blink sketch takes 158 bytes of flash program memory rather than the Arduino’s 928. It also does not create any variables in RAM. Uploading AVR Projects
There’s no difference in uploading the compiled AVR C/C++ code to the AVR microcontroller than previously. It is done with a bootloader or with an ICSP device as normal. In the former case:
pio  run  -e  328p16m  -t upload
Or with an ICSP device:
pio run -e 328p16m_programmer -t program

Remember to edit the platformio.ini file if you want to use a programmer instead of, or as well as, the bootloader – see preceding text.

The output from the upload is very similar to that when an Arduino-style project is uploaded, so has not been reproduced here.

The plain AVR style of programming takes less time to compile as it is not required to compile all the Arduino support code – what you see in the program is what you get.

Those readers who are slightly ahead of me here will realize that this means that you don’t get things like millis() and micros(),Timer/counter 0 Overflow interrupts, or even interrupts enabled when you compile an AVR-style project. Everything you need, you have to enable. The Arduino does a heck of a lot of stuff in the background to make your life easy.

I wish I had £1.00 (about $1.28 currently) for every project that didn’t work initially because I forgot to enable interrupts!

You should now be able to see the built-in LED flashing away merrily in the usual manner, but this time, using far fewer bytes of flash – 158 as opposed to 928 – and even better 0 bytes of scarce Static RAM for variables, as opposed to 9 bytes previously.

6.1.3 Burning Bootloaders

The PlatformIO software has the ability to burn a bootloader for your device. When you have initialized the board in a project, you need to simply do the following:
  • Edit the platformio.ini file and add the lines required to use an ICSP device, rather than the bootloader, to do the uploading.

  • Run the command pio run --target bootloader to do the burn.

As mentioned, you will have to use an ICSP device, or a spare Arduino to use as an ISP, but it does work and is simple. The current version of the arduino-cli, 0.6.0 (see Section 6.2), does not yet have the ability to burn a bootloader.

So that’s a very brief introduction to the PlatformIO system on the command line. As promised, I shall now demonstrate how you can install PlatformIO so that you can use an existing IDE to develop code for your Arduino boards – be that in Arduino Language or plain AVR C/C++ – the choice is yours.

6.1.4 PlatformIO in an IDE

You have seen how PlatformIO can be used in the command line, but let’s face it. It’s not an easy task switching from the editor back to the command line to compile, then back to the editor to fix, and so on. Some Linux editors do allow you to open a terminal within the editor, so that’s handy. However, wouldn’t it be nice to add PlatformIO to our current favorite development IDE?

There is a variant of PlatformIO which is named PlatformIO IDE and this is used as a plugin for, among others, the Atom editor (https://atom.io/) or the VSCode editor (https://code.visualstudio.com/).

The VSCode editor apparently feeds some data back to Microsoft. People are upset about this, and as the code for VSCode is open source, a variant of VSCode without the telemetry has been created. This is named VSCodium and can be obtained from https://github.com/VSCodium/vscodium/releases/latest if you don’t want data fed back to Microsoft.

Having said that, the PlatformIO IDE Integration page at https://docs.platformio.org/en/latest/ide.html gives details on using PlatformIO Core (yes, Core, not IDE) to create project files for various IDEs. For the rest of the PlatformIO section of the book, I’ll be looking at Code::Blocks (http://codeblocks.org/) as that’s an IDE that I use on Windows (at work) and on Linux.

Run the following in a command-line session:
pio init --help
The output is as follows with the current version:
Usage: pio init [OPTIONS]
  -d, --project-dir DIRECTORY
  -b, --board ID
  --ide [atom|clion|codeblocks|eclipse|emacs|netbeans|
  -O, --project-option TEXT
  --env-prefix TEXT
  -s, --silent
  -h, --help                      Show this message and exit.

These are the various options that the command accepts. Look at the --ide options. There are facilities for PlatformIO to create project files for a number of IDEs such as Eclipse, QT Creator, Code::Blocks, etc. I’m a big fan of QT Creator and Code::Blocks, so let’s set up a project for Code::Blocks.

I have recently noticed that QT Creator projects don’t compile properly. This is due to the AVR version of the g++ compiler not being correctly configured into the project file. Unlike Code::Blocks, which does include the compiler in the project settings, QT Creator doesn’t. You have to pick it manually in the settings to use the AVR version of g++.

There’s no other software that you need to install. The PlatformIO Core software has everything you need to integrate into one of the many supported IDEs. Set Up a New Code::Blocks Arduino Project

If you are not already there, change your working directory back to the default location for PlatformIO projects as before and initialize a new Arduino-style project as follows. Adjust the board to match your specific setup of course:
mkdir CodeBlocks
cd CodeBlocks
pio init --ide codeblocks --board diecimilaatmega328

Once the command has completed, you will be able to find the usual files and folders as previously; however, in addition, there will be a platformio.cbp file – a Code::Blocks project file.

Open the Code::Blocks IDE as usual and select File ➤ Open, navigate to the directory where the project was just initialized within, and open the project file platformio.cbp in the normal manner.

Open the platformio.ini file within the IDE. You may be asked to select which editor to use – simply scroll down the list and allow it to be opened within Code::Blocks itself. The file looks like the following:
platform = atmelavr
board = diecimilaatmega328
framework = arduino

This time, it doesn’t matter that the environment has such a long-winded name. I’ll never be referring to it again. Please also note that in this case I’m not intending to use the programmer – I’ll be using the normal Arduino bootloader to program my board this time, but the process of setting up the ICSP is as before.

Click File ➤ New to add a new file. Select “Blank File” when prompted, and then choose to add it to the project. You will have to save the file first, so make sure that you select the src directory and save the file as Blink.cpp as usual.

When saved, you can accept the option to add the file to both the release and debug projects. The file will now be opened in the editor. Add the code in Listing 6-5 to the new file. This is a comment-free version of the standard Arduino Blink sketch that we created previously.
#include "Arduino.h"
#define LED_BUILTIN 13
void setup()
void loop()
    digitalWrite(LED_BUILTIN, HIGH);
    digitalWrite(LED_BUILTIN, LOW);
Listing 6-5

Code::Blocks blink sketch

Now click the build button – it looks like a cogwheel – or click Build ➤ Build or press Ctrl+F9 to compile the code. The build log tab at the bottom should fill with the usual text.

When it compiles cleanly, it’s ready to be uploaded. This is done by clicking the run button (the green arrow), clicking Build ➤ Run, or pressing Ctrl+F10.

A new window will open, and the normal PlatformIO text that we are by now used to will scroll up the screen. Your Arduino board has now been programmed and should once more be blinking. Press Enter in the newly opened window to close it and return to the IDE. Set Up a New Code::Blocks AVR Project

It is just as simple to create a new project to code in plain AVR C/C++.

Change directory back to the default location for PlatformIO projects as before and then initialize a new AVR-style project as follows:
mkdir CodeBlocksAVR
cd CodeBlocksAVR
pio init --ide codeblocks --board 328p16m

Open the platformio.ini file within the Code::Blocks IDE as normal and delete the last line that says framework = arduino – we don’t need it. You may also add the lines to use your programmer, if necessary. I’m again using a bootloader in this example.

As before, click File ➤ New to add a new file. Select “Blank File” when prompted, and then choose to add it to the project. You will have to save the file first, so make sure that you select the src directory and save the file as Blink.cpp as usual.

When saved, accept the option to add the file to both the release and debug projects. The file will now be opened in the editor. Add the following code – which is a comment-free version of the standard AVR Blink sketch which we created previously and is shown in Listing 6-6.
int main(void)
    DDRB = (1 << DDB5);
    while (1)
        PINB = (1 << PINB5);
    return 0;
Listing 6-6

Another blink sketch.

Now click the build button, click Build ➤ Build, or press Ctrl+F9 to compile the code. When it compiles cleanly, it’s ready to be uploaded. This is done by clicking the run button (the green arrow), clicking Build ➤ Run, or pressing Ctrl+F10.

A new window will open, and the normal PlatformIO text that we are by now used to will scroll up the screen. Your Arduino board has now been programmed and should once more be blinking. Press Enter in the newly opened window to close it and return to the IDE.

You should be aware that there isn’t, apparently, an option to determine the environment/board to be compiled and programmed when using the IDE in this manner. At least, I’ve not been able to find one! So don’t go adding numerous boards to your project file at creation time.

Also, as before, when programming your device with an ICSP device, you will need to edit the platformio.ini file to suit your programmer.

6.1.5 PlatformIO Home

This is an interesting development in the PlatformIO system. Running the command for the first time will download some files, and then this text will appear on the screen:
 /\-_--\     PlatformIO Home
/  \_-__\
|[]| [] |

After a short delay, the default browser on your system should open at the page http://localhost:8008, and an IDE-alike screen will be waiting for you.

The pio home command will hang up the session it was opened in until closed by pressing Ctrl+C. If you do this while the browser is open, then the browser will stop responding as the HTML server has just been "crashed."

When you are finished in the "pio home" page, close it in the browser and then shut down the server by pressing Ctrl+C in the session you typed the pio home command.

On this browser screen, you can search for boards, libraries, etc. and install them. You can add new or update existing platforms and so on. However, this is where you can create new projects without needing to remember all those pio init commands and options. Creating Projects

Click the “New Project” button, and on the following dialogue, give the project a name and choose a board and a framework – if there are options available for the chosen board, the framework will usually be automatically selected based on the board you have chosen. When choosing a board, start typing, and the list will be filtered according to your typed text.

This is where the pio settings get projects_dir is used. As you may have noticed, although you set this way back when installing PlatformIO, it was never apparently used for new projects – you always had to be in the desired location to create a new project. So leaving the box ticked to “use the default location,” the project will be created there. Hover over the “?” to see where the default location will be. You can also click the “custom” link to define where you want to create the new project.

Click the “Finish” button, and after a small delay, a message will appear telling you that the project has been created and will be found wherever you chose and that you can process it with the command platformio run. Hmmm – and there was me thinking I could use PIO Home to edit projects. No such luck I’m afraid. Opening Projects

It’s useful to create projects, but the option to “Open Project” simply tells you to go to the location where the project is and open it in your favorite IDE. However, maybe the future will be different, and we can edit and so on within the browser. That would be fun!

So, after creating the project, you are pretty much back in your favorite IDE or editor, editing and running pio run commands to compile the code and pio run -t upload to upload with the bootloader. Importing Arduino Sketches

Another useful feature of PIO Home is that existing Arduino projects can be imported from the PIO Home main screen. To do this, you simply click “Import Arduino Project” and follow the on-screen prompts to
  • Choose a board.

  • Select the existing Arduino project.

Then click the “Import” button.

A new folder will be created in your default PlatformIO project directory. It will not be named in a meaningful manner, so renaming it might be helpful and wise. Within that directory, you will find the usual PlatformIO directories and files, and, finally, the original sketch will be found in the src directory, with its original *.ino name.

To compile the imported project, simply execute the command pio run within the project’s top-level directory where the file platformio.ini is to be found.

To be honest, pio home doesn’t appear to be of much use as described earlier, other than as an easy way to import existing Arduino sketches. However, when you use the PlatformIO IDE variant, coming next, that home screen is much more useful in that environment.

6.1.6 PlatformIO IDE

This section of the book will concentrate on the PlatformIO IDE variant using the VSCode editor or, in my case, the VSCodium version which is slightly different from the original in that it doesn’t “phone home” to Microsoft with details of what you might have been doing.

What’s the difference between PlatformIO Core and PlatformIO IDE?

PlatformIO Core, as discussed, installs a number of command-line tools to allow you to use your favorite editor, or programming IDE, to develop software for the Arduino board, either in Arduino Language or in the AVR’s native C++ format. You normally edit the code in your editor and compile it from the command line.

PlatformIO IDE is simply an extension or plugin to the Atom and the VSCode editors which effectively turns those two editors into an IDE to develop Arduino software. In this variant, you develop the code in the editor and then compile and upload it also from within the editor.

Don’t get confused. PlatformIO IDE isn’t necessary to generate project files for a number of IDEs.

The "IDE" part of the name comes from the fact that this version of PlatformIO turns your favorite editor into an IDE for generating code for your embedded devices. This assumes that your favorite editor is Atom or VSCode/VSCodium, that is!

I use VSCodium as my editor, so the remainder of this chapter will look at installing PlatformIO into that specific editor; however, the process is almost identical if you are using Atom.

With Atom, there is a toolbar with big chunky buttons down the left side where you can compile and upload projects. With VSCode/VSCodium, there is an alien’s head icon added to the left toolbar, which opens the Project Tasks list, from where you can click any of the activated options to run compilations and so on. There does appear to be more options in VSCode/VSCodium than in Atom. Installation

To install PlatformIO IDE in VSCodium, either
  • Open the extensions view on the left side.

  • Press Ctrl+Shift+X.

  • Click View ➤ Extensions.

The extensions search should now open on the left side of the editor window.

In the search box at the top, type in “PlatformIO” and choose to install the option which appears, probably at the top of the list, “PlatformIO IDE.”

After it installs, a window at the bottom of the editor window will open, and a few tools will be installed to enable PlatformIO IDE. Do not close the window or move to another editor tab until you are advised that the installation is complete.

To start using the new plugin, you have to restart the editor, so click the button helpfully displayed by the PlatformIO installation process.

After restarting, you will notice the following:
  • A new editor tab appears named “PIO Home,” with an alien’s head as an icon.

  • The command palette (View ➤ Command Palette or Ctrl+Shift+P) has a number of new PlatformIO commands listed. PIO Home Tab

On the PIO Home tab in the editor, you have a new toolbar on the left and some options on the main display area. On the toolbar, there are icons for the following:
  • Home – Takes you back to the PIO Home page, if you happen to have chosen one of the following options.

  • Inspect – Allows various details of an existing project to be inspected and displayed. The code is analyzed as in memory usage and so on.

  • Libraries – Allows you to search for and install libraries that may be required by some Arduino and AVR projects.

  • Boards – Allows you to search for a board. Try entering “Duemilanove” and clicking the search icon. The two variants of the board will be shown alongside some important details of the boards such as the platform and framework required, the memory sizes, etc.

  • Platforms – Displays any installed platforms and allows you to uninstall them or to install any new requirements for a new board.

  • Devices – Assuming you have an Arduino board attached, this option will display details of the board(s), the type of communications chip, and the port it is attached to. Similarly to the arduino-cli, this option will not detect an ICSP device.

While on the main page itself, we can see these options:
  • New Project – This should be fairly obvious. It allows you to create a new project and pick as many boards for it as you wish.

  • Import Arduino Project – This option lets you navigate to an existing Arduino project and import it into PlatformIO’s favored format.

  • Open Project – Opens an existing project within the editor and allows you to continue developing it.

  • Example Projects – Displays a few example projects and allows you to import them into PlatformIO for inspection or learning. You can, of course, compile the example projects.

At the bottom of the screen is a couple of items of recent PlatformIO news and a list of your most recent projects. If this is a new install, you probably don’t have any listed. If you did, you have options here to hide the projects from this list or to open it. Clicking the “open” link opens the VSCode/VSCodium explorer on the left side of the screen to the top-level directory for the project. From there you can open files for editing in the usual manner. Creating a New Project

A new project is created from the PIO Home screen. If it is not already being displayed, then click the PlatformIO icon in the left-side toolbar to open the PlatformIO menu. Under quick access, open the list of options under “PIO Home” and click “Open.” A new tab appears named “PIO Home.”

On this tab, click the new project option. Then fill in (or select) the appropriate options for the project name, the board you want to use, and the location for the project. You can supply a custom location or simply accept the default, which will be displayed if you hover over the “?”.

If you wish to use the same source code for numerous different boards, simply keep adding new ones until you have everything set up as you wish.

Click the “Finish” button to create the project. After a short delay, the PIO Home tab will close. This never used to happen prior to version 4.1.0, so was a little disconcerting! On the left, the explorer will open with the various files and directories of the new project on display. The PIO Home tab will then reopen, after another short delay. I usually keep it closed as it is of little use during development.

You can now edit your files in the usual manner. Opening Existing Projects

If your project already exists, then you can use the PIO Home tab to open it. As in the preceding text, make sure the tab is visible in the editor, then click the “Open Project” option, navigate to the project’s location, and click the “Open <project name>” button.

As before, the PIO Home tab will close, and the explorer on the left will open at the project’s location. The project can now be edited as required. Editing the Project

The PlatformIO system expects to find header files within the include directory and source files in the src directory under the project’s home. To test a new project, open the dummy src/main.cpp file and type in the code from Listing 6-7.
#include <avr/io.h>
#include <util/delay.h>
int main(void)
    // D13 is actually PortB Pin 5. Configure
    // that pin as an output.
    // This equates to the Arduino setup() function.
    DDRB = (1 << DDB5);
    // This equates to the Arduino loop() function.
    while (1)
        // Toggle the LED by writing to PINB.
        PINB |= (1 << PINB5);
    return 0;
Listing 6-7

Yet another blink sketch! Compiling a Project

Once editing is done, you can compile the project either from the left-side toolbar or from a terminal which can be opened in the editor (Terminal ➤ New Terminal or Ctrl+Shift+#). If you use the terminal, it opens into the project’s location, and you can run any of the usual PlatformIO commands already described in Section 6.1.2, “Testing PlatformIO Core.”

To compile from the toolbar, you need to open the PlatformIO window on the left by clicking the icon. Then, under the “Project Tasks” list, select “Build” to build the project. You can also press Ctrl+Alt+B to build it from within one of the project’s files open in the editor.

A new terminal will open, and progress will be displayed. You are requested to close this terminal when finished with it, by pressing any key. This will only work if the terminal has focus. (Ask me how I know this.)

If all looks okay with the compilation, we are ready to upload or program the board. Upload or Program a Project

In the Project Tasks list, click “Upload” to upload using a bootloader or “Upload using programmer” to use an ICSP device. This latter option requires that you have added the device’s details to the platformio.ini file in the usual manner:
;uploader = usbtinyisp
upload_protocol = usbtiny

From PlatformIO 4.1.0 onward, the preceding first line is no longer required and has been commented out.

Another new terminal will open, and progress messages will be displayed as appropriate. Then when it completes, your Arduino board should be blinking happily again.

That’s a very quick and high-level overview of something that’s becoming very popular in the Arduino world, especially with users of 3D printers running the Marlin Firmware as Marlin 2.0 onward uses PlatformIO to build and upload the firmware.

I personally have swapped over from the Arduino IDE to PlatformIO for most of my projects and experimenting with AVRs and Arduinos. I find it a lot faster to develop software using PlatformIO, and I’m more comfortable in the editor than in the Arduino IDE.

Coming up next, I’ll be taking a look at the new, but still under development, Arduino CLI – a command-line version of the Arduino IDE which allows you to use your own favorite editor and make files to create and build your Arduino projects.

6.2 Arduino Command Line

The Arduino IDE has always had a sort of command-line version, at least on Linux. Windows had a separate version due to the way that Windows GUI and command-line applications are so different. However, in August 2018, a new Arduino command-line application, arduino-cli, came out in alpha test.

It is possible that what is explained here might be likely subject to some changes as time goes by.

When I first started looking into the arduino-cli, it was September 2018, and the latest release at that time was 0.2.1-alpha. It had quite a few bug and foibles, but hey, it’s an alpha release so it’s expected. The version I’m looking at here is a much later release, 0.6.0 (it’s November 2019 now), and many of the original bugs and foibles have been sorted. As more releases are, ahem, released, things can only get better.

The use of the Arduino CLI is to help in getting code built using make files, for incorporating into other IDEs and, most likely, as the basis for whatever IDE changes appear in version 2 of the Arduino IDE itself. It has been noted on the Arduino Developers Google Group (https://groups.google.com/a/arduino.cc/forum/#!forum/developers) that visually impaired users have great difficulty with IDEs and that command-line versions are much better, so this can only be helpful.

6.2.1 Obtaining the Arduino CLI

Stable releases are available for download from the project’s main release page at https://github.com/arduino/arduino-cli/releases which has links to the all binary releases for numerous platforms – there are 32- and 64-bit versions for Linux and a version for Linux on ARM chips – Raspberry Pi, for example. Users with Windows or Macs are not left out either. Click the link to download the appropriate version for your system.

You can always get to the most up-to-date release using the URL https://github.com/arduino/arduinocli/releases/latest.

On the main project page, there are additional links to various nightly builds. These can be used if you want the latest, bleeding-edge release; however, you are advised to stick with the stable releases.

Failing all this, you can, if you are so inclined, build your own from the source code. Links are on the preceding home page with the details. I’m avoiding this option as I’d want to look at the source and I’m not a Go (Golang) developer, yet.

6.2.2 Installing

There are details on the home page which hint that you can download and install the arduino-cli with a single curl command. While I’m sure that this will work perfectly well, I would be blindly allowing a script, downloaded from the Internet, to be executed without checking what it might do. I’m going with the latest release from the preceding URL instead.

Pick the file that’s appropriate for your system. I’m on 64-bit Linux Mint, so my file is arduino-cli_0.6.0_Linux_64bit.tar.gz.

After downloading, simply unzip (Windows users) or use tar on Linux and MacOS, as shown in the following Linux example:
tar -zvjf arduino-cli_0.6.0_Linux_64bit.tar.gz
On Windows, right-click the file and “extract,” or in the command line, the following will suffice:
unzip  arduino-cli_0.6.0_Windows_64bit.zip
The current version, 0.6.0, expands to three files:
  • arduino-cli which is the utility itself.

  • License.txt which is the license.

  • readme.md which is a markdown file used on the project’s main GitHub page. This is a good place to start reading.

If the location you unzipped/untarred the download isn’t on your path, you can either add it to the path or copy the arduino-cli file to a directory that is on your path:
cp arduino-cli ~/bin/
Test that the downloaded file works with this command:
arduino-cli version
arduino-cli Version: 0.6.0 Commit: 3a08b07
You can get a feel for the various commands and options available in the latest version with the following command:
arduino-cli help
The 0.6.0 version returns the following output:
Arduino Command Line Interface (arduino-cli).
  arduino-cli [command]
  arduino-cli <command> [flags...]
Available Commands:
  board         Arduino board commands.
  compile       Compiles Arduino sketches.
  config        Arduino Configuration Commands.
  core          Arduino Core operations.
  daemon        Run as a daemon on port :50051
  help          Help about any command
  lib           Arduino commands about libraries.
  sketch        Arduino CLI Sketch Commands.
  upload        Upload Arduino sketches.
  version       Shows version number of arduino CLI.
... Lots more output omitted here ...
Use "arduino-cli [command] --help" for more information.

6.2.3 Configuring the CLI

By default, the configuration assumes a number of details about your system. It’s best we find those out before we dive in and start creating sketches:
arduino-cli  config  dump
There’s not much to see though:
proxy_type: auto
sketchbook_path: /home/norman/Arduino
arduino_data: /home/norman/.arduino15
board_manager: {}
Unfortunately, we cannot change these settings from the command line yet as there are no options to the config command other than dump or init. If you need to make changes, then the following will be required:
arduino-cli config init
Config file PATH: /home/norman/.arduino15/arduino-cli.yaml

You now have all the default config stored in the file name listed earlier. You may edit the file and change the configuration as desired. Currently, there’s not much to change, but the readme file that came with the download does have some information of uses for this file, which may be of interest, but are not covered here.

Things are changing with regard to configuration, even as I type! I raised issue 503 (https://github.com/arduino/arduino-cli/issues/503) on GitHub, because the sketch new command no longer created sketches in the sketchbook location. I was advised that:
  • If a sketch name only is passed as parameter, assume that the sketch directory is the current directory.

  • If a relative path for the sketch is passed, join it with the current directory path.

  • If an absolute path for the sketch is passed, simply use it.

    This is to be applied to the sketch new command in addition to compile and upload commands.

    So, no reference at all to the sketchbook , so I would consider leaving the configuration well alone until the application settles down to at least release candidate or release level.

6.2.4 Creating Sketches

As the readme advises, the first task is to create a new sketch. This is done with the arduino-cli sketch new <name> command:
arduino-cli  sketch  new  MyFirstSketch
Sketch created in: /home/norman/MyFirstSketch
That was easy, and it has created a blank sketch, MyFirstSketch.ino, with the content in Listing 6-8.
void setup() {
void loop() {
Listing 6-8

A brand-new sketch

As noted earlier, version 0.6.0 is not using the default sketchbook location from the config dump – previous versions did. The sketch is created within the current directory. For now, let’s move it to the proper place:
mv  ~/MyFirstSketch/  ~/Arduino/
cd  ~/Arduino/MyFirstSketch
You can see from the preceding code that there’s not much to a new sketch, but it’s a start on developing your next great project. The almost obligatory Blink sketch is obviously required at this point, so edit the generated MyFirstSketch.ino file with your favorite editor with the code in Listing 6-9.
void setup() {
void loop() {
    for (short x = 0; x < 4; x++) {
        digitalWrite(LED_BUILTIN, HIGH);
        digitalWrite(LED_BUILTIN, LOW);
Listing 6-9


Well, I can’t just keep using the same blink sketch all the time, can I? I need a bit of variety!

We are now ready to compile the sketch, but as this is a new installation, we have a bit of preliminary work to do. First, we should update the system to make sure we have the latest package index file:
arduino-cli core update-index
Updating index: package_index.json downloaded
Now, connect your Arduino to the USB port in the normal manner. Leave a few seconds for it to initialize, and search to see what you have. I have a genuine Duemilanove and a non-genuine Mega 2560 attached. Let’s see what the system recognizes:
arduino-cli board list

After a short delay, I see the information relating to my two boards displayed. Unfortunately, the lines of text are far too wide for this page, so I shall have to summarize.

For my Mega 2560 clone, I see that it
  • Is attached to port /dev/ttyACM0 which is a Serial Port (USB) type of port

  • Has a board name of Arduino/Genuino Mega 2560

  • Uses the arduino:avr core

  • Has arduino:avr:mega as its FQBN

The board’s FBQN is its “Fully Qualified Board Name” and is required, later, to compile and upload sketches.

For my genuine Duemilanove, I see the following:
  • Attached to port /dev/ttyUSB0 which is a Serial Port (USB) port type

  • Has a board name of Unknown

  • Apparently, has no known core

  • Apparently, has no FQBN

    The readme advises that boards will be listed as “unknown” if they are connected via an FTDI adaptor. I’m using the Arduino-supplied USB cable on my Duemilanove, but on that board there is an FTDI chip, so I see the board listed as unknown.

Boards are detected based on a vendor and product ID, VID/PID. On boards with FTDI, these identifiers are generic, so the actual board cannot be determined. This is not a major problem here, however, as both my boards have at least been identified as being present.

Boards will not be detected if they are connected to your computer with an ICSP device.

Now we need to install a core (aka platform) for the boards. The Mega is easy as it lists its core as “arduino:avr” which is useful. The Duemilanove, on the other hand, doesn’t list a core, so how do we determine the correct core?

Well, given that it too is an Arduino and has an AVR ATmega328P on board, I’m certain that it has the same core. However, we can search
arduino-cli board listall | grep -i duemilanove
Arduino Duemilanove or Diecimila    arduino:avr:diecimila

This is useful as it lists the board’s name followed by its FQBN. The Fully Qualified Board Name is required when compiling or uploading sketches to the board. It also starts with the appropriate platform/core name, “arduino:avr”.

6.2.5 Installing Platforms

At the moment, you only have the command-line tool itself. In order to compile code for Arduino boards with AVR microcontrollers, you must first install a “core” or “platform.” In my own case, I need the arduino:avr core for both my boards:
arduino-cli core install arduino:avr
Downloading packages...
arduino:avr-gcc@7.3.0-atmel3.6.1-arduino5 downloaded
arduino:avrdude@6.3.0-arduino17 downloaded
arduino:arduinoOTA@1.3.0 downloaded
arduino:avr@1.8.1 downloaded
Installing arduino:avr-gcc@7.3.0-atmel3.6.1-arduino5...
arduino:avr-gcc@7.3.0-atmel3.6.1-arduino5 installed
Installing arduino:avrdude@6.3.0-arduino17...
arduino:avrdude@6.3.0-arduino17 installed
Installing arduino:arduinoOTA@1.3.0...
arduino:arduinoOTA@1.3.0 installed
Updating arduino:avr@1.6.23 with arduino:avr@1.8.1...
arduino:avr@1.8.1 installed
We can check what we have now with the following:
arduino-cli core list
ID             Installed Latest Name
arduino:avr    1.8.1     1.8.1  Arduino AVR Boards

Those numbers seem to match up with the list of downloaded “stuff,” so we should be good to go.

Don’t worry. You don’t need to jump through all these hoops every time you want to compile with the arduino-cli, just the first time, or when you add a new board that had a different core of course.

6.2.6 Compiling Sketches

The time has arrived. We are now ready to compile our first sketch. You should still be located within the sketch’s directory. In my case, that’s /home/norman/Arduino/MyFirstSketch, so let’s compile it, first for my Mega 2560:
arduino-cli compile --fqbn arduino:avr:mega MyFirstSketch
Error during build: build failed: unable to stat Sketch
      location: stat /home/norman/Arduino/MyFirstSketch/
      MyFirstSketch: no such file or directory
Oh well, that didn’t go well. Why not?
  • If you are located in the parent directory of the sketch, /home/norman/Arduino in my case, the preceding command works fine.

  • If you are located elsewhere on the system, perhaps in /home/norman, then you need to supply the full path to the sketch’s top-level directory – /home/norman/Arduino/MyFirstSketch or, the short version, ~/Arduino/MyFirstSketch.

Let’s try another compilation, from the sketch’s parent directory instead:
cd ..
arduino-cli compile --fqbn arduino:avr:mega MyFirstSketch

When compiling a sketch, you must supply the board’s FQBN. If the compilation succeeds, the usual details about the RAM usage will be displayed. If you need verbose output, simply add the -v command-line option to the preceding compile command.

Now, let’s compile the same source code for the Duemilanove:
arduino-cli compile \
            --fqbn arduino:avr:diecimila \

Yes, on Linux, you can split a long command over a number of lines if it makes it more readable. Each line, except the last, must end with the backslash character and an immediate linefeed – there are no spaces between those characters.

You will see other occasions in this book where I do this. It helps keep things on the page.

Sometimes, when compiling, you might see an error like this:
avr-g++: error: missing device or architecture after '-mmcu='
Error: exit status 1
Compilation failed.

This is a compiler error, and it is telling you that the command line passed to the compiler was missing an option telling it the microcontroller in use on the board in question.

In older versions of arduino-cli, this used to happen for my Duemilanove and my Nano, so I had to update the board’s FQBN at compile and upload time, to specify the appropriate AVR microcontroller. This actually makes sense as some early boards have an ATmega168, while later boards, mine, for example, have an ATmega328.

The following should resolve the issue if you suffer from these errors:
arduino-cli compile \
            --fqbn arduino:avr:diecimila:cpu=atmega328 \

Adding the cpu to the FQBN gets rid of the error, and the sketch compiles.

If, like me, you have a Duemilanove or a Nano, then either of these can use the ATmega168 or the Atmega328 family of microcontrollers. While you might not need to, you can always specify the cpu option and be absolutely certain that the compiler will compile the code for the correct CPU. Doing this is the equivalent of selecting the correct board option from the Tools ➤ Boards menu in the IDE.

You can use the -v option on the compile command to see the full, verbose, compilation text. This is similar to what is produced in the Arduino IDE when you have the verbose compilation option enabled.

6.2.7 Uploading Sketches

Uploading a sketch uses a similar command to compiling, and you must remember to always use the same FQBN, or it will ask you to compile the sketch first. The port name is always required when uploading. If you forget the port, then the arduino-cli board list command is your friend.

Uploading to my Duemilanove is carried out as follows:
arduino-cli upload \
            --fqbn arduino:avr:diecimila \
            --port /dev/ttyUSB0 \

If you left the cpu=atmega328 parameter attached to the FQBN, it will be ignored.

There will be no output displayed on screen if all went well with the upload. If you wish to see the upload details, just add -v to the command, and you will get the same verbose output as you would have seen in the IDE.

If you see something like the following error message, change up into the parent directory – you are inside the sketch directory:
Error during Upload: compiled sketch
    not found
It is possible that you might encounter the following upload error, at least, on Linux:
ser_open(): can't open "/dev/ttyUSB0": Permission denied
Error: exit status 1
Error during upload
This is simply because the user that you are logged in as is not a member of the group which owns the port that the upload command was trying to use to communicate with the board. You can check, and fix the problem, as follows:
ls -l /dev/ttyUSB0
crw-rw-rw- 1 root dialout 188, 0 Nov 30 19:09 /dev/ttyUSB0
norman: norman adm cdrom sudo dip lpadmin sambashare
usermod -a -G dialout your_user

Unfortunately, you will need to log out and back in again to pick up the new group. This is mildly irritating, so here’s a quick tip on temporarily working around the need to log out.

Instead of logging out and back in again, just, from the command-line session that you are in, run this:
su - your_name

You will be asked for your password and will start a new shell with the new group assigned. Test it by running the groups command again, and note that now you do have dialout present.

Now you can upload to your Arduino board.

6.2.8 Uploading Sketches with an ICSP

At present, uploading is only possible when you are using an Arduino board which still has a bootloader present. In-Circuit System Programmers (ICSPs) are not yet supported. If you have a board connected using an ICSP, then the arduino-cli board list command will not see it.

There is a workaround, but it’s not completely ideal, but it does work.

If you open a sketch in the Arduino IDE, then go to File ➤ Preferences and set the upload option to verbose. Now, compile and upload the sketch to the board with the ICSP device you want to use with arduino-cli. Any sketch will do, even an empty one.

In the output area, look for and highlight (or select) the line which mentions “avrdude.” It will look something like this exceedingly long line:
6.3.0-arduino17/bin/avrdude -C/home/norman/.arduino15/packages/
arduino/tools/avrdude/6.3.0-arduino17/etc/avrdude.conf -v
-patmega328p -cusbtiny -Uflash:w:/tmp/arduino_build_760389/
Now, in my case, that is an upload to a Duemilanove using my USBtiny programmer. From this, I can see the various parameters on the command line and can write a shell script to do the required work after compiling with arduino-cli. You can see what I created in Listing 6-10 where the code for upload.sh is shown.
# a script to program a Duemilanove with a compiled sketch
# created by the Arduino CLI utility. Currently, version 0.6.0
# does not have the ability to upload by any means except a
# bootloader.
# The following command is slightly adapted from that used in
# the Arduino IDE to program a Duemilanove with the USBtiny
# programmer. It requires a filename on the command line,
# which is the name of the compiled hex file to be uploaded.
# Devices and ports etc.
# AVR device:
AVR=atmega328p                             ①
# ICSP device:
ICSP=usbtiny                               ②
# programs and config files
# Where everything lives:                  ③
# Avrdude executable:                      ④
# Avrdude configuration file:              ⑤
# Leave blank if you don't want all the output.
# Otherwise, use -v
VERBOSE=-v                                 ⑥
# Get hex file.
HEXFILE="${1}"                             ⑦
# Did we get a parameter?
if [ "${HEXFILE}" == "" ]
    echo A hex filename must be passed.
    exit 1
# Does the file exist?
if ([ ! -f "${HEXFILE}" ])
    echo "${HEXFILE}" is not a filename.
    exit 1
# Does the file have the 'hex' extension? ⑧
if [ "${EXTENSION}" != "hex" ]
    echo "${HEXFILE}" is not a valid compiled Arduino sketch
    exit 1
# Upload via the ICSP device.             ⑨
     -p "${AVR}" -c "${ICSP}" -Uflash:w:"${HEXFILE}":i
Listing 6-10

The upload.sh shell script for Linux

  •     ①    This is a copy of the device name extracted from the upload command line displayed by the IDE.

  •     ②    This is a copy of the uploading device name, again, extracted from the IDE’s command line for the upload.

  •     ③    This is the parent directory where the avrdude applications, bin directories, etc. are to be found. No trailing “/” is required. As before, this path is extracted from the IDE output.

  •     ④    This is where we find the avrdude file, relative to $AVRDUDE_HOME.

  •     ⑤    This is where we find the avrdude.conf file, relative to $AVRDUDE_HOME.

  •     ⑥    We want verbose output. If you don’t want all the chatter from avrdude, remove the -v and you will get less output when uploading.

  •     ⑦    From here down, we do a bit of validation. Did we get a file name passed to us? Is it actually a file name for a file that physically exists? Does it have an extension of “hex” on the file name?

  •     ⑧    Nobody said that bash coding was easy! This extracts the file name’s extension. We are looking for “hex.”

  •     ⑨    The upload happens here. Of course, this will overwrite your Arduino’s bootloader, so from now on, you will have to continue using the ICSP device or burn a new bootloader.

Your will need to check and maybe replace my directory names to those appropriate to your system.

If you decide to follow my example and create an upload.sh script, beware when using the Arduino IDE if it asks to update your boards or libraries. This can have the effect of changing the names of some of the directories I’ve used in the following in my script. If you suddenly start getting upload errors, check what’s been updated and amend the script.

This script also doesn’t upload any data to the ATmega328P’s EEPROM. I did say that this wasn’t an ideal solution.

So I hear you ask, “How do I find out where the compiled hex file is?” Simple. Once your sketch compiles correctly, with no errors or warnings, you will be able to find the required .hex file in the sketch’s directory:
ls -1 MyFirstSketch      # That's a one, not an ell.
MyFirstSketch.arduino.avr.diecimila.elf MyFirstSketch.arduino.avr.diecimila.hex MyFirstSketch.arduino.avr.mega.elf MyFirstSketch.arduino.avr.mega.hex MyFirstSketch.ino

We can see from the preceding code that different boards have different files. The cpu that we may have needed to use when compiling, however, is not part of the output file name – only the FQBN is used.

You can now pass the required file name to the upload.sh script as a parameter, and the sketch will be uploaded via the ICSP device instead of using the bootloader.

6.2.9 Burning Bootloaders

As of version 0.6.0, the latest version available at the time I wrote this, it is still not possible to burn a bootloader with the arduino-cli utility. So, if you have managed to either corrupt or overwrite the bootloader, what can you do?

In a manner similar to that described earlier to enable uploads using an ICSP device, there is a workaround – and again, the Arduino IDE is required.

Obviously, if you have the Arduino IDE installed, you can burn a bootloader for your device at any time you like using Tools ➤ Burn Bootloader.

The process is very similar to that described earlier to upload a sketch with an ICSP. We use the Arduino IDE, in verbose mode, to extract the required command lines used when burning a bootloader and then simply create a shell script with just about everything parameterized, ready to be run on the command line.

Extracting was easy. I burned a bootloader to my Duemilanove with my USBtiny device and copied the two calls to avrdude from the verbose output. You will see them at the bottom of Listing 6-11, my burnBootloader.sh script.

If you are writing the script for a different device or board, you will need to change the device and board names and locations to match your system.
# A script to program a Duemilanove with the default Arduino
# bootloader and fuses. The script assumes that the device
# is indeed a Duemilanove, that the default bootloader hex
# file exists and that a USBtiny programmer will be used.
# You CANNOT burn a bootloader using the current bootloader!
# Run the "burn bootloader" command in the IDE, for your
# device, with verbose upload configured to see your command
# line, and change the necessary variables below.
# Devices and ports etc.
# AVR device:
AVR=atmega328p                             ①
# ICSP device:
ICSP=usbtiny                               ②
# programs and config files
# Where everything lives:                  ③
# The following long path is split on two lines. There
# are no leading or trailing spaces on each line.
# Avrdude executable:                      ④
# Avrdude configuration file:              ⑤
# Leave blank if you don't want all the output.
# Otherwise, use -v
VERBOSE=-v                                 ⑥
# Bootloader details:                      ⑦
# Fuses:                                   ⑧
# Does the bootloader file exist?          ⑨
if ([ ! -f "${BOOTLOADER}" ])
    echo "${BOOTLOADER}" is not a valid filename.
exit 1
# Does the bootloader file have the 'hex' extension?
if [ "${EXTENSION}" != "hex" ]
    echo "${BOOTLOADER}" is not a valid bootloader file
    exit 1
# Set the fuses for a Duemilanove.         ➉
-p "${AVR}" -c "${ICSP}" -e -Ulock:w:"${UNLOCK}":m \
-Uefuse:w:"${EFUSE}":m -Uhfuse:w:"${HFUSE}":m \
# Burn the actual bootloader hex file.        ⑪
-p "${AVR}" -c "${ICSP}" -Uflash:w:"${BOOTLOADER}":i \
Listing 6-11

The burnBootloader.sh shell script for Linux

  •     ①    This is a copy of the device name extracted from the upload command line displayed by the IDE.

  •     ②    This is a copy of the uploading device name, again, extracted from the IDE’s command line for the upload.

  •     ③    This is the parent directory where the avrdude applications, bin directories, etc. are to be found. No trailing “/” is required. As before, this path is extracted from the IDE output.

  •     ④    This is where we find the avrdude file, relative to $AVRDUDE_HOME.

  •     ⑤    This is where we find the avrdude.conf file, relative to $AVRDUDE_HOME.

  •     ⑥    We want verbose output. If you don’t want all the chatter from avrdude, remove the -v and you will get less output when uploading.

  •     ⑦    This is where the bootloader used by the IDE is to be found. This is extracted from the command line passed from the IDE to avrdude.

  •     ⑧    We need to set three fuse bytes for the Duemilanove. These are defined here and are as per the IDE’s command line. There are a couple of lock bytes also that we need.

  •     ⑨    From here down, we do a bit of validation. Is the bootloader file name a file that physically exists? Does it have an extension of “hex” on the file name?

  •     ➉    This call to avrdude unlocks the device and sets the three fuse bytes as required for a Duemilanove. The fuse settings can be extracted from the Arduino installation’s boards.txt file, if you are stuck, but they are also able to be extracted from the command line used by the IDE to burn the bootloader.

  •     ⑪    This call to avrdude burns the bootloader file, as supplied with the Arduino IDE software, and then locks the device again.

If you decide to follow my example and create a burnBootloader.sh script, beware when subsequently using the Arduino IDE if it asks to update your boards or libraries. This can have the effect of changing the names of some of the directories I’ve used in the following in my script.

If you suddenly start getting upload errors, check what’s been updated and amend the script. In my case, the BOOTLOADER_HOME location changed from version 1.8.1 to 1.8.2 and broke my script.

I did say that this wasn’t an ideal solution, but at least it was a simple fix.

6.2.10 Serial Usage

If you need to monitor what the Arduino is sending to the serial device, you will not yet be able to do so with the current version of the arduino-cli. However, if you install the screen utility on Linux, you can see what the board is sending back as follows:
screen /dev/ttyUSB0 9600
Windows or, indeed, Linux users can use the freely available putty utility with the following command:
putty -serial COM4 -sercfg 9600

You would most likely replace COM4 with the port you uploaded the sketch on. Linux users would use /dev/ttyuUSB0 or similar.

After a short pause, the display should begin showing what the Arduino is sending. To exit from the screen session, press Ctrl+A followed by the letter “k” in upper- or lowercase. You will be asked to confirm your wish to kill the session. Press “y” to continue.

To exit from putty, just close the window and confirm your intention to do so when prompted.

Obviously, 9600 is the baud rate and must match that which you used in your call to Serial.begin(), and /dev/ttyUSB0 is the same port that you uploaded the sketch to. Use the arduino-cli board list to see what it should be if necessary.

It’s possible to send data from the screen session to the Arduino, but it doesn’t echo on the display and seems to be quite slow for some reason. Further investigations appear to be required – there’s probably an option when starting screen that resolves it.

If you only need to send data down to the Arduino, that’s simple too:
echo [-n] "Hello World" > /dev/ttyUSB0

The -n may be required to flush the buffer and make sure that the data are sent to the Arduino.

That’s a very quick overview of something that’s to come soon in the Arduino world. It certainly looks interesting, and I’ll be keeping an eye on it. It’s not yet perfect, it is an alpha release after all, and I have not covered all the options and commands available – installing libraries, for example. I think it’s best to wait a while for a proper release to see if anything changes, but hopefully, this will have whetted your appetite.

I’ve seen comments on the Arduino forums about this utility, and a lot of people seem to be very happy that they no longer require to install Java to develop Arduino code.

After that slight deviation, I think it’s now time to look into the features of the ATmega328P that we need to know about in order to continue from where we were in Chapter 5 where we started replacing functions like pinMode(), digitalWrite(), and digitalRead() with plain AVR C++ variations.