Skip to content

Commit b3fcd30

Browse files
committed
initial implementation of ESP32 direct serial communication
1 parent db55576 commit b3fcd30

File tree

2 files changed

+67
-14
lines changed

2 files changed

+67
-14
lines changed

tools/edrumulus_gui.py

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,20 @@
2727
import threading
2828
import math
2929
from pathlib import Path
30-
use_rtmidi = "rtmidi" in sys.argv # use rtmidi instead of jack audio
30+
31+
use_rtmidi = "rtmidi" in sys.argv # use this for native USB MIDI devices like Teensy
32+
use_serial = "serial" in sys.argv # use this for direct ESP32 connection via serial
3133
no_gui = "no_gui" in sys.argv # no GUI but blocking (just settings management)
3234
non_block = "non_block" in sys.argv # no GUI and non-blocking (just settings management)
3335
use_lcd = "lcd" in sys.argv # LCD GUI mode on Raspberry Pi
3436
use_webui = "webui" in sys.argv # web UI GUI mode on Raspberry Pi
3537
use_ncurses = not no_gui and not non_block and not use_lcd and not use_webui # normal console GUI mode (default)
38+
use_jack = not use_rtmidi and not use_serial
3639
if use_rtmidi:
3740
import rtmidi
38-
from rtmidi.midiutil import open_midiinput
39-
from rtmidi.midiutil import open_midioutput
41+
elif use_serial:
42+
import serial
43+
import rtmidi # serial needs rtmidi out port
4044
else:
4145
import jack
4246
if use_lcd:
@@ -91,7 +95,7 @@
9195
kit_vol_str = ""
9296

9397
# initialize jack audio for MIDI
94-
if not use_rtmidi:
98+
if use_jack:
9599
client = jack.Client("EdrumulusGUI")
96100
input_port = client.midi_inports.register("MIDI_in")
97101
output_port = client.midi_outports.register("MIDI_out")
@@ -134,9 +138,9 @@ def process_user_input(ch):
134138
send_value_to_edrumulus(cmd_val[sel_cmd], database[sel_cmd])
135139
elif ch == "a" or ch == "A": # enable/disable auto pad selection
136140
auto_pad_sel = ch == "a" # capital "A" disables auto pad selection
137-
elif (ch == "k" or ch == "K") and not use_rtmidi: # kit selection (only for jack audio mode)
141+
elif (ch == "k" or ch == "K") and use_jack: # kit selection (only for jack audio mode)
138142
ecasound_switch_chains(ch == "k")
139-
elif (ch == "v" or ch == "V") and not use_rtmidi: # kit volume (only for jack audio mode)
143+
elif (ch == "v" or ch == "V") and use_jack: # kit volume (only for jack audio mode)
140144
ecasound_kit_volume(ch == "v")
141145

142146
def get_linear_pad_type_index(d):
@@ -559,7 +563,7 @@ def act_on_midi_in(status, key, value):
559563
################################################################################
560564
# MIDI handling (via jack audio) ###############################################
561565
################################################################################
562-
if not use_rtmidi:
566+
if use_jack:
563567
def send_value_to_edrumulus(command, value):
564568
global midi_send_cmd, midi_send_val
565569
(midi_send_cmd, midi_send_val) = (command, value);
@@ -600,6 +604,34 @@ def __call__(self, event, data=None):
600604
act_on_midi_in(event[0][0], event[0][1], event[0][2])
601605

602606

607+
################################################################################
608+
# MIDI handling (via serial) ###################################################
609+
################################################################################
610+
if use_serial:
611+
serial_message = []
612+
def send_value_to_edrumulus(command, value):
613+
global midi_send_cmd, midi_send_val
614+
(midi_send_cmd, midi_send_val) = (command, value);
615+
ser.write(bytearray([185, midi_send_cmd, midi_send_val]))
616+
midi_previous_send_cmd = midi_send_cmd # store previous value
617+
midi_send_cmd = -1 # invalidate current command to prepare for next command
618+
619+
def receive_from_serial():
620+
global serial_message
621+
while ser.isOpen():
622+
try:
623+
data = ser.read()
624+
if (data[0] & 0x80) != 0:
625+
serial_message = [data[0]] # status byte is first on message
626+
else:
627+
serial_message.append(data[0])
628+
if len(serial_message) == 3: # we only support three bytes commands
629+
act_on_midi_in(serial_message[0], serial_message[1], serial_message[2])
630+
midiout.send_message(serial_message)
631+
except:
632+
pass
633+
634+
603635
################################################################################
604636
# Main function ################################################################
605637
################################################################################
@@ -620,11 +652,20 @@ def __call__(self, event, data=None):
620652
try:
621653
in_name = "EdrumulusIn" if [s for s in rtmidi.MidiIn().get_ports() if "EdrumulusIn" in s] else "Edrumulus"
622654
out_name = "EdrumulusOut" if [s for s in rtmidi.MidiOut().get_ports() if "EdrumulusOut" in s] else "Edrumulus"
623-
midiin, port_name_in = open_midiinput([s for s in rtmidi.MidiIn().get_ports() if in_name in s][0], client_name="EdrumulusGUI")
624-
midiout, port_name_out = open_midioutput([s for s in rtmidi.MidiOut().get_ports() if out_name in s][0], client_name="EdrumulusGUI")
655+
midiin, port_name_in = rtmidi.open_midiinput([s for s in rtmidi.MidiIn().get_ports() if in_name in s][0], client_name="EdrumulusGUI")
656+
midiout, port_name_out = rtmidi.open_midioutput([s for s in rtmidi.MidiOut().get_ports() if out_name in s][0], client_name="EdrumulusGUI")
625657
midiin.set_callback(MidiInputHandler(port_name_in))
626658
except:
627659
raise Exception("No native Edrumulus USB device (e.g., Teensy) nor loopMIDI driver found.")
660+
elif use_serial:
661+
try:
662+
midiout = rtmidi.MidiOut() # somehow we need to call this twice: once with error and once with success
663+
except:
664+
midiout = rtmidi.MidiOut()
665+
midiout.open_virtual_port("EdrumulusOut")
666+
# TODO do not use fix values here
667+
ser = serial.Serial("/dev/ttyUSB0", 38400)
668+
threading.Thread(target = receive_from_serial).start()
628669
else: # initialize jack midi
629670
client.activate()
630671
try:
@@ -647,7 +688,7 @@ def __call__(self, event, data=None):
647688
do_update_display = True
648689

649690
# it takes time for ecasound to start up -> we need a timer thread for socket connection
650-
if not use_rtmidi: # ecasound is only supported for jack audio mode
691+
if use_jack: # ecasound is only supported for jack audio mode
651692
threading.Timer(0.0, ecasound_connection).start()
652693

653694
# main loop
@@ -676,6 +717,9 @@ def __call__(self, event, data=None):
676717
if use_rtmidi:
677718
midiin.delete()
678719
midiout.delete()
720+
elif use_serial:
721+
ser.close()
722+
midiout.delete()
679723
else:
680724
client.deactivate()
681725
client.close()

tools/run_edrumulus.sh

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ if [[ "$1" == no_gui ]]; then
4747
gui_mode="no_gui"
4848
fi
4949

50+
# check if direct serial connection to ESP32 shall be used
51+
if [[ "$1" == serial ]]; then
52+
echo "-> direct serial connection mode enabled"
53+
is_serial=true
54+
gui_mode="serial"
55+
fi
56+
5057
# special mode: UART connection with console GUI
5158
if [[ "$1" == uartgui ]]; then
5259
echo "-> UART GUI mode enabled"
@@ -192,10 +199,12 @@ else
192199
pigs modes 9 w
193200
pigs w 9 1
194201
else
195-
if [ -r "/dev/ttyACM0" ]; then
196-
mod-ttymidi/ttymidi -b 38400 -s /dev/ttyACM0 & # ESP32-S3
197-
else
198-
mod-ttymidi/ttymidi -b 38400 & # ESP32
202+
if [[ -z is_serial ]]; then
203+
if [ -r "/dev/ttyACM0" ]; then
204+
mod-ttymidi/ttymidi -b 38400 -s /dev/ttyACM0 & # ESP32-S3
205+
else
206+
mod-ttymidi/ttymidi -b 38400 & # ESP32
207+
fi
199208
fi
200209
fi
201210
fi

0 commit comments

Comments
 (0)