emlid / Navio2

C++ and Python sensor examples for developers
BSD 3-Clause "New" or "Revised" License
173 stars 122 forks source link

Operation not permitted pwm #48

Closed atahabaki closed 3 years ago

atahabaki commented 4 years ago

I'm using custom arducopter build which is not sending any pwm signals after pin 7. I connected servo to pin 9. On first run (arducopter service running already) everything works well, second time I run it. Says operation not permitted.

Here is the code I use for testing:

import sys
import time

import navio2.pwm
import navio2.util

PWM_OUTPUT = 8
SERVO_MIN = 1.250 #ms
SERVO_MAX = 1.750 #ms

with navio2.pwm.PWM(PWM_OUTPUT) as pwm:
    pwm.set_period(50)
    pwm.enable()

    start=time.time()
    while (True):
        now=time.time()
        t=now-start
        if t >= 3:
            break
        pwm.set_duty_cycle(SERVO_MAX)
    start=time.time()
    while (True):
        now=time.time()
        t=now-start
        if t >= 3:
            break
        pwm.set_duty_cycle(SERVO_MIN)
    pwm.disable()

When I run second time here what error I get:

PermissionError: [Errno 1] Operation not permitted

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "Servo.py", line 13, in <module>
    with navio2.pwm.PWM(PWM_OUTPUT) as pwm:
  File "/home/atahabaki/Navio2/Python/navio2/pwm.py", line 15, in __enter__
    self.initialize()
  File "/home/atahabaki/Navio2/Python/navio2/pwm.py", line 34, in initialize
    pwm_export.write(str(self.channel))
PermissionError: [Errno 1] Operation not permitted

The issue is runs first time perfectly. Second time I run, throws that exception above. Is there any group for pwm i/o system? Why it is giving me an error? arducopter is not using the pin I connected servo to... I didn't understand why this is happening... Any help will be appreciated. Thanks!..

drobban commented 4 years ago

The error occurs here https://github.com/emlid/Navio2/blob/0eb90b7d0ace9b88f886f3482c4f275b0a34efe8/Python/navio/pwm.py#L34

Try debugging the cause to why you arent permitted to append to path.

2lian commented 3 years ago

Reviving this thread : I solved it by waiting between the calls of the functions.

2lian commented 3 years ago

I modified pwm.py like that, and it seems to work for now. I added time.sleep(1) in initialize and deinitialize between the opening and the closing of the file, and added "file".close() every time a file was open (not sure it's requiered every time).

One should avoid calling (de)initialize a lot, because it is REALY slow (waiting less than a second, like 0.5s, does not always work for me). Closing the file seems to be enough for all the other functions.

import os.path
import time

class PWM():
    SYSFS_PWM_PATH_BASE = "/sys/class/pwm/pwmchip0/"
    SYSFS_PWM_EXPORT_PATH = "/sys/class/pwm/pwmchip0/export"
    SYSFS_PWM_UNEXPORT_PATH = "/sys/class/pwm/pwmchip0/unexport"

    def __init__(self, channel):
        self.channel = channel
        self.channel_path = self.SYSFS_PWM_PATH_BASE + "pwm{}/".format(self.channel)
        self.is_initialized = False
        self.is_enabled = False

    def __enter__(self):
        print('enter')
        self.initialize()
        return self

    def __exit__(self, *args):
        self.deinitialize()

    def deinitialize(self):
        if self.is_enabled:
            self.set_period(1)
            self.disable()
        with open(self.SYSFS_PWM_UNEXPORT_PATH, "a") as pwm_unexport:
            pwm_unexport.write(str(self.channel))
            time.sleep(1)
            pwm_unexport.close()

    def initialize(self):
        if not os.path.exists(self.SYSFS_PWM_PATH_BASE):
            raise OSError("rcio_pwm module wasn't loaded")

        if not os.path.exists(self.channel_path):
            with open(self.SYSFS_PWM_EXPORT_PATH, "a") as pwm_export:
                pwm_export.write(str(self.channel))
                time.sleep(1)
                pwm_export.close()

        self.is_initialized = True

    def enable(self):
        with open(self.channel_path + "enable", "w") as pwm_enable:
            pwm_enable.write("1")
            self.is_enabled = True
            pwm_enable.close()

    def disable(self):
        with open(self.channel_path + "enable", "w") as pwm_enable:
            pwm_enable.write("0")
            self.is_enabled = False
            pwm_enable.close()

    def set_period(self, freq):
        if not self.is_initialized:
            raise RuntimeError("PWM not initialized. Call initialize first")

        period_ns = int(1e9/freq)
        with open(self.channel_path + "period",  "w") as pwm_period:
            pwm_period.write(str(period_ns))
            pwm_period.close()

    def set_duty_cycle(self, period):
        if not self.is_initialized:
            raise RuntimeError("PWM not initialized. Call initialize first")

        period_ns = int(period*1e6)
        with open(self.channel_path + "duty_cycle", "w") as pwm_duty:
            pwm_duty.write(str(period_ns))
            pwm_duty.close()
AlexanderDranitsa commented 3 years ago

Hi!

@atahabaki , do I get it right the issue is solved?

atahabaki commented 3 years ago

I don't know if the issue is solved or not, I'm not using emlid toys anymore...

AlexanderDranitsa commented 3 years ago

Okay then. As I can see @hubble14567 and @drobban end up with a working code here. Closing this one. Thanks!