GPIO Pins to control volume

Hi I was wondering if it is possible to control the volume of Volumio via GPIO pins? I ask as I am making a WiFi speaker and would like to have control on the speaker for volume up/down and mute - just to make it easy to adjust volume rather than having to get phone out :slight_smile:

Hello

I suggest you to add an ask in this message : interface-with-gpio-and-rs232-spi-full-communication-t2192.html
We are a least 2 guy to ask something like that :slight_smile:.

1 Like

I believe what you want isn’t the same as what Glop asks, or i just didn’t understand what you actually want. Glop wants basic control using a few buttons over GPIO. This isn’t hard to achieve.

Get some buttons connect them on 3.3v and a GPIO in.
Create a python script, import bash/shell commands and send mpc commands

At least thats the simplest way i can think of, you could skip the bash import with mpc and use python-mpd but then you’ll need to install python-mpd.

You could take a loot at this thread for inspiration: detect-when-something-streaming-t2056.html

I think that the volume buttons on GPIO is in the same way that I suggeste to configure via GUI the GPIO actions In or Out. (or more deeply within serial communication).

I think it’s posible t do that with script and other but it will be “endUser” to propose that on the config in GUI… :smiley:

Buttons shouldn’t be a big thing. A volume knob i think is more difficult, but this variant would be nice! :smiley:

You could use a rotary encoder to do that. Adafruit sells those they also made a sample script to control windows audio. See adafruit.com/product/377

Hi,

A Volume knob is not that difficult!

Hardware:
You need a rotary encoder and some wires. Dupont cables (male/female) are handy, you can solder one side to the encoder and the other side (female connector) can easily be connected with de GPIO connector.

Rotary.jpg

SOFTWARE:

Gpio
To get access to the GPIO-pins you have to install the RPi.GPIO python module:

Download the package from sourceforge

wget http://sourceforge.net/projects/raspberry-gpio-python/files/raspbian-wheezy/python-rpi.gpio_0.5.8-1_armhf.deb

Install the package

sudo dpkg -i python-rpi.gpio_0.5.8-1_armhf.deb

Rotary class
You can use the rotary-class from Bob Rathbone source: http://www.bobrathbone.com/raspberrypi_rotary.htm
(Bob has also a fantastic tuturial for building a radio project!)

Save the rotary class in: /home/pi

Script
Then you need some code to control the volume (based on the test-script from Bob):

#!/usr/bin/env python
#
# Raspberry Pi Rotary Test Encoder Class
#
# Author : Bob Rathbone
# Site   : http://www.bobrathbone.com
#
# This class uses a standard rotary encoder with push switch
#
import subprocess
import sys
import time
from rotary_class import RotaryEncoder

# Define GPIO inputs (BCM)
PIN_A = 23 	          # Pin 16
PIN_B = 24	          # Pin 18
BUTTON = 25	  # Pin 22

# This is the event callback routine to handle events
def switch_event(event):
        if event == RotaryEncoder.CLOCKWISE:
              subprocess.call(['mpc', 'volume', '+2' ])
              time.sleep(.2)
        elif event == RotaryEncoder.ANTICLOCKWISE:
              subprocess.call(['mpc', 'volume', '-2' ])
              time.sleep(.2)
        elif event == RotaryEncoder.BUTTONDOWN:
              subprocess.call(['mpc', 'toggle' ])
        #elif event == RotaryEncoder.BUTTONUP:
              #print "Button up"
        return

# Define the switch
rswitch = RotaryEncoder(PIN_A,PIN_B,BUTTON,switch_event)

while True:
        time.sleep(0.5)

Save this script in: /home/pi/VolumeKnob.py

Auto-start the script:

nano /etc/rc.local

Add this line before the exit 0:

python /home/pi/VolumioKnob.py &

Reboot the Pi and you can control the volume with the rotary-encoder and with a button press you can pause/play the music!

Succes!

Harry

Hi!
great !!! that is exactly what I was looking for !!!
But I’ve got a problem with Volumio 1.5, I can’t install python-rpi.gpio. The message is :

i@volumio-bureau:~$ sudo apt-get install python-rpi.gpio Reading package lists... Done Building dependency tree Reading state information... Done E: Unable to locate package python-rpi.gpio E: Couldn't find any package by regex 'python-rpi.gpio'
Any idea ?
Thanks !

Ok
I did it following this thread : rpi-volumio-gpio-for-python-t1491.html
It works now ! But I change step from 2 to 4 in the VolumeKnob.py file.
Thanks a lot!

too bad…
I thought it was ok but after a reboot mpd does not work anymore. No webUI… I can ssh to my pi. Get the following error :

pi@volumio-bureau:~$ sudo service mpd restart [ ok ] Stopping Music Player Daemon: mpd. [....] Starting Music Player Daemon: mpdIllegal instruction failed!
What to do to avoid a complete re-install ?
info : At boot time I hear the boot sound of Volumio

I bet some packages have been upgraded during the installation. A complete re-install is the fastest way to get you back up and running.

To prevent this from happening you could try and change “jessie” into “wheezy” in the sources file. This should stop apt from getting newer packages that corrupt volumio and the required services. BUT PLEASE NOTE this needs to be tested, if you see that packages need to be upgraded before you can install some thing using apt try to use an older version of the package you want to install. Many upgrade packages break volumio.

Balbuze you could try and see if you can find the illegal instuction in the config files of mpd.

This difficult is the reason to have something in volumio directly in standard for GPIO :slight_smile:.
Good luck :wink:.

I’m looking for an alternative to python-rpi.gpio, going trough the raspberry pi wiki i found that you should be able to control the GPIO’s using bash scripts. If this works your not required to install python-rpi.gpio and risk corrupting volumio.

I cant test this today, ill see if i can try something tomorrow.

see: elinux.org/RPi_Low-level_peripherals
“Bash shell script, using sysfs, part of the raspbian operating system”

Ooops, I have not publiced my latest instructions!

I had the same problems as Balbuze. But I is possible to install rpi-gpio without killing Volumio!

wget http://sourceforge.net/projects/raspberry-gpio-python/files/raspbian-wheezy/python-rpi.gpio_0.5.8-1_armhf.deb
sudo dpkg -i python-rpi.gpio_0.5.8-1_armhf.deb

…and this works

NespressoPlayer.jpg
Succes!

Harry

Hi
I re-did a fresh install. Now it works following Buckler instructions.
Thank you !
Now, next step is having a display lcd or oled .
Buckler, what model do you use ? wich code and what information are displayed ?
I 'd like to add button to go to next/previous song etc…

@Balbuze

This a really cheap display about 4 euro, 2x16 characters blue & white (HD44780). A OLED would be nice but so expensive…
The first line is showing Time + Status + Volume
The second line is showing Stationname + Artist + Title (scrolling)
When you play a song the time will show the remaining time and the second line will show you the artist + Title

A nice feature is to use an I2C backpack, you will only have 4 wire between the Pi and the Lcd+BP.

You want a display and buttons, is an Adafruit LCD+keypad kit not an options?
There are a lot of python scripts available wich can be installed with Volumio!

Greetings

Harry

Hi
I ran into the same problem with Volumio and GPIO Python. I then searched and found a solution here in this forum (can not currently find it):
I installed the GPIO Python not with this “sudo apt-get install python-rpi.gpio” but instead did this

sudo su
wget http://sourceforge.net/projects/raspberry-gpio-python/files/raspbian-wheezy/python-rpi.gpio_0.5.8-1_armhf.deb
dpkg -i python-rpi.gpio_0.5.8-1_armhf.deb

After that I was able to access GPIO from Python and still have Volumio running.

I have the following system setup and wrote a script to check for 4 button presses (next song, previous song, first song in playlist, random play toggle). The script also reads a 10k (analog) potentiometer using MCP3008 and sets mpd volume accordingly. Works fine.

System Schematics here

xmasTV-schema.jpg

Here is a pic of the back of my gadget running volumio (backpanel with shutdown pushbutton)

Rear_2.jpg

Here is the script

#!/usr/bin/python
# coding: utf-8
#------------------------------------------------------
# Client for mpd-Server
#   Programm starts 8 Threads
#   waits fĂĽr button click on four buttons - sets up three interrupt routines
#   button 1 (GPIO23) :  jump to next title in playlist
#   button 2 (GPIO24) :  jump to previuos title in playlist
#   button 3  (GPIO8) :  jump to first title in playlist
#   button 4  (GPIO15) : set mpd player to random mode (toggle)
#   Waits also for random seconds to play werbung, suspends currentplaylist
#   
#   Led1 blinks as long as program runs
#   Pressing a buttons blinks Led2 twice for visual feedback
#   Successful connection to mpd Server blinks Led2 twice
#   keyboard Interrupt is catched for testing purposes in Terminal, signals termination to all threads vi variable exitapp
#
#   script also starts thread to read volume potentiometer and sets mpd volume accordingly
#   code to read pot via chip MCP3008 is taken from Adafruits learning unit
#   Analog Inputs for Raspberry Pi Using the MCP3008 (they call it bit banged). 
#   This script uses GPIO Pins 4/17/27/22  for CLK/DOUT/CS/CS
#
#   Script is started via /etc/rc.local
#
#   Pretty simple version found on the Volumio-Forum
#   Extended and adapted by Peter K. Boxler, December 2014 
#-------------------------------------------------------

import RPi.GPIO as GPIO
import sys, getopt, os
import mpd
import time
import random
from threading import Thread

GPIO.setmode(GPIO.BCM)
buttonPinNext 	= 8
buttonPinPrev 	= 24
buttonPinStart 	= 23
buttonPinRandom = 15
potschalter =   14

led1=25                 # green led blinks as long as script runs
led2=7                  # red led blinks three times: acks button press
led3=9                  # green led on= random play on off= random play off

# GPIO Pins for bit banged AD Converter (from Adafruit example)
SPICLK = 4
SPIMISO = 17
SPIMOSI = 27
SPICS = 22



debug=0
FALSE=0
TRUE=1
exitapp = FALSE
sleep_ping=30
randomstate=0
waitwerbungfactor_dead=100
waitwerbungfactor_blues=15
waitwerbung=0

global TEST_MPD_HOST, TEST_MPD_PORT, TEST_MPD_PASSWORD
TEST_MPD_HOST     = "localhost"
TEST_MPD_PORT     = "6600"
TEST_MPD_PASSWORD = "volumio"   # password for Volumio / MPD


# ***** Function Parse commandline arguments ***********************
# get and parse commandline args

def arguments(argv):
    global  debug
    try:
        opts, args=getopt.getopt(argv,"dh")
    except getopt.GetoptError:
        print ("Parameter Error")
        sys.exit(2)
    for opt, arg in opts:
        if opt in ("-h", "--help"):
            print ("App mpd-client -----------------------------------")
            print ("usage: mpd-client [-s -h]")
            sys.exit(2)
        elif opt == '-d': 	debug = 1
	
# ***********************************************

#-----------------------------------------------------------  
# get next song to be played          
def get_nexid(mpd):
    global  debug

    status = mpd.status()
    for text in status:
            if text=="nextsongid":
                nexid=int(status.get(text))
                if debug: print "Next SongID: %d" % nexid 
    return(nexid)


#----------------------------------------------------------- 
# lower volume from current value to zero, returns current value           
def lower_volume(mpd):
    global  debug

    status = mpd.status()
    for text in status:
            if text=="volume":
                curvol=int(status.get(text))
                if debug: print "Current Volume: %d" % curvol 
    volume=curvol
    if debug: print "Decreasing Volume"
    while TRUE:
        volume=volume-10
        if volume<0: 
            volume=0
            mpd.setvol(volume)
            if debug: print "Volume ist NULL"
            break   
        mpd.setvol(volume) 
        time.sleep(1.5)
    
    return(curvol)
        


#-----------------------------------------------
def connect_mpd():
# Connect with MPD (music player daemon)
#------------------------------------------------
    global debug
    connected = False
    retry_count=3
    i=1
    while connected == False:
        connected = True
        try:
             client.connect(TEST_MPD_HOST, TEST_MPD_PORT)
        except SocketError as e:
             connected = False
        if connected == False:
                if debug: print "Couldn't connect. Retrying"
                i=i+1
                if i > retry_count:
                    return(9)
                time.sleep(3)
    if debug: print("Connected to MPD-Server")
    return(0)

#---------------------------------------------------------
# read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7)
def readadc(adcnum, clockpin, mosipin, misopin, cspin):
        if ((adcnum > 7) or (adcnum < 0)):
                return -1
        GPIO.output(cspin, True)

        GPIO.output(clockpin, False)  # start clock low
        GPIO.output(cspin, False)     # bring CS low

        commandout = adcnum
        commandout |= 0x18  # start bit + single-ended bit
        commandout <<= 3    # we only need to send 5 bits here
        for i in range(5):
                if (commandout & 0x80):
                        GPIO.output(mosipin, True)
                else:
                        GPIO.output(mosipin, False)
                commandout <<= 1
                GPIO.output(clockpin, True)
                GPIO.output(clockpin, False)

        adcout = 0
        # read in one empty bit, one null bit and 10 ADC bits
        for i in range(12):
                GPIO.output(clockpin, True)
                GPIO.output(clockpin, False)
                adcout <<= 1
                if (GPIO.input(misopin)):
                        adcout |= 0x1

        GPIO.output(cspin, True)
        
        adcout >>= 1       # first bit is 'null' so drop it
        return adcout


#-----------------------------------------------------------
def blink_led1():  # blink led coninuosly when program runs
    while not exitapp:
        for i in range(3):
            GPIO.output(led1, True)
            time.sleep(0.2)
            GPIO.output(led1, False)
            time.sleep(0.8)
    GPIO.output(led1, False)

#-----------------------------------------------------------
def blink_led2(anzahl,laenge):  # Visual feedback if button pressed and connected
    for i in range(anzahl):
        GPIO.output(led2, True)
        time.sleep(laenge)
        GPIO.output(led2, False)
        time.sleep(0.1)


#-----------------------------------------------------------
def my_callback_nx(channel):
    if debug: print "Pin Falling: %d" % channel
    time.sleep(.2)  # confirm the movement by waiting 1 sec 
    if not GPIO.input(buttonPinNext): # and check again the input
        if debug: print("ok, pin ist tief!")
        ret=client.next()
        blink_led2(2,0.1)
        if debug: print ret

#-----------------------------------------------------------
def my_callback_pr(channel):
    if debug: print "Pin Falling: %d" % channel
    time.sleep(.2)  # confirm the movement by waiting 1 sec 
    if not GPIO.input(buttonPinPrev): # and check again the input
        if debug: print("ok, pin ist tief!")
        client.previous()
        blink_led2(2,0.1)

#-----------------------------------------------------------        
def my_callback_start(channel):
    if debug: print "Pin Falling: %d" % channel
    time.sleep(.2)  # confirm the movement by waiting 1 sec 
    if not GPIO.input(buttonPinStart): # and check again the input
        if debug: print("ok, pin ist tief!")
        client.play(0)
        blink_led2(2,0.1)

#-----------------------------------------------------------
def my_callback_rd(channel):
    global randomstate
    if debug: print "Pin Falling: %d" % channel
    time.sleep(.2)  # confirm the movement by waiting 1 sec 
    if not GPIO.input(buttonPinRandom): # and check again the input
        if debug: print("ok, pin ist tief!")
        if randomstate:
            GPIO.output(led3, False)
            client.random(0)
            randomstate=0
        else:
            GPIO.output(led3, True)
            client.random(1)
            randomstate=1
        
        blink_led2(2,0.1)


#-----------------------------------------------------------            
def connection_status():
#    pings server every 60 seconds and prints "pinging" (if debug is specified).
    global sleep_ping, exitapp
    while not exitapp:  # main thread gives terminate signal here
        if debug: print "Connection: pinging the server"
        client.ping()
        wait=sleep_ping
        while wait > 0:
            time.sleep(3)
            wait=wait-3
            if debug: print "Connection: Waiting 3 seconds"
            if exitapp: break

    client.close()      # we need to terminate
    if debug: print "-->Thread Connection terminating..."
    sys.exit(0)

#-----------------------------------------------------------    
def button_nx():
    global exitapp
    GPIO.add_event_detect(buttonPinNext, GPIO.FALLING, callback=my_callback_nx, bouncetime=300)
#
    while not exitapp:
        time.sleep(1)
        pass    # pass ist leer statement
        time.sleep(1)
    if debug: print "-->Thread NEXT terminating..."
    sys.exit(0)

#-----------------------------------------------------------
def button_pr():
    global exitapp
    GPIO.add_event_detect(buttonPinPrev, GPIO.FALLING, callback=my_callback_pr, bouncetime=300)
# 
    while not exitapp:   # main thread gives terminate signal here
        time.sleep(1)
        pass    # pass ist leer statement
        time.sleep(1)
    if debug: print "-->Thread PREVIOUS terminating..."
    sys.exit(0)

#-----------------------------------------------------------    
def button_st():
    global exitapp
    GPIO.add_event_detect(buttonPinStart, GPIO.FALLING, callback=my_callback_start, bouncetime=300)
#
    while not exitapp:   # main thread gives terminate signal here
        time.sleep(1)
        pass    # pass ist leer statement
        time.sleep(1)
    if debug: print "-->Thread START PL terminating..."        
    sys.exit(0)

#-----------------------------------------------------------    
def button_rd():
    global exitapp
    GPIO.add_event_detect(buttonPinRandom, GPIO.FALLING, callback=my_callback_rd, bouncetime=300)
#
    while not exitapp:   # main thread gives terminate signal here
        time.sleep(1)
        pass    # pass ist leer statement
        time.sleep(1)
    if debug: print "-->Thread RANDOM terminating..."        
    sys.exit(0)


#-----------------------------------------------------------------
# function (thread) to read pot and set volumne
def read_pot():
    global exitapp, debug
# 10k trim pot connected to adc #0
    potentiometer_adc = 0;

    last_read = 0       # this keeps track of the last potentiometer value
    tolerance = 10       # to keep from being jittery we'll only change
                    # volume when the pot has moved more than 5 'counts'

    while not exitapp:  # loop til main threads signals termination
        # we'll assume that the pot didn't move
        trim_pot_changed = False

        # read the analog pin
        trim_pot = readadc(potentiometer_adc, SPICLK, SPIMOSI, SPIMISO, SPICS)
        # how much has it changed since the last read?
        pot_adjust = abs(trim_pot - last_read)


        if ( pot_adjust > tolerance ):
               trim_pot_changed = True
           

        if ( trim_pot_changed ):
                if debug:   
                    print "Pot changed more than tolerance %d", tolerance
                    print "Pot before:", trim_pot
                    print "Pot has changed by:", (trim_pot - last_read)
                    print "Pot last_read", last_read
                set_volume = trim_pot / 10.24           # convert 10bit adc0 (0-1024) trim pot read into 0-100 volume level
                set_volume = round(set_volume)          # round out decimal value
                set_volume = int(set_volume)            # cast volume as integer

                if debug: print 'Volume new = {volume}%' .format(volume = set_volume)
 #               set_vol_cmd = 'sudo amixer cset numid=1 -- {volume}% > /dev/null' .format(volume = set_volume)
 #               os.system(set_vol_cmd)  # set volume
                client.setvol(set_volume)
    

                # save the potentiometer reading for the next loop
                last_read = trim_pot

        # hang out and do nothing for a half second
        time.sleep(0.5)
        
        
# ------------------------------------------------------
# waits randoms seconds and plays werbung
# decreases volume , adds werbung to current playlist, plays werbung, deletes werbung from playlist
# resumes play at next song
def werbung():
    global exitapp, waitwerbung

    while not exitapp:   # main thread gives terminate signal here
        wait=random.randint(5, 15)  
        if debug: 
            wait=wait*15
        else:
            wait=wait*waitwerbung
        if debug: print "Werbung: Going to wait for %d seconds" % wait
        while wait > 0:
            time.sleep(3)
            wait=wait-3
            if debug: print "Werbung: Waiting another %d seconds" % wait
            if exitapp: break
#        ret=connect_mpd(client)
#        if debug: print "Werbung Return from Connect MPD: %d" % ret
        if exitapp: break
        nxid=get_nexid(client)                  # nächstes stück der Playlist holen (ID), wird nach Werbung gespielt
        ident=client.addid("werbung_1.mp3")     # werbung.mp3 zur aktuellen playlist addieren und ID merken
        if debug: print "Werbung ID: %s" % ident
        cur_vol=lower_volume(client)            # Volume vermindern auf Null, aktuellen Wert vorher speichern      
        client.setvol(cur_vol)                  # Volume wieder erstellen
        if debug: print "Werbung: Volume reset to %d" % cur_vol
        client.playid(int(ident))               # Werbung spielen
        time.sleep(9)                           # warten bis gespielt
        client.playid(nxid)                     # vorher gespeichtertes nächstes Stück spielen
        client.deleteid(int(ident))             # Werbung mp3 entfernen aus der Playlist
        time.sleep(5)
#        client.disconnect()
#        if debug: print " Disconnected from MPD"
    if debug: print "-->Thread Werbung terminating..."
    sys.exit(0)


#----------------------------------------------------------
def termplay():    # function not used here
    volume=60
    for i in range(15):
        volume=volume-15
        GPIO.output(led2, True)
        time.sleep(0.3)
        GPIO.output(led2, False)
        time.sleep(3)
        print "Set Volume to %d" % volume
        client.setvol(volume)
        time.sleep(3)

#-----------------------------------------------------------
# --------- MAIN --------

arguments(sys.argv[1:])  # get commandline arguments

#GPIO.cleanup(buttonPinNext)  # cleanup GPIO Pins
#GPIO.cleanup(buttonPinPrev)  # cleanup GPIO Pins
#GPIO.cleanup(buttonPinStart)  # cleanup GPIO Pins

GPIO.setwarnings(False)
GPIO.setup(led1, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(led2, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(led3, GPIO.OUT, initial=GPIO.LOW)

GPIO.setup(buttonPinNext, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(buttonPinPrev, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(buttonPinStart, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(buttonPinRandom, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(potschalter, GPIO.IN, pull_up_down=GPIO.PUD_UP)

#------------------------------------------------
# set up the SPI interface pins (ADConverter)
GPIO.setup(SPIMOSI, GPIO.OUT)
GPIO.setup(SPIMISO, GPIO.IN)
GPIO.setup(SPICLK, GPIO.OUT)
GPIO.setup(SPICS, GPIO.OUT)


time.sleep(1)
# setup mpd Client Object
client = mpd.MPDClient()

ret=connect_mpd()                    # connect to MPD server
if ret==9:
    if debug: print "Could not connect to MPD"
    sys.exit(2)


blink_led2(2,0.5)
# reset random play
client.random(0)
# start play with volume 60%
client.setvol(60)
# clear current playlist
client.clear()
if debug:  print "Playlist cleared"
if GPIO.input(potschalter):     # Schalter ist off (ganz links gedreht)
# start playing using playlist Blues-1
    client.load("Blues-1")
    waitwerbung=waitwerbungfactor_blues
    if debug: print "Blues-1 loaded"
else:
# start playing using playlist Dead-1
    client.load("Dead-1")
    waitwerbung=waitwerbungfactor_dead
    if debug: print "Dead-1 loaded"
# start with first song in playlist
if debug: print "WaitFactor: %d" % waitwerbung
client.play(0)


if debug: print "MPD-Version: %s" % client.mpd_version

# create and Start Threads
connect_thread = Thread(target = connection_status)
button_nx_thread = Thread(target = button_nx)
button_pr_thread = Thread(target = button_pr)
button_st_thread = Thread(target = button_st)
button_rd_thread = Thread(target = button_rd)
werbung_thread = Thread(target = werbung)
led1_thread = Thread(target = blink_led1)
volume_thread = Thread(target = read_pot)


connect_thread.start()          # thread to keep connection to mpd
button_nx_thread.start()        # thread to watch for NEXT button
button_pr_thread.start()        # thread to watch for PREVIOUS button
button_st_thread.start()        # thread to watch for START Playlist button
button_rd_thread.start()        # thread to watch for RANDOM PLAY button
werbung_thread.start()          # thread to play werbung at random intervalls
led1_thread.start()             # thread to blink green led
volume_thread.start()           # thread to read pot and set volume

# Main thread goes into loop (wait for ctrl-C)
# ctrl-C bloss fĂĽr Tests im Vorground !! 
# posit
try:
    while True:
        time.sleep(3)
        if debug: print "Main thread waiting"
        pass    # pass ist leer statement
        
# somebody wants us to stop doing what we are doing...        
except KeyboardInterrupt:
    # cleanup
    blink_led2(5,0.1)
    exitapp = TRUE      # signal exit to the other threads, they will terminate themselfs
    if debug: print "Main: exitapp Signaled"
    raise
    if debug: print "Main: Terminating, waiting 6 seconds"
    GPIO.cleanup(buttonPinNext)  # cleanup GPIO Pins
    GPIO.cleanup(buttonPinPrev)  # cleanup GPIO Pins
    GPIO.cleanup(buttonPinStart)  # cleanup GPIO Pins
    GPIO.output(led3, False)
    GPIO.cleanup(led1)  # cleanup GPIO Pins
    GPIO.cleanup(led2)  # cleanup GPIO Pins
    GPIO.cleanup(led3)  # cleanup GPIO Pins
    GPIO.cleanup(SPIMOSI)
    GPIO.cleanup(SPIMISO)
    GPIO.cleanup(SPICLK)
    GPIO.cleanup(SPICS)
    
    time.sleep(6)       # wait for other threads
    client.disconnect()

    sys.exit(0)

#-----------------------------------------------------------
# The End
#-----------------------------------------------------------

The script is normally started in /etc/rc.local but for testing purposes I run it in a terminal window on my Mac. I use ctrl-C to terminate all threads. It accepts -d as commandline parm for debugging to stdout.
I also have a shutdown script that detects yet another button press from the shutdown button. I might combine these two scripts into one.

Hope this helps.

Greetings, Peter

Sorry, I could not figure out how to add a picture to my post. Help welcome.

Thanks, Peter

Peter,

You can add a picture with the button “Upload Attachment”

Attachement.png

After the upload you place the picture inline with the button [Place inline]

Attachemntinline.png

Succes!

Harry

Thanks Harry

I managed to include inline pics.

Greetings, Peter from Switzerland