2

And first of all sorry for my bad English.

I am trying to write code that allows the user to change between continuous or manual mode, but I am having some issues getting it to work. The goal is to be able to switch from manual SOS (user pressing the button to SOS) and automatic SOS (the led automatically blinking SOS morse).

Currently, when I press down the button it loops once the automatic SOS and returns back to its normal state with LED on.

How would I make it, for an example switch the mode to manual when holding down button and giving user some sort of indication when changing the mode (etc blinking the led for X amount of time?)

What I am looking to achieve is:

  1. Turn LEDS on when pressing the button.
  2. Start looping automatic SOS signal
  3. Press or hold down the button to change mode.
    • Blink LEDS to indicate the mode change.
  4. Switch to manual SOS signal (user pressing the button to signal)
  5. Press or hold down the button to change mode.
    • Blink LEDS to indicate the mode change.
  6. Switch back to an automatic loop.

The code so far:

// set pin numbers:
const int buttonPin = 2; // the number of the pushbutton pin
const int ledPin =  13; // the number of the LED pin
int mode;

// variables will change:
int buttonState = 0; // variable for reading the pushbutton status

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
}

void loop() {
  // read the state of the pushbutton value:
  mode = digitalRead(buttonPin);

  // Manual SOS mode.
  buttonState = mode;

  // check if the pushbutton is pressed.
  // if it is, the buttonState is HIGH:
  if (buttonState == LOW) {
    digitalWrite(ledPin, HIGH); // turn LED on:
  } else {
    digitalWrite(ledPin, LOW); // turn LED off:
  }

  // Automatic SOS mode.
  if (mode == HIGH) {
    // check if the pushbutton is pressed.
    // if it is, starts looping automatic SOS.
    int i = 0;
    for (i=0; i<3; i++) {
      digitalWrite(ledPin, HIGH);   // turn the LED on 
      delay(300); // wait for a second
      digitalWrite(ledPin, LOW); // turn the LED 
      delay(300); // wait for a second
    }

    for (i=0; i<3; i++){
      digitalWrite(ledPin, HIGH); // turn the LED on
      delay(1200); // wait for a second
      digitalWrite(ledPin, LOW); // turn the LED 
      delay(1200); // wait for a second
    }

    for (i=0; i<3; i++){
      digitalWrite(ledPin, HIGH); // turn the LED on 
      delay(300); // wait for a second
      digitalWrite(ledPin, LOW); // turn the LED 
      delay(300); // wait for a second
    }
    digitalWrite(ledPin, LOW);
    // waits 3 seconds before starting the loop again
    delay(3000); 
  }
}
2
  • What you want to achieve is definitely not clear to me: can you please clearly state what would you expect your Arduino to do? Eq: "when button is pressed automatic SOS is interrupted and manual SOS signal is emitted once, then no signal is emitted until the button is pressed again". Commented Sep 9, 2016 at 18:11
  • Sorry, English is not my primary language but I will try my best: What I want is the program to have 2 different modes: mode 1: automatic SOS signal and mode 2: manual SOS signal (led blinking). And then the option to swap between these two modes by pressing/holding down the button
    – TRMN
    Commented Sep 9, 2016 at 18:31

1 Answer 1

1

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 autoMode = true;

void switchMode() {
  autoMode= !autoMode;
  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 (autoMode) {
    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 (!autoMode) {
        // 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.

UPDATE: apparently auto is a reserved word, so I changed it to autoMode.

9
  • 1. #define is not better than const in C++. 2. You forgot the short inter-letter space. 3. Your code is not afflicted by the millis() rollover. Commented Sep 10, 2016 at 12:51
  • 1. While better is definitely arguable (what's better for me might not be better for you) #define does not increase memory usage, const requires an additional pointer. 2. you are right 3. it is affected by millis() rollover if you keep the button pressed for 49 days... Commented Sep 10, 2016 at 14:32
  • Thank you, I will take a look at this once I am back at home. It seems complicated but I am sure I can understand it if I just give it enough time.
    – TRMN
    Commented Sep 11, 2016 at 9:06
  • 1
    1. No, const doesn't cost more memory than #define. Try it! 3. You are right, I edited the answer accordingly. Commented Sep 11, 2016 at 10:19
  • Once again your comment on point 1 is correct: I thought #define uses FLASH while const uses SRAM but I believe my assumption is not always correct... not sure about all the details and how much compiler optimization options have influence on this. Thanks for the corrections! Commented Sep 11, 2016 at 12:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.