Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hardware PWM using lightweight rpi_hardware_pwm library #474

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Install the plugin using the Plugin Manager bundled with OctoPrint, you can sear

This plugin support many hardware temperature sensors, led, relays, heater...

Here are detailled instructions on how to setup them.
Here are detailed instructions on how to setup them.

### Temperature sensors

Expand Down Expand Up @@ -136,7 +136,7 @@ If your setup does not have pip install pip:
Install the required library:
`sudo pip install rpi_ws281x`

rpi_ws281x really needs sudo, and you need to setup up so your rpi does not ask for a password when runing a python script, so run:
rpi_ws281x really needs sudo, and you need to setup up so your rpi does not ask for a password when running a python script, so run:

`sudo visudo`

Expand All @@ -148,6 +148,20 @@ Also backlist the audio kernel:

add the `blacklist snd_bcm2835` to the end of the file.

### Hardware PWM

Hardware PWM is required in order to drive higher frequency signals, such as the 25000 Hz needed by Noctua PWM fans. The rpi-hardware-pwm module is used as a lightweight alternative to pigpio. Install the required library: `sudo pip install rpi-hardware-pwm`
shortbloke marked this conversation as resolved.
Show resolved Hide resolved

Enable PWM0 (GPIO 18) by adding the following line to `/boot/config.txt`

```
dtoverlay=pwm
```

If you wish to use both PWM0 and PWM1 then use `dtoverlay=pwm-2chan` instead. If you wish to use the alternate PWM pin numbers, then read the [rpi-hardware-pwm module readme](https://github.com/Pioreactor/rpi_hardware_pwm) for how this can be configured.

After editing `boot/config.text` reboot your Raspberry Pi.

### GPIO

This release uses RPi.GPIO to control IO of raspberry pi, it should install and work automatically. If it doesn't please update your octoprint with the latest release of octopi.
Expand Down Expand Up @@ -186,8 +200,8 @@ Outputs are meant to control THINGS (temperature, lights, locker, extra enclosur
Outputs can be set to the following types:

* Regular GPIO
* PWM GPIO
* Neopixel Control via Microcontroler
* PWM GPIO (Software or Hardware controlled PWM)
* Neopixel Control via Microcontroller
* Neopixel Control directly from raspberry pi
* Temperature and Humidity Control
* Temperature Alarm
Expand Down
45 changes: 35 additions & 10 deletions octoprint_enclosure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ def CheckInputActiveLow(Input_Pull_Resistor):
else:
return False

#Function that returns the hardware PWM channel or raises ValueError otherwise
def Pwm_Channel(pin):
pwm0_pins = [12, 18]
pwm1_pins = [13, 19]
if pin in pwm0_pins:
return 0
elif pin in pwm1_pins:
return 1
else:
raise ValueError("Not a Hardware PWM pin")

class EnclosurePlugin(octoprint.plugin.StartupPlugin, octoprint.plugin.TemplatePlugin, octoprint.plugin.SettingsPlugin,
octoprint.plugin.AssetPlugin, octoprint.plugin.BlueprintPlugin,
octoprint.plugin.EventHandlerPlugin):
Expand Down Expand Up @@ -167,16 +178,16 @@ def on_after_startup(self):
self.print_complete = False

def get_settings_version(self):
return 10
return 11

def on_settings_migrate(self, target, current=None):
self._logger.warn("######### current settings version %s target settings version %s #########", current, target)
self._logger.info("######### Current settings #########")
self._logger.info("rpi_outputs: %s", self.rpi_outputs)
self._logger.info("rpi_inputs: %s", self.rpi_inputs)
self._logger.info("######### End Current Settings #########")
if current >= 4 and target == 10:
self._logger.warn("######### migrating settings to v10 #########")
if current >= 4 and target == 11:
self._logger.warn("######### migrating settings to v11 #########")
old_outputs = self._settings.get(["rpi_outputs"])
old_inputs = self._settings.get(["rpi_inputs"])
for rpi_output in old_outputs:
Expand All @@ -199,7 +210,9 @@ def on_settings_migrate(self, target, current=None):
if 'gpio_i2c_register_status' not in rpi_output:
rpi_output['gpio_i2c_register_status'] = 1
if 'shutdown_on_error' not in rpi_output:
rpi_output['shutdown_on_error'] = False
rpi_output['shutdown_on_error'] = False
if 'hw_pwm' not in rpi_output:
rpi_output['hw_pwm'] = False
self._settings.set(["rpi_outputs"], old_outputs)

old_inputs = self._settings.get(["rpi_inputs"])
Expand Down Expand Up @@ -1546,15 +1559,27 @@ def configure_gpio(self):
GPIO.setup(pin, GPIO.OUT, initial=initial_value)
for gpio_out_pwm in list(filter(lambda item: item['output_type'] == 'pwm', self.rpi_outputs)):
pin = self.to_int(gpio_out_pwm['gpio_pin'])
self._logger.info("Setting GPIO pin %s as PWM", pin)
pwm_freqency = self.to_int(gpio_out_pwm["pwm_frequency"])
for pwm in (pwm_dict for pwm_dict in self.pwm_instances if pin in pwm_dict):
self.pwm_instances.remove(pwm)
self.clear_channel(pin)
GPIO.setup(pin, GPIO.OUT)
pwm_instance = GPIO.PWM(pin, self.to_int(gpio_out_pwm['pwm_frequency']))
self._logger.info("starting PWM on pin %s", pin)
pwm_instance.start(0)
self.pwm_instances.append({pin: pwm_instance})
try:
if "hw_pwm" in gpio_out_pwm and gpio_out_pwm["hw_pwm"] is True:
from rpi_hardware_pwm import HardwarePWM
pwm_channel_number = Pwm_Channel(pin) # Raises valueError if not a hardware PWM pin
self._logger.info("starting Hardware PWM on pin %i channel %i at %i Hz", pin, pwm_channel_number, pwm_freqency)
# If RPi dtoverlay has not been configured this will throw rpi_hardware_pwm.HardwarePWMException with information on what to do.
pwm_instance = HardwarePWM(pwm_channel=pwm_channel_number, hz=pwm_freqency)
else:
self._logger.info("starting PWM on pin %s at %i Hz", pin, pwm_freqency)
GPIO.setup(pin, GPIO.OUT)
pwm_instance = GPIO.PWM(pin, pwm_freqency)
pwm_instance.start(0)
self.pwm_instances.append({pin: pwm_instance})
except ImportError as error:
self._logger.error("HardwarePWM module not installed. Install using pip install rpi-hardware-pwm")
except ValueError as error:
self._logger.error("Invalid Hardware PMW pin. pwm0 is GPIO pin 18 is physical pin 12 and pwm1 is GPIO pin 19 is physical pin 13")
for gpio_out_neopixel in list(
filter(lambda item: item['output_type'] == 'neopixel_direct', self.rpi_outputs)):
pin = self.to_int(gpio_out_neopixel['gpio_pin'])
Expand Down
1 change: 1 addition & 0 deletions octoprint_enclosure/static/js/enclosure.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ $(function () {
gpio_status: ko.observable(false),
hide_btn_ui: ko.observable(false),
active_low: ko.observable(true),
hw_pwm: ko.observable(false),
pwm_temperature_linked: ko.observable(false),
toggle_timer: ko.observable(false),
toggle_timer_on: ko.observable(0),
Expand Down
15 changes: 15 additions & 0 deletions octoprint_enclosure/templates/enclosure_settings.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,21 @@
</div>
<!-- /ko -->

<!-- ko if: ($data.output_type() == "pwm" ) -->
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" data-bind="checked: hw_pwm"> {{ _('Use Hardware PWM') }}
</label>
<span class="help-inline">
<span class="label label-important">Attention</span> You need to install and configure the rpi-hardware-pwm module to utilise the hardware PWM on the Pi, check
the documentation on the plugin
<a href=" https://github.com/vitormhenrique/OctoPrint-Enclosure">github</a> page. Note: Hardware PWM is only available on GPIO 18 (PWM0) and GPIO 19 (PWM1).</span>
</span>
</div>
</div>
<!-- /ko -->

<!-- ko if: ($data.output_type() == "pwm" ) -->
<div class="control-group">
<div class="controls">
Expand Down