samd2695 + esp32s3
akai mpk play has built in sounds and a speaker but wasnt a fan. you could get a qy100 or some other old midi module on ebay or a pawn shop. An mt-32, soundcanvas, maybe a proteus. but now for 30$ on ali you can get one of these
samd2695 + esp32s3
akai mpk play has built in sounds and a speaker but wasnt a fan. you could get a qy100 or some other old midi module on ebay or a pawn shop. An mt-32, soundcanvas, maybe a proteus. but now for 30$ on ali you can get one of these
so you've recorded a video on linux with guvcview and now you want to play it/edit it on an iphone,
heres a one liner you can use with ffmpeg to convert it to mp4:
for f in *.mkv; do ffmpeg -i "$f" -c:v libx264 -profile:v high -level 4.1 -c:a aac -movflags +faststart "${f%.mkv}.mp4"; done
pretty cool these days to be able to dip your toes into medical ultrasound technology using cheaply available microcontrollers and sensors.
diy sonar scanner
the mcp3008 for adc good for adding pots
picoIO64
22$ some soldering required. gives you 64 gpio
modmypi mcp2308 pi zero hat adds 16 gpio 14.95$
reposting this because I put down sunvox for a while and had to relearn it.
#!/usr/bin/env python3
import evdev
import rtmidi
from evdev import ecodes
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import os
import sys
DEVICE_PATH = '/dev/input/by-id/usb-Logitech_Logitech_Dual_Action_DF5CB332-event-joystick'
# ----------------------------
# Corrected human-readable button mappings
# ----------------------------
buttons = {
'A': 61, # physical A triggers MIDI note B
'B': 62, # physical B triggers MIDI note X
'X': 60, # physical X triggers MIDI note A
'Y': 63,
'L': 64,
'R': 65,
'Select': 66,
'Start': 67,
'Up': 68,
'Down': 69,
'Left': 70,
'Right': 71
}
evdev_button_map = {
288: 'A',
289: 'B',
290: 'X',
291: 'Y',
292: 'L',
293: 'R',
296: 'Select',
297: 'Start',
294: 'Up',
295: 'Down',
298: 'Left',
299: 'Right'
}
# ----------------------------
# Axis mappings
# ----------------------------
axes = {
'LX': (10, -32768, 32767),
'LY': (11, -32768, 32767),
'LT': (12, 0, 255),
'RT': (13, 0, 255),
'RX': (14, -32768, 32767),
'RY': (15, -32768, 32767)
}
evdev_axis_map = {0: 'LX', 1: 'LY', 2: 'LT', 5: 'RT', 3: 'RX', 4: 'RY'}
# ----------------------------
# MIDI setup
# ----------------------------
midi_out = rtmidi.MidiOut()
midi_out.open_virtual_port("F310 MIDI")
def scale(value, min_val, max_val):
return int((value - min_val) / (max_val - min_val) * 127)
# ----------------------------
# Main gamepad loop
# ----------------------------
def run():
gamepad = evdev.InputDevice(DEVICE_PATH)
print(f"Listening on {gamepad.name} ({gamepad.path})")
for event in gamepad.read_loop():
if event.type == ecodes.EV_KEY and event.code in evdev_button_map:
label = evdev_button_map[event.code]
note = buttons[label]
if event.value == 1:
midi_out.send_message([0x90, note, 112])
print(f"Button pressed: {label}")
elif event.value == 0:
midi_out.send_message([0x80, note, 0])
elif event.type == ecodes.EV_ABS and event.code in evdev_axis_map:
label = evdev_axis_map[event.code]
cc, min_val, max_val = axes[label]
val = scale(event.value, min_val, max_val)
midi_out.send_message([0xB0, cc, val])
# ----------------------------
# Watchdog for hot-reload
# ----------------------------
class ReloadHandler(FileSystemEventHandler):
def on_modified(self, event):
if event.src_path.endswith(os.path.basename(__file__)):
print("Script changed. Restarting...")
os.execv(sys.executable, ['python3'] + sys.argv)
observer = Observer()
observer.schedule(ReloadHandler(), path='.', recursive=False)
observer.start()
# ----------------------------
# Run forever
# ----------------------------
try:
while True:
run()
except KeyboardInterrupt:
observer.stop()
observer.join()
gpg - c creates, gpg - d decodes
this uses python qrcode (pip install it if you dont have it)
read -p "Input GPG file: " infile; read -p "Output PNG file: " outfile; python3 -c "import qrcode, base64; f=open('$infile','rb'); data=base64.b64encode(f.read()).decode(); f.close(); qrcode.make(data).save('$outfile')"
this passes the input of the qr as input and decodes the gpg (requires pyzbar. opencv is good to have also
read -p "QR PNG file: " qrfile; read -p "Output file: " outfile; python3 -c "import cv2, pyzbar.pyzbar as pyzbar, base64, sys; img=cv2.imread('$qrfile'); data=pyzbar.decode(img)[0].data; sys.stdout.buffer.write(base64.b64decode(data))" | gpg -d > "$outfile"
This script lets you ask tgpt a question and it will send the response as notification to your phone
using the ios app ntfy.sh
you can remotely trigger it outside your local network using raspberry pi connect
alias ntfygpt='f(){ curl -d "$(tgpt -q "$*")" https://ntfy.sh/YOURTOPICGOESHERE; }; f'
make sure YOURTOPICGOES HERE is replaced on your phone, thats where it will be sent to
/etc/udev/rules.d/99-gamepad.rules
- CREATED# Start service when F310 is plugged in
ACTION=="add", SUBSYSTEM=="input", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c216", KERNEL=="event*", TAG+="systemd", ENV{SYSTEMD_WANTS}="macros@%k.service"
# Stop service when F310 is unplugged
ACTION=="remove", SUBSYSTEM=="input", ENV{ID_MODEL}=="Logitech_Dual_Action", KERNEL=="event*", RUN+="/bin/systemctl stop macros@%k.service"
Purpose: Detects when the Logitech F310 gamepad (product ID c216) is plugged in or unplugged and triggers systemd service actions.
/etc/systemd/system/macros@.service
- CREATED[Unit]
Description=Logitech F310 Macros for %i
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/mint22/macros.py
Restart=on-failure
User=mint22
Group=mint22
Environment=HOME=/home/mint22
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Purpose: Systemd template service that runs the Python script when triggered by udev. The @
makes it a template service that can be instantiated with device names (like macros@event13.service
).
/home/mint22/macros.py
- EXISTING (permissions updated)Changes made:
chmod +x /home/mint22/macros.py
sudo usermod -a -G input mint22
Purpose: Your existing Python script that contains the gamepad macro functionality.
# Reload udev rules to recognize the new gamepad detection rule
sudo udevadm control --reload-rules
# Reload systemd to recognize the new service template
sudo systemctl daemon-reload
# Set proper permissions
chmod +x /home/mint22/macros.py
sudo usermod -a -G input mint22
macros@eventX.service
→ runs macros.py
macros.py
The system automatically handles starting and stopping the script based on the physical presence of the gamepad.
alias prettifyjson='f(){ jq . "$1" > tmp && mv tmp "$1"; }; f'
source ~/.bashrc
This tutorial shows how to use your Logitech FCB310 USB gamepad as a macropad on a Raspberry Pi 4, with automatic startup and hotplug detection. When plugged in, your gamepad buttons will trigger keyboard macros on the Pi, no manual script launching needed.
Open a terminal and run:
sudo apt update
sudo apt install python3-pip xdotool
pip3 install inputs pyudev watchdog
xdotool
: simulates keyboard keypressesinputs
: reads gamepad eventspyudev
: detects USB device plug/unplugwatchdog
: monitors macro config file changesCreate a CSV file to map gamepad buttons to keyboard shortcuts.
Example file: /home/pi/macropad/macros.csv
BTN_A,ctrl+alt+t
BTN_B,ctrl+w
BTN_A
)xdotool
(e.g., ctrl+alt+t
)Create a folder and the Python script:
mkdir -p ~/macropad
vim ~/macropad/gamepad_macro.py
Paste this full script into it:
import csv
import subprocess
from inputs import get_gamepad
import pyudev
import threading
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import time
mapping = {}
def load_macros():
global mapping
mapping = {}
try:
with open("/home/pi/macropad/macros.csv", newline='') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
if len(row) >= 2:
btn = row[0].strip()
keys = row[1].strip()
mapping[btn] = [keys]
print("Macros reloaded:", mapping)
except Exception as e:
print("Failed to load macros:", e)
def send_macro(keys):
for combo in keys:
subprocess.run(["xdotool", "key", combo])
def listen_gamepad(stop_event):
while not stop_event.is_set():
try:
events = get_gamepad()
for event in events:
if event.ev_type == "Key" and event.state == 1:
if event.code in mapping:
send_macro(mapping[event.code])
except Exception:
pass
class ConfigChangeHandler(FileSystemEventHandler):
def on_modified(self, event):
if event.src_path.endswith("macros.csv"):
print("Config file changed, reloading macros")
load_macros()
def device_event(observer, device):
global gamepad_thread, stop_event
if device.action == "add" and "event" in device.device_node:
print("Gamepad connected, starting listener")
load_macros()
if gamepad_thread and gamepad_thread.is_alive():
stop_event.set()
gamepad_thread.join()
stop_event.clear()
gamepad_thread = threading.Thread(target=listen_gamepad, args=(stop_event,), daemon=True)
gamepad_thread.start()
elif device.action == "remove":
print("Gamepad disconnected")
stop_event.set()
if gamepad_thread:
gamepad_thread.join()
if __name__ == "__main__":
stop_event = threading.Event()
gamepad_thread = None
context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by('input')
observer = pyudev.MonitorObserver(monitor, device_event)
observer.start()
# Watch macros.csv for changes
config_handler = ConfigChangeHandler()
config_observer = Observer()
config_observer.schedule(config_handler, path="/home/pi/macropad/", recursive=False)
config_observer.start()
print("Waiting for gamepad to connect...")
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
stop_event.set()
if gamepad_thread:
gamepad_thread.join()
config_observer.stop()
config_observer.join()
chmod +x ~/macropad/gamepad_macro.py
Create a service file:
sudo vim /etc/systemd/system/macropad.service
Add:
[Unit]
Description=Gamepad Macro Pad Service
After=graphical.target
[Service]
ExecStart=/usr/bin/python3 /home/pi/macropad/gamepad_macro.py
Restart=always
User=pi
[Install]
WantedBy=graphical.target
Enable and start service:
sudo systemctl enable macropad.service
sudo systemctl start macropad.service
Reboot your Pi:
sudo reboot
macros.csv
./home/pi/macropad/macros.csv
anytime./dev/input/
(use evtest
to confirm).xdotool
docs).journalctl -u macropad.service -f
.nmtui worked for me to connect my rav filehub
I'm using it so I can run a server with music on a raspberry pi that I can connect to from the ios files app
It's cool because I can use Tau DJ without worrying about running out of internal storage space
add to /etc/samba/smb.conf
force user = bweew
vfs objects = streams_xattr
Instrument | MIDI Command |
---|---|
drums | B0 00 78 B0 20 00 C0 |
piano | B0 00 00 B0 20 00 C0 01 |
bass | B0 00 00 B0 20 00 C0 21 |
jazz-gt | B0 00 00 B0 20 00 C0 1A |
on release, scroll down to the bottom where Midi Actions are to choose send midi message. Custom hex midi messages are key! Before I tried using program change and it wouldn't let me get my drums even if i told it it send on midi channel 10.
my updated loopy pro templatezxcv records first 4 loops, asdf selects drums,bass,jazzgt,piano. I added buttons to select other sounds, kept it small to work on an iphone se but more roomy on a bigger screen of course.
#!/bin/bash
# Loop through all PDF files in the current directory
for file in *.pdf; do
# Skip if no PDF files
[ -e "$file" ] || continue
# Set output file name
output="${file%.pdf}.bw.pdf"
echo "Converting: $file -> $output"
# Ghostscript command to convert to grayscale
gs \
-sDEVICE=pdfwrite \
-dCompatibilityLevel=1.4 \
-dProcessColorModel=/DeviceGray \
-dColorConversionStrategy=/Gray \
-dColorConversionStrategyForImages=/Gray \
-dAutoRotatePages=/None \
-dNOPAUSE -dBATCH -dQUIET \
-sOutputFile="$output" "$file"
done