Skip to content

Commit

Permalink
Merge pull request #1 from shortbloke/hardware_pwm
Browse files Browse the repository at this point in the history
Hardware PWM using rpi_hardware_pwm library
  • Loading branch information
tlachmann authored Jul 20, 2024
2 parents 7a33af2 + 63dc991 commit 5b74097
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 14 deletions.
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,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 @@ -139,7 +139,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 @@ -151,6 +151,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`

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 @@ -189,8 +203,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 @@ -1570,15 +1583,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

0 comments on commit 5b74097

Please sign in to comment.