A handheld 6-track step sequencer inspired by the Elektron Model:Samples workflow. Create lo-fi beats with touch control, real-time BPM adjustment, and custom sample loading from SD card.
- 6 audio tracks with individual step sequences
- 16-step sequencer with visual grid interface
- PWM audio output via ESP32 internal DAC (GPIO25)
- Resistive touchscreen control with stylus support
- microSD card sample loading (8-bit unsigned mono .raw files)
- Real-time BPM control (60-200 BPM)
- Minimalist black/red UI design
- Modular code architecture for easy expansion
| Part | Description | Qty | Example Part # | Approx. Cost |
|---|---|---|---|---|
| ESP32 Dev Board | ESP32-DevKitC or compatible | 1 | ESP32-DevKitC-32D | $8-15 |
| TFT Display | Adafruit 2.8" ILI9341 TFT + Resistive Touch | 1 | Adafruit 1770 / 2478 | $35-40 |
| microSD Card | 2GB-32GB FAT32 formatted | 1 | SanDisk 8GB | $5 |
| Breadboard | 830 point solderless breadboard | 1 | - | $5 |
| Jumper Wires | Male-to-male breadboard wires (20 pack) | 1 | - | $3 |
| Stylus | Capacitive/resistive stylus (optional) | 1 | - | $2 |
| Part | Description | Qty | Notes |
|---|---|---|---|
| 3.5mm Audio Jack | Panel mount or PCB mount | 1 | Stereo jack (use tip + sleeve) |
| 10μF Capacitor | Electrolytic or ceramic, 16V+ | 1 | For DC blocking |
| 1kΩ Resistor | 1/4W, through-hole | 1 | Current limiting |
| Part | Description | Qty | Notes |
|---|---|---|---|
| Prototype PCB | Universal perfboard | 1 | For permanent soldering |
| Power Bank | 5V USB power bank | 1 | For portable operation |
| Enclosure | 3D printed or project box | 1 | Protect electronics |
| Power Switch | SPST toggle or slide switch | 1 | For power control |
Total Estimated Cost: $60-80 USD (excluding optional components)
- Adafruit (USA): https://www.adafruit.com/
- SparkFun (USA): https://www.sparkfun.com/
- AliExpress (Worldwide): https://www.aliexpress.com/ (budget option)
- Mouser / DigiKey: For individual electronic components
ESP32 | ILI9341
----------|----------
GPIO23 | MOSI
GPIO18 | CLK
GPIO19 | MISO
GPIO5 | CS
GPIO2 | DC
GPIO4 | RST
3.3V | VCC
GND | GND
ESP32 | Touch Pin
----------|----------
GPIO34 | Y+ (YP) - Must be analog input capable
GPIO33 | X- (XM) - Must be digital I/O capable
GPIO14 | Y- (YM) - Must be digital I/O capable
GPIO32 | X+ (XP) - Must be analog input capable
Note: On ESP32, GPIO34-39 are input-only pins. GPIO34 is used for YP as it needs analog reading capability.
ESP32 | SD Card
----------|----------
GPIO23 | MOSI
GPIO18 | CLK
GPIO19 | MISO
GPIO15 | CS
3.3V | VCC
GND | GND
ESP32 | Audio Circuit
----------|----------
GPIO25 | Signal → [1kΩ] → [10μF] → 3.5mm Jack Tip
GND | 3.5mm Jack Sleeve
Audio Circuit Explanation:
- The 1kΩ resistor limits current from GPIO25
- The 10μF capacitor blocks DC offset and passes audio signal
- Connect capacitor with correct polarity (negative side to jack)
- Use tip and sleeve of stereo jack (ring can be left unconnected)
- Inspect the ESP32 board for any damage
- Identify GPIO pins using the pinout diagram for your specific board
- Connect ESP32 to computer via USB to verify it powers on
- Test basic communication using Arduino IDE's Serial Monitor
- Power off the ESP32 (disconnect USB)
- Connect SPI pins from ESP32 to TFT display:
- GPIO23 (MOSI) → TFT MOSI
- GPIO18 (CLK) → TFT CLK
- GPIO19 (MISO) → TFT MISO
- GPIO5 → TFT CS (Chip Select)
- GPIO2 → TFT DC (Data/Command)
- GPIO4 → TFT RST (Reset)
- Connect power: ESP32 3.3V → TFT VCC, GND → TFT GND
- Double-check connections before powering on
- Locate touch pins on the TFT shield (Y+, X-, Y-, X+)
- Connect touchscreen pins:
- Touch Y+ → ESP32 GPIO34
- Touch X- → ESP32 GPIO33
- Touch Y- → ESP32 GPIO14
- Touch X+ → ESP32 GPIO32
- Note: GPIO34 is input-only on ESP32 (perfect for analog touch reading)
- Insert formatted SD card into TFT shield (if built-in) OR use separate SD module
- Connect SD card pins (shares SPI bus with TFT):
- Same MOSI, CLK, MISO as TFT
- GPIO15 → SD CS (separate chip select)
- 3.3V → SD VCC, GND → SD GND
- Solder components in this order:
GPIO25 → [1kΩ resistor] → [10μF capacitor +] → [3.5mm jack tip] [cap −] → GND ESP32 GND → [3.5mm jack sleeve] - Check polarity on electrolytic capacitor (stripe = negative)
- Test connections with multimeter (check for shorts)
- Organize wiring to prevent shorts and tangles
- Secure components to breadboard
- Label connections with tape/labels for future reference
- Take photos of your wiring for troubleshooting later
- Visual inspection of all connections
- Connect USB power and check for:
- ESP32 power LED should light up
- TFT backlight should turn on
- No burning smell or excessive heat
- If any issues, power off immediately and check wiring
- Install ESP32 board package in Arduino IDE
- Install required libraries:
Adafruit GFX LibraryAdafruit ILI9341TouchScreenSD
DriftRiffMini/
├── main.cpp # Main application loop
├── sequencer.h/cpp # Sequencer logic and step management
├── ui.h/cpp # User interface and display handling
├── audioengine.h/cpp # PWM audio output and sample playback
├── sdloader.h/cpp # SD card sample loading
└── touchscreen.h/cpp # Touch input processing
Create the following folder structure on your SD card:
/samples/
├── kick.raw
├── snare.raw
├── hihat.raw
├── perc.raw
├── bass.raw
└── lead.raw
- Format: 8-bit unsigned mono
- Sample Rate: 22,050 Hz
- File Extension: .raw
- Max Size: 32KB per sample
Using Audacity:
- Import your audio file
- Convert to Mono (Tracks → Mix → Mix and Render)
- Set Project Rate to 22050 Hz
- Export → Export Other → Raw (header-less)
- Choose Unsigned 8-bit PCM
- Format SD card to FAT32 format
- Create folder
/samples/on the SD card root - Add sample files (kick.raw, snare.raw, hihat.raw, perc.raw, bass.raw, lead.raw)
- Use the provided sample files OR convert your own (see "Converting Audio" below)
- Insert SD card into the TFT shield
- Upload code to ESP32 via Arduino IDE
- Open Serial Monitor (115200 baud) to view startup messages
- Wait for "DRIFTONE MINI Ready!" message
- Touch the grid to start creating beats!
- Power on - Device initializes and loads samples
- Touch grid squares to toggle steps on/off
- Red squares = active steps
- White outline = current playing step
- Touch +/- buttons to adjust BPM
- Touch PLAY/PAUSE to control playback
The sequencer starts with a basic demo pattern:
- Track 0 (KICK): Steps 1, 5, 9, 13
- Track 1 (SNARE): Steps 5, 13
- Track 2 (HIHAT): Every other step
- Track 0: KICK - kick.raw
- Track 1: SNARE - snare.raw
- Track 2: HIHAT - hihat.raw
- Track 3: PERC - perc.raw
- Track 4: BASS - bass.raw
- Track 5: LEAD - lead.raw
- Convert audio to 8-bit unsigned mono .raw format
- Copy to
/samples/folder on SD card - Rename to match expected filenames
- Restart device to reload samples
Edit color definitions in ui.h:
#define COLOR_BG ILI9341_BLACK
#define COLOR_STEP_ON ILI9341_RED
#define COLOR_CURRENT ILI9341_WHITEModify audio parameters in audioengine.h:
#define SAMPLE_RATE 22050
#define MAX_CONCURRENT_SAMPLES 4Symptoms: Red error message on screen, device halts at startup
Solutions:
- Verify SD card is inserted correctly
- Format SD card as FAT32 (not exFAT or NTFS)
- Windows: Right-click drive → Format → FAT32
- Mac: Disk Utility → Erase → MS-DOS (FAT)
- Check SD card wiring:
- CS pin must be GPIO15 (not shared with TFT)
- Verify SPI bus connections (MOSI, MISO, CLK)
- Try different SD card (some cards have compatibility issues)
- Check Serial Monitor for detailed error messages
Symptoms: Touches register in wrong location or not at all
Solutions:
- Run touch calibration:
- Call
touchHandler.calibrate()in setup() - Follow on-screen prompts to touch 5 calibration points
- Copy the calibration values to
touchscreen.h:
#define TS_MINX 150 // Replace with your values #define TS_MINY 120 #define TS_MAXX 920 #define TS_MAXY 940
- Call
- Verify touch wiring:
- GPIO34 (Y+) - Must be input-capable pin
- GPIO33 (X-)
- GPIO14 (Y-)
- GPIO32 (X+)
- Use stylus instead of finger for better precision
- Check Serial Monitor for touch coordinate readings
Symptoms: Device works but no sound from headphones/speakers
Solutions:
- Check audio circuit:
- GPIO25 → 1kΩ resistor → 10μF capacitor → jack tip
- ESP32 GND → jack sleeve
- Verify capacitor polarity (negative to GND)
- Check Serial Monitor for "Audio engine initialized" message
- Verify samples loaded:
- Serial should show "Loaded sample 0 (XXXX bytes): /samples/kick.raw"
- If not, samples failed to load from SD card
- Test output:
- Use headphones (line-level output, not amplified)
- Volume may be low - increase with
setMasterVolume()
- Check PWM output:
- Use multimeter on GPIO25 (should show ~1.65V DC)
- Use oscilloscope to verify PWM signal
Symptoms: "Warning: Some samples failed to load" in Serial Monitor
Solutions:
- Verify folder structure on SD card:
/samples/ kick.raw snare.raw hihat.raw perc.raw bass.raw lead.raw - Check file names are EXACTLY as shown (case-sensitive)
- Verify sample format:
- Must be RAW PCM format (no WAV header)
- 8-bit unsigned mono
- 22,050 Hz sample rate
- .raw extension
- Check file size - must be < 32KB each
- Re-export samples using Audacity (see "Converting Audio")
Symptoms: Device resets, "Guru Meditation Error", or random crashes
Solutions:
- Reduce sample sizes:
- Keep samples under 32KB each
- Shorter samples = less memory usage
- Adjust max sample size in
sdloader.h:#define MAX_SAMPLE_SIZE 16384 // Reduce from 32768 to 16KB
- Monitor memory via Serial output
- Simplify patterns - fewer concurrent samples
Symptoms: Blank screen, garbled display, or backlight only
Solutions:
- Check power: Ensure 3.3V (NOT 5V) to TFT
- Verify SPI connections:
- MOSI, MISO, CLK must be correct
- CS, DC, RST pins critical for communication
- Test with simple sketch:
- Use Adafruit GFX library test examples
- Verify display works before running Driftone code
- Check screen rotation: Code uses
tft.setRotation(3)for landscape
Symptoms: Calibration routine doesn't complete or values seem wrong
Solutions:
- Use stylus for precise calibration
- Touch firmly but not too hard
- Wait for visual feedback (green circle) after each point
- Restart calibration if values seem incorrect
- Manual calibration:
- Open Serial Monitor
- Touch corners and note raw X/Y values
- Calculate min/max for each axis
- Update
touchscreen.hmanually
Symptoms: Some samples don't play when many triggers happen at once
Explanation: This is expected behavior! The audio engine supports maximum 4 concurrent samples (polyphony limit). When more than 4 tracks trigger simultaneously, some samples will be dropped.
Solutions:
- Adjust patterns to avoid too many simultaneous triggers
- Increase MAX_CONCURRENT_SAMPLES in
audioengine.h(may impact performance):#define MAX_CONCURRENT_SAMPLES 6 // Increase from 4 to 6
- Use shorter samples so they finish playing before next trigger
- Check Serial Monitor at 115200 baud for detailed debug messages
- Verify ESP32 board is functioning (try basic LED blink test)
- Check all physical connections - loose wires are common culprit
- Try known-good components to isolate faulty parts
- Take photos of your setup and compare to wiring diagrams
- Open an issue on GitHub with:
- Full error message from Serial Monitor
- Photos of your wiring
- SD card contents
- ESP32 board model
- Pattern chaining and song mode
- Real-time effects (bitcrush, delay, reverb)
- Sample recording via I2S microphone
- MIDI sync input/output
- Parameter automation
- Multiple pattern banks
- Rotary encoders for parameter control
- Hardware buttons for track mute/solo
- LED indicators for visual feedback
- External audio input for sampling
- Battery power with charging circuit
Open source hardware/software project. Feel free to modify and share!
Inspired by Elektron Model:Samples workflow and designed for lo-fi beat making enthusiasts.
Happy beat making! 🎵