Gpio.cleanup usage

After using gpio.cleanup in different plugins I faced a strange behavior. Is it passible that

def on_shutdown(self):
        GPIO.cleanup()

from filament sensor plugin, cleans all GPIOs also from other plugins?
If I run my new plugin in combination like this:

def on_shutdown(self):
		self._bedpower(0)
		GPIO.cleanup()
		
def _bedpower(self, state):
		if state == 1:
			self._powerup = 1
			GPIO.output(self.pin, GPIO.HIGH)
			self._logger.info("BedPower connected")
		else:
			self._powerup = 0
			GPIO.output(self.pin, GPIO.LOW)
			self._logger.info("BedPower disconnected")

		self._plugin_manager.send_plugin_message(self._identifier, dict(bedpower=self._powerup))

the last self._bedpower(0) fails because there is no GPIO anymore.

Probably, yes, the other may plugins get there first. Try running in debug mode octoprint serve --debug and you should see the execution order.

indeed; (another test)

2020-08-16 21:46:52,121 - octoprint.plugin - DEBUG - Calling on_shutdown on MyPlugin
2020-08-16 21:46:52,131 - octoprint.plugin - DEBUG - Calling on_shutdown on heatbedsavety
2020-08-16 21:46:52,135 - octoprint.plugin - ERROR - Error while calling plugin heatbedsavety
Traceback (most recent call last):
  File "/home/pi/devel/OctoPrint/src/octoprint/plugin/__init__.py", line 230, in call_plugin
    result = getattr(plugin, method)(*args, **kwargs)
  File "/home/pi/devel/OctoPrint-Heatbedsavety/octoprint_heatbedsavety/__init__.py", line 33, in on_shutdown
    self._bedpower(0)
  File "/home/pi/devel/OctoPrint-Heatbedsavety/octoprint_heatbedsavety/__init__.py", line 83, in _bedpower
    GPIO.output(self.pin, GPIO.LOW)
RuntimeError: Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)
2020-08-16 21:46:52,136 - octoprint.server - INFO - Goodbye!

but what is the result? Using GPIO.cleanup() is suggested, isn't it?
Using of GPIO.getmode() just show the created setup within the dedicated plugin and not the other ones, but GPIO.cleanup() seem destroing all channels from all plugins. Pretty bad :frowning:

Probably the reason it's doing that is because the plugins are all run from the same process. The way OctoPrint runs plugins is by importing their plugin class, so they get to run inside it. As a result, the GPIO library can't tell the difference. From your code snippet, you are trying to turn your pin off, on shutdown? Might be able to find some kind of workaround for that, as if another plugin calls cleanup() before you want to do something then it's a bit of a problem. I don't have familiarity with the GPIO lib in question, so can't help beyond OctoPrint

It seems to be process dependend because GPIOs from other processes and the overlay GPIOs are not involved.
In my opinion the challange is not keep my plugin working, but rather the handling of GPIO.cleanup() within plugins. If it has a behavoir like this and the usage is obligatory, then octoprint itself may call the cleanup and the usage within plugins should be forbitten.

Continuing the discussion from Gpio.cleanup usage:

Thing with that is, it should not be OctoPrint's responsibility. You cannot put gpio.cleanup() into OctoPrint, and call it a day. Vast majority of people do not use anything connected to GPIO, so that is not the responsibility of OctoPrint - it should be the plugin that uses it.
Here's a solution for you to try - multiprocessing. Now I'm not sure if this will work with the GPIO lib, but you could instantiate within a new process, then send a message to KILL or similar to the process, which then kills GPIOs it has used?

If that isn't clear, you could take a look at my plugin https://github.com/cp2004/OctoPrint-WS281x_LED_Status, as I have a secondary process going all the time.

Thank you very much for your suggestions. Learning Multiprocessing with python is now on my agenda :slight_smile: I think as a result we can say there is no perfect solution because the troublle is in the GPIO lib and no real clean workaround using this lib within a octoprint plugin, right?

Seems reasonable. If you already know the threading API, then you can use multiprocessing, it is just a little bit more finnicky WRT shutdown of the interpreter - and Python 2/3 compatibility, although only a small change, which I do on the top of my plugin.

1 Like