Using ESP32 and PlatformIO

This tutorial covers how to get started using the library with an ESP32 and PlatformIO. We’ll cover setting up the tooling, creating a blank project, and running the VT client example.

Installation

  1. Install Visual Studio Code.

  2. Install Git

  3. Install the ESP-IDF extension in VS Code

  • Search for ESP-IDF in the extension marketplace and click install

    Installing ESP-IDF with VS Code Marketplace
  • Select the newly installed extension, select “Express”

    Selecting the Express installation method
  • Select the most recent version of ESP-IDF and click “install”. This might take several minutes to complete.

    Choosing the version of ESP-IDF to install
  1. Install the PlatformIO Extension in VS Code

Installing the PlatformIO extension
  1. Select the PlatformIO Extension and wait for it’s installation to finish. This might take several minutes.

  2. Once PlatformIO is done installing, it might ask you to restart VS Code, or to reload it. Do this if it asks.

Everything you need should now be installed!

Getting Started With a Blank Project

To create a blank PlatformIO project that uses AgIsoStack, here are the basic steps.

  1. Open VS Code, select the PlatformIO extension, click “Create New Project”, and/or “New Project”

Creating a new Platform IO project
  1. Give your project a name, select your target ESP32 board, and make sure to select Espidf as your framework.

Setting Platform IO project settings
  1. Wait while PlatformIO creates the project. This can take several minutes depending on how much data PlatformIO has to fetch from the internet.

  2. VS Code may ask you to trust your workspace once the project is created. Click “yes”, and optionally consider checking the box to always trust your projects folder.

Setting Platform IO project settings

Now you’ve got a blank project, but we need to do a few more things to actually bring in the library and ensure it compiles.

  1. Rename your main.c file to main.cpp.

The AgIsoStack++ library is a C++ library, so we need to compile our main file as a C++ file instead of a C file.

  1. Change the definition for app_main to be extern "C" void app_main().

Because we are changing to a .cpp file, but the ESP-IDF framework expects app_main to have C-linkage, we need to add this decoration, or you may get a linker error when you build the project.

  1. Add the following line to your platformio.ini file: lib_deps = https://github.com/Open-Agriculture/AgIsoStack-plus-plus.git

This will tell PlatformIO to reach out to GitHub, download the library, and automatically integrate it via CMake.

Warning

PlatformIO will never automatically update AgIsoStack. You must run pio pkg update to update it periodically from the PlatformIO core CLI. It is important that you do this from time to time to ensure you have the latest features and bug fixes.

  1. Optionally, add lines to your platformio.ini file to allow the CAN stack to send logged errors and warning messages through the serial monitor, and to have backtraces parsed for you if your application crashes.

build_type = debug
upload_protocol = esptool
monitor_speed = 115200
monitor_rts = 0
monitor_dtr = 0
monitor_filters = esp32_exception_decoder

Your platformio.ini file should end up looking something like this, though your board and environment may be different:

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:denky32]
platform = espressif32
board = denky32
framework = espidf

lib_deps = https://github.com/Open-Agriculture/AgIsoStack-plus-plus.git

build_type = debug
upload_protocol = esptool
monitor_speed = 115200
monitor_rts = 0
monitor_dtr = 0
monitor_filters = esp32_exception_decoder

Specifically, this configuration was created for the ESP-WROOM-32 (denky32), so if you have a different board, make sure to edit this appropriately.

  1. Include the header files you might need in main.cpp.

AgIsoStack++ is a somewhat large library. There are a lot of files, each separated by specific CAN or ISOBUS functionality. Check out the other tutorials, or the VT example below for what you might want to include.

At an absolute minimum, you’ll need these files to send or receive anything.

#include "isobus/hardware_integration/twai_plugin.hpp"
#include "isobus/hardware_integration/can_hardware_interface.hpp"
#include "isobus/isobus/can_network_manager.hpp"
#include "isobus/isobus/can_partnered_control_function.hpp"
  1. Set up the ESP32’s TWAI

The ESP32 has what is essentially a built-in classic CAN 2.0 controller. Because of this, you can use the TWAI interface on your ESP32 instead of having to add a serial CAN controller like the MCP2515.

Note

You will still require a CAN transceiver like this one to convert the low voltage outputs of the TWAI controller to actual CAN signaling voltages. You cannot hook a CAN bus directly to the TWAI pins.

Adding the following code will configure the TWAI.

extern "C" void app_main()
{
        twai_general_config_t twaiConfig = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_21, GPIO_NUM_22, TWAI_MODE_NORMAL);
        twai_timing_config_t twaiTiming = TWAI_TIMING_CONFIG_250KBITS();
        twai_filter_config_t twaiFilter = TWAI_FILTER_CONFIG_ACCEPT_ALL();
        std::shared_ptr<isobus::CANHardwarePlugin> canDriver = std::make_shared<isobus::TWAIPlugin>(&twaiConfig, &twaiTiming, &twaiFilter);
}

This code sets GPIO 21 and GPIO 22 to the be the TWAI transmit and receive pins respectively, and configures the default ISO11783/J1939 baud rate of 250k bits. It also ensures that all messages on the bus are passed through to the stack, and none are filtered out by the hardware. Lastly, it creates an instance of the AgIsoStack TWAI driver class, which will manage the TWAI for you.

You do not need to choose these same GPIO pins, but these are known to work well.

  1. Set up our device’s NAME, and start the CAN stack.

This is boilerplate code that can be found in nearly every AgIsoStack project that sets up your device and starts the CAN stack, with slight modifications for ESP32.

extern "C" void app_main()
{
        twai_general_config_t twaiConfig = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_21, GPIO_NUM_22, TWAI_MODE_NORMAL);
        twai_timing_config_t twaiTiming = TWAI_TIMING_CONFIG_250KBITS();
        twai_filter_config_t twaiFilter = TWAI_FILTER_CONFIG_ACCEPT_ALL();
        std::shared_ptr<isobus::CANHardwarePlugin> canDriver = std::make_shared<isobus::TWAIPlugin>(&twaiConfig, &twaiTiming, &twaiFilter);

        isobus::CANHardwareInterface::set_number_of_can_channels(1);
        isobus::CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver);
        isobus::CANHardwareInterface::set_periodic_update_interval(1);

        if (!isobus::CANHardwareInterface::start() || !canDriver->get_is_valid())
        {
                ESP_LOGE("AgIsoStack", "Failed to start hardware interface, the CAN driver might be invalid");
        }

        isobus::NAME TestDeviceNAME(0);

        //! Make sure you change these for your device!!!!
        //! This is an example device that is using a manufacturer code that is currently unused at time of writing
        TestDeviceNAME.set_arbitrary_address_capable(true);
        TestDeviceNAME.set_industry_group(1);
        TestDeviceNAME.set_device_class(0);
        TestDeviceNAME.set_function_code(static_cast<std::uint8_t>(isobus::NAME::Function::RateControl));
        TestDeviceNAME.set_identity_number(2);
        TestDeviceNAME.set_ecu_instance(0);
        TestDeviceNAME.set_function_instance(0);
        TestDeviceNAME.set_device_class_instance(0);
        TestDeviceNAME.set_manufacturer_code(64);
        auto TestInternalECU = isobus::InternalControlFunction::create(TestDeviceNAME, 0x81, 0);

        while (true)
        {
                // CAN stack runs in other threads. Do nothing forever.
                vTaskDelay(10);
        }

        isobus::CANHardwareInterface::stop();
}

This is the absolute minimum for the stack to address claim for you, and for it to be ready to accept your messages.

  1. Set up your ESP32’s OS and PThread options

As mentioned before, AgIsoStack is a fairly large multi-threaded library, so we need to adjust some platform settings to allow the library to run smoothly. Not doing this will probably cause your device to repeatedly crash at runtime! More specifically, we need to adjust the default stack size, and the amount of stack allocated to the pthreads task.

In PlatformIO, run menuconfig by either running pio run -t menuconfig or by selecting the option from the PlatformIO extension.

Running menuconfig

Warning

You may need to comment out the following line(s) in your root CMakeLists.txt file if you experience an error running menuconfig. Make sure you un-comment the line once you are done with menuconfig.

target_add_binary_data(TestAgIsoStack.elf "src/object_pool/object_pool.iop" BINARY)

Once menuconfig is running, navigate to Component config -> PThreads and change the settings to match the following:

Running menuconfig

Then, navigate to Component config -> FreeRTOS -> Kernel and configure the configTICK_RATE_HZ to be higher, at least more than 250, but not too high. A good value that worked when writing this tutorial was 1000 as shown below.

Running menuconfig
  1. Add your application code and build your project! Using the PlatformIO extension, click “build” to compile your project.

If you made it this far, but you’re not sure how to use the library to make a functional application yet, or you are having trouble compiling, check out the VT Client example in the next section, and be sure to read the other tutorials.

VT Client Example

To build and run a minimal, but interactive project that will load an ISOBUS object pool to a virtual terminal display, download the ESP32 PlatformIO example from our GitHub repository and copy all the ESP32 project’s files into a blank PlatformIO project like the one we created earlier.

Note

This example sets up the TWAI interface to run on GPIO 21 and 22. It does not require any external CAN controller, but does require a CAN transceiver like this one.

Note

The example’s platformio.ini file is configured for a WROOM/Denky-32 board, so you may need to edit it to match your board type.

The Wiring

In the example, we’re using GPIO 21 and 22 to drive our CAN transceiver. In the following image, you can see an example of how you might connect your ESP32, CAN transceiver, and bus using this configuration.

ESP32 wiring example

In this case we’re using a Deutsch DT 4 way plug to connect to the CAN bus, but the same can also be accomplished with a number of standard ISOBUS connectors such as this diagnostic connector:

An ISOBUS diagnostic connector wired only for the implement bus

Building and Running

Once you have copied all of the example files into a project, set up your specific board, and properly connected your device, use platform IO to build the project, and flash it to your ESP32 device.

If everything is set up correctly, once programmed, the ESP32 will automatically load the example object pool to your ISOBUS display.

An ISOBUS diagnostic connector wired only for the implement bus

If nothing happens, use the serial monitor to look for error messages from the application. These messages may help you troubleshoot what is happening.

Going Beyond the Basics

If you want to edit the VT example to run your own VT application, you will need to replace the object_pool.iop file with your own ISOBUS object pool, and you’ll need to edit the example to handle the inputs and outputs inside that pool.

Check out the Virtual Terminal Client Tutorial for more information on how to do that.

AgIsoStack has lots of other interfaces to make developing your own ISOBUS application on ESP32 easy, such as:

  • Vehicle guidance and speed messaging

  • ISO11783-10 Task Controller Client for implement section control and prescription maps

  • ISOBUS Shortcut Button (ISB)

  • Standard ISOBUS diagnostics

Take some time to review all the various header files in the library for protocols and technologies that are interesting to you!

If you would like to see more tutorials or have other feedback on this tutorial, please visit our GitHub page and feel free to open a discussion, or contribute to an existing discussion.