Skip to content

Commit 91d76ed

Browse files
authored
Merge pull request #3195 from adafruit/midi_breath
adding midi breath controller code
2 parents 6dda9d2 + 6b2fd60 commit 91d76ed

File tree

1 file changed

+98
-0
lines changed
  • QT_Py/MIDI_Breath_Controller

1 file changed

+98
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 Liz Clark for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: MIT
4+
'''QT Py RP2040 MIDI Breath Controller with BMP585'''
5+
import time
6+
import board
7+
import simpleio
8+
from adafruit_bmp5xx import BMP5XX
9+
from adafruit_seesaw import digitalio, neopixel, rotaryio, seesaw
10+
import usb_midi
11+
import adafruit_midi
12+
from adafruit_midi.control_change import ControlChange
13+
from adafruit_midi.channel_pressure import ChannelPressure
14+
15+
SEALEVELPRESSURE_HPA = 1013.25
16+
change_sense = 0.1 # change sensitivity
17+
low_press = 995 # lowest pressure reading range
18+
high_press = 1032 # highest pressure reading range
19+
20+
i2c = board.STEMMA_I2C()
21+
bmp = BMP5XX.over_i2c(i2c)
22+
bmp.sea_level_pressure = SEALEVELPRESSURE_HPA
23+
24+
seesaw = seesaw.Seesaw(i2c, addr=0x36)
25+
seesaw.pin_mode(24, seesaw.INPUT_PULLUP)
26+
button = digitalio.DigitalIO(seesaw, 24)
27+
encoder = rotaryio.IncrementalEncoder(seesaw)
28+
pixel = neopixel.NeoPixel(seesaw, 6, 1)
29+
pixel.brightness = 0.2
30+
31+
midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)
32+
# CC message channels
33+
# last index is place holder for Channel Pressure message type
34+
messages = [1, 2, 7, 64, 0]
35+
# neopixel colors to associate with CC messages
36+
colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255),
37+
(255, 255, 0), (255, 0, 255)]
38+
39+
pressure = 0
40+
mod_val1 = 0
41+
mod_val2 = 0
42+
effect_index = 0
43+
last_position = 0
44+
button_state = False
45+
pixel.fill(colors[effect_index])
46+
47+
while True:
48+
position = -encoder.position
49+
if position != last_position:
50+
# encoder changes midi CC message type
51+
if position > last_position:
52+
effect_index = (effect_index + 1) % len(messages)
53+
else:
54+
effect_index = (effect_index - 1) % len(messages)
55+
pixel.fill(colors[effect_index])
56+
last_position = position
57+
if not button.value and not button_state:
58+
pixel.fill((255, 255, 255))
59+
# button press sends MIDI panic
60+
panic = ControlChange(123, 0)
61+
midi.send(panic)
62+
# and turns sustain off
63+
sus_off = ControlChange(64, 0)
64+
midi.send(sus_off)
65+
button_state = True
66+
if button.value and button_state:
67+
# reset neopixel to CC msg color
68+
pixel.fill(colors[effect_index])
69+
button_state = False
70+
if bmp.data_ready:
71+
# get pressure reading
72+
pressure = bmp.pressure
73+
# if the pressure has changed enough
74+
# (adjust change_sense value at top to inc or dec)
75+
if abs(pressure - mod_val2) > change_sense:
76+
# map pressure reading to CC range
77+
mod_val1 = round(simpleio.map_range(pressure, low_press, high_press, 0, 127))
78+
# updates previous value to hold current value
79+
mod_val2 = pressure
80+
# MIDI data has to be sent as an integer
81+
modulation = int(mod_val1)
82+
# possible midi messages determined by effect_index value:
83+
# 1: modulation
84+
# 2: breath controller
85+
# 7: volume
86+
# 64: sustain
87+
# ChannelPressure(modulation)
88+
if effect_index < 4:
89+
# prep CC message with CC number and value as mapped pressure reading
90+
modWheel = ControlChange(messages[effect_index], modulation)
91+
else:
92+
# prep Channel Pressure message with value as mapped pressure reading
93+
modWheel = ChannelPressure(modulation)
94+
# CC message is sent
95+
midi.send(modWheel)
96+
# print(modWheel)
97+
# delay to settle MIDI data
98+
time.sleep(0.001)

0 commit comments

Comments
 (0)