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:
- a long pulse lasts 3 times a short one
- 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.