Development

This chapter shows which options are available to customize the charge controller to your own needs. It starts with creating your own EVerest modules that can be cross-compiled and run on the target, up to creating fully customized firmware images that can be installed on the target. This chapter also explains how to debug issues within the EVerest environment on the target.

Preparation Checklist for Development

The following bullet points should be checked before starting with the development of a custom EVerest module:

  • Please make sure that your charge controller is started and running (see Getting Started chapter). It is recommended to connect the charge controller to the local computer via Ethernet. This makes it easier to share files (e.g. configuration, image, binary, or logging files) between the local computer and the charge controller.

  • The EVerest development environment should be set up on the local Linux machine. Please follow the instructions in the EVerest documentation, in particular the chapter Prepare Your Development Environment and the chapter A Kind Of Quick Guide To EVerest. If you have problems setting up EVerest, please check the Zulip chat to see whether this problem is already known and whether solutions have already been found. To verify that everything is installed correctly, EVerest should be compiled once natively on your system.

  • Keep in mind that if you want to cross-compile an EVerest module, it must be compatible with the EVerest release used in the firmware. Please have a look at the official EVerest documentation to learn how to check out a dedicated EVerest release. The everest-core version used by the firmware can be found in the release notes of the firmware image.

Best practices for developing EVerest modules

It is recommended to develop your custom software on your local PC Linux environment, using a compiler, debugger, and tools that you are familiar with. Using tools like autotools, cmake, and pkg-config in your own projects makes the integration and cross-compiling process much easier.

A native installation of EVerest on your host system is also recommended, as it includes simulation examples that allow you to start and test EVerest locally on your machine. If the software you developed does not depend on specific hardware components, it is also possible to test your EVerest module natively on the local machine before it is executed on the target hardware platform. Please refer to Simulating EVerest for more information on how to start your EVerest module in the SiL environment.

If you are starting your program from scratch and considering additional libraries to support specific functionality, first review the libraries already required by EVerest or included in the firmware’s Linux distribution. Minimizing dependencies helps reduce the size of the firmware update image, resulting in faster transfer and flashing times during board updates.

When developing your software component, you need to decide how it will interact with the EVerest stack. You can either implement your software as an EVerest module and use the internal interfaces to communicate with the stack, or you can use the external MQTT-based EVerest API API provided by the API module to access the EVerest stack. For energy management use cases, the second option is certainly a valid approach. However, the standard and more flexible path is to create a dedicated EVerest module. For energy management use cases, a websocket JSON-RPC-based approach to communicate with the EVerest stack will be available in the near future. For more information, feel free to contact us.

The process of creating your own EVerest modules is described in the EVerest documentation: How to: Develop New Modules.

Note

It is also possible to create your module directly in the everest-core project in the modules directory. This is only recommended if you either want to work on a fork of everest-core or if you want to contribute the module to the EVerest core project. In general, it is recommended to create your own module in a separate EVerest module repository as described in the EVerest documentation.

To test your own modules, they can be cross-compiled for the target platform and tested directly on the target. How this is done is described in the next section. As soon as the test phase is completed, the EVerest module can be integrated into your firmware image.

In general, there are two ways to create customized firmware images. Either you create a complete Yocto image based on the layers provided by chargebyte. Or, as already described in the Cross-compilation of EVerest modules section, you cross-compile individual EVerest modules, integrate them into an already existing mounted root file system and create an installable rauc image.

Building a complete Yocto image is the recommended way to create customized firmware images. It is more complex to set up the Yocto build environment, but it is the most flexible way to create customized firmware images. Adding individual EVerest modules to an already existing mounted root file system is easier to set up, but less flexible. It is recommended if you only need to make minor changes to the firmware image (e.g. adding a new EVerest module).

In summary, for more complex projects, the second approach is typically used to test smaller changes to the image, while the first approach is used to create production-ready images. The following sections describe the first option in detail. For the second option, see the section Creating Customized Firmware Images with Yocto.

Cross-compiling for Charge SOM

Cross-compilation is the fastest and most convenient way to test your own modules directly on the target system during development. The cross-compiled project can then either be transferred directly via SFTP to the charge controller or integrated into a firmware image and installed on the target using the rauc command.

