Thank you for your donation!


Cloudsmith graciously provides open-source package management and distribution for our project.


Instruction Guide Use hardware momentary switches to control moOde
#32
Dear Tim and Remy, I have added a rotaty encoder for volume control (based on hrvoje's algorithm, see link in code). And a way to add the current song to a favorites list (or a list of files to be deleted) by communicating with mpd directly.

Code:
#!/usr/bin/python
#coding: utf8
# Script for mpc/mpd audio GPIO buttons partly based on remy1961's code on http://moodeaudio.org/forum/showthread.php?tid=198&pid=8382#pid8382
# Rotary encoder mostly based on hrvoje's code https://www.raspberrypi.org/forums/viewtopic.php?t=140250 . Comments on the reliability of RPi.GPIO library there
# mpd on default settings: localhost 6600
# Worked with one rotary encoder only (with two not reliably enough) in my case
# All buttons and rotary encoder connect to ground without hardware debouncing or hardware pull-up/-down. Rotary encoder bouncing is not improved with capacitors across A/B and ground.

import RPi.GPIO as GPIO             # pigpio library does NOT work, due to loud noise (louder than the music)
import threading                    # rotary interrupt
import time
import os
import subprocess
#import datetime                    # for testing only

#GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM)              # dset GPIO mode instead of GPIO.setmode(GPIO.BOARD) - Zählweise der Pins festlegen

#On playlist names no spaces
PLFAV = "Favoriten"                 # This is the playlist to which files are saved when pushing the button. /var/lib/mpd/playlists/Favoriten.m3u will be created if it does not exist yet.
PLDEF = "Aktuell"                   # This is the default playlist which is played when pushing the respective button (/var/lib/mpd/playlists/Aktuell.m3u needs to be generated e.g. by renaming an existing .m3u)
PLDLT = "Delete"                    # This is the delete list. /var/lib/mpd/playlists/Delete.m3u will be created if it does not exist.

#Define your GPIO pins (pin 20, 18, etc. not suitable in my case, i.e. not working or crashing), 7 buttons + 1 rotary encoder with button
SW_PREV = 22                        # Previous
SW_NEXT = 4                         # Next
SW_FAV = 26                         # Add to favorites
SW_POWEROFF = 6                     # Shut down
SW_PLAY = 17                        # Toggle play/pause, e.g. button on the rotary encoder
SW_FW = 5                           # Go forward within current song
SW_DEF = 13                         # Play default playlist
SW_DLT = 25                         # adds the currently played song to the delete list (to be deleted semi-manually on the master repository - the Raspberry Pi accesses a copy on my NAS only)
RO_A = 24                           # Volume up
RO_B = 23                           # Volume down

PRELL = 500                         # debouncing for regular buttons
PRELLL = 1500                       # debouncing for "add to list" buttons
PRELLROT = 30                       # debouncing for rotary, favorable to diminish skipping. Higher value allows only slow volume increase
SCHRITT = 5                         # % change in Volume
SPRING = 10                         # % forward within current song
LockRotary = threading.Lock()        # create lock for rotary switch
Current_A = 0                        # Assume that rotary switch is not moving while we init software
Current_B = 0

#Code to manage BUTTONS
GPIO.setup(SW_PREV, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(SW_NEXT, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(SW_FAV, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(SW_POWEROFF, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(SW_PLAY, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(SW_FW, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(SW_DEF, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(SW_DLT, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(RO_A, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(RO_B, GPIO.IN, pull_up_down=GPIO.PUD_UP)

#Interrupt events
def eSW_PREV(channel):
   subprocess.call(['mpc', 'prev' ])
#   print str(datetime.datetime.now())[:19] + " previous"               # for testing only

def eSW_NEXT(channel):
   subprocess.call(['mpc', 'next' ])
#   print str(datetime.datetime.now())[:19] + " next"                   # for testing only

def eSW_FAV(channel):
   NAMEN = subprocess.check_output("mpc -f %file% current", shell=True)[:-1]
   NAME = NAMEN.replace("\'","\\'\\'\'")                               # for the sigle quotes in echo
   PFAD = "echo 'playlistadd " + PLFAV + " \"" + NAME + "\"' | netcat -q 0 localhost 6600"  # or "-w 1" instead of "-q 0" if not saving reliably?
   os.system(PFAD)
   print "Zu " + PLFAV + ": " + NAMEN

def eSW_POWEROFF(channel):
   subprocess.call(['mpc', 'stop' ])
   subprocess.call(['sudo', 'poweroff' ])
#   print str(datetime.datetime.now())[:19] + " shutting down"          # for testing only

def eSW_PLAY(channel):
   subprocess.call(['mpc', 'toggle' ])
#   print str(datetime.datetime.now())[:19] + " play"                   # for testing only

def eSW_FW(channel):
   subprocess.call(['mpc', 'seek', '+'+str(SPRING)+'%' ])
#   print str(datetime.datetime.now())[:19] + " forward"                # for testing only
       

def eSW_DEF(channel):
   subprocess.call(['mpc','clear' ])
   subprocess.call(['mpc','load',PLDEF ])
   subprocess.call(['mpc','play' ])
#   print str(datetime.datetime.now())[:19] + " default playlist"       # for testing only

def eSW_DLT(channel):
   NAMEN = subprocess.check_output("mpc -f %file% current", shell=True)[:-1]
   NAME = NAMEN.replace("\'","\\'\\'\'")                               # for the sigle quotes in echo
   PFAD = "echo 'playlistadd " + PLDLT + " \"" + NAME + "\"' | netcat -q 0 localhost 6600"  # or "-w 1" instead of "-q 0" if not saving reliably?
   os.system(PFAD)
   print "Zur Löschliste " + PLDLT + ": " + NAMEN

# Rotary encoder interrupt is called for both inputs from rotary switch (A and B)
def eRO(A_or_B):
    global Current_A, Current_B, LockRotary
   
    Switch_A = GPIO.input(RO_A)                                         # Read both of the switches
    Switch_B = GPIO.input(RO_B)
   
    if Current_A == Switch_A and Current_B == Switch_B:                    # Now check if state of A or B has changed. If not that means that bouncing caused it
        return                                                               # Same interrupt as before (Bouncing)? Ignore interrupt!

    Current_A = Switch_A                                                # remember new state
    Current_B = Switch_B                                                # for next bouncing check

    if (Switch_A == 0 and Switch_B == 0):                                # Both one active? Yes -> end of sequence
        LockRotary.acquire()                                            # get lock
        if A_or_B == RO_B:                                                # Turning direction depends on which input gave last interrupt
                   subprocess.call(['mpc', 'volume', '+'+str(SCHRITT) ])
        else:                                                            # so depending on direction either increase or decrease counter
                   subprocess.call(['mpc', 'volume', '-'+str(SCHRITT) ])
        LockRotary.release()                                            # and release lock
    return                                                                # done

# Declare interrupt events
GPIO.add_event_detect(SW_PREV, GPIO.FALLING, callback = eSW_PREV, bouncetime = PRELL)
GPIO.add_event_detect(SW_NEXT, GPIO.FALLING, callback = eSW_NEXT, bouncetime = PRELL)
GPIO.add_event_detect(SW_FAV, GPIO.FALLING, callback = eSW_FAV, bouncetime = PRELLL)
GPIO.add_event_detect(SW_POWEROFF, GPIO.FALLING, callback = eSW_POWEROFF, bouncetime = PRELL)
GPIO.add_event_detect(SW_PLAY, GPIO.FALLING, callback = eSW_PLAY, bouncetime = PRELL)
GPIO.add_event_detect(SW_FW, GPIO.FALLING, callback = eSW_FW, bouncetime = PRELL)
GPIO.add_event_detect(SW_DEF, GPIO.FALLING, callback = eSW_DEF, bouncetime = PRELL)
GPIO.add_event_detect(SW_DLT, GPIO.FALLING, callback = eSW_DLT, bouncetime = PRELLL)
GPIO.add_event_detect(RO_A, GPIO.FALLING, callback=eRO, bouncetime = PRELLROT)
GPIO.add_event_detect(RO_B, GPIO.FALLING, callback=eRO, bouncetime = PRELLROT)

# Main
while True:
   time.sleep(1)
# the following is for testing pins. In my case e.g. pin 20 was low (0) all the time
'''
   print str(GPIO.input(SW_PREV)) + " SW_PREV " + str(SW_PREV) + "   |    " +  \
   str(GPIO.input(SW_NEXT)) + " SW_NEXT " + str(SW_NEXT) + "   |    " +  \
   str(GPIO.input(SW_POWEROFF)) + " SW_POWEROFF " + str(SW_POWEROFF) + "   |    " +  \
   str(GPIO.input(SW_PLAY)) + " SW_PLAY " + str(SW_PLAY) + "   |    " +  \
   str(GPIO.input(RO_A)) + " RO_A " + str(RO_A) + "   |    " +  \
   str(GPIO.input(RO_B)) + " RO_B " + str(RO_B) + "   |    " +  \       # for testing only
'''
Reply


Messages In This Thread
RE: Use hardware momentary switches to control moOde - by Cyanoazimin - 04-07-2019, 06:43 PM

Forum Jump: