For this lesson:
This article explains how to run and debug Python Applications on TorizonCore using the Visual Studio Code extension provided by Toradex.
This article complies to the Typographic Conventions for Torizon Documentation.
F1 on Visual Studio Code and then type
Torizon/C-C++: Create C/C++ application to create an application:
After creating it, write the application name on the same command bar:
You will be asked to browse for a folder, select the parent folder for your new project (for example your home folder).
You'll have to select an application template from a list, select "Makefile template".
A bunch of options will appear, make sure to select the right target architecture:
torizon on the username label:
In the last step you'll be required to select a configuration between debug and release, since we want to deploy and debug our test application, please select "debug".
The system will build the SDK container. For C/C++ application the extension will build an SDK container, running on your development machine, that will provide the toolchain and all the libraries needed to build your application.
After a few minutes, your folder will be re-opened inside the container (notice the green box in the bottom-right corner of Visual Studio Code window) and you'll be ready to start developing.
Understand how to add libraries and more in the next subsections.
For a C/C++ application we will need to add packages to the SDK container (headers and libraries that should be linked at build time) and to the runtime container (libraries and components that are dynamically linked with our application or executed on the target).
In this case we will add support for libgpiod, used to access GPIO pins from user mode.
Open the Torizon configuration page and edit the
devpackages configuration, you just need to click on the pen icon:
As an example, add the package libgpiod-dev:armhf (notice that you have to specify CPU architecture here, if you selected a 64-bit platform you'have to add the "arm64" suffix instead of "armhf"):
Once you changed this setting the editor will ask you if you want to rebuild the SDK container, we will need an updated container to be able to build our application using libgpiod, so select the option that will rebuild the SDK container immediately.
Devpackages are installed inside the SDK container (this is why you need to specify an architecture suffix if they are not multi-architecture), they will allow you to build your code on your development machine.
Thanks to Debian multi-architecture support, all the native libraries we will need to cross-compile our application will be installed and ready to use.
Open the Torizon configuration page and edit the
extrapackages configuration, you just need to click on the pen icon and add libgpio2 package:
Extrapackages are installed inside the container that will run on the target (notice that you don't need to specify a suffix, since the container won't be multi-architecture) and provide runtime components required to run your application. Usually you will have one devpackage (headers and libs) and one extrapackage (runtime components), but this is not a strict rule. Some libraries may have a single development package but may be splitted in multiple runtime ones, each one with some "sub-features".
First of all, move to the folder created on the previous steps and replace the content of your C file with the following:
Feel free to change and edit the code to try how intellisense, refactoring and the other Visual Studio Code features work with your code.
You will also need to add a reference to libgpiod in your makefile.
Open "Makefile" in the editor, locate the following lines:
helloworld: $(OBJ) $(CC) -o $@ $^ $(CFLAGS)
and change them to:
helloworld: $(OBJ) $(CC) -o $@ $^ $(CFLAGS) -l gpiod
To access hardware from our container we will need to add some configuration settings. We will need to mount gpiochip devices inside the container and add torizon user inside the container to the gpio group, allowing applications running with that account to access the devices.
To mount devices inside the container you will have to add all the /dev/gpiochip* devices to your configuration.
In the configuration window press the "+" icon next to the " devices" entry.
Then insert the device path:
Repeat the operation for all the gpiochip devices you need to access. For example, on imx7 you have 7 devices, numbered from 0 to 6:
# ls /dev/gpiochip* -la crw-rw-r-- 1 root gpio 254, 0 Jun 17 06:39 /dev/gpiochip0 crw-rw-r-- 1 root gpio 254, 1 Jun 17 06:39 /dev/gpiochip1 crw-rw-r-- 1 root gpio 254, 2 Jun 17 06:39 /dev/gpiochip2 crw-rw-r-- 1 root gpio 254, 3 Jun 17 06:39 /dev/gpiochip3 crw-rw-r-- 1 root gpio 254, 4 Jun 17 06:39 /dev/gpiochip4 crw-rw-r-- 1 root gpio 254, 5 Jun 17 06:39 /dev/gpiochip5 crw-rw-r-- 1 root gpio 254, 6 Jun 17 06:39 /dev/gpiochip6
You also need to add the torizon user to the gpio group (groups are mirrored between the base OS and torizon-debian containers).
To do this, edit the "buildcommands" property and set it to:
RUN usermod -a -G gpio torizon
Let the debug process begin! Press F5 and the IDE will build and deploy the debug container image to the computer on module. The first time you run the application, the system needs to build and deploy the container image, this may take a few minutes.
Following executions will be much faster since only your application will be deployed.
The system will break on the first instruction of your main function.
Now you can execute your code step by step, add new breakpoints, inspect variables etc.
If you don't want that the debugger breaks on the first instruction, just change:
in .vscode/launch.json inside your project folder.
The application has an infinite loop, you can just stop the debugger to kill it.
After going on the process of deploying and debugging the application, you can now deploy and release it.
F1 in Visual Studio Code to open the command bar and write the command
Torizon C/C++: Switch to release configuration.
Visual studio code will reload the workspace and will be ready to rebuild your app in release mode.
First you need to clean the current build output, to ensure that everything will be rebuilt from scratch. You can press F1 again, select "Tasks: Run Task" and then select the "clean" task from the list.
You can also open a new terminal and type:
$ make clean
at the prompt. All terminals you open from Visual Studio Code will run inside your container (on a Linux system, even if you are running Windows as your main OS) and you can execute build commands directly there. The main advantage of using Visual Studio code tasks is that output will be parsed and you could reach a code line reported in an error message by just clicking on the message itself.
Now you can press ctrl+shift+B to build your code.
If build completes successfully you can now build a release container for your application. Press F1 and select "Torizon: Build release container for the application".
Note: This entire process can take a few minutes.
Your terminal output should be like the following:
Building release container for C/C++ application... Deploying application to local folder... Building release image (this may take some time)... Release image has been built successfully.
Attention: Just note that the building process can take some time.
Now that your container was successfully built, you can press
F1 again and enter the
Torizon: Deploy release container, which deploys your container.
Deploying release container... Please select a device. Device XXXXXXXX selected. Deploying image to target (this may take a few minutes). Container has been deployed.
You can confirm if the image was actually deployed to your Torizon device by running the following command on the module:
# docker images
If you want to run your new container you need to check your configuration id and platformid properties in the configuration view. Then you can run your container:
docker run --device /dev/gpiochip0 --device /dev/gpiochip1 --device /dev/gpiochip2 --device /dev/gpiochip3 --device /dev/gpiochip4 --device /dev/gpiochip5 --device /dev/gpiochip6 <platformid>_<id>_release:latest
If you connected an LED to your GPIO pin, you should see it blinking.
To get more information about Docker Containers and their commands, please refer to Docker Oficial Documentation.
This section shows the possible settings for the extension.
Those tags can usually be modified using the IDEs plugins user interface, but for some specific scenarios, it may be required to edit them manually inside the YAML configuration files.
|platform.id||string||Unique id of the platform (folder name)|
|platform.name||string||Mnemonic name of the platform|
|platform.version||string||Version of the platform|
|platform.folder||path||Absolute path of the folder where platform configuration is stored (can be used to add files to a container)|
|platform.baseimage||string||Base image of the container template (used in FROM clause of Dockerfile)|
|platform.sdkbaseimage||string||Base image of the SDK template (can be empty if the platform does not support an SDK)|
|platform.runtimes||string||Runtimes supported by the image. Currently supported runtimes are: ccpp, ccpp-no-ssh, python3, dotnet, aspnet|
|platform/application.ports||key/value pairs||Ports exposed by the container (those configured by application configuration will be merged with those provided by the platform, replacing those with same keys and adding others )|
|platform/application.volumes||key/value pairs||Volumes mounted in the container (where "key "is the local path or volume name, "value" is the path inside the container and, optionally, ",ro" to mount read-only)|
|platform/application.devices||string||List of paths of devices that should be mapped inside the container (ex: /dev/gpiochip0)|
|platform/application.networks||string||List of networks that should be connected to the container. For a network created by a docker-compose script associated with the appplication configuration you've to prepend "#%application.id%#_" to the actual name)|
|platform/application.extraparams||key/value pairs||This tag can be used to add specify some additional custom settings. Check docker python API documentation of container.run method for a list of the supported parameter. "Key" should be parameter name, "value" must be YAML representation of the value. For example to set host network mode, add "network_mode" as key and "host" as value.|
|platform/application.startupscript||relative path||The script that will be launched before starting the container, tags can be used inside the script. The script must be in the same folder as platform/application config file or in a subfolder, path must be relative. If the script is specified for both platform and application, only the application one is executed (but it can invoke the platform one that will be parsed and copied to the target anyway).|
|platform/application.shutdownscript||relative path||A script that will be launched after the container has been stopped. Tags can be used inside the script. The script must be in the same folder as platform/application config file or in a subfolder. If the script is specified for both platform and application, only the application one is executed (but it can invoke the platform one that will be parsed and copied to the target anyway).|
|platform/application.dockercomposefile||relative path||The docker-compose script that will be used to start other containers required to run the application, tags can be used inside the script. The script must be in the same folder as platform/application config file or in a subfolder. The path must be relative. If the compose file is specified for both platform and application, only the application one is used.|
|application.id||string||Application unique id (used also as a prefix for docker-compose created resources like volumes or networks)|
|application.expose||docker command||Ports exposed by the application in the format: "EXPOSE NN NN" Where NN are port number (ex: "EXPOSE 80 8080)|
|application.arg||docker command||Docker build arguments in the format: ARG NAME=VALUE. You can also specify multiple values. This can be useful only if you plan to use the generated dockerfile in a standalone build|
|application.env||docker command||Environment variables in the format: ENV NAME=VALUE. Multiple entries can be specified and VALUE can contain other tags (ex: ENV FOLDER="/home/dummy" FILE="filename")|
|application.preinstallcommands||docker command||Commands that will be executed during container build before any package installation. The format must be the one used in Dockerfiles. This can be used to add Debian package feeds to apt list, add security keys, etc.|
|application.extrapackages||string||Additional packages that should be installed inside the container. You can specify multiple packages separated by spaces.|
|application.devpackages||string||Development packages that will be installed in the SDK container. If a package has architecture-specific versions you’ll have to specify the correct architecture. ex: libopencv:armhf or libopencvf:aarch64|
|application.sdkpackages||string||Additional packages that will be installed in the SDK container. This can be used to install additional tools or compilers.|
|application.buildfiles||docker command||This command can be used to add additional files to the image using the ADD or COPY command. Files must be placed inside the application configuration folder.|
|application.buildcommands||docker command||Command that will be executed after having installed all packages and having configured the debugger and the services. This will give you a chance to change configuration before the actual command is executed|
|application.targetfiles||docker command||Command that will be executed at the end of the build, can be used to add files to the container (ex: providing pre-configuration for services or overriding the default configuration files)|
|application.targetcommands||docker command||Command executed when the container runs, this may be used to override execution of the application in release containers|
|application.appname||string||mnemonic name of the application, for application create using Visual Studio Code it will match folder name|
|application.exename||string||relative path (from application install folder) of the exe started when the container starts. Used only by VSCode|
|application.appargs||string||optional arguments that should be passed to the application|
|application.username||string||username used to run the container CMD. Other commands will be executed as root.|
|application.sdkpreinstallcommands||docker command||Command executed before installing packages into the SDK container can be used to add Debian feeds or keys|
|application.sdkpostinstallcommands||docker command||Command executed after devpackages and skdpackages have been installed|
|application.main||string||Used only for python application. Provides the name of the python file that container the main entry point|
Both applications and platforms provide a generic entry named “props” where you can specify your own properties that will be replaced as tags using the same logic applied for standard tags.
In the extension UI, those will be referenced as "custom properties".
You can define your custom tags and use them in your dockerfile templates or inside other tags.