Skip to main content
3 of 4
There is a rollover problem only if the button is held down for 49 days.
Edgar Bonet
  • 44.3k
  • 4
  • 41
  • 79

Ok, I'll try to split the code into multiple sections for clarity, but all this goes into one single sketch.

First we define the PINs: defines are pre-processor macros, even better than const in terms of performance and space consumption:

#define BTN_PIN 2
#define LED_PIN 13

Let's initialize the board as per our requirements:

void setup() {
  pinMode(BTN_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);
}

Now we define the code which emits and SOS signal in morse code using the standard timing:

  1. a long pulse lasts 3 times a short one
  2. a space lasts 6 times a short pulse

That boils down to the following definitions considering a standard unit of 300ms:

#define UNIT 300
#define SHORT (UNIT)
#define LONG (UNIT*3)
#define PAUSE (UNIT*3)
#define SPACE (UNIT*6)

I'm also going to define another macro to emit a generic light pulse, for simplicity I'll use the delay(...) function, but this will prevent detecting button presses while signalling:

#define PULSE(TYPE) digitalWrite(LED_PIN, HIGH);delay(TYPE); digitalWrite(LED_PIN, LOW);delay(UNIT)

With the above in place we can define a function to send an SOS which should be both very compact and also readable:

void sendSOS() {
  PULSE(SHORT);PULSE(SHORT);PULSE(SHORT);
  PULSE(PAUSE); // inter letter pause
  PULSE(LONG);PULSE(LONG);PULSE(LONG);
  PULSE(PAUSE); // inter letter pause
  PULSE(SHORT);PULSE(SHORT);PULSE(SHORT);
}

Now, let's describe what the mode changing should be notified to the user:

#define QUICK 100
bool auto = true;

void switchMode() {
  auto = !auto;
  for (unsigned int i = 0; i < 25; i++) {
    digitalWrite(LED_PIN, HIGH);
    delay(QUICK);
    digitalWrite(LED_PIN, LOW);
    delay(QUICK);
  }
}

Now our main loop will have to check the button and act accordingly, right?

#define LONGPRESS 500
unsigned long elapsed;
bool clicked = false;
unsigned long duration;

void loop() {
  if (auto) {
    sendSOS();
  }
  elapsed = millis();
  while(clicked || millis() - elapsed <= SPACE) {
    // keep waiting to emit a SPACE or until the button is released

    if (digitalRead(BTN_PIN)) {
      // button has been clicked: trace it and start counting time
      clicked = true;
      duration = millis();
    } else if (clicked) {
      // button has been released: stop tracing and verify click duration
      clicked = false;
      if (millis() - duration <= LONGPRESS) {
        // switch mode on long press
        switchMode();
      } else if (!auto) {
        // short press has a meaning only when not in auto mode
        sendSOS();
      }
    }
  }
  

The solution above has great space for improvement:

  • there is no de-bouncing on the button
  • the board is unresponsive while signalling
  • keeping the button pressed forever will prevent any signalling to occur in any mode
  • the code is afflicted by millis() rollover if you keep the button pressed for long periods (around 49 days)

UPDATE: I've added the inter-letter pause as suggested in the comments.