The following steps describe how to cross-compile a module for the Charge SOM platform.

  1. On an Ubuntu or Debian-based Linux distribution, install the cross-compilers for Charge SOM:

    sudo apt install build-essential libc6-arm64-cross gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-arm-linux-gnueabihf gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
    
  2. Download chargebyte’s digital certificate and use it to extract the root filesystem from the firmware image. The digital certificate is the same between Charge SOM and Charge Control C.

    rauc extract --keyring=<chargebyte_certificate>.crt <shipped_firmware>.image bundle-staging
    

    Note

    Alternatively, if the above command does not work, you can use the following command:

    unsquashfs -d bundle-staging <shipped_firmware>.image
    

    However, this will not verify the signature of the firmware image.

  3. Mount the extracted ext4 root filesystem image as a loop device:

    sudo mkdir -p /mnt/rootfs
    sudo mount bundle-staging/core-image-minimal-chargesom.ext4 /mnt/rootfs
    
  4. Create a new directory in your everest-workspace directory (in parallel to the everest-core directory) and create a new file named toolchain.cmake:

    cd everest-workspace
    mkdir toolchain
    cd toolchain
    touch toolchain.cmake
    
  5. Save the following content in the toolchain.cmake file:

    set(CMAKE_SYSTEM_NAME Linux)
    
    if(EXISTS ${CMAKE_SYSROOT} AND IS_DIRECTORY ${CMAKE_SYSROOT})
      execute_process(COMMAND file ${CMAKE_SYSROOT}/bin/bash.bash
    		  OUTPUT_VARIABLE TARGET_BASH_OUTPUT)
      if(TARGET_BASH_OUTPUT MATCHES aarch64)
        message(STATUS "Setting architecture to aarch64")
        set(CMAKE_SYSTEM_PROCESSOR aarch64)
        set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc)
        set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++)
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} " CACHE STRING "" FORCE )
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} " CACHE STRING "" FORCE )
      else()
        message(STATUS "Setting architecture to arm")
        set(CMAKE_SYSTEM_PROCESSOR arm)
        set(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabihf-gcc)
        set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabihf-g++)
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-psabi" CACHE STRING "" FORCE )
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi" CACHE STRING "" FORCE )
      endif()
    else()
      message(FATAL_ERROR "ERROR: SYSROOT '${CMAKE_SYSROOT}' not found!!!")
    endif()
    
     if(CMAKE_BUILD_TYPE MATCHES Debug)
        # Debug flags
        message("Enabling Debug build")
        set(CMAKE_CXX_FLAGS_DEBUG "-g")
    else()
        # Enable compiler optimization flags
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os")
    
        # Strip debug symbols
        set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s")
    endif()
    
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -L${CMAKE_SYSROOT}/usr/lib")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SYSROOT}/usr/lib")
    
    set(ENV{PKG_CONFIG_PATH} "${CMAKE_SYSROOT}/usr/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}")
    
    set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_SYSROOT}/usr/lib/libstdc++.so")
    
    set(NODEJS_INCLUDE_DIR /usr/include/node) # make sure that nodejs is installed. If not, sudo apt-get install nodejs-dev
    
    set(PYTHON_INCLUDE_DIRS "${CMAKE_SYSROOT}/usr/include/python3.10")
    set(PYTHON_LIBRARIES "${CMAKE_SYSROOT}/usr/lib/libpython3.10.so")
    
    set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
    set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
    set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
    
  6. The resulting directory structure should look like this:

    everest-workspace/
    |── {MyEVerestModule}
    ├── everest-core
    └── toolchain
        └── toolchain.cmake
    
  7. Create a new build_csom directory in the EVerest project directory (e.g. within your own EVerest module project directory or everest-core if you want to build the everest-core modules):

    cd ../{MyEVerestModule}
    mkdir build_csom
    cd build_csom
    
  8. Run the following command inside the build_csom directory to configure the build:

    cmake -DCMAKE_TOOLCHAIN_FILE=../../toolchain/toolchain.cmake -DCMAKE_SYSROOT=/mnt/rootfs ..
    
  9. When this completes successfully, start cross-compiling using make:

    make install -j$(nproc)
    
  10. If the build was successful, a dist directory will be created with the cross-compiled binaries and the manifest files of the modules. Please check if the following directory structure was created:

    dist/
    └── libexec
        └── everest
            └── modules
                └── {MyEVerestModule}
                    ├── {MyEVerestModule} (binary)
                    └── manifest.yaml (manifest file)
    
  11. Verify that the resulting binaries were compiled for the Charge SOM platform:

    file dist/libexec/everest/modules/{MyEVerestModule}/{MyEVerestModule}
    

    The output should be something like:

    dist/libexec/everest/modules/{MyEVerestModule}/{MyEVerestModule}: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV),
    dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=ad2342fdd3b8fb1949fc3e13b77382d3da72f28a, for GNU/Linux 3.7.0, stripped
    
  12. The resulting binary and manifest can be found in the dist/libexec/everest/modules/{MyEVerestModule} directory. If you want to test the module on the target system, you can copy the module directory using scp or rsync:

scp -r dist/libexec/everest/modules/{MyEVerestModule} root@<ip_address>:/usr/libexec/everest/modules/
  1. To include the new module in a firmware image, copy the module directory into the mounted root filesystem:

    cp -r dist/libexec/everest/modules/{MyEVerestModule} /mnt/rootfs/usr/libexec/everest/modules/
    
  2. Unmount the loop device:

    sudo umount /mnt/rootfs
    
  3. Ensure that the modified filesystem is in a clean state:

    fsck.ext4 -f bundle-staging/core-image-minimal-chargesom.ext4
    

#. Follow the steps under the section Firmware Update Customization and Signing to install your PKI certificate, repackage the modified root filesystem into a firmware update image, and test the new firmware.

Creating Customized Firmware Images with Yocto

As already mentioned in the Firmware chapter, chargebyte’s firmware images are built using the Yocto Project build system. This chapter describes how to set up the Yocto build environment for chargebyte’s firmware images and how to create a customized firmware image by adding a new EVerest module based on the chargebyte Yocto layer.

Setting up the Yocto Build Environment

The Yocto build environment is based on the chargebyte BSP (Board Support Package). Please refer to our Board Support Package documentation in our public GitHub repository: https://github.com/chargebyte/chargebyte-bsp for detailed instructions.

  1. Install the required packages for Yocto on a Linux machine or virtual machine. (Note: We usually set up the Yocto build environment on an Ubuntu 20.04 or later Linux distribution.)

  2. Install repo to help set up your Yocto environment. The repo utility makes it easy to reference several Git repositories within a top-level project, which you can then clone to your local machine all at once.

    mkdir ~/bin
    curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
    chmod a+x ~/bin/repo
    

    Ensure that ~/bin is added to your PATH variable (On Ubuntu, this directory is usually added automatically).

    echo 'export PATH="$PATH":~/bin' >> ~/.bashrc
    
  3. Use the repo tool to check out the Yocto layers required to build the firmware image. This requires a manifest file that contains information about the repositories for the necessary Yocto layers and the specific branches to be checked out. The manifest file can be found in a repository called “chargebyte-bsp”. (Note: chargebyte’s Yocto build environment is currently based on ‘Kirkstone’ – an LTS release of the Yocto Project, which ensures long-term support and stability for embedded Linux systems.)

    mkdir yocto
    cd yocto
    repo init -u https://github.com/chargebyte/chargebyte-bsp -b kirkstone-everest
    repo sync
    

    It may take a few minutes to download all repositories using the repo sync command. After execution, you should see three folders in the created yocto directory:

    1. source: Contains all the repositories representing the layers.

    2. chargebyte-bsp: A clone of the ‘chargebyte-bsp’ repository, including the manifest and configuration files.

    3. build: Initially links to the conf folder in chargebyte-bsp.

Follow the documentation in the ‘chargebyte-bsp’ repository to build a firmware image based on the specific board support package (BSP). Currently supported hardware platforms are Tarragon (Charge Control C) and Charge SOM.

The next step is to integrate a new EVerest module and build a custom image that incorporates it.

Integrating a Custom EVerest Module

The EVerest documentation explains the module concept in detail, their configuration, and provides a guide on how to develop a new EVerest module.

This section focuses on integrating the module into the Yocto build system.

  1. To integrate custom EVerest modules into the Yocto build system, you can either create a new Yocto layer or extend an existing one. This section assumes that a new layer has been created and added to the BBLAYERS variable in the build/conf/bblayers.conf file.

  2. A recipe file is required to build the module. A recipe is a file with the extension .bb and contains information such as the source code location, dependencies, and build instructions. Refer to the Yocto documentation for a guide on writing recipe files. Let’s assume the new recipe is named my-module.bb. It might look like this:

    SUMMARY = "My Module"
    DESCRIPTION = "A new EVerest module"
    
    LICENSE = "APACHE-2.0"
    LIC_FILES_CHKSUM = "file://LICENSE;md5=1234567890"
    
    SRC_URI = "git://github.com/my_org/my-module.git;branch=main"
    S = "${WORKDIR}/git"
    
    inherit cmake
    
    DEPENDS = "lib1 lib2"
    
    do_install() {
        install -d ${D}${bindir}
        install -m 0755 ${B}/my-module ${D}${bindir}
    }
    
  3. Add the recipe name my-module to the IMAGE_INSTALL variable in the build/conf/local.conf file to include the module in the image.

