Arduino & ESP32 programming with PlatformIO and Visual Studio Code

The Arduino development environment can do nothing, and that’s a good thing. Because this development environment was meant to keep the entry barrier into microcontroller programming as simple as possible and not to overwhelm the newbie with too many functions.

I admit, I am a professional developer. And I am missing some features in the Arduino development environment that make programming more fun for me. Therefore, the Arduino IDE is not enough for me.

Instead I use PlatformIO!

What is PlatformIO?

PlatformIO is a free tool especially for working with microcontroller systems like ESPs or Arduino.

It is installed as a plugin in the Visual Studio Code Editor (VS Code for short), which is also free.

So by working with VS Code, we have a very powerful development environment that offers many more features than the Arduino IDE. By the way, Visual Studio Code is not to be confused with Visual Studio (without Code), the big software development platform. I think the very similar names are a bit of an unfortunate choice.

We will now install VS Code. Then I’ll show you how to install the PlatformIO extension in VS Code. And parallel to my video where I compare Arduinos and ESPs, I’ll show how to use PlatformIO to load a program on an Arduino and on an ESP.

First of all, download VS Code for your operating system: Visual Studio Code

I would recommend the stable version of VS Code.

To install PlatformIO, we go to the Extensions section, sort of an app store for VS Code add-ons. There we search for the official PlatformIO extension, click install and wait for the installation to be finished.

Recommendations may pop up that you should install additional recommended add-ons. These are add-ons for the C/C++ programming language. Since the Arduino framework uses C++, the installation makes sense.

Create a new PlatformIO project

Now we create a first project in PlatformIO for the Arduino UNO board.

For this we go to the PlatformIO home page. Then click on New Project.

In the form we choose a project name, select the Arduino UNO as board and Arduino as framework.

For Location I always leave the default value, this is the easiest for now. If you move the mouse over the question mark, the default location is shown. For me this is my user folder/Documents/PlatformIO/Projects/.

Click OK, and the project will be created.

PlatformIO Folder Structure

In PlatformIO the projects are structured a little bit different than in the Arduino development environment. Let’s have a look at the folder structure of the empty project.

We can neglect the folder TEST for now. You only need it if you want to work with unit tests.

SRC contains all source code files. Here is also our main.cpp.

INCLUDE can be used to store header files here.

And LIB is used to store project specific libraries.

The platformio.ini is the configuration file for PlatformIO projects. But we will get back to that one later.

For now we are only interested in the SRC folder, because this is where the main.cpp is located, which we will now open with a double click.

PlatfomIO already fills the main.cpp with the bare setup() and loop() method.

Furthermore we have to include the Arduino.h library at the top. In the Arduino development environment this happens automatically in the background.

I’m including the same code here that I used in my video comparing Arduino boards to ESP boards.

The PlatformIO Buttons

Now let’s take a look at these little but important buttons down here.

These are the buttons that go with PlatformIO.

  • The house icon opens the PlatformIO home page
  • The checkmark icon checks the current code for errors.
  • The arrow icon also checks the code and if no error is found, it loads the current code onto the board.
  • The trash icon deletes any compiled libraries. These would then be recompiled on the next check or upload. This is how you can force a recompilation of your whole project.
  • The plug icon opens the serial console.
  • And the last icon opens a shell, if you want to enter PlatformIO or shell commands manually.
  • The two areas on the far left are only interesting for Git and version control.
  • And this button in between shows errors and warnings and presents them in a clickable list. So you can quickly move to a specific error in the code.

At the bottom right:

  • This is the position of the cursor.
  • And here you can set the size and type of indentation in the code. If you change this value, you should execute a Format Document afterwards. To do this, right-click in the source code and select Format Document.

Well, now we know how to upload the code. Then let’s do it.

The code is compiled and uploaded. And we should see, our LED blinking. Yay!

PlatformIO Project for ESP

Now we repeat the whole thing with an ESP32. Before I remove the Arduino now, let’s go to the Devices section of PlatformIO.

Here all boards are shown, which PlatformIO has discovered. My Arduino UNO is always shown as /dev/cu.usbmodem14101.

Now we connect the ESP32 and click Refresh.

Two new USB ports appear in the list of devices. Don’t ask me why there are two. But I know that /dev/cu.SLAB_USBtoUART is the correct port for the ESP32.

These two ports will be a problem in a moment. That’s why I’ll show you how to deal with it.

Now we create a new project.

We choose a project name and as board I take the generic Espressif ESP32 Dev Module. If you are using a different board that is listed right here, then of course I would take that.

As framework we take Arduino again of course.

We open the main.cpp and copy our Blinky code into it.

Click on Upload, and …. we get an error.

In the error message you can see what happened: PlatformIO tried to upload the code to this USB port. But this is the wrong port. PlatformIO doesn’t know which of the two ports is the right one, and of course promptly takes the wrong one.

But we can solve this problem with the project settings.

How to Solve Upload Errors Due to Wrong Upload Port

Go to the Projects section of PlatformIO, find the right project in the list and click on Configure.

You should see a tab called esp32dev.

We need to add an option called upload_port.

To do this, I type the word port in the field at New Option.

PlatformIO presents a list of all options with the word port.

I choose upload_port.

A new field upload_port is added and the cursor jumps to this new field.

Now we can just click into this new field and get a list with all USB ports as we have already seen in the Devices section.

Here we select the /dev/cu.SLAB_USBtoUART port, and … very important … click on Save in the upper right corner. I always forget this one …

Under Windows this will be a COM port. There you have to watch the Devices list before and after connecting the boards.

By the way: if the Devices list does not change after connecting a new device try to restart VS Code. After a restart the new device should be listed.

If we upload our code now, everything should work because we told PlatformIO to always use this port /dev/cu.SLAB_USBtoUART when we click upload.

What Are Function Declarations and Definitions in Arduino?

PlatformIO sticks to the proper coding standards, while the Arduino IDE has taken some of the work off of us. Adding the Arduino.h header file, for example, is one such case.

Another important point is the declaration of functions.

Let’s take the code from the loop() section, put it into a function and call this new function in the loop() section instead.

Now, once we trigger the code check, we get an error. The problem is that our new function hasn’t been declared yet.

In C/C++, you have to declare functions as sort of registering that this function even exists.

In the Arduino IDE this was not a problem, because there it automatically looked for functions in the main file, and declared them for us under the hood.

If you still want to work with only one main.cpp there are two easy ways: either declare the functions correctly before using them.

To do this, just take the function name including the return value and parameters and write it once in the top section before using the function.

Or move setup() and loop() to the very end in the code, below all function definitions.

The code of the function itself is the function definition.

Registering a function before using it for the first time is the function declaration.

Helpful Tools in VSCode

Now of course the legitimate question: Why do we actually do this, if the Arduino IDE makes it more comfortable for us?

My experience is: when a project reaches a certain size or complexity, working with the Arduino IDE becomes very confusing. As soon as several functions or custom classes over several files are added, it is no fun working in the Arduino IDE any longer.

Here are a few helpful functions that the Arduino IDE does not have at the moment:


Where exactly Intellisense starts and stops is still not clear to me. For me these are the helpful suggestions when I start typing a command or function name. And also the parameter suggestions that are displayed to me. Also the function descriptions.

Go to Declaration/Definition

You can right click on a function and go directly to a function with Go to Definition or Go to Declaration. This also works with variables.

And Go to References lists all usages of a function or variable.

Peek does basically the same, but does not leave the current file, but shows a little preview window at the cursor’s position.

Rename Symbol

Rename Symbol is very useful if you are not satisfied with a variable or function name. All declarations, definitions and also calls can be changed in one go. In other development environments this is called refactoring.

Comments for function descriptions in Intellisense

Last tip: how to get your function descriptions to display with Intellisense?

If you provide a documentation for a function with certain keywords and a certain syntax, then these are displayed in the Intellisense popups you can see if you hover with the mouse over a function name.

The whole thing works according to a standard called Doxygen. I put a link to further Doxygen keywords in the list at the description below.


This brings us to the end of this PlatformIO intro. We have installed Visual Studio Code and the PlatformIO extension.

We’ve looked at the project structure and gone over a few important differences from the Arduino development environment.

We looked at how to use PlatformIO’s project settings to fix upload issues with the USB ports.

We did a quick blinky test with an Arduino and an ESP32 board.

And in the end, we learned about helpful features of PlatformIO that make programming easier.

I hope I could convince one or the other of PlatfomIO. Have fun, tinker around and stay healthy.