Compare commits
6 Commits
Author | SHA1 | Date |
---|---|---|
ABelliqueux | 51ea4af2f5 | |
ABelliqueux | 4a6fb79c21 | |
ABelliqueux | 3afc1555f4 | |
ABelliqueux | 5260330863 | |
ABelliqueux | 485586af3e | |
ABelliqueux | 1a0b03d991 |
65
mpdlisten.py
65
mpdlisten.py
|
@ -17,21 +17,6 @@ from luma.core.render import canvas
|
|||
from luma.oled.device import ssd1306
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
# Pot_cap
|
||||
import pigpio
|
||||
import pot_cap
|
||||
min_val = 8
|
||||
max_val = 298
|
||||
vol_mult = 100/(max_val-min_val)
|
||||
volume = 0
|
||||
v_1 = 0
|
||||
v_2 = 0
|
||||
ctrlc_pressed = False
|
||||
pot_cap_gpio = 23
|
||||
drain_ms = 0.8
|
||||
timeout_s = 1.0
|
||||
|
||||
|
||||
jfont = ImageFont.truetype('DejaVuSansMono.ttf', 10)
|
||||
|
||||
# MPD config
|
||||
|
@ -46,6 +31,13 @@ GPIO.setmode(GPIO.BCM)
|
|||
RELAIS_1_GPIO = 17
|
||||
GPIO.setup(RELAIS_1_GPIO, GPIO.OUT) # GPIO Assign mode
|
||||
|
||||
# Rotary encoder
|
||||
from rot_lib import Encoder
|
||||
clk = 27
|
||||
dt = 22
|
||||
init_volume = 30
|
||||
enc=Encoder(dt,clk,0,100,3)
|
||||
|
||||
# Buttons GPIO
|
||||
BTNS = { 'BTN_1' : dict(GPIO=7, state=1),
|
||||
'BTN_2' : dict(GPIO=8, state=1),
|
||||
|
@ -357,11 +349,6 @@ def send_mpd_cmd(client, cmd:str, ui_state:dict):
|
|||
def main(args):
|
||||
# Idle timer
|
||||
ui_idle_for = 0
|
||||
# Pot_cap
|
||||
# Connect to Pi.
|
||||
pi = pigpio.pi()
|
||||
# Instantiate Pot/Cap reader.
|
||||
pc = pot_cap.reader(pi, pot_cap_gpio, drain_ms, timeout_s)
|
||||
start = time()
|
||||
|
||||
previous_song_id = None
|
||||
|
@ -391,29 +378,27 @@ def main(args):
|
|||
# List song by album
|
||||
# ~ songs_from_album = client.list('title', 'album', album_by_x[x])
|
||||
|
||||
# Set Init volume in encoder and mpd
|
||||
volume = init_volume
|
||||
enc.setCounter(volume)
|
||||
client.setvol(volume)
|
||||
|
||||
offset_polygons(MODES)
|
||||
global ui_vol_icon_polygon
|
||||
ui_vol_icon_polygon = apply_xy_offset(ui_vol_icon_polygon, ui_vol_icon_coords)
|
||||
global static_ui
|
||||
static_ui = generate_static_ui(current_mode)
|
||||
while ctrlc_pressed is False:
|
||||
# pot_cap
|
||||
global v_1
|
||||
global v_2
|
||||
global volume
|
||||
s, v, r = pc.read()
|
||||
if s and r < 4:
|
||||
volume = round(v*vol_mult)
|
||||
if (abs(volume - v_1) > 1) and (abs(volume - v_2) > 2):
|
||||
print("Volume: {}".format(volume))
|
||||
if volume < min_val:
|
||||
volume = 0
|
||||
if volume > 100:
|
||||
volume = 100
|
||||
client.setvol(100-volume)
|
||||
ui_idle_for = 0
|
||||
v_2 = v_1
|
||||
v_1 = volume
|
||||
# Rotary encoder
|
||||
cur_vol = int(client.getvol()['volume'])
|
||||
if cur_vol != volume:
|
||||
volume = cur_vol
|
||||
elif int(enc.getValue()) != volume:
|
||||
volume = int(enc.getValue())
|
||||
client.setvol(volume)
|
||||
enc.setCounter(volume)
|
||||
|
||||
#ui_idle_for = 0
|
||||
# MPD
|
||||
mpd_status = client.status()
|
||||
if len(mpd_status):
|
||||
|
@ -470,12 +455,10 @@ def main(args):
|
|||
# Avoid further execution
|
||||
ui_idle_for = 11
|
||||
device.hide()
|
||||
# pot_cap
|
||||
pc.cancel() # Cancel the reader.
|
||||
pi.stop() # Disconnect from Pi.
|
||||
|
||||
|
||||
device.cleanup()
|
||||
client.disconnect()
|
||||
GPIO.cleanup()
|
||||
return 0
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ After=mpd.service
|
|||
|
||||
[Service]
|
||||
WorkingDirectory=/home/%u/mpdlisten
|
||||
ExecStart=/home/%u/mpdlisten/mpdlisten.py
|
||||
ExecStart=/home/%u/mpdlisten/mpdlistenpy.sh
|
||||
KillSignal=SIGINT
|
||||
Restart=always
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/env bash
|
||||
cd "$(dirname "$0")"
|
||||
python mpdlisten.py
|
45
readme.md
45
readme.md
|
@ -5,8 +5,8 @@ This python script is supposed to run on a rpi and does the following :
|
|||
* Relay trigger : Watches a MPD instance for play/pause/stop states and activates a relay via GPIOs accordingly ; the usecase for this is powering on/off a pair of speakers only when something is running.
|
||||
* OLED display : Drives a 0.96" OLED display (ssd1306) to display MPD status (volume, current artist, album, title...)
|
||||
* Button input : watches GPIOs for button input ; I'm using 5 push buttons for controlling playback (previous, next, toggle playback, stop, menu) or browse the database and add stuff to the playlist.
|
||||
* Potentiometer input : Watches a GPIO for estimating the resistance of a rotary potentiometer and set volume accordingly.
|
||||
|
||||
* Rotary encoder for volume control.
|
||||
|
||||
## Setup
|
||||
|
||||
### Software
|
||||
|
@ -32,7 +32,7 @@ This python script is supposed to run on a rpi and does the following :
|
|||
| OLED | GND, 3.3V, 2(SDA), 3(SCL) |
|
||||
| 5 BUTTONS | GND, 7, 8, 9, 11, 25 |
|
||||
| POTENTIOMETER | GND, 3.3V, 23 |
|
||||
| RELAY | GND, 5V, 17 |
|
||||
| ROTARY ENCODER | GND, 3V3, 27, 22 |
|
||||
|
||||
You can check these here : [https://pinout.xyz/](https://pinout.xyz/)
|
||||
|
||||
|
@ -77,39 +77,18 @@ If you're short on GPIOs, there is a way to use a single GPIO for multiple butto
|
|||
|
||||
[https://www.instructables.com/RaspberryPi-Multiple-Buttons-On-One-Digital-Pin/](https://www.instructables.com/RaspberryPi-Multiple-Buttons-On-One-Digital-Pin/)
|
||||
|
||||
#### Potentiometer circuit
|
||||
#### Rotary encoder circuit
|
||||
|
||||
Using the 'step response' method described [here](http://razzpisampler.oreilly.com/ch08.html#Discussion) (It's the same principle as in the link above),
|
||||
we can use an analog potentiometer to control volume.
|
||||
Using [https://github.com/Tr3m/rpi-rotary-encoder](https://github.com/Tr3m/rpi-rotary-encoder), we can use a 3-pins rotary encoder to control volume.
|
||||
|
||||
The solution described in the previous link is not very accurate though, so we'll be using an alternative solution offered by abyz.me.uk :
|
||||
##### Wiring
|
||||
|
||||
[https://abyz.me.uk/rpi/pigpio/examples.html#Python_pot_cap_py](https://abyz.me.uk/rpi/pigpio/examples.html#Python_pot_cap_py)
|
||||
|
||||
##### Components
|
||||
|
||||
* 100nf Ceramic capacitor
|
||||
* 5KΩ Potentiometer
|
||||
|
||||
##### Diagram
|
||||
|
||||
```
|
||||
3V3 ----- Potentiometer --+-- Capacitor ----- Ground
|
||||
|
|
||||
+-- GPIO 23
|
||||
```
|
||||
|
||||
Instructions for use are in the source file. The library ('pot_cap.py') is included in this project for convenience.
|
||||
|
||||
You might have to install the 'pigpio' library though :
|
||||
|
||||
```
|
||||
sudo apt install python3-pigpio
|
||||
```
|
||||
|
||||
You can find out more about installing 'pigpio' here :
|
||||
|
||||
[https://abyz.me.uk/rpi/pigpio/download.html](https://abyz.me.uk/rpi/pigpio/download.html)
|
||||
| ENCODER | RPI GPIO # |
|
||||
| --- | --- |
|
||||
| GND | GND |
|
||||
| + | 3V3 |
|
||||
| CLK | 27 |
|
||||
| DT | 22 |
|
||||
|
||||
## Optional steps
|
||||
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
# 2023 - Tr3m
|
||||
# Source : https://github.com/Tr3m/rpi-rotary-encoder
|
||||
# Added a setCounter() method
|
||||
|
||||
import RPi.GPIO as GPIO
|
||||
from time import sleep
|
||||
|
||||
class Encoder:
|
||||
|
||||
def __init__(self, pin_a: int, pin_b: int, range_min, range_max, increment):
|
||||
if type(pin_a) == type(pin_b) == int:
|
||||
self.__pin_a = pin_a
|
||||
self.__pin_b = pin_b
|
||||
|
||||
self.__counter = None
|
||||
self.__range_min = None
|
||||
self.__range_max = None
|
||||
self.__increment = None
|
||||
|
||||
self.setRange(range_min, range_max, increment)
|
||||
|
||||
GPIO.setwarnings(True)
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
|
||||
GPIO.setup(self.__pin_a, GPIO.IN)
|
||||
GPIO.setup(self.__pin_b, GPIO.IN)
|
||||
|
||||
GPIO.add_event_detect(self.__pin_a, GPIO.RISING, callback=self.__rotation_decode, bouncetime=10)
|
||||
|
||||
else:
|
||||
raise TypeError("Arguments need to be of type int")
|
||||
|
||||
|
||||
def getValue(self):
|
||||
return self.__counter
|
||||
|
||||
|
||||
def setRange(self, range_min, range_max, increment):
|
||||
if type(range_min) != type(range_max):
|
||||
raise TypeError("Arguments need to be same type (int or float)")
|
||||
|
||||
if type(range_min) == int or type(range_min) == float:
|
||||
if type(increment) != type(range_min):
|
||||
raise TypeError("Increment type needs to be the same type as range_min and range_max")
|
||||
|
||||
if range_min > range_max:
|
||||
raise Exception("Invalid range")
|
||||
|
||||
self.__range_min = range_min
|
||||
self.__range_max = range_max
|
||||
self.__increment = increment
|
||||
self.__counter = (range_min + range_max) / 2
|
||||
|
||||
return
|
||||
|
||||
raise TypeError("Unsupported operand:", type(range_min))
|
||||
|
||||
|
||||
def setCounter(self, counter):
|
||||
self.__counter = float(counter)
|
||||
|
||||
def __rotation_decode(self, pin: int):
|
||||
sleep(0.002)
|
||||
Switch_A = GPIO.input(self.__pin_a)
|
||||
Switch_B = GPIO.input(self.__pin_b)
|
||||
|
||||
if (Switch_A == 1) and (Switch_B == 0):
|
||||
if (self.__counter > self.__range_min) and ((self.__counter - self.__increment) > self.__range_min):
|
||||
self.__counter -= self.__increment
|
||||
else:
|
||||
self.__counter = self.__range_min
|
||||
|
||||
while Switch_B == 0:
|
||||
Switch_B = GPIO.input(self.__pin_a)
|
||||
while Switch_B == 1:
|
||||
Switch_B = GPIO.input(self.__pin_b)
|
||||
return
|
||||
|
||||
elif (Switch_A == 1) and (Switch_B == 1):
|
||||
if (self.__counter < self.__range_max) and (self.__counter + self.__increment < self.__range_max):
|
||||
self.__counter += self.__increment
|
||||
else:
|
||||
self.__counter = self.__range_max
|
||||
|
||||
while Switch_A == 1:
|
||||
Switch_A = GPIO.input(pin)
|
||||
return
|
||||
else:
|
||||
return
|
||||
|
||||
def __clear(self):
|
||||
GPIO.remove_event_detect(self.__pin_a)
|
Loading…
Reference in New Issue