The module is now integrated into the Yocto build system. The next step is to build the custom image.

Creating a Development Image

To build the custom image, follow the section “Building an image” in the “chargebyte-bsp” repository. This process will generate a Linux root filesystem, which can then be either flashed directly, or used to create a firmware image using RAUC.

The resulting custom image should now include your new EVerest module.

Logging and Debugging

There are different ways to analyze the EVerest charging software. In the following, different ways are described. In preparation for starting this chapter, it is necessary to be connected to the board via Ethernet. This is also necessary to copy logs and traces from the board to the local PC via a SFTP tool like WinSCP or FileZilla.

journalctl Logs

As already described in the Starting and Monitoring the Charging Process section, the EVerest framework uses journalctl for logging. journalctl is a system service that collects and stores logging data. To view the logs, connect to the charge controller via SSH and run the following command:

journalctl -f -n 100 -u everest

The -f flag is used to follow the logs live, and the -n flag is used to view the last 100 lines. The -u flag is used to filter the logs for the everest service. To stop following the logs, press Ctrl + C.

The logs are stored by the systemd journal service in a binary format in “/var/log/journal”. To open the binary in a human-readable format, use the following command:

journalctl -u everest --file /path/to/your/file.journal

As shown in the Starting and Monitoring the Charging Process section, a log line consists, among other things, of a timestamp, a log level, a module id, a module name, and a message. The log level can be one of the following:

  • “VERB” (Verbose)

  • “DEBG” (Debug)

  • “INFO” (Information)

  • “WARN” (Warning)

  • “ERRO” (Error)

  • “CRIT” (Critical)

The log level can be configured for each EVerest module separately in the logging configuration file. The logging configuration file is located at “/etc/everest/default_logging.cfg”. The filter can be configured using the log level and the module identifier as defined in the EVerest configuration. Here is an example to configure the message filter to change the log level of the EvseManager module to DEBUG:

# The filter expression consists of two parts:
# 1. Logs with severity level INFO or higher (affects all EVerest modules).
# 2. Logs from the EvseManager with module id "connector" with severity level DEBUG or higher.
Filter="(%Severity% >= INFO) or (%Process% contains connector and %Severity% >= DEBG)"

pcap Traces

pcap traces can be used to debug and analyze the high-level communication (HLC) between SECC and EVCC. In general, there are two ways to capture pcap traces:

  1. Capture via tcpdump command

    tcpdump -i eth1 -w /srv/charge_com_01.pcap
    

    This command captures all packets on the eth1 (PLC) interface and writes them to a pcap file. Stop the capture by pressing Ctrl + C.

    Note

    Please stop the trace before copying the file to a local machine. The file may be incomplete if copied while the tcpdump is still running.

  2. Using the EVerest “PacketSniffer” module

    The PacketSniffer module is part of everest-core and can be used to capture pcap traces automatically. Please look into the module manifest to see how to configure the PacketSniffer module.

pcap traces with V2G communication can be analyzed using Wireshark with a V2G plugin like from dSPACE.

Note

TLS encrypted communication cannot be analyzed with Wireshark without the TLS session key. Please look in the module manifest which is responsible for the TLS handshake and check if there is an option to export the TLS session key. E.g. the IsoMux module is able to export the TLS session key.

Session Logging of the EVSEManager

The EVerest “EVSEManager” can generate session log files with information about the current session state, configured CP state and duty cycle. It can also decode and log the content of the V2G messages. Please look into the module manifest to see how to configure the EVSEManager module to activate session logging.

Note

The session logging is pretty performance intensive and should only be used for debugging purposes.

MQTT logs

It is also possible to observe the internal MQTT communication between the EVerest modules. When developing new modules, this can be helpful to better understand the internal processes regarding the use of the interfaces. We recommend using the MQTT Explorer tool to monitor and analyze the MQTT communication. It is also possible to use the following command to monitor the MQTT communication, but this is not so easy to read:

mosquitto_sub -F "[@H:@M:@S.@N] %t %p" -t 'everest/#' -t 'everest_api/#' > /srv/charge_com_mqtt_01.log

Summary

There are several ways to debug and analyze the EVerest framework. Which way to choose depends on the specific use case and the information needed. The most common way is using journalctl logs. If it is necessary to analyze the high-level communication between SECC and EVCC, pcap traces are the best choice. If it is not clear how the internal communication between the EVerest modules works, the MQTT logs can be helpful.