Built-in project · Arduino Nano

Drum pads on an Arduino Nano

Arduino drum pads: three force-sensing resistors play a kick, a snare and a hi-hat on a piezo, harder hits longer, with a MIDI note and velocity for each. The whole build, an Arduino Nano and 4 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate Runs in your browser. Free, and no account needed.

The parts retail for about $40. On Mokxi they're free.

Drum pads · Arduino Nanolive0.000 s 0.00x
Click to open it in the editor
The circuit itself, running here on the simulator. Press what can be pressed; click anything else to open it in the editor.

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Nano
// Drum pads: three force-sensing resistors play a kick, a snare and a hi-hat
// on a piezo, harder hits louder and longer, on an Arduino Nano.
//
//   kick pad     FSR from A0 to GND
//   snare pad    FSR from A1 to GND
//   hi-hat pad   FSR from A2 to GND
//   piezo        D9 and GND
//
// Tap a pad (drag its slider in the simulator) and the piezo plays that drum.
// The Serial Monitor prints the hit with its MIDI note number and a velocity
// from 1 to 127, the numbers a real electronic drum kit sends to a computer.
//
// # Reading a pad
//
// An FSR is a resistor that drops from millions of ohms to a few hundred as
// it is pressed. Most tutorials put a 10 k resistor beside it to make a
// divider. This sketch uses the one already inside the chip instead:
// pinMode(A0, INPUT_PULLUP) connects A0 to 5 V through about 35 k, and the
// pad connects it to ground. Nobody pressing: the pad is open and A0 reads
// close to 1023. Pressing harder pulls A0 lower. So 1023 minus the reading
// is how hard the pad is pressed, and no extra resistors are needed.
//
// # Turning a press into one hit
//
// A drum stick pushes the pad down and lets it go in a few milliseconds, and
// the pressure climbs to a peak and falls back. The sketch waits for the
// pressure to cross a threshold, then keeps reading for 4 ms to find the peak:
// that peak is how hard the hit was. The pad is ready for the next hit only
// once the pressure has fallen to half the threshold, so one press is one hit
// however long it lasts.
//
// # Drum sounds from a beeper
//
// A piezo can only play one pitch at a time, so each drum is a quick change
// of pitch. The kick slides down from 150 to 50 Hz like a big drum's thud;
// the snare jumps between random pitches every 2 ms, which the ear
// hears as a rattle; the hi-hat does the same, but much higher and shorter.
// A harder hit plays the sound for longer.

const uint8_t PADS = 3;
const uint8_t PAD_PIN[PADS] = {A0, A1, A2};
const char *const PAD_NAME[PADS] = {"kick", "snare", "hi-hat"};
const uint8_t MIDI_NOTE[PADS] = {36, 38, 42};  // the General MIDI drum map
const uint8_t PIEZO = 9;

const int THRESHOLD = 100;      // a pressure above this is a hit starting
const unsigned long PEAK_MS = 4;

enum PadState { READY, PEAKING, WAITING };
PadState state[PADS];
int peak[PADS];
unsigned long hitAt[PADS];

// The sound playing now: which drum, when it started and how long it lasts.
int8_t playing = -1;
unsigned long soundStart = 0;
unsigned long soundLength = 0;
unsigned long soundStepAt = 0;

void play(uint8_t pad, uint8_t velocity) {
  playing = pad;
  soundStart = millis();
  soundStepAt = soundStart - 10;  // the first pitch plays at once
  // 30 to 157 ms, longer for a harder hit; the hi-hat is half that
  soundLength = 30 + velocity;
  if (pad == 2) soundLength /= 2;
}

void updateSound(unsigned long now) {
  if (playing < 0) return;
  unsigned long t = now - soundStart;
  if (t >= soundLength) {
    noTone(PIEZO);
    playing = -1;
    return;
  }
  // A new pitch every 10 ms for the kick and every 2 ms for the others: a
  // pitch has to last a few of its own waves to be heard as one.
  if (now - soundStepAt < (playing == 0 ? 10UL : 2UL)) return;
  soundStepAt = now;
  unsigned int hz;
  if (playing == 0) {
    hz = 150 - 100 * t / soundLength;  // the kick's thud slides down
  } else if (playing == 1) {
    hz = random(800, 2500);           // the snare's rattle
  } else {
    hz = random(5000, 9000);          // the hi-hat's hiss
  }
  tone(PIEZO, hz);
}

void hit(uint8_t pad, int pressure) {
  // The peak pressure, THRESHOLD to 1000 or more, as a MIDI velocity, 1 to 127.
  uint8_t velocity = map(constrain(pressure, THRESHOLD, 1000), THRESHOLD, 1000, 1, 127);
  play(pad, velocity);
  Serial.print(PAD_NAME[pad]);
  Serial.print(" (note ");
  Serial.print(MIDI_NOTE[pad]);
  Serial.print(") velocity ");
  Serial.println(velocity);
}

void readPad(uint8_t pad, unsigned long now) {
  int reading = 1023 - analogRead(PAD_PIN[pad]);  // 0 is not pressed at all
  switch (state[pad]) {
    case READY:
      if (reading > THRESHOLD) {
        state[pad] = PEAKING;
        peak[pad] = reading;
        hitAt[pad] = now;
      }
      break;
    case PEAKING:
      if (reading > peak[pad]) peak[pad] = reading;
      if (now - hitAt[pad] >= PEAK_MS) {
        hit(pad, peak[pad]);
        state[pad] = WAITING;
      }
      break;
    case WAITING:
      if (reading < THRESHOLD / 2) state[pad] = READY;
      break;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(PIEZO, OUTPUT);
  for (uint8_t pad = 0; pad < PADS; pad++) pinMode(PAD_PIN[pad], INPUT_PULLUP);
  // Nothing is wired to A7, so it reads noise: a different rattle each run.
  randomSeed(analogRead(A7));
  Serial.println("Drum pads ready: hit a pad");
}

void loop() {
  unsigned long now = millis();
  for (uint8_t pad = 0; pad < PADS; pad++) readPad(pad, now);
  updateSound(now);
}

Parts list

5 parts, plus the jumper wires. They retail for about $40; on Mokxi they're free.

How it is wired

8 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.

  • Arduino Nano pin 9; Piezo speaker pin 1
  • Arduino Nano pin A2; Force-sensing resistor (3) pin 2
  • Arduino Nano pin A1; Force-sensing resistor (2) pin 2
  • Arduino Nano pin A0; Force-sensing resistor (1) pin 2
  • Ground: Force-sensing resistor (1) pin 1
  • Ground: Force-sensing resistor (2) pin 1
  • Ground: Force-sensing resistor (3) pin 1
  • Ground: Piezo speaker pin 2

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.