Evan Rust
Published © GPL3+

Teensy 4 Large Music Synth

Place several screws into their respective locations to build a song, then simply let the large wheel turn to trigger some notes!

IntermediateFull instructions provided6 hours3,442
Teensy 4 Large Music Synth

Things used in this project

Hardware components

Teensy 4.0 Development Board
Teensy 4.0 Development Board
×1
Stepper Motor with Planetary Gearbox
From Banggood.com
×1
Driver DRV8825 for Stepper Motors for Theremino System
Driver DRV8825 for Stepper Motors for Theremino System
×1
20x4 LCD
×1
Capacitive Touch Sensor Breakout - MPR121
Adafruit Capacitive Touch Sensor Breakout - MPR121
×1
Limit Switch, 5 A
Limit Switch, 5 A
×8
Teensy Audio Board
Teensy Audio Board
×1

Software apps and online services

VS Code
Microsoft VS Code
Arduino IDE
Arduino IDE
XYZWare
3D printing slicer

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

Stepper Motor Stand

Machine Screw Stand

Left Limit Switch Holder

Right Limit Switch Holder

Schematics

Wiring

Code

Music Box

C/C++
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <synth_simple_drum.h>
#include "DRV8825.h"
#include <LiquidCrystal_I2C.h>
#include "Adafruit_MPR121.h"

// GUItool: begin automatically generated code
AudioSynthSimpleDrum     drum1;          //xy=197,198
AudioSynthSimpleDrum     drum6;          //xy=196,456
AudioSynthSimpleDrum     drum2;          //xy=197,252
AudioSynthSimpleDrum     drum5;          //xy=197,403
AudioSynthSimpleDrum     drum3;          //xy=198,308
AudioSynthSimpleDrum     drum7;          //xy=198,506
AudioSynthSimpleDrum     drum8;          //xy=198,561
AudioSynthSimpleDrum     drum4;          //xy=199,358
AudioEffectFreeverb      freeverb1;      //xy=417,661
AudioMixer4              mixer1;         //xy=434,289
AudioMixer4              mixer2;         //xy=437,467
AudioMixer4              mixer3;         //xy=590,372
AudioOutputI2S           i2s1;           //xy=604,656
//AudioControlSGTL5000     sgtl5000_1;
AudioConnection          patchCord1(drum1, 0, mixer1, 0);
AudioConnection          patchCord2(drum6, 0, mixer2, 1);
AudioConnection          patchCord3(drum2, 0, mixer1, 1);
AudioConnection          patchCord4(drum5, 0, mixer2, 0);
AudioConnection          patchCord5(drum3, 0, mixer1, 2);
AudioConnection          patchCord6(drum7, 0, mixer2, 2);
AudioConnection          patchCord7(drum8, 0, mixer2, 3);
AudioConnection          patchCord8(drum4, 0, mixer1, 3);
AudioConnection          patchCord9(freeverb1, 0, i2s1, 0);
AudioConnection          patchCord10(mixer1, 0, mixer3, 0);
AudioConnection          patchCord11(mixer2, 0, mixer3, 1);
AudioConnection          patchCord12(mixer3, freeverb1);
// GUItool: end automatically generated code

#define MOTOR_STEPS 1036    // 200 * 5.18 (ratio)
#define MICROSTEPS 1        // 1 : 1 stepping ratio
#define DIR 0
#define STEP 1
#define STEP_SIZE 5

DRV8825 stepper(MOTOR_STEPS, DIR, STEP);

LiquidCrystal_I2C lcd(0x20, 20, 4);

Adafruit_MPR121 cap = Adafruit_MPR121();

AudioSynthSimpleDrum *drums[8] = {&drum1, &drum2, &drum3, &drum4, &drum5, &drum6, &drum7, &drum8};
// Set inital values below
int drumFrequencies[8] = {50, 120, 230, 300, 520, 630, 800, 1000};
float pitchMods[8] = {.25, .25, .25, .25, .25, .25, .25, .25};
float volume = 0.5, secondMix = 0.2, reverb = .2;
int soundDuration = 1200, wheelRPM = 2, current_setting = 1, current_channel = 0;
bool isRunning = false;

#define BUTTON_PRESS_DELAY 400  // Wait 400ms after each button press before next one is registered

const static uint8_t pins[8] = {2,3,4,5,6,9,10,11};     // Pins that the limit switches connect to

void setup()
{
    Serial.begin(115200);
	AudioMemory(25);
    AudioNoInterrupts();
    initDrums();
    initPins();
    //sgtl5000_1.enable();
    //sgtl5000_1.volume(volume);
    lcd.init();
    lcd.backlight();
    lcd.noCursor();
    lcd.clear();
    stepper.begin(wheelRPM, MICROSTEPS);
    if(!cap.begin(0x5A))
    {
        Serial.println("MPR121 not found!");
        while(1);
    }
    freeverb1.damping(.8);
    freeverb1.roomsize(reverb);
    lcd.print((isRunning) ? "Now running" : "Now stopped");     // display first menu option initially
    AudioInterrupts();
}

elapsedMillis lastButtonPress;

void loop()
{
	if(isRunning)
    {
        stepper.move(STEP_SIZE);    // Rotate the wheel at given RPM
    }
    int amountToChange = 0;     // Add this value to current value in setting
    
    /*
    channel     |      command
    0   |   start/stop toggle
    1   |   change channel
    2   |   change frequency for channel
    3   |   change pitch mod for channel
    4   |   adjust wheel RPM
    5   |   adjust reverb amount
    6   |   change volume
    7   |   + button
    8   |   - button
    */
   uint16_t mpr_vals = cap.touched();
   if(mpr_vals)
   {
        float pad_pressed = log(mpr_vals) / log(2);     // Get position of bit that was pressed for the channel
        int active_pressed = (int) pad_pressed;     // Cast to int
        if(lastButtonPress > BUTTON_PRESS_DELAY)
        {
            Serial.println(active_pressed);
           switch(active_pressed)
            {
                case 0:
                    isRunning = !isRunning;
                    lcd.clear();
                    lcd.print((isRunning) ? "Now running" : "Now stopped");
                    delay(1000);
                    break;
                case 1:
                    current_setting = 1;
                    change_channel(0);
                    break;
                case 2:
                    current_setting = 2;
                    change_freq(0);
                    break;
                case 3:
                    current_setting = 3;
                    change_pitch_mod(0);
                    break;
                case 4:
                    current_setting = 4;
                    change_rpm(0);
                    break;
                case 5:
                    current_setting = 5;
                    change_reverb(0);
                    break;
                case 6:
                    current_setting = 6;
                    change_volume(0);
                    break;
                case 7:
                    amountToChange = 1;
                    break;
                case 8:
                    amountToChange = -1;
                    break;
            }
            lastButtonPress = 0;
       }
   }
    if(amountToChange != 0)
    {
        switch(current_setting)     // Display certain items on LCD
        {
            case 1:
                change_channel(amountToChange);
                break;
            case 2:
                change_freq(amountToChange);
                break;
            case 3:
                change_pitch_mod(amountToChange);
                break;
            case 4:
                change_rpm(amountToChange);
                break;
            case 5:
                change_reverb(amountToChange);
                break;
            case 6:
                change_volume(amountToChange);
                break;
        }
    }
}

void blank_screen()
{
    lcd.clear();
    lcd.print("Current chan: ");
    lcd.print(current_channel+1);
    lcd.setCursor(0, 1);
}

void change_channel(int change)
{
    blank_screen();
    lcd.print("Change button chan");
    if(change==-1 && current_channel <=0);
    else if(change==1 && current_channel >= 7);
    else
    {
        current_channel += change;
        lcd.setCursor(0, 0);
        lcd.print("Current chan: ");
        lcd.print(current_channel+1);
    }
    lcd.setCursor(0,3);
    lcd.print("Current value: ");
    lcd.print(current_channel+1);
}

void change_freq(int change)
{
    blank_screen();
    lcd.print("Change frequency");
    lcd.setCursor(0,2);
    lcd.print("Channel specific");
    lcd.setCursor(0,3);
    lcd.print("Current value: ");
    int current_freq = drumFrequencies[current_channel];
    if(change==-1 && current_freq <=25);
    else if(change==1 && current_freq >= 2000);
    else
    {
        drumFrequencies[current_channel] += (change * 25);
        AudioSynthSimpleDrum* drum = drums[current_channel];
        drum->frequency(drumFrequencies[current_channel]);
    }
    lcd.print(drumFrequencies[current_channel]);
}

void change_pitch_mod(int change)
{
    blank_screen();
    lcd.print("Change pitch modulation");
    lcd.setCursor(0,2);
    lcd.print("Channel specific");
    lcd.setCursor(0,3);
    lcd.print("Current value: ");
    float current_pitchMod = pitchMods[current_channel];
    if(change==-1 && current_pitchMod <=0.00);
    else if(change==1 && current_pitchMod >= 1.0);
    else
    {
        pitchMods[current_channel] += change * .05;
        AudioSynthSimpleDrum* drum = drums[current_channel];
        drum->pitchMod(pitchMods[current_channel]);
    }
    lcd.print(pitchMods[current_channel]);
}

void change_rpm(int change)
{
    blank_screen();
    lcd.print("Change wheel RPM");
    lcd.setCursor(0,3);
    lcd.print("Current value: ");
    if(change==-1 && wheelRPM <= 1);
    else if(change==1 && wheelRPM >= 60);
    else
    {
        wheelRPM += change;
        stepper.setRPM(wheelRPM);
    }
    lcd.print(wheelRPM);
}

void change_reverb(int change)
{
    blank_screen();
    lcd.print("Change reverb");
    lcd.setCursor(0,3);
    lcd.print("Current value: ");
    if(change==-1 && reverb <= 0.05);
    else if(change==1 && reverb >= .95);
    else
    {
        reverb += 0.05 * change;
        freeverb1.roomsize(reverb);
    }
    lcd.print(reverb);
}

void change_volume(int change)
{
    blank_screen();
    lcd.print("Change volume");
    lcd.setCursor(0,3);
    lcd.print("Current value: ");
    if(change==-1 && volume <= 0.0);
    else if(change==1 && volume >= 1.0);
    else
    {
        volume += change * 0.05;
        //sgtl5000_1.volume(volume);
    }
    lcd.print(volume);
}

void (*FunctionPointers[])() = {&playSound1, &playSound2, &playSound3, &playSound4, &playSound5, &playSound6, &playSound7, &playSound8};

void initPins()
{
    for(int pin = 0; pin < 8; pin++)
    {
        pinMode(pins[pin], INPUT_PULLUP);
        attachInterrupt(pins[pin], FunctionPointers[pin], FALLING);
    }
}

void playSound1() {drum1.noteOn(); Serial.println("Drum");}
void playSound2() {drum2.noteOn();}
void playSound3() {drum3.noteOn();}
void playSound4() {drum4.noteOn();}
void playSound5() {drum5.noteOn();}
void playSound6() {drum6.noteOn();}
void playSound7() {drum7.noteOn();}
void playSound8() {drum8.noteOn();}

void initDrums()
{
    for(int drumNum = 0; drumNum < 8; drumNum++)
    {
        AudioSynthSimpleDrum *drum = drums[drumNum];
        drum->frequency(drumFrequencies[drumNum]);
        drum->length(soundDuration);
        drum->secondMix(secondMix);
        drum->pitchMod(pitchMods[drumNum]);
    }
}

Credits

Evan Rust

Evan Rust

122 projects • 1087 followers
IoT, web, and embedded systems enthusiast. Contact me for product reviews or custom project requests.

Comments