PiPHP / GPIO

A PHP library for accessing the GPIO pins on a Raspberry Pi.
MIT License
431 stars 42 forks source link

Pins multiple times exported #40

Closed unreal4u closed 3 years ago

unreal4u commented 4 years ago

Nice to see this project has new life!

I however came across a bug which might be my fault. I am subscribing to a MQTT server and it stays connected listening for new messages to come in.

Inside that loop, I had the following code:

foreach ($subscribe->loop($client) as $message) {
    // bla bla bla, some irrelevant code
    if ($stateFile['command'] !== $command) {
        $gpio = new GPIO();
        $pin = $gpio->getOutputPin(RELAY_PIN);

         if ($command === 'on') {
            $pin->setValue(PinInterface::VALUE_LOW);
        } else {
            $pin->setValue(PinInterface::VALUE_HIGH);
        }
    }
}

This meant that the first time this code was called, everything went ok. The second time however, it errored out:

PHP Fatal error:  Uncaught RuntimeException: fwrite(): write of 2 bytes failed with errno=16 Device or resource busy in /home/ubuntu/gpio/controlVentilatorBaseroom/vendor/piphp/gpio/src/FileSystem/FileSystem.php:54
Stack trace:
#0 /home/ubuntu/gpio/controlVentilatorBaseroom/vendor/piphp/gpio/src/FileSystem/FileSystem.php(45): PiPHP\GPIO\FileSystem\FileSystem->exceptionIfFalse()
#1 /home/ubuntu/gpio/controlVentilatorBaseroom/vendor/piphp/gpio/src/Pin/Pin.php(115): PiPHP\GPIO\FileSystem\FileSystem->putContents()
#2 /home/ubuntu/gpio/controlVentilatorBaseroom/vendor/piphp/gpio/src/Pin/Pin.php(55): PiPHP\GPIO\Pin\Pin->writePinNumberToFile()
#3 /home/ubuntu/gpio/controlVentilatorBaseroom/vendor/piphp/gpio/src/Pin/Pin.php(39): PiPHP\GPIO\Pin\Pin->export()
#4 /home/ubuntu/gpio/controlVentilatorBaseroom/vendor/piphp/gpio/src/Pin/OutputPin.php(17): PiPHP\GPIO\Pin\Pin->__construct()
#5 /home/ubuntu/gpio/controlVentilatorBaseroom/vendor/piphp/gpio/src/GPIO.php(41): PiPHP\GPIO\Pin\OutputPin->__construct()
#6 /home/ubuntu/gpio/controlVenti in /home/ubuntu/gpio/controlVentilatorBaseroom/vendor/piphp/gpio/src/FileSystem/FileSystem.php on line 54

I fixed it by moving the following piece of code out of the loop:

$gpio = new GPIO();
$pin = $gpio->getOutputPin(RELAY_PIN);

I've been running this code for a few years now and this wasn't an issue before. I understand the problem and I know it is not desirable to create a new instance of the same pin within the same flow of a program, so here my question: was this a bug on my side or do I make an attempt at trying to fix this one?

Greetings.

unreal4u commented 3 years ago

Ok, so after some investigation (yeah, just now I made some time for this :( ) it turns out that you can only export a certain pin once.

If you try to export it again it will fail with a "Device or resource busy" error.

A temporal solution is to unexport the pin manually:

sudo echo 17 > /sys/class/gpio/unexport

(Replace 17 with your pin number).

A better solution would be to automatically unexport the pin when your program stops executing or check on startup of the pin whether it is already exported or not. The latter is more user-friendly but does ignore the underlying issue, the former is actually the correct thing to do: luckily, unexport() is actually implemented: https://github.com/PiPHP/GPIO/blob/master/src/Pin/Pin.php#L72

Closing this issue :)