Hello, I am trying to run the following pwm sample https://github.com/toradex/torizon-samples/tree/bullseye/pwm/pwm with ApolloX on VS Code to control PWM1 on Apalis iMX6 Dual with Ixora carrier board.
The only difference is that I have rewritten the code for C++ but I have not changed anything of the functionality. Basically only to use cout instead of printf.
The code compiles but I get the following errors when I run it:
Error Number 30
failed to export
[Inferior 1 (process 37) exited with code 01]
I checked and the /sys/class/pwm/pwmchip0/export exists on my system.
Is that a problem regarding permission of accessing the files or something else?
Also the Error Number 30, where can I check the error numbers to know what I am dealing with?
Greetings @Svetoslav,
How exactly did you port this sample into an ApolloX project? Did you just copy and modify the code, or did you do any other changes?
Best Regards,
Jeremias
I just copied and modified the code, as I didnât find in the website describing the use of PWM any other changes that need to be made on Dockerfile or Makefile.
Hello @Svetoslav ,
Maybe our documentation on PWM might help you:
Best regards,
Josep
Hello,
I checked the documentation and I see they make some con figurations in the dockerfile.
I am fairly new to using docker and torizon but from what I understand they just make some basic configuration regarding the system and architecture that is used and mine is a bit different so I am not sure if I should add any of these configurations.
This is my docker file:
# ARG CROSS_SDK_BASE_TAG=2.7-bullseye
ARG CROSS_SDK_BASE_TAG=3.0.2-20230323-bookworm
ARG BASE_VERSION=2.5-bullseye
##
# Board architecture
# arm or arm64
##
ARG IMAGE_ARCH=
##
# Application Name
##
ARG APP_EXECUTABLE=pesho
# BUILD ------------------------------------------------------------------------
FROM torizon/debian-cross-toolchain-${IMAGE_ARCH}:${CROSS_SDK_BASE_TAG} As Build
ARG IMAGE_ARCH
ARG COMPILER_ARCH
ENV IMAGE_ARCH ${IMAGE_ARCH}
# __deps__
RUN apt-get -q -y update && \
apt-get -q -y install libcurl4-gnutls-dev:armhf \
apt-get -q -y install \
# DOES NOT REMOVE THIS LABEL: this is used for VS Code automation
# __torizon_packages_dev_start__
# __torizon_packages_dev_end__
# DOES NOT REMOVE THIS LABEL: this is used for VS Code automation
libgpiod-dev:$CROSS_TC_IMAGE_ARCH \
libgpiod2:$CROSS_TC_IMAGE_ARCH \
&& \
apt-get clean && apt-get autoremove && \
rm -rf /var/lib/apt/lists/*
# __deps__
COPY . /app
WORKDIR /app
# RUN echo arm-linux-gnueabihf-g++ --version
RUN if [ "$IMAGE_ARCH" = "arm64" ] ; then \
make ARCH=. CC=aarch64-linux-gnu-g++ ; \
elif [ "$IMAGE_ARCH" = "arm" ] ; then \
make ARCH=. CC=arm-linux-gnueabihf-g++ ; \
elif [ "$IMAGE_ARCH" = "amd64" ] ; then \
make ARCH=. CC=x86_64-linux-gnu-g++ ; \
fi
# BUILD ------------------------------------------------------------------------
# DEPLOY ------------------------------------------------------------------------
FROM --platform=linux/${IMAGE_ARCH} torizonextras/debian:${BASE_VERSION} AS Deploy
ARG IMAGE_ARCH
ARG APP_EXECUTABLE
ENV APP_EXECUTABLE ${APP_EXECUTABLE}
RUN apt-get -y update && apt-get install -y --no-install-recommends \
# DOES NOT REMOVE THIS LABEL: this is used for VS Code automation
# __torizon_packages_prod_start__
# __torizon_packages_prod_end__
# DOES NOT REMOVE THIS LABEL: this is used for VS Code automation
&& apt-get clean && apt-get autoremove && rm -rf /var/lib/apt/lists/*
# copy the build
COPY --from=Build /app/bin /app
# ADD YOUR ARGUMENTS HERE
CMD [ "./app/pesho" ]
# DEPLOY ------------------------------------------------------------------------
And Iâm almost sure there is not problem in my code but this is the one Iâm using for the PWM:
#include <iostream>
#include <string>
#include <cstring>
#include <cstdint>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "pwm_utils.h"
#define MAX_BUF 50
int pwm_write_val(char *file, uint32_t val)
{
int fd;
char buf[MAX_BUF];
fd = open(file, O_WRONLY);
if(fd < 0)
{
std::cout << "Error Number " << errno << std::endl;
perror("pwm");
return -1;
}
if( snprintf(buf, MAX_BUF, "%u", val) < 0)
goto failed;
if( write(fd, buf, strlen(buf)) < 0)
{
std::cout << "Error Number " << errno << std::endl;
perror("pwm");
goto failed;
}
close(fd);
return 0;
failed:
close(fd);
return -1;
}
int pwm_write_str(char *file, char *val)
{
int fd;
fd = open(file, O_WRONLY);
if(fd < 0)
{
std::cout << "Error Number " << errno << std::endl;
perror("pwm");
return -1;
}
if( write(fd, val, strlen(val)) < 0)
{
std::cout << "Error Number " << errno << std::endl;
perror("pwm");
close(fd);
return -1;
}
close(fd);
return 0;
}
int pwm_read(char *file, char *val, uint8_t size)
{
int fd;
fd = open(file, O_RDONLY);
if(fd < 0)
{
std::cout << "Error Number " << errno << std::endl;
perror("pwm");
return -1;
}
if( read(fd, val, size) < 0)
{
std::cout << "Error Number " << errno << std::endl;
perror("pwm");
close(fd);
return -1;
}
close(fd);
return 0;
}
#include <iostream>
#include <cstring>
#include <cstdint>
#include <cstdlib>
#include "pwm_utils.h"
#define PERIOD 1000000000
#define DUTY_CYCLE 500000000
#define POLARITY "normal"
int main(int argc, char **argv)
{
if (pwm_write_val("/sys/class/pwm/pwmchip0/export", 0) < 0)
{
std::cout << "failed to export" << std::endl;
std::exit(EXIT_FAILURE);
}
if (pwm_write_val("/sys/class/pwm/pwmchip0/pwm0/period", PERIOD) < 0)
{
std::cout << "failed to set period" << std::endl;
std::exit(EXIT_FAILURE);
}
if (pwm_write_val("/sys/class/pwm/pwmchip0/pwm0/duty_cycle", DUTY_CYCLE) < 0)
{
std::cout << "failed to set duty cycle" << std::endl;
std::exit(EXIT_FAILURE);
}
if (pwm_write_str("/sys/class/pwm/pwmchip0/pwm0/polarity", POLARITY) < 0)
{
std::cout << "failed to set polarity" << std::endl;
std::exit(EXIT_FAILURE);
}
if (pwm_write_val("/sys/class/pwm/pwmchip0/pwm0/enable", 1) < 0)
{
std::cout << "failed to enable pwm" << std::endl;
std::exit(EXIT_FAILURE);
}
char d_cycle[10] = {0};
if (pwm_read("/sys/class/pwm/pwmchip0/pwm0/duty_cycle", d_cycle, 9) < 0)
std::cout << "unable to read duty cycle" << std::endl;
else
std::cout << "duty cycle is " << d_cycle << std::endl;
char polarity[10] = {0};
if (pwm_read("/sys/class/pwm/pwmchip0/pwm0/polarity", polarity, 6) < 0)
std::cout << "failed to read polarity" << std::endl;
else
std::cout << "polarity is " << polarity << std::endl;
return 0;
}
The original code works fine for me if I follow the article. How are you launching your container? Are you bind-mounting /sys
? If not then the container wonât have access to the PWM interfaces.
Best Regards,
Jeremias
I am trying to do that as I see in the sample they use the following command:
docker run -it --rm -v /sys:/sys pwm-sample
I am trying to add it to my Dockerfile so it will launch when I upload the code and I donât need to do it every time by adding the following arguments at the end of the file that I sent previously:
# ADD YOUR ARGUMENTS HERE
CMD [ "./app/pesho" , "-it", "--rm", "-v", "/sys:/sys"]
I also tried like this:
# ADD YOUR ARGUMENTS HERE
CMD [ "./app/pesho" , "-it", "--rm", "-v", "/sys:/sys", "pesho_debeliq"]
and:
# ADD YOUR ARGUMENTS HERE
CMD [ "./app/pesho" , "-it", "--rm", "-v", "/sys:/sys" "pesho_debeliq-debug"]
But nothing of them works.
How do I add this to the dockerfile so it can be executed every time when I upload the command. I do not want to do it separately and donât want to run the command trough the command line like the example but trough VS Code.
Thatâs not how that is supposed to be used, docker run
is executed on the command line to launch your container image. Putting these arguments in the Dockerfile does not make sense.
For ApolloX the container is launched via docker-compose file, therefore you need to translate these arguments to the docker-compose file. To translate -v /sys:/sys
into your compose-file look at this example: Volumes top-level element | Docker Documentation
services:
backend:
image: awesome/database
volumes:
- db-data:/etc/data
backup:
image: backup-service
volumes:
- db-data:/var/lib/backup/data
volumes:
db-data:
Notice the volumes
property, here you want to specify /sys:/sys
, or whatever path you want to be available inside the container. Do that for the docker-compose file that is in your project in VSCode.
Best Regards,
Jeremias
Thank you for your help.
I added the lines the following way:
version: "3.9"
services:
pesho_debeliq-debug:
build:
context: .
dockerfile: Dockerfile.debug
image: ${LOCAL_REGISTRY}:5002/pesho_debeliq-debug:${TAG}
ports:
- 2230:2230
volumes:
- /sys:/sys
- type: bind
source: /media
target: /media
pesho_debeliq:
build:
context: .
dockerfile: Dockerfile
image: ${DOCKER_LOGIN}/pesho_debeliq:${TAG}
volumes:
- /sys:/sys
- type: bind
source: /media
target: /media
Now the export function is done and when I check the pwm0 with the sudo cat /sys/kernel/debug/pwm it shows sysfs as it should.
But then the error comes immediately on the next pwm_write function:
Error Number 13
failed to set period
Shouldnât the command like this give access to all the functions available for the PWM?
Strange it works if I build and run it as documented in the README: torizon-samples/README.md at bullseye · toradex/torizon-samples · GitHub
Maybe itâs a side effect of trying to compile and run it as a C++ program? Or possibly the extension is doing something unexpected compared to when I build and run the example outside of the extension. I believe âError 13â typically corresponds to a permission issue with the file involved. Though thatâs strange since as I said I can get it to work outside of the extension. I may need to do more digging to figure out why the extension behaves differently here.
Best Regards,
Jeremias
I tried also runnning the C code as it is given here.
But I get the same error so it shouldnât be from the fact that is C++.
The only thing I see in the C++ code is that I get a warning: ISO C++ forbids converting a string constant to âchar*â [-Wwrite-strings] on the pwm_write() function. But as I said the C code gives me the same error.
Can it be a problem that I have to define something else also and not only - /sys:/sys in the volumes.
This is what I saw is in here.
I tried also runnning the C code as it is given here .
But I get the same error so it shouldnât be from the fact that is C++.
Are you building and running it outside of the use of the extension as I am? Cause when I do this without the extension it works as expected. I only experience issues if I try to use it in the extension for some reason.
Best Regards,
Jeremias
After some more testing, it seems if you add a sleep in the code between writing to the export file and writing to the period file the code now works. Though the sleep is only required when running this code from the extension. Why this sleep is required itâs not quite clear but our team is looking into it.
Best Regards,
Jeremias
Hi @jeremias.tx ,
You are right, with a sleep function everything works normally, thanks for the tip.
It is a strange behaviour indeed.
But if the reason for that is found Iâd like to know why this is the case.
While itâs not confirmed, our best hunch at the moment is that somehow running the debugger (gdb) in parallel with the application somehow affects the execution. It appears when not running the debugger then the execution is as one would expect. Though as I said this is mostly theory at the time.
Another of my coworkers created different PWM logic and has no issues:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
// Export the PWM device
ofstream export_stream("/sys/class/pwm/pwmchip0/export");
export_stream << "0" << endl; // Export PWM0
export_stream.close();
// Open the sysfs files for the PWM device
string period_file = "/sys/class/pwm/pwmchip0/pwm0/period";
string duty_cycle_file = "/sys/class/pwm/pwmchip0/pwm0/duty_cycle";
string enable_file = "/sys/class/pwm/pwmchip0/pwm0/enable";
string polarity_file = "/sys/class/pwm/pwmchip0/pwm0/polarity";
ofstream period_stream(period_file);
ofstream duty_cycle_stream(duty_cycle_file);
ofstream enable_stream(enable_file);
ofstream polarity_stream(polarity_file);
// Set the period and duty cycle values
period_stream << "1000000" << endl; // 1ms
duty_cycle_stream << "500000" << endl; // 50% duty cycle
// Enable the PWM device
enable_stream << "1" << endl;
// Set the polarity of the PWM device to high
polarity_stream << "normal" << endl;
// Close the sysfs files
period_stream.close();
duty_cycle_stream.close();
enable_stream.close();
polarity_stream.close();
return 0;
}
So perhaps itâs a combination of the specific code and debugger, but honestly itâs hard to say.
Best Regards,
Jeremias
1 Like