Developed under the Arduino for fans of the command line: PlatformIO or how to stop using the Arduino IDE


    Over the past year I have written quite a lot of code for Arduino and in the process I changed several development tools. The article mentions the options that I tried and in more detail about where I stopped. We will talk about a set of tools for the case when> 10 projects for different boards and a little about the development and installation of libraries.


    Development environment


    What is the problem?


    Probably because Arduino is not focused on professional developers, the ecosystem around the standard IDE is lacking the usual tools for me:
    • Only in the latest versions some kind of library management has appeared, so far without the similarity of Gemfile / requirements.txt / package.json, that is, it is impossible to specify for the project which versions are used.
    • no integration with git or other vcs
    • text editor does not compare with my favorite text editor
    • there is no possibility to save the board selection in the project
    • awkward compilation error output

    The Arduino website has a list of alternative development tools . In this list there are options that for various reasons did not begin to try. For example, Atmel Studio and Visual Studio CE did not consider. I wanted to find a tool that supports work from the command line.


    What I tried


    Ino


    Ino - a project from the Russian company Amperka, a command line utility for the Arduino firmware.
    Quite a popular project was,> 200 forks. Last commit in April 2014, therefore does not work with fresh versions of IDE (it seems starting from 1.5).
    There is a live fork Arturo , used it a little, but there were problems in some exotic cases.


    Arduino-makefile


    Arduino-Makefile - compile and load with make. Arduino Due, Zero and other 32-bit boards are not supported. The difference from the standard IDE is that the methods must be declared before use, so that when transferring finished projects you may need to edit the source code. If I remember correctly, I could not make friends with the Arduino-Makefile and SparkFun Pro Micro.


    What I use


    PlatformIO


    PlatformIO is an excellent project created by developers from Ukraine. It includes a command line utility through which you can start compiling and downloading programs to several microcontroller families (Atmel AVR, Atmel SAM, ST STM32, TI MSP430 and others). At the same time, different sets of libraries are supported (on the PlatformIO website they are called frameworks): Arduino, Energia, mbed, and also the native code for Atmel AVR, espressif, MSP430.
    PlatformIO is initially oriented to work from the command line, there are also plugins for integration with text editors and IDE: Atom, CLion, Eclipse, Emacs, NetBeans, Qt Creator, Sublime Text, Vim and Visual Studio
    PlatformIO is especially suitable if you:
    • one project for several boards, i.e. the same code should be compiled for different boards
    • many projects for different fees, i.e. each project is under one fee, but there are many projects and boards are different
    • there is a need to work via ssh, for example, if PlatformIO is installed on Raspberry Pi
    • keen rejection of the graphical interface

    image


    Use with Arduino

    I will not retell the documentation, here you can see the installation instructions , usage, in the Quick Start section.
    The project folder structure for PlatformIO differs from the Arduino IDE project, each project contains a platformio.ini file which indicates which boards are used. Thus, it is not necessary to choose the right board each time.
    I'll tell you by example how I use PlatformIO when developing a library for Arduino. The library has two examples, each of them is a project in the PlatformIO format. The projectio file platformio.ini lists all the cards on which the library should work:
    [env:nanoatmega328]
    platform = atmelavr
    framework = arduino
    board = nanoatmega328
    [env:sparkfun_promicro16]
    platform = atmelavr
    framework = arduino
    board = sparkfun_promicro16
    [env:due]
    platform = atmelsam
    framework = arduino
    board = due
    [env:teensy31]
    platform = teensy
    framework = arduino
    board = teensy31
    [env:nodemcu]
    platform = espressif
    framework = arduino
    board = nodemcu
    [env:uno]
    platform = atmelavr
    framework = arduino
    board = uno
    

    You can compile an example for all boards using the command:
    platformio run

    You can compile only for uno like this:
    platformio run -e uno

    Download uno firmware:
    platformio run --target upload -e uno

    Start the serial monitor:
    platformio serialports monitor

    Added aliases to .zshrc to make the commands shorter:
    alias compile="platformio run"alias upload="platformio run --target upload"alias serial="platformio serialports monitor"

    With them the same sequence of actions:
    compile         # компиляция для всех плат
    compile -e uno  # компиляция только uno
    upload  -e uno  # прошивка uno
    serial          # монитор последовательного порта

    There is also integration with Travis CI and other CI tools, more here .
    Actually, the Arduino IDE has a command line interface , but it is far from perfect.


    Nuances of PlatformIO

    PlatformIO speeds up work; it is more flexible than the Arduino IDE and it’s easier to automate routine tasks. There are several things to consider:
    • compilation in PlatformIO is not always equivalent to compiling in the Arduino IDE, something that was collected in PlatformIO may not compile in the Arduino IDE and vice versa
    • project folder structure does not match the structure for the Arduino IDE
    • Not all libraries are available for installation via platformio lib

    Serial.print ("Maybe better");


    What is the problem?


    Standard Serial.print () is slightly inconvenient in case you need to print the
    name and value of a variable, for example, to display "pin_2 = <pin state 2>, pin_3 = <pin state 3>" you have to do this:
    Serial.print("pin_2 = ");
    Serial.print(digitalRead(2));
    Serial.print(", pin_3 = ");
    Serial.println(digitalRead(3));

    Sometimes you also want to partially or completely disable the output to the series, for example, if it is used only for debugging. Of course, you can comment on the calls to Serial.print () for this, but I would like a more elegant option.


    What I tried


    arduinoLogging


    This lib uses printf-like syntax for printing, and also allows you to set LOGLEVEL and thus disable the output of some or all messages. Messages are displayed using the Error, Info, Debug, and Verbose methods.
    Example:
    #include"Logging.h"// LOGLEVEL может быть LOG_LEVEL_X, где X ∈ { NOOUTPUT, ERRORS, INFOS, DEBUG, VERBOSE }#define LOGLEVEL LOG_LEVEL_INFOSvoidsetup(){
        Serial.begin(9600);
        Log.Init(LOGLEVEL, &Serial);
        // это будет напечатано
        Log.Info("pin_2 = %d, pin_3 = %d"CR, digitalRead(2), digitalRead(3));
        // это не будет напечатано
        Log.Debug("это не будет напечатано, так как LOGLEVEL = LOG_LEVEL_INFOS");
      }

    Available modifiers

    wildcardcommentExample
    % sreplace with an string (char *)Log.Info ("String% s", myString);
    % creplace with an characterLog.Info ("use% c as input", myChar)
    % dreplace with an integer valueLog.Info ("current value% d", myValue);
    % lreplace with an long valueLog.Info ("current long% l", myLong);
    % xreplace and convert integer value into hexLog.Info ("as hex% x), myValue);
    % Xlike% x but combine with 0x123ABLog.Info ("as hex% X), myValue);
    % breplace and convert integer value into binaryLog.Info ("as bin% b), myValue);
    % Blike% x but combine with 0b10100011Log.Info ("as bin% B), myValue);
    % treplace and convert boolean value into "t" or "f"Log.Info ("is it true?% T), myBool);
    % Tlike% t but convert to "true" or "false"Log.Info ("is it true?% T), myBool);

    What I use


    advancedSerial


    The names of the Error, Info, Debug, and Verbose message levels in arduinoLogging are not neutral. Error does not necessarily print an error, it is just a message that is output on any LOGLEVEL (except NOOUTPUT).
    Considering also some disadvantages of printf, I wrote my own version, advancedSerial .
    Actually advancedSerial is two things: the ability to call print () and println () in a chain and message levels.
    int a = 1;
      int b = 2;
      aSerial.print("a = ").print(a).print("b = ").println(b);
      // также доступны короткие названия методов
      aSerial.p("a = ").p(a).p("b = ").pln(b);

    Full example Basic.ino
    Since the names of the methods coincide with the names of the standard methods Serial.print () and Serial.println (), then, if desired, in the source code you can simply replace Serial with aSerial.
    For the name of the message levels, I chose v, vv, vvv, vvvv, a fairly common way to denote the levels of detail of the output messages, usually found as flags -v, -vv and so on.
    With such names it is easier to edit one level to another, for example, vv -> vvv is easier than Info -> Debug.
    #include"advancedSerial.h"voidsetup(){
        Serial.begin(9600);
        aSerial.setPrinter(Serial);    // выбираем сериал через который будем печатать// устанавливаем фильтр, будут выводиться только сообщения уровня v и vv, а vvv и vvvv выводится не будут
        aSerial.setFilter(Level::vv);
      }
       voidloop(){
         aSerial.l(Level::vv).pln("Будет напечатано");
         aSerial.l(Level::vvv).pln("Не будет напечатано");
         delay(3000);
       }
    

    Full example Advanced.ino


    Memory saving

    If you wrap a string in the F () macro, it will not be loaded into memory (SRAM), so to save memory, use F ():
      aSerial.print(F("экономия 16 байт"));

    Of course, using advancedSerial adds some overhead projector compared to the standard Serial, I tried to estimate which one. Below are the compilation results for the Arduino Uno, since it has 2KB of memory and this is the minimum among the boards that I usually use.

    The usual series, without any libraries:
    voidsetup(){
       Serial.begin(9600);
      }
      voidloop(){
        Serial.print("test");
        Serial.println("test");
      }

    storage space: 5%
    dynamic memory: 9%

    advancedSerial:
    #include<advancedSerial.h>voidsetup(){
       Serial.begin(9600);
       aSerial.setPrinter(Serial);
       aSerial.setFilter(Level::vv);
      }
      voidloop(){
        aSerial.print("test").println("test");
      }

    storage space: 5%
    dynamic memory: 10%

    examples / Advanced.ino
    storage space: 9%
    dynamic memory: 26%

    examples / Advanced.ino using the F () macro
    storage space: 9%
    dynamic memory: 10%
    It turns out that the memory usage increases slightly. But advancedSerial is not the optimal solution in terms of resources, there are alternative implementations, for example, Debug .


    Installing Libraries


    What is the problem?


    By default, the Arduino IDE installs globally and in the sketch it does not fix exactly which libraries are used (except for #include directives of course) and which versions. Because of this, in order to compile a sketch on another computer, you need to know where to download the necessary libraries, again the versions of the libraries also need to be specified. To avoid such problems, I install libraries only locally, inside the folder with the sketch. Below are two ways to install libraries locally for the Arduino IDE and for PlatformIO.


    Arduino IDE


    I rarely use the Arduino IDE, maybe there is a better way. The method is as follows: install the libraries in a subfolder of your project and place the symlinks (shortcuts?) For each library in the libraries folder (in the folder where the Arduino IDE libraries are installed).
    By the way, if I remember correctly, the Arduino IDE compiles all libraries from the libraries folder when compiling any sketch, so the compile time increases if there are many libraries in libraries. Another reason not to use the Arduino IDE.


    PlatformIO


    Each PlatformIO project has a lib subfolder into which libraries can be placed. This is when manually installing libraries. Also, PlatformIO has a separate command to install the platformio lib libraries, unfortunately it defaults to installing libraries globally so that the libraries are installed locally in the lib subfolder you need to add to the platformio.ini of the project:
    [platformio]
    lib_dir = ./lib

    Read more about platformio lib in the documentation .

    Also popular now: