Using GPIO pin to cycle through pedalboards

hello
I’ve been trying to do this for a while and I managed to make it work
so here is a simple script if you want to use a GPIO pin on to a push button to navigate between pedalboards

we’re still be using modep-ctrl.py to control it and for me it is ok
I have to push button connected to GPIO pin 24 and 25
when you push one it will go to the next pedalboard in the list
when you press the other it will go to the previous

the same script can be customized to do more based on modep-ctrl.py functionality, but I didn’t need it

I’m using a sript made as a service so the buttons are always active
pedal.service
and you need to place it in /usr/lib/systemd/system/
make it active so it will load every time

/usr/lib/systemd/system/pedal.service

[Unit]
 Description=My Pedal Service
 After=multi-user.target

 [Service]
 Type=idle
 ExecStart=/usr/bin/python3 /home/patch/Documents/pedal.py

 [Install]
 WantedBy=multi-user.target
/usr/lib/systemd/system/pedal.service (END)

Pedal.py

#! /usr/bin/env python

from gpiozero import Button
import subprocess
from signal import pause

buttonN = Button(24)
buttonP = Button(25)

def next():
    subprocess.Popen(['next.sh'])
    print ("next pedal")

def prev():
    subprocess.Popen(['prev.sh'])
    print ("previus pedal")

buttonN.when_pressed = next
buttonP.when_pressed = prev

pause()
(END)

then in /usr/bin I have 2 simple bash script to go next and previous
next.sh

#!/bin/bash
/usr/local/pisound/scripts/pisound-btn/modep-ctrl.py next

prev.sh

#!/bin/bash
/usr/local/pisound/scripts/pisound-btn/modep-ctrl.py prev

it works well for me

hopefully someone will get use of it and maybe improve on it
the modep-ctrl.py contains more than just next and previous so you could use extra buttons to do more

anyway - hope someone can make use of it

thank you all

PS: next project is to attach a small OLED screen and pull the loaded pedalboard (using the same modep-ctrl.py) and display it

3 Likes

This is awesome, looking forward to seeing your screen work!

hey @craighissett
yes I managed to make the screen work just to display the pedalboard that is loaded at present
however, I’m not a coder, so it is very messy the way I did it :slight_smile:
I’m sure someone can get all this and make it into one single file and would work just the same

“edited the previous post”

now for the screen, you need to first have a working screen
mine is very small 0.9" so I can only fit the name
once I had that working to display something I added to the example script that came with the setup a simple line to read from a file

file = open('/home/patch/test1', 'r+')
    file.seek(0)
    data = file.read()
    print(data)
    file.close()
    draw.text((7,0), (data), font = font2, fill = 0)

again, this was added to the script that came with the display, I removed the image that was displaying and added these in order to get the information from a file (in this case test1) :smiley:
also I’ve added to the next.sh and prev.sh 2 more lines of code, one to change that test1 file every time the pedalboard is changed, and one to read and update the display

as I said is very rudimental and messy and I’m sure someone can make it nice and elaborate into one python script, just not me :smiley:

the lines that I’ve added to the .sh scripts are as follow (but remember this is my case so change path and files names etc)
this is the full prev.sh that I have now

#!/bin/bash
/usr/local/pisound/scripts/pisound-btn/modep-ctrl.py prev
wait
/usr/local/pisound/scripts/pisound-btn/modep-ctrl.py current |grep -oP 'pedalboards\K[^ ]+' |sed 's/^.//;s/.pedalboard//' > /home/patch/test1
wait
sudo python3 /home/patch/OLED_0in91_my.py

I’m not sure if I can add a video here on this forum so I could show you, but overall it works for my
simple project :slight_smile:
I just needed to navigate between pedalboards and see which one was loaded and that seems to work
if someone can improve on it or make it nice and clean I would very much appreciate

The next step would be, to put everything into a box :wink:

Thank you for the update buddy!

I’m looking to create an Arduino-based footswitch to control my instance of MODEP.
I am aiming to communicate via MIDI, but I had no idea how exactly to handle the pedalboard switching and information display part.

I’m going to look at perhaps using your code as a base, and have the script receiving serial commands from the arduino for specific button presses. Then I am free to a)switch boards and send information back for on-screen display and b) set up a virtual MIDI port with Python and send commands from the script.

hey @craighissett
I couldn’t manage to get the pedalboard to work with midi
there is a server2midi somewhere here on the forum somewhere but I found it easier to use gpio

for midi, I use a raspberry pi PICO to send Control Change for effects ON OFF
I have one with only 4 buttons at present but the code is pretty simple
let me know if you’re interested in that one too but code will be different than Arduino
and there is plenty online for Arduino (was very difficult to find the correct code for the PICO especially for Control Change)

good luck with your project

Dude, our projects sound like they have a few similarities ha ha - I’m also going to he using a pico as a secondary controller for this!
I’ve got one of the 4x4 pico rgb keypads, so eventually I’d like to have it sending CC messages and also use the RGB to display status indicators.

I also have another Pico, never thought about putting that into the foot controller to be honest.

Have you gone for micropython on your pico?

hey :slight_smile:
nice to hear
no I’ve used circuitpython
I use it with only 4 switches and it will probably be integrated in the same box (haven’t decided yet)
so will have 2 buttons for next / prev pedalboard
and the 4 switches to do the rest via midi (on / off effects, loop etc)
works well so far that is all in the open :smiley:
will see if still works once I package all into one box
here the code for the PICO if you’re interested

import time
import board
import digitalio
import usb_midi
import adafruit_midi  # MIDI protocol encoder/decoder library
from adafruit_midi.control_change import ControlChange
midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)

button1 = digitalio.DigitalInOut(board.GP13)
button1.switch_to_input(pull=digitalio.Pull.DOWN)
button2 = digitalio.DigitalInOut(board.GP12)
button2.switch_to_input(pull=digitalio.Pull.DOWN)
button3 = digitalio.DigitalInOut(board.GP11)
button3.switch_to_input(pull=digitalio.Pull.DOWN)
button4 = digitalio.DigitalInOut(board.GP10)
button4.switch_to_input(pull=digitalio.Pull.DOWN)

flag = 1

while True:
    if button1.value and flag != 1:
        midi.send(ControlChange(12, 100))
        print("You sent ON")
        flag = 1
        time.sleep(0.75)
    elif button1.value and flag != 0:
        midi.send(ControlChange(12, 60))
        print("you sent off")
        flag = 0
        time.sleep(0.75)
    if button2.value and flag != 1:
        midi.send(ControlChange(13, 100))
        print("You sent ON")
        flag = 1
        time.sleep(0.75)
    elif button2.value and flag != 0:
        midi.send(ControlChange(13, 60))
        print("you sent off")
        flag = 0
        time.sleep(0.75)
    if button3.value and flag != 1:
        midi.send(ControlChange(14, 100))
        print("You sent ON")
        flag = 1
        time.sleep(0.75)
    elif button3.value and flag != 0:
        midi.send(ControlChange(14, 60))
        print("you sent off")
        flag = 0
        time.sleep(0.75)
    if button4.value and flag != 1:
        midi.send(ControlChange(15, 100))
        print("You sent ON")
        flag = 1
        time.sleep(0.75)
    elif button4.value and flag != 0:
        midi.send(ControlChange(15, 60))
        print("you sent off")
        flag = 0
        time.sleep(0.75)

good luck
and let me know if you find any problems using any of the code
PS: the pin are pull down so you need to provide the 5v for them to work, I couldn’t make work with pullUP and ground the pins

2 Likes

Thanks buddy, I’ll look at getting my keypad up and running tomorrow.
I have a few little boxes that I’ve added footswitches to, so will aim to get that soldered up over the weekend and hopefully have a rig to play with next week!
I’ve got three separate MODEP installations dotted around the house, ha ha.

Got my 4x4 button pad working!

import time
import board
import busio
import usb_midi

import adafruit_midi
from adafruit_midi.control_change import ControlChange
from adafruit_bus_device.i2c_device import I2CDevice
import adafruit_dotstar

from digitalio import DigitalInOut, Direction, Pull

# Based on RGB MIDI controller example for Pimoroni RGB Keypad for Raspberry Pi Pico

# Pull CS pin low to enable level shifter
cs = DigitalInOut(board.GP17)
cs.direction = Direction.OUTPUT
cs.value = 0

# Set up APA102 pixels
num_pixels = 16
pixels = adafruit_dotstar.DotStar(board.GP18, board.GP19, num_pixels, brightness=0.1, auto_write=True)

# Set up I2C for IO expander (addr: 0x20)
i2c = busio.I2C(board.GP5, board.GP4)
device = I2CDevice(i2c, 0x20)

# Set USB MIDI up on channel 0
midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)

# Function to map 0-255 to position on colour wheel
def colourwheel(pos):
    if pos < 0 or pos > 255:
        return (0, 0, 0)
    if pos < 85:
        return (255 - pos * 3, pos * 3, 0)
    if pos < 170:
        pos -= 85
        return (0, 255 - pos * 3, pos * 3)
    pos -= 170
    return (pos * 3, 0, 255 - pos * 3)

# List to store the button states and toggle states
held = [0] * 16
toggled = [0] * 16

# Keep reading button states, setting pixels, sending notes
while True:
    with device:
        # Read from IO expander, 2 bytes (8 bits) correspond to the 16 buttons
        device.write(bytes([0x0]))
        result = bytearray(2)
        device.readinto(result)
        b = result[0] | result[1] << 8

        # Loop through the buttons
        for i in range(16):
            if not (1 << i) & b:  # Pressed state
                pixels[i] = colourwheel(i * 16)  # Map pixel index to 0-255 range
                if not held[i]:
                    if not toggled[i]:
                        #send on CC
                        midi.send(ControlChange(60 + i, 100))
                        toggled[i] = 1
                    else:
                        #send off CC
                        midi.send(ControlChange(60 + i, 60))
                        toggled[i] = 0
                held[i] = 1
            else:  # Released state
                pixels[i] = (0, 0, 0)  # Turn pixel off
                held[i] = 0  # Set held state to off

It’s really basic at the moment, and uses random LED colours for each button, but it works.
Next step is to set the colours, and use the held option I’ve left in to add extra functions.

1 Like

Hey buddy, sorry to bother you!
I have a quick question about the Pi Pico - do you have issues with it not working?
My board code seems fine, however when I plug it I a Pi it doesn’t respond.
I have a feeling it is due to it not mounting properly, as in windows it pops up as a storage medium as well as a midi device.
On patchbox with the ui you need to click to mount it I think, but headless doesn’t have that option.

Is this something you had to deal with or is it an issue with something I’ve done? Ha ha ha.

Ideally I want to leave it plugged in with the Pi and it retain its mount and be able to operate the way it did on its last use.

hey @craighissett
no for me it works when I plug it in
I don’t have to do anything
mounting is automatic regardless if I have the UI on or off it gets recognized as a USB controller and it just works

sorry I didn’t have that problem so not sure if I can help :expressionless:

1 Like

Oh balls, I was hoping it wasn’t just a ‘me’ problem. Ha ha ha!
I might try dropping the code on it again, see if it sorts itself out.
It works briefly if you plug it into a different port, bit will always fail after. I’ll check to see if my code is missing something that yours has to ensure it works without needing mounting.

Cheers!

How long is taking to switch?

I have done a very similar thing, and I found I had to talk directly to mod-host to get it fast enough to switch signal paths fast enough to be useful live.

ATM I am getting 50-80ms. Not quite audible, or maybe.

1 Like

hey @worik
yes you are right, it is not fast enough for me and not usable at all for a live session to switch between pedalboard in the same song
there is no tailing and it takes seconds for me (didn’t measure but it is not ms)
hence I built the midi controller to switch on/off parts of it in order to use it live as that works just fine

I think Snapshot was fast to change in the same pedalboard but that has been removed from the code and is not available at present

if you managed to get to a working level I would love to see your code

thanx

1 Like

I’m really interested in how you are talking directly buddy.
While I’m happy using MIDI for my button input I am building a larger controller and I’m 50:50 on whether to build as a midi pedal or as a serial device, with a python script catching input and sending commands via a virtual midi port and communicating with MOD UI

hey @craighissett
on a different project I have the same problem
I can send the midi through the USB device but I can’t seem to find any documentation to trigger a midi event from a pushbutton on the same device and send it via midi on the same device :expressionless:
this is a project for Bluetooth midi device
I’m able to send those inputs if I use a USB device attached to the RPI (like the PICO I built) but not if triggered from the same
I’m sure it is possible but haven’t found a way yet
if you do - please share :slight_smile:

1 Like

Ah, I do have a python script I started working on some time ago which received input from buttons and sent a midi event via virtual midi, could be helpful?

I could definitely try it :slight_smile:
send it over whenever you can with whatever information may be required to make it work :wink:
thanx

Try this matey - it was based on William Hofferbert’s code(he’s on here somewhere as whofferbert I think).
I took his rotary encoder code and added some GPIO button event triggers for button input.

Check his original gihub repo out that’s in the comments of the code, as it details the permissions to run it as SUDO without password on startup :slight_smile:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# based on script by William Hofferbert
#
# https://github.com/whofferbert/rpi-midi-rotary-encoder
#
# watches raspberry pi GPIO pins and translates that
# behavior into midi data. Midi data is accessible to
# other clients through a virtual midi device that is
# created with amidithru, via os.system

