Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
.. _exp_linux_yocto:
|
||||
|
||||
#########################
|
||||
Linux / Yocto
|
||||
#########################
|
||||
|
||||
You will find different explanations about using EVerest with
|
||||
Linux and Yocto in the following sections:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
setting-up-linux-os
|
||||
building-yocto
|
||||
ota-updates
|
||||
partitioning-schemes-for-rauc-ota
|
||||
|
||||
Make sure to check out the :doc:`How-to-guide on cross-compilation </how-to-guides/yocto-cross-compilation>`
|
||||
for a step-by-step guide on how to cross-compile EVerest for a Yocto-based Linux system.
|
||||
@@ -0,0 +1,248 @@
|
||||
.. _exp_linux_yocto_ota_updates:
|
||||
|
||||
##########################
|
||||
Over-the-air-updates (OTA)
|
||||
##########################
|
||||
|
||||
One of the most important (and often underestimated) features of a
|
||||
charging station is the ability to remotely update the software when the
|
||||
charger is installed. Updates can provide:
|
||||
|
||||
- General bug fixes
|
||||
- Fixing compatibility issues with new EVs (or old EVs with new
|
||||
firmware versions)
|
||||
- Fixing compatibility issues with OCPP backends (or new versions
|
||||
deployed on the backend side)
|
||||
- Security issues
|
||||
- New features
|
||||
|
||||
Updates may be delivered remotely over a network, called Over-the-Air (OTA),
|
||||
or may be provided locally where supported by the charging station.
|
||||
|
||||
EVerest supports *RAUC* as an update tool, which has the following advantages:
|
||||
|
||||
- Open source project with a large community:
|
||||
https://rauc.io
|
||||
- Secure by design: The update files are cryptographically signed (and
|
||||
optionally encrypted). Signature is checked during installation, so
|
||||
the source of the update file can be trusted. This simplifies the
|
||||
update delivery process a lot compared to other tools that only rely
|
||||
on transport mechanism security. Updates can be downloaded from a
|
||||
simple unencrypted HTTP server or even a local USB flash drive
|
||||
without compromising security.
|
||||
- Robust: Uses A/B partitioning and does full image updating
|
||||
- Atomic switching between A/B slots can be implemented
|
||||
- Support partial downloads by HTTP streaming: Block based partial
|
||||
downloads reduce the bandwidth needed
|
||||
|
||||
There are some considerations to make when choosing an update system:
|
||||
|
||||
+-----------------------------------+-----------------------------------+
|
||||
| Full image updates | Partial component / individual |
|
||||
| | file updating |
|
||||
+===================================+===================================+
|
||||
| Very robust. The complete image | Risk of producing an installed |
|
||||
| always has the correct | combination where one component |
|
||||
| dependencies built in. | is too old to work with the other |
|
||||
| | recently updated component. |
|
||||
| | Requires careful tracking of |
|
||||
| | compatibility between components. |
|
||||
+-----------------------------------+-----------------------------------+
|
||||
| Writing full images to A/B slots | Often quite complex |
|
||||
| is straightforward. Combined with | implementations. That can |
|
||||
| an atomic switch between the | introduce a lot of room for bugs |
|
||||
| boot slots, there is no critical | which brick devices during failed |
|
||||
| time where e.g. a power loss | updates, power losses during |
|
||||
| could brick a device. | updates or upgrading to |
|
||||
| | incompatible updater software |
|
||||
| | versions. |
|
||||
+-----------------------------------+-----------------------------------+
|
||||
| Simple versioning: a single | Complex versioning: Always a |
|
||||
| version number is enough to | combination of the different |
|
||||
| specify which software image | components / files. |
|
||||
| version is installed. | |
|
||||
+-----------------------------------+-----------------------------------+
|
||||
| Recovers from file system errors | Relies entirely on the filesystem |
|
||||
| in the root partition: It writes | implementation to repair itself |
|
||||
| a new clean FS on every update | and may brick if that fails. |
|
||||
+-----------------------------------+-----------------------------------+
|
||||
| Updates everything: rootfs, | Often limited to e.g. application |
|
||||
| kernel, bootloader, … | update. It may e.g. not update |
|
||||
| | kernel or base system. |
|
||||
+-----------------------------------+-----------------------------------+
|
||||
| Downside: Full image updates | Advantage: only download changed |
|
||||
| require more download | files and thus have the smallest |
|
||||
| bandwidth/data. Can be mitigated | possible download. |
|
||||
| to some extent by block based | |
|
||||
| partial download. | |
|
||||
+-----------------------------------+-----------------------------------+
|
||||
|
||||
|
||||
An update process should consider the bootloader, loading the Linux kernel, and
|
||||
the root file system. A root file system can be a standard Linux partition (ext4).
|
||||
Other solutions are available including: squashfs, file system snapshots, and
|
||||
bundle based solutions (NixOS, Snap). The root file system is usually read-only
|
||||
and an overlay file system is used to support charger specific updates.
|
||||
|
||||
An OTA solution needs to consider how configuration information is maintained
|
||||
across root file system updates.
|
||||
|
||||
EVerest has chosen RAUC as the most suitable update system, mainly due to its
|
||||
robust, brick-free mechanisms and its inherent security features.
|
||||
|
||||
RAUC can support adaptive updates that use HTTP streaming to only download
|
||||
blocks that have changed between releases. This can reduce the overheads of using
|
||||
full images.
|
||||
|
||||
Security is provided on a block-based level, so there is no need to
|
||||
first download the complete image and validate signature etc. It is done
|
||||
on the fly.
|
||||
|
||||
This also means that no extra disk space is needed to store the update
|
||||
image: It will be directly streamed from the source into the inactive
|
||||
slot partition.
|
||||
|
||||
RAUC implementation in EVerest
|
||||
------------------------------
|
||||
|
||||
EVerest interacts with RAUC via its D-Bus interface. This is provided by the
|
||||
`Linux Systemd Rauc module </reference/modules/Linux_Systemd_Rauc>`_.
|
||||
|
||||
In EVerest the update process is fully integrated with OCPP.
|
||||
In the OCPP use case, the CPO will need to provide storage for the
|
||||
update file that is accessible via HTTP with range requests. The CSMS
|
||||
then sends this URL in the update request to EVerest, and EVerest will
|
||||
trigger RAUC on the D-Bus to actually perform the update.
|
||||
|
||||
You will need to implement the following in your Yocto system as this is
|
||||
very system dependent:
|
||||
|
||||
- A partitioning setup that provides A/B slots for rootfs, A/B boot and
|
||||
data partitions (more details about this is covered in the appendix!)
|
||||
- RAUC configuration file for your setup (system.conf)
|
||||
- RAUC backend that performs switching slots/marking good/bad. RAUC
|
||||
already comes with backend support for many bootloaders such as
|
||||
U-Boot and Barebox etc.
|
||||
- PKI to be used for signing / optionally encrypting the update files
|
||||
- A recipe that builds RAUC bundles (update files) directly in Yocto
|
||||
|
||||
If you use PHYTEC SoMs: Their *ampliPHY* distribution already has working
|
||||
examples for all of the above in *ampliphy-rauc* or *ampliphy-secure*
|
||||
distributions.
|
||||
|
||||
Refer to RAUC's integration documentation for more information:
|
||||
|
||||
https://rauc.readthedocs.io/en/latest/integration.html
|
||||
|
||||
RAUC has support for atomic switching between slots and uses features from
|
||||
the bootloader. It is important to understand this interaction since the
|
||||
bootloader may be able to automatically rollback if an update is not successful.
|
||||
|
||||
Some processors also support secure and encrypted boot options which can ensure
|
||||
that only valid images are loaded. They may also provide mechanisms to support
|
||||
dual boot loaders.
|
||||
|
||||
.. tip::
|
||||
|
||||
Look at the documentation for your processor and chosen bootloader to
|
||||
understand what options are provided for slot switching and automatic boot
|
||||
failure recovery.
|
||||
|
||||
Test your integration locally first using RAUC on the command line:
|
||||
|
||||
.. tip::
|
||||
|
||||
rauc install http://myudateserver.com/version1.raucb
|
||||
|
||||
RAUC should perform a successful installation on the currently unused
|
||||
slot. Once that is done, issue a reboot and verify it cleanly boots into
|
||||
the new slot.
|
||||
|
||||
Once booted successfully into the new slot, you need to mark the slot as
|
||||
“good”, otherwise it may fall back to the previous one on the next
|
||||
boot.
|
||||
|
||||
Some implementations do this in a *systemd* service that runs at the end
|
||||
of the boot process. This is not recommended in production. EVerest
|
||||
will take care of marking the slot as "good" when EVerest starts up
|
||||
successfully. It will then also report the status to the OCPP backend
|
||||
automatically etc.
|
||||
|
||||
To mark it "good", manually use:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
rauc status mark-good
|
||||
|
||||
You also may want to check RAUC's status before and after the update to
|
||||
verify it is configured correctly. It shows an output like this:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root@mysystem:~# rauc status
|
||||
=== System Info ===
|
||||
Compatible: mysystem-v1
|
||||
Variant:
|
||||
Booted from: rootfs.0 (system0)
|
||||
|
||||
=== Bootloader ===
|
||||
Activated: rootfs.0 (system0)
|
||||
|
||||
=== Slot States ===
|
||||
[bootloader.0] (/dev/mmcblk1, boot-emmc, inactive)
|
||||
|
||||
o [rootfs.1] (/dev/mmcblk1p6, ext4, inactive)
|
||||
bootname: system1
|
||||
boot status: good
|
||||
[boot.1] (/dev/mmcblk1p2, vfat, inactive)
|
||||
|
||||
x [rootfs.0] (/dev/mmcblk1p5, ext4, booted)
|
||||
bootname: system0
|
||||
mounted: /
|
||||
boot status: good
|
||||
[boot.0] (/dev/mmcblk1p1, vfat, active)
|
||||
|
||||
Also try to use *mark-bad* and test if it falls back to the previous one
|
||||
on the next boot.
|
||||
|
||||
EVerest interacts with RAUC via D-Bus, so make sure it is running as a
|
||||
D-Bus service. The D-Bus interface is also the boundary between
|
||||
EVerest and the underlying Linux system here.
|
||||
|
||||
Once you verified that RAUC performs updating and fall-backs in manually
|
||||
controlled command line mode, you should be all set up for EVerest
|
||||
updates.
|
||||
|
||||
Custom Update Mechanism
|
||||
------------------------
|
||||
|
||||
In case you do not want to use RAUC and/or integrate your custom update
|
||||
mechanism into EVerest, you can also implement the
|
||||
`EVerest System API <../../reference/api/system_API/index.html>`_.
|
||||
This would still allow you to update EVerest via OCPP, but you would need to handle
|
||||
the actual update process yourself and provide status updates to EVerest via the
|
||||
System API.
|
||||
|
||||
Optimize the base system
|
||||
------------------------
|
||||
|
||||
If you have a lot of processes running in the Linux system and a very
|
||||
high CPU load (which easily happens on small embedded systems), take
|
||||
some time to select the correct nice levels for all services running on
|
||||
the system. You can set the nice level in the systemd unit files.
|
||||
|
||||
.. tip::
|
||||
|
||||
Being "nicer" means getting CPU less often if lots of processes are scheduled.
|
||||
|
||||
Especially for high-level communication (aka ISO 15118), run EVerest at
|
||||
e.g. a nice level of -20 to ensure it is getting enough CPU slices
|
||||
during the charging process. If you have other tasks outside of
|
||||
EVerest, make sure they have a higher nice level.
|
||||
|
||||
Using a preemptive kernel is also a good idea to ensure low latencies in
|
||||
user space. Check *CONF_PREEMT* documentation in the Linux kernel.
|
||||
|
||||
--------------------------------
|
||||
|
||||
**Authors**: Cornelius Claussen, Manuel Ziegler, Piet Gömpel
|
||||
@@ -0,0 +1,61 @@
|
||||
.. _partitioning-schemes-for-rauc-ota:
|
||||
|
||||
#################################
|
||||
Partitioning schemes for RAUC OTA
|
||||
#################################
|
||||
|
||||
As there are many ways to set up partitions on the storage device for
|
||||
RAUC-based updates, this chapter will only provide a few ideas. Your
|
||||
actual implementation may be different in the end.
|
||||
|
||||
As a target, we would like to have:
|
||||
|
||||
- two full-size A/B rootfs partitions
|
||||
- two full-size A/B boot partitions for bootloader and FIT image
|
||||
(containing kernel/initrd/device tree for secure boot)
|
||||
- one/two user data partition(s)
|
||||
- one small factory data partition that contains (read only) files that
|
||||
are programmed once during production and will never change
|
||||
(e.g. serial numbers, certification region config etc)
|
||||
|
||||
The user data partition can be mounted as overlayfs on specific folders
|
||||
to store run time-generated data (e.g. log files, user configuration
|
||||
files, certificates, ...).
|
||||
|
||||
A factory reset should be implemented that clears the overlayfs.
|
||||
|
||||
The most simple version of this is to use a single user data partition
|
||||
and mount it as overlay (e.g. on */var*) both for slot A and B. Then all
|
||||
changes in the overlay will survive an update of the underlying rootfs.
|
||||
For an example on how to do this, refer to the BelayBox Yocto sources.
|
||||
The Raspberry Pi uses three boot partitions, for most other boards only
|
||||
two are needed.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
part --source bootimg-partition --ondisk mmcblk0 --fstype=vfat --label boot --active --align 4096 --fixed-size 512
|
||||
part --source bootimg-partition --ondisk mmcblk0 --fstype=ext4 --label boot_a --align 4096 --fixed-size 512
|
||||
part --source bootimg-partition --ondisk mmcblk0 --fstype=ext4 --label boot_b --align 4096 --fixed-size 512
|
||||
part --source rootfs --ondisk mmcblk0 --fstype=ext4 --label root_A --align 4096 --fixed-size 3000
|
||||
part --source rootfs --ondisk mmcblk0 --fstype=ext4 --label root_B --align 4096 --fixed-size 3000
|
||||
part --ondisk mmcblk0 --fstype=ext4 --label factory_data --align 4096 --fixed-size 128
|
||||
part --ondisk mmcblk0 --fstype=ext4 --label overlay --align 4096 --fixed-size 7000
|
||||
|
||||
The disadvantage of this is the following: If the configuration file
|
||||
format changes due to an update of the underlying rootfs, a separate job
|
||||
may need to be run on the first boot into the new slot to transfer the
|
||||
configuration files to the new format. If the boot into the new slot
|
||||
fails, it will fall back to the old slot. The older version then is
|
||||
maybe not compatible with the new config file format, so a full fallback
|
||||
is not possible in this case.
|
||||
|
||||
To allow for better config file migration with fallback, consider to use
|
||||
two user data partitions and a separate migration task (e.g. in initrd)
|
||||
that transfers the files from the old user data partition to the new
|
||||
one. It may adapt the file format in the process. In this case, falling
|
||||
back to the old rootfs will work as it will also use the older overlay
|
||||
user partition.
|
||||
|
||||
If you have an eMMC device, consider using the hardware boot partition
|
||||
feature that eMMC offers for the bootloader. This will enable atomic
|
||||
switching between the active boot slots.
|
||||
@@ -0,0 +1,105 @@
|
||||
.. _exp_linux_yocto_setup_linux_os:
|
||||
|
||||
#####################################
|
||||
Setting up the Linux operating system
|
||||
#####################################
|
||||
|
||||
In principle, you can use any Linux-based operating system as long as it
|
||||
comes with the required dependencies to run EVerest.
|
||||
|
||||
We strongly recommend using Yocto as it has some advantages over other
|
||||
distributions:
|
||||
|
||||
- It can be set up to do reproducible builds with versioning.
|
||||
- Most CPUs and SoMs already come with Yocto board support packages
|
||||
(BSP).
|
||||
- EVerest comes with full support for selected Yocto long term support
|
||||
releases (LTS) (scarthgap as of the time of writing).
|
||||
- It can be nicely integrated with your CI/CD to build complete
|
||||
production images and update packages.
|
||||
- It provides a software bill of materials of all packages in the Linux
|
||||
system for managing licenses.
|
||||
- Broad community
|
||||
- Automatic generation of cross-compile toolchains, that can be used
|
||||
during the development phase.
|
||||
|
||||
You can find more information about the Yocto project here:
|
||||
https://www.yoctoproject.org
|
||||
|
||||
.. warning::
|
||||
|
||||
Setting up the Linux base system for your product is a quite complex task
|
||||
that should be performed by domain experts. In case you do not have experts
|
||||
in your team, consider getting help from a company specialized on this.
|
||||
|
||||
The end product's reliability, security and user experience strongly depends
|
||||
on a sound architecture, implementation and maintenance strategy of the base
|
||||
Linux system. This should not be underestimated.
|
||||
|
||||
Covering all aspects of setting up a Linux base system is out of the
|
||||
scope of this documentation, but we would like to give some examples and ideas
|
||||
and point out some typical solutions to questions you will have on your
|
||||
journey. Do not consider this complete!
|
||||
|
||||
Setup a Yocto build environment
|
||||
-------------------------------
|
||||
|
||||
Yocto has comprehensive caching capabilities that mean build times are substantially
|
||||
reduced for successive builds. However an initial build will take hours since initial
|
||||
versions need to be fetched and built so that caches are populated. There is support
|
||||
for sharing downloads and caches that can reduce build times and are worth
|
||||
considering where you have a co-located team.
|
||||
|
||||
A good build machine will have lots of RAM, SSD storage and multi-core processor as
|
||||
well as a fast Internet connection.
|
||||
|
||||
It is possible to use a high-performance laptop especially for incremental builds
|
||||
once the initial build is complete.
|
||||
|
||||
.. warning::
|
||||
|
||||
Running this inside of a virtual machine is not recommended.
|
||||
|
||||
A full Yocto build easily requires 50-100 GB of disk space, and it will
|
||||
use multiple cores. So, make sure you have enough RAM per core (e.g. 2-3
|
||||
GB per (hyperthread) core).
|
||||
|
||||
Install a Linux distribution supported by Yocto and install all
|
||||
necessary dependencies. See here for more information about that:
|
||||
|
||||
https://docs.yoctoproject.org/ref-manual/system-requirements.html
|
||||
|
||||
Alternatively, consider building in a container. Once you move to
|
||||
production, a build container will probably be needed anyway to build
|
||||
images in your CI/CD.
|
||||
|
||||
It is also recommended to archive the containers to be able to do fully
|
||||
reproducible builds of older versions in the future.
|
||||
|
||||
Yocto itself can produce builds that are completely tagged (i.e. each
|
||||
source package is tagged with a fixed version or Git hash), so they are
|
||||
in principle reproducible.
|
||||
|
||||
There are - however - a few build dependencies to the host system that
|
||||
may prevent you from building your released 1.0 version in ten years
|
||||
from now. As an example, the Python version in ten years from now may
|
||||
not run the old bitbake correctly anymore. Also, the Yocto recipes
|
||||
contain only download URLs and version tags, but not the source packages
|
||||
itself.
|
||||
|
||||
Let's start with an example and set up the Yocto build environment that
|
||||
we use for EVerest on the BelayBox hardware.
|
||||
|
||||
Building the BelayBox Yocto image
|
||||
---------------------------------
|
||||
|
||||
An example can be found here for the BelayBox:
|
||||
|
||||
https://github.com/PionixPublic/dev-hardware-yocto
|
||||
|
||||
Check out the *README* in this repository on how to build and install
|
||||
this Yocto on the BelayBox.
|
||||
|
||||
--------------------------------
|
||||
|
||||
**Authors**: Cornelius Claussen, Manuel Ziegler
|
||||
Reference in New Issue
Block a user