First, the STEMMA_I2C
port is setup for the I2C devices. Then the TSC2007 and ADXL343 are setup.
# I2C setup for STEMMA port i2c = board.STEMMA_I2C() # touchscreen setup for TSC2007 irq_dio = None tsc = adafruit_tsc2007.TSC2007(i2c, irq=irq_dio) # accelerometer setup accelerometer = adafruit_adxl34x.ADXL343(i2c) accelerometer.enable_tap_detection()
A number of variables and states are declared. Their function is commented in the code.
Most importantly, the notes
array holds the MIDI note numbers that are sent to the Pure Data script and played through the synth. If you want to play different notes, you'll want to edit that array.
# MIDI notes - 2 octaves of Cmaj7 triad notes = [48, 52, 55, 59, 60, 64, 67, 71] # reads touch input point = tsc.touch # accelerometer x coordinate acc_x = 0 # accelerometer y coordinate acc_y = 0 # last accelerometer x coordinate last_accX = 0 # last accelerometer y coordinate last_accY = 0 # mapped value for touchscreen x coordinate x_map = 0 # mapped value for touchscreen y coordinate y_map = 0 # last mapped value for touchscreen x coordinate last_x = 0 # last mapped value for touchscreen y coordinate last_y = 0 # state for whether synth is running run = 0 # tap detection state last_tap = False # new value detection state new_val = False
Socket Connection
After the QT Py ESP32-S2 connects to your WiFi network, it connects to your computer with a socket connection. The socket needs to know your computer's IP address, which is stored in host
in the secrets.py file. The port is defined as 12345
.
Once a socket connection is established with the Pure Data script, the loop begins to run.
HOST = secrets["host"] PORT = 12345 TIMEOUT = 5 INTERVAL = 5 MAXBUF = 256 # connect to WIFI print("Connecting to %s"%secrets["ssid"]) wifi.radio.connect(secrets["ssid"], secrets["password"]) print("Connected to %s!"%secrets["ssid"]) pool = socketpool.SocketPool(wifi.radio) ipv4 = ipaddress.ip_address(pool.getaddrinfo(HOST, PORT)[0][4][0]) buf = bytearray(MAXBUF) print("Create TCP Client Socket") s = pool.socket(pool.AF_INET, pool.SOCK_STREAM) print("Connecting") s.connect((HOST, PORT))
The Loop
The loop begins by checking for tap detection from the accelerometer. If the ADXL343 detects a tap, it will either start or stop the Pure Data synth from playing.
# tap detection # if tap is detected and the synth is not running... if accelerometer.events["tap"] and not last_tap and not run: # run is updated to 1 run = 1 # last_tap is reset last_tap = True print("running") # message is sent to Pd to start the synth # all Pd messages need to end with a ";" size = s.send(str.encode(' '.join(["run", str(run), ";"]))) # if tap is detected and the synth is running... if accelerometer.events["tap"] and not last_tap and run: # run is updated to 0 run = 0 # last_tap is reset last_tap = True print("not running") # message is sent to Pd to stop the synth # all Pd messages need to end with a ";" size = s.send(str.encode(' '.join(["run", str(run), ";"]))) # tap detection debounce if not accelerometer.events["tap"] and last_tap: last_tap = False
Reading the Touch Screen
If a touch input is detected with the TSC2007, the x
and y
coordinates are mapped from their 12-bit value to a range of 0
to 8
. This mapping matches with the sequencer array in the Pure Data script.
# if the touchscreen is touched... if tsc.touched: # point holds touch data point = tsc.touch # x coordinate is remapped to 0 - 8 x_map = simpleio.map_range(point["x"], 0, 4095, 0, 8) # y coordinate is remapped to 0 - 8 y_map = simpleio.map_range(point["y"], 0, 4095, 0, 8)
Reading the ADXL343
The x
and y
values from the ADXL343 are mapped to match values for the synth's filters in the Pure Data script.
# accelerometer x value is remapped for synth filter acc_x = simpleio.map_range(accelerometer.acceleration[0], -10, 10, 450, 1200) # accelerometer y value is remapped for synth filter acc_y = simpleio.map_range(accelerometer.acceleration[1], -10, 10, 250, 750)
Comparing Values
The current and previous values from the TSC2007 and ADXL343 are compared using if
statements. If the values do not match, then the previous value is updated and the state of new_val
is set to True
. This indicates that a new value has been received and should be sent to the Pure Data script.
# if any of the values are different from the last value... if x_map != last_x: # last value is updated last_x = x_map # new value is detected new_val = True if y_map != last_y: last_y = y_map new_val = True if int(acc_x) != last_accX: last_accX = int(acc_x) new_val = True if int(acc_y) != last_accY: last_accY = int(acc_y) new_val = True
Sending Data Over the Socket
If a new value is received from one of the sensors, then a message is sent over the socket with the new readings. Each piece of data is preceded by a variable name that Pure Data will be listening for.
A new note is sent to the sequencer in the Pure Data patch with the note
variable. The y_map
coordinate on the touch screen is used as an index marker in the notes
array.
It's important to note that Pure Data uses a networking protocol called FUDI. As a result, each message needs to end with a semicolon (;
). This allows the message to be terminated and sent to the Pure Data script.
# if a new value is detected... if new_val: # note index is updated to y coordinate on touch screen note = notes[int(y_map)] # message with updated values is sent via socket to Pd # all Pd messages need to end with a ";" size = s.send(str.encode(' '.join(["x", str(x_map), ";", "y", str(y_map), ";", "aX", str(acc_x), ";", "aY", str(acc_y), ";", "n", str(note), ";"]))) # new_val is reset new_val = False
Page last edited March 08, 2024
Text editor powered by tinymce.