import RPi.GPIO as GPIO
import time, datetime
import mido
import os
import re

# midi device naming and setup
name = "GuitarRotaryEncoder"

#Pins needed:
#P1 - 3.3v
#3-SDA|5-SCL
#8-tx|10-rx

#set up pi GPIO pins for Footswitches
B1 = 29
B2 = 31
B3 = 33
B4 = 35
B5 = 37

# set up pi GPIO pins for rotary encoder
sw_pin = 19
dt_pin = 21
clk_pin = 23

# button midi info; cc#12 = effect control 1 
button_state = 0
button_channel = 0
button_cc_num = 12
# don't let button signals trigger until X ms have passed
button_stagger_time = 220

# knob midi info; cc#7 = volume, default position near half
position = 63
rotary_increment = 1
rotary_channel = 0
rotary_cc_num = 7

# wait some seconds for other software after reboot
init_sleep_secs = 10

#
# main stuff below
# TODO maybe use the pythonic if __name__ == "__main__":
#

GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)

# wait some seconds, so we don't step on MODEP's toes
time.sleep(init_sleep_secs)

# set up backend
mido.set_backend('mido.backends.rtmidi')

# system command to set up the midi thru port
# TODO would be nice to do this in python, but
# rtmidi has issues seeing ports it has created
runCmd = "amidithru '" + name + "' &"
os.system(runCmd)

# regex to match on rtmidi port name convention
#GuitarRotaryEncoder:GuitarRotaryEncoder 132:0
# TODO is it necessary to write:  "\s+(\d+)?:\d+)"  instead?
nameRegex = "(" + name + ":" + name + "\s+\d+:\d+)"
matcher = re.compile(nameRegex)
newList = list(filter(matcher.match, mido.get_output_names()))
# all to get the name of the thing we just made
output_name = newList[0]

def ret_mili_time():
  current_milli_time = int(round(time.time() * 1000))
  return current_milli_time

# starting time
last_time = ret_mili_time()

print('System start/restart - ' + str(datetime.datetime.now()))

#Switches to be connected to pins and 3.3v pin
GPIO.setup(B1, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(B2, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(B3, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(B4, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(B5, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)

# Set up the GPIO channels
#GPIO.setup(dt_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # dt
#GPIO.setup(sw_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # sw
#GPIO.setup(clk_pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # clk

#This function will run when the button is triggered
def Notifier(channel):
  if channel==B1:
    print('Button 1')
  elif channel==B2:
    print('Button 2')          
  elif channel==B3:
    print('Button 3')
  elif channel==B4:
    print('Button 4')
  elif channel==B5:
    print('Button 5')
    
GPIO.add_event_detect(B1, GPIO.RISING)
GPIO.add_event_detect(B2, GPIO.RISING)
GPIO.add_event_detect(B3, GPIO.RISING)
GPIO.add_event_detect(B4, GPIO.RISING)
GPIO.add_event_detect(B5, GPIO.RISING)

# rotary encoder
#GPIO.add_event_detect(clk_pin,GPIO.BOTH,callback=rotary_callback)

def send_cc(channel, ccnum, val):
  msg = mido.Message('control_change', channel=channel, control=ccnum, value=val)
  output = mido.open_output(output_name)
  output.send(msg)

while True:
  #print('Looping')
  if GPIO.event_detected(B1):
    time.sleep(0.005) # debounce for 5mSec
    # only show valid edges
    if GPIO.input(B1)==1:
      Notifier(B1)
      send_cc(0, 65, 127)
  if GPIO.event_detected(B2):
    time.sleep(0.005)
    if GPIO.input(B2)==1:
      Notifier(B2)
      send_cc(0, 66, 127)
  if GPIO.event_detected(B3):
    time.sleep(0.005)
    if GPIO.input(B3)==1:
      Notifier(B3)
      send_cc(0, 67, 127)
  if GPIO.event_detected(B4):
    time.sleep(0.005)
    if GPIO.input(B4)==1:
      Notifier(B4)
      send_cc(0, 68, 127)
  if GPIO.event_detected(B5):
    time.sleep(0.005)
    if GPIO.input(B5)==1:
      Notifier(B5)
      send_cc(0, 69, 127)
  time.sleep(0.5)
        
GPIO.cleanup()

Reflashing circuitpython and adding an updated version of the libraries seems to have got me much more stability!

It’s working well!
16 buttons
4 on a four way toggle box
4 on a second toggle box
3 on a looper
3 on a second looper
1 to toggle tuner on/off
1 to toggle noise gate