Built-in project · Arduino Uno R3

KY switch modules on pins 2 to 6, on an Arduino Uno

A KY-031 knock module and an SW-18010P spring switch on the interrupt pins 2 and 3, and a KY-017 tilt, a KY-021 reed and a KY-004 button module on 4, 5 and 6, each pulling its pin low. The whole build, an Arduino Uno and 5 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.

Beginner About 20 minutes to build and run. Runs in your browser. Free, and no account needed.

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

The circuit runs here once the page has loaded.

What you'll learn

  • Interrupts with attachInterrupt
  • Volatile flags shared with loop
  • Reading switches that pull a pin LOW
  • Reporting only when a state changes
  • Difficulty: Beginner
  • Time: About 20 minutes
  • Board: Arduino Uno R3
  • Parts: 7

How it works

Five switch modules each pull their signal pin LOW when they close. The KY-004 button, KY-017 tilt and KY-021 reed on pins 6, 4 and 5 are read in a loop, and the sketch prints a line when each one changes.

The knock module on pin 2 and the spring switch on pin 3 close for only a millisecond or two. That is too quick to poll. So they use interrupts on pins 2 and 3, which set a flag, and loop prints it. A 250 ms pause then hides the rattling.

Parts list

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

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Knock sensor, KY-031 pin GNDArduino Uno R3 pin GND
Spring vibration switch, SW-18010P pin 2Arduino Uno R3 pin GND
Mercury tilt switch, KY-017 pin GNDArduino Uno R3 pin GND
Mini reed switch, KY-021 pin GNDArduino Uno R3 pin GND
Button module, KY-004 pin GNDArduino Uno R3 pin GND
Button module, KY-004 pin SArduino Uno R3 pin 6
Mini reed switch, KY-021 pin SArduino Uno R3 pin 5
Mercury tilt switch, KY-017 pin SArduino Uno R3 pin 4
Spring vibration switch, SW-18010P pin 1Arduino Uno R3 pin 3
Knock sensor, KY-031 pin SArduino Uno R3 pin 2
Knock sensor, KY-031 pin VCCArduino Uno R3 pin 5V
Mercury tilt switch, KY-017 pin VCCArduino Uno R3 pin 5V
Mini reed switch, KY-021 pin VCCArduino Uno R3 pin 5V
Button module, KY-004 pin VCCArduino Uno R3 pin 5V

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 Uno R3
// Five switch modules: a KY-031 knock module on pin 2, an SW-18010P spring
// switch on pin 3, a KY-017 mercury tilt module on 4, a KY-021 mini reed
// module on 5 and a KY-004 button module on 6.
//
//   KY-031 S -> pin 2   SW-18010P: pin 3 -> switch -> GND
//   KY-017 S -> pin 4   KY-021 S -> pin 5   KY-004 S -> pin 6
//   every + -> 5V   every - -> GND
//
// Each KY board has a 10k from + to S and its switch from S to -, so S is
// HIGH until the switch closes and pulls it LOW. The two springs touch their
// pins for a millisecond or two at a time, too quick to poll, so pins 2 and 3
// (the Uno's interrupt pins) catch them with attachInterrupt(). The bare
// spring switch has no resistor of its own: INPUT_PULLUP is it.
//
// Knock, tip the tilt module, drag the magnet and hold the button.

volatile bool knocked = false;
volatile bool shaken = false;

void knock() { knocked = true; }
void shake() { shaken = true; }

const int pins[] = {4, 5, 6};
const char *closed[] = {"tipped", "magnet", "pressed"};
const char *opened[] = {"upright", "no magnet", "released"};
int last[] = {-1, -1, -1};

void setup() {
  pinMode(2, INPUT);
  pinMode(3, INPUT_PULLUP);
  for (int i = 0; i < 3; i++) pinMode(pins[i], INPUT);
  attachInterrupt(digitalPinToInterrupt(2), knock, FALLING);
  attachInterrupt(digitalPinToInterrupt(3), shake, FALLING);
  Serial.begin(9600);
  Serial.println("Mokxi Uno: knock 2, spring 3, tilt 4, reed 5, button 6");
}

void loop() {
  if (knocked) {
    Serial.println("knock");
    delay(250);
    knocked = false;
  }
  if (shaken) {
    Serial.println("spring");
    delay(250);
    shaken = false;
  }
  for (int i = 0; i < 3; i++) {
    int now = digitalRead(pins[i]) == LOW ? 1 : 0;
    if (now != last[i]) {
      last[i] = now;
      Serial.println(now ? closed[i] : opened[i]);
    }
  }
  delay(10);
}

Code walkthrough

The key functions of the sketch, and what each one does.

knock sketch.ino

The interrupt handler for pin 2. It only sets the knocked flag to true and returns right away. Handlers should be this short.

void knock() { knocked = true; }

shake sketch.ino

The interrupt handler for pin 3, the spring switch. Like knock, it just raises the shaken flag so loop can report it.

void shake() { shaken = true; }

setup sketch.ino

Sets pin 2 as input, pin 3 as INPUT_PULLUP because the bare spring switch has no resistor, and the other three as inputs. It attaches both interrupts on FALLING and prints a hello line.

void setup() {
  pinMode(2, INPUT);
  pinMode(3, INPUT_PULLUP);
  for (int i = 0; i < 3; i++) pinMode(pins[i], INPUT);
  attachInterrupt(digitalPinToInterrupt(2), knock, FALLING);
  attachInterrupt(digitalPinToInterrupt(3), shake, FALLING);
  Serial.begin(9600);
  Serial.println("Mokxi Uno: knock 2, spring 3, tilt 4, reed 5, button 6");
}

loop sketch.ino

Prints knock or spring when a flag is set, waits 250 ms, then clears it. Next it reads pins 4, 5 and 6 and prints a message such as tipped or released only when one changes. It ends with a 10 ms delay.

void loop() {
  if (knocked) {
    Serial.println("knock");
    delay(250);
    knocked = false;
  }
  if (shaken) {
    Serial.println("spring");
    delay(250);
    shaken = false;
  }
  for (int i = 0; i < 3; i++) {
    int now = digitalRead(pins[i]) == LOW ? 1 : 0;
    if (now != last[i]) {
      last[i] = now;
      Serial.println(now ? closed[i] : opened[i]);
    }
  }
  delay(10);
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • A knock prints more than once This project
    The module can ring several times. The 250 ms delay in loop is there to absorb this, so keep it.
  • The spring switch never reports This project
    It has no resistor of its own. Pin 3 must use INPUT_PULLUP, with the switch between pin 3 and GND.
  • Interrupts do not fire on other pins This project
    On an Uno only pins 2 and 3 take attachInterrupt. That is why those two modules sit there.
  • A single knock gives several triggers. Knock sensor
    The spring bounces on its pin and pulses S low a few times in about 30 ms. Wait 50 ms or more after the first pulse before counting another knock, or use a pin interrupt with a lockout.
  • The reading never changes or is random. Knock sensor
    Connect VCC, GND and S correctly (check the labels on the module). Read S as a digital input. It sits HIGH at rest and goes LOW on a knock, so look for the falling edge.
  • The input always reads the same, or reads random values. Spring vibration switch
    The switch is normally open. Use INPUT_PULLUP with one leg to the pin and the other to GND. A knock then reads LOW.
  • Knocks are missed. Spring vibration switch
    The spring touches for only a few milliseconds. Poll often, avoid long delay() calls, or use an interrupt. Hold the result in code for a moment.
  • The reading flickers when you move it. Mercury tilt switch
    The mercury drop bounces and makes and breaks contact quickly. Wait 20 to 50 ms before accepting a new state, and read the pin a few times.

Change it and keep it

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