mechanical key/Using it/08. A key that remembers
Using it · 08 of 9

A key that remembers

Each press moves to the next mode, off, red, green, blue, and the key shows the mode it is in. It needs one press to be one step, so the sketch waits for the contacts to stop bouncing: 20 ms of a steady reading before it believes a change.

The light says the state

The first sketch lit the key while it was held, which tells you nothing you did not know. This one gives the key a memory: a mode that each press advances, and a colour for each mode.

ModeColour
0off
1red
2green
3blue

After blue it wraps round to off. Replace the colours with meanings and it is a control: a mode switch for a project, armed and disarmed, a count of something.

One press has to be one step

One press, one step
The sketch
Rising edges
0
Steps taken
0
The key shows
off
Press the key. The contacts touch, part, touch again, and only then stay closed. Each touch is a rising edge the pin can see.

A metal contact does not close cleanly. It touches, springs apart, touches again, for a few milliseconds, and each touch is a rising edge your pin can see. A sketch that steps on every rising edge can step two or three times for one press, and the key jumps from off past red to blue. The figure's waveform is a picture of the behaviour rather than a measurement of this switch, whose maker is not recorded; Cherry specifies its own MX switches at under 5 ms of bounce.

Wait until it stops moving

The sketch keeps the last raw reading and the time it last changed. Every change restarts that clock. Only when the reading has stayed the same for DEBOUNCE_MS, 20 ms, and differs from the last accepted state, does the sketch accept it:

if (now - lastChange >= DEBOUNCE_MS && reading != state) {
  state = reading;
  if (state == HIGH) {        // one real press: next mode

20 ms is far longer than the bounce and far shorter than a person can press twice, so it costs nothing you can feel. The TK04 book takes the same code apart line by line.

What you should see

Each press prints one line and changes the colour once:

mode 1  red
mode 2  green
mode 3  blue
mode 0  off

On an ESP32, an ESP32-S3 or a Pico, with VCC on 3V3, expect red to be the brightest of the three.

The code

Adafruit NeoPixel for the light, digitalRead and millis() for the key. Every change in the reading restarts a clock; once the reading has held for DEBOUNCE_MS and differs from the last settled state, the sketch accepts it, and on each accepted press steps the mode, shows its colour and prints it.

key_modes.ino
/*
  Mechanical Key and LED - a key that remembers          TK96 / /p/tk96

  Each press moves to the next mode, and the key shows the mode it
  is in: off, red, green, blue, then off again.

  Wiring. Count from the square pad on the TinkerBlock board, key
  up, header at the bottom:

    GND    -> GND
    VCC    -> 5V on an Uno; 3V3 on an ESP32, ESP32-S3 or Pico
              (pressed, BUTTON gives your pin whatever VCC is)
    WS2812 -> D6 on an Uno, GPIO 4 on an ESP32, GPIO 5 on an
              ESP32-S3, GP14 on a Raspberry Pi Pico
    BUTTON -> D2 on an Uno, GPIO 25 on an ESP32, GPIO 4 on an
              ESP32-S3, GP15 on a Raspberry Pi Pico

  Arduino IDE
    Tools > Board                 your board, e.g. ESP32S3 Dev Module
    Tools > Port                  the one that appears when you plug in
    Tools > USB CDC On Boot       Enabled   (ESP32-S3 only)
    Library Manager: install "Adafruit NeoPixel".
*/

#include <Adafruit_NeoPixel.h>

// Uno: 2. ESP32: 25. ESP32-S3: 4. Pico: 15.
const int BUTTON_PIN = 4;
// Uno: 6. ESP32: 4. ESP32-S3: 5. Pico: 14.
const int LED_PIN = 5;
const unsigned long DEBOUNCE_MS = 20;

Adafruit_NeoPixel key(1, LED_PIN, NEO_GRB + NEO_KHZ800);

const int MODES = 4;
const uint32_t COLOUR[MODES] = { 0x000000, 0xFF0000, 0x00FF00,
                                 0x0000FF };
const char *NAME[MODES] = { "off", "red", "green", "blue" };

int mode = 0;                   // what the key remembers
int lastReading = LOW;
int state = LOW;
unsigned long lastChange = 0;

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON_PIN, INPUT);   // the block has its own pull-down
  key.begin();
  key.show();                   // a dark frame: mode 0 is off
}

void loop() {
  int reading = digitalRead(BUTTON_PIN);
  unsigned long now = millis();

  if (reading != lastReading) { // the contacts moved: restart the clock
    lastReading = reading;
    lastChange = now;
  }

  if (now - lastChange >= DEBOUNCE_MS && reading != state) {
    state = reading;
    if (state == HIGH) {        // one real press: next mode
      mode = (mode + 1) % MODES;
      key.setPixelColor(0, COLOUR[mode]);
      key.show();
      Serial.print("mode ");
      Serial.print(mode);
      Serial.print("  ");
      Serial.println(NAME[mode]);
    }
  }
}

The mode is the sketch's memory; the colour only reports it. Nothing uses delay(), so the loop reads the key thousands of times a second. The sketch compiles for an ESP32-S3 and an Uno.

View on GitHub · blocks/tk96-mechanical-key/arduino/key_modes/key_modes.ino @ v1.8

When it does not work

One press sometimes skips a mode.

The contacts bounced and the sketch counted more than one press. Check DEBOUNCE_MS is still 20 and that the step happens only when state changes to HIGH, not on every loop while the reading is HIGH. A bounce longer than 20 ms is unusual; try 30 if a worn switch still skips.

Green and blue are much dimmer than red.

On a 3.3 V board that is expected: VCC is on 3V3, under the LED's 3.5 V minimum, and green and blue need the most voltage. On an Uno, with VCC on 5V, all three should look alike. For full colour on a 3.3 V board see the divider in One VCC, two jobs.

Holding the key down does nothing more.

It is meant not to. The sketch steps once when the key goes down, then waits for it to come up and go down again. Holding it is one press, however long.

The mode is lost when I unplug the board.

The mode is a variable in RAM, so a reset or power cut starts again at off. To keep it, save it in the ESP32's Preferences or the Uno's EEPROM when it changes, and read it back in setup().

Where this goes next

The key and the light fail apart. Which half is it, and where to look.

When it misbehaves →

Edit this page — content/books/mechanical-key/a-key-that-remembers.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

Mechanical Key and LED

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum →