Void loop inside a switch case?

For instance on thirdSequence a high output on positions 3 & 4 only,

You should use one of the button library (like OneButton or Toggle or EasyButton or Bounce2, ...) ➜ that would make your code easier to read and write as they handle debouncing for you.

I see how you select one of the sequence, but how does the operator validate a certain sequence so that the machine starts?

Yes, that's it. I'm not good with words :slightly_smiling_face:

Yes I will have a look into debouncing the sensor, push switch is working well. With the machine, the operator chooses a sequence to suit the task, starts the machine going and it is fully automated. The operator can follow progress by observing the lcd. When all the jobs are done, he can stop the machine change the sequence , re-start again... Assuming the sensor works correctly everytime all is well.

here is an example of the code with a button library and leveraging arrays to group functions that could be used in a state machine (Here is a small introduction to the topic if needed Yet another Finite State Machine introduction)

when you select a sequence an init function is called upon switching, then the loop continuously call the sequence function and when you switch again, and end function is called before calling the new init function.

click to see the code
#include <LiquidCrystal.h>
#include <Toggle.h>

const byte buttonPin = 8;    // the number of the pushbutton pin
Toggle selectButton;
LiquidCrystal lcd( 7,  6,  5, 4, 3, 2);

const byte sequenceCount = 6;
byte currentSequence = 0;

// prototypes to declare the arrays
void initSequence0();
void initSequence1();
void initSequence2();
void initSequence3();
void initSequence4();
void initSequence5();

void Sequence0();
void Sequence1();
void Sequence2();
void Sequence3();
void Sequence4();
void Sequence5();

void endSequence0();
void endSequence1();
void endSequence2();
void endSequence3();
void endSequence4();
void endSequence5();

// arrays of functions pointers
void (*initSequence[])() = {initSequence0, initSequence1, initSequence2, initSequence3, initSequence4, initSequence5};
void (*sequence[])() = {Sequence0, Sequence1, Sequence2, Sequence3, Sequence4, Sequence5};
void (*endSequence[])() = {endSequence0, endSequence1, endSequence2, endSequence3, endSequence4, endSequence5};

// some heartbeat for the current sequence
void heartbeat() {
  static unsigned long chrono = 0;
  if (millis() - chrono >= 5000) {
    chrono = millis();
    Serial.print("In sequence #");Serial.println(currentSequence+1);
  }
}


// Implementation of the functions for the sequences

void initSequence0() {Serial.print("configuring for sequence #"); Serial.println(currentSequence+1);}
void initSequence1() {Serial.print("configuring for sequence #"); Serial.println(currentSequence+1);}
void initSequence2() {Serial.print("configuring for sequence #"); Serial.println(currentSequence+1);}
void initSequence3() {Serial.print("configuring for sequence #"); Serial.println(currentSequence+1);}
void initSequence4() {Serial.print("configuring for sequence #"); Serial.println(currentSequence+1);}
void initSequence5() {Serial.print("configuring for sequence #"); Serial.println(currentSequence+1);}

void Sequence0() {heartbeat();}
void Sequence1() {heartbeat();}
void Sequence2() {heartbeat();}
void Sequence3() {heartbeat();}
void Sequence4() {heartbeat();}
void Sequence5() {heartbeat();}

void endSequence0() {Serial.print("Ending sequence #"); Serial.println(currentSequence+1);}
void endSequence1() {Serial.print("Ending sequence #"); Serial.println(currentSequence+1);}
void endSequence2() {Serial.print("Ending sequence #"); Serial.println(currentSequence+1);}
void endSequence3() {Serial.print("Ending sequence #"); Serial.println(currentSequence+1);}
void endSequence4() {Serial.print("Ending sequence #"); Serial.println(currentSequence+1);}
void endSequence5() {Serial.print("Ending sequence #"); Serial.println(currentSequence+1);}


void displayCurrentSequence() {
  lcd.clear();
  lcd.print("Sequence ");
  lcd.print(currentSequence + 1);
  lcd.setCursor(0, 1);
  lcd.print("Count to ");
  lcd.print(currentSequence + 4);
}

void handleSelectButton() {
  selectButton.poll();
  if (selectButton.onPress()) {
    endSequence[currentSequence]();
    currentSequence = (currentSequence + 1) % sequenceCount;
    displayCurrentSequence();
    initSequence[currentSequence]();
  }
}

void setup() {
  selectButton.begin(buttonPin);
  Serial.begin(115200);
  lcd.begin(16, 2);
  lcd.print("-- SEQUENCING --");
  delay(1000);
  displayCurrentSequence();
  initSequence[currentSequence]();
}

void loop() {
  handleSelectButton();         // are we switching sequence ?
  sequence[currentSequence]();  // call in a loop the function for the sepcific sequence
}

That looks really interesting, will have to get my other UNO on the go so I can run both projects at the same time.

I like simple. I think using INPUT_PULLUP (and a button PRESSED = LOW) will better suit your code.

#include <Wire.h>
#include "LiquidCrystal.h"

LiquidCrystal lcd(7, 6, 5, 4, 3, 2);


int WhichSequence = 1;  // This variable stores the current Screen number
boolean hasChanged = true;
const int buttonPin = 8;    // the number of the pushbutton pin
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup() {
  lcd.begin(16, 2); // initialize LCD
  pinMode(buttonPin, INPUT_PULLUP); // set button to INPUT with internal PULLUP resistor
}

void loop() {
  if (hasChanged == true)
    sequence(WhichSequence);

  int reading = digitalRead(buttonPin);
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;

      if (buttonState == LOW) { // LOW FOR BUTTON PRESSED WITH INPUT_PULLUP
        hasChanged = true;
        WhichSequence++;
      }
    } else {
      hasChanged = false;
    }
  }
  lastButtonState = reading;
  if (WhichSequence > 6) {
    WhichSequence = 1;
  }
}

void sequence(int value) {
  int count;
  lcd.clear();
  lcd.setCursor(0, 0); // Column, line
  lcd.print("  Sequence ");
  lcd.print(value);
  lcd.setCursor(0, 1);
  lcd.print("  Count to ");
  switch (value) {
    case (1): count = 4; break;
    case (2): count = 5; break;
    case (3): count = 6; break;
    case (4): count = 7; break;
    case (5): count = 8; break;
    case (6): count = 9; break;
    default: break;
  }
  lcd.print(count);
}

Hi xfpd, I have wired up your example and it works well, simple!

Hi JML, I have also wired up your example, I starts with "--SEQUENCING--" on the lcd. Then it shows "Sequence 1 / Count to 4" and I can step through the sequences with the button, but there isn't a second button wired in to step through a particular sequence i.e. counting 1,2,3,4 and back to 1,2 etc or am I missing something?
Thanks to everyone for your help so far.

There is not at the moment as there was none in your first code.

The specs are unclear in terms of how the user experience should be and what's really happening in each sequence.

If you explain more, we can help better...

Here is the code we got to where everything worked as designed apart from knowing how to implement a digitalWrite in certain positions;

#include <Wire.h>
#include "LiquidCrystal.h"

//LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

const int buttonPin = 2;  // the number of the pushbutton pin
const int sensorPin = 3;  // the number of the sensor pin

int WhichSequence = 1;  // This variable stores the current Screen number
boolean hasChanged = true;
int buttonState;                     // the current reading from the input pin
int lastButtonState = LOW;           // the previous reading from the input pin
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup()
{
  pinMode(buttonPin, INPUT);
  pinMode(sensorPin, INPUT_PULLUP);
}

bool checkButton(void)
{
  //-------------------------------
  // BEGIN of the switch debouncing code
  int reading = digitalRead(buttonPin);
  if (reading != lastButtonState)
  {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay)
  {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState)
    {
      buttonState = reading;

      // only toggle the LED if the new button state is HIGH
      if (buttonState == HIGH)
      {
        hasChanged = true;
        WhichSequence++;
        if (WhichSequence > 6)
        {
          WhichSequence = 1;
        }
      }
    }
    else
    {
      // hasChanged = false;
    }
  }
  lastButtonState = reading;
  // END of the switch Debouncing code
  // --------------------------------------

  return hasChanged;
}

void loop()
{

  if (hasChanged == true)
  {
    hasChanged = false;
    switch (WhichSequence)
    {
      case 1:
        {
          firstSequence();
        }
        break;

      case 2:
        {
          secondSequence();
        }
        break;

      case 3:
        {
          thirdSequence();
        }
        break;

      case 4:
        {
          fourthSequence();
        }
        break;

      case 5:
        {
          fifthSequence();
        }
        break;

      case 6:
        {
          sixthSequence();
        }
        break;
      case 0:
        {
        }
        break;
    }
  }

  checkButton();

  if (digitalRead(sensorPin) == LOW)
  {
    performSequence(WhichSequence);
  }
}

void performSequence(int sequence)
{
  int count;
  switch (sequence)
  {
    case 1:
      count = 4;
      break;
      //todo: other cases

    case 2:
      count = 5;
      break;

    case 3:
      count = 6;
      break;    

    case 4:
      count = 7;
      break;  

    case 5:
      count = 8;
      break;  

    case 6:
      count = 9;
      break;    
  }

  int position = 1;

  while (!hasChanged)
  {
    lcd.setCursor(0, 1);
    lcd.print("  Position ");
    lcd.print(position);

    while ((digitalRead(sensorPin) == LOW) && !checkButton())
    {
    }

    while ((digitalRead(sensorPin) == HIGH) && !checkButton())
    {
    }

    position++;
    if (position > count)
      position = 1;
  }
}

void firstSequence()
{
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("  Sequence 1");
  lcd.setCursor(0, 1);
  lcd.print("  Count to 4");
}
void secondSequence()
{
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("Sequence 2");
  lcd.setCursor(0, 1);
  lcd.print("Count to 5");
}
void thirdSequence()
{
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("  Sequence 3");
  lcd.setCursor(0, 1);
  lcd.print("  Count to 6");
}
void fourthSequence()
{
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("Sequence 4");
  lcd.setCursor(0, 1);
  lcd.print("Count to 7");
}
void fifthSequence()
{
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("  Sequence 5");
  lcd.setCursor(0, 1);
  lcd.print("  Count to 8");
}
void sixthSequence()
{
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("Sequence 6");
  lcd.setCursor(0, 1);
  lcd.print("Count to 9");
}

A summary of how the electronics interfaces with the machine is;
There is just one switch, each press steps on the sequence starting at firstSequence, the second, third, forth, fifth, sixth, back to first, then the second etc the operator presses the button until it shows the sequence he requires. The machine is started up, when the mechanism gets to position1 a magnetc sensor is activated (connected to pin 3), the lcd shows "position 1", it the moves to "position 2", and when it gets to say position 3 (depending on which sequence has been selected) lcd shows "position 3" and solenoid is energised (digitalWrite here), etc, it finally gets to say position 6, the next sensor activation takes it back to showing "position 1" , then, "position 2".....

The "position x" shown on the lcd is due to this code;

int position = 1;

  while (!hasChanged)
  {
    lcd.setCursor(0, 1);
    lcd.print("  Position ");
    lcd.print(position);

    while ((digitalRead(sensorPin) == LOW) && !checkButton())
    {
    }

    while ((digitalRead(sensorPin) == HIGH) && !checkButton())
    {
    }

    position++;
    if (position > count)
      position = 1;
  }

so I can't see where to add a digitalWrite

but what happens say If I want sequence 3 :

I start at sequence 1, is it already running before I press ?
I presse once, screen shows sequence 2. but does it start? does sequence 1 need to be stopped ?
I press the button a second time, screen shows sequence 3. does it start? does sequence 2 need to be stopped ?

➜ it seems weird there is no "run" button to trigger the mechanism that is moving in between positions.

No, the control box doesn't start the machine, it is all controlled by hydraulic control levers. All the operator has to do is select a sequence with the push button suitable for the job it is set up to do. He starts the machine and the Arduino monitors each "position" and at certain "positions" it will operate a solenoid for a special function. The operator can leave the machine and walk away for a day, letting it run continuously. If the operator changes the sequence mid-flow it will only put the sequences out of sync. Hope that helps

Here is one possible implementation:

#include <Wire.h>
#include "LiquidCrystal.h"

//LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

const int buttonPin = 2;  // the number of the pushbutton pin
const int sensorPin = 3;  // the number of the sensor pin

const int output1Pin = 11;  // output 1
const int output2Pin = 12;  // output 2
const int output3Pin = 13;  // output 3


int WhichSequence = 1;  // This variable stores the current Screen number
boolean hasChanged = true;
int buttonState;                     // the current reading from the input pin
int lastButtonState = LOW;           // the previous reading from the input pin
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup()
{
  pinMode(buttonPin, INPUT);
  pinMode(sensorPin, INPUT_PULLUP);

  pinMode (output1Pin, OUTPUT);
  pinMode (output2Pin, OUTPUT);
  pinMode (output3Pin, OUTPUT);
}

bool checkButton(void)
{
  //-------------------------------
  // BEGIN of the switch debouncing code
  int reading = digitalRead(buttonPin);
  if (reading != lastButtonState)
  {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay)
  {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState)
    {
      buttonState = reading;

      // only toggle the LED if the new button state is HIGH
      if (buttonState == HIGH)
      {
        hasChanged = true;
        WhichSequence++;
        if (WhichSequence > 6)
        {
          WhichSequence = 1;
        }
      }
    }
    else
    {
      // hasChanged = false;
    }
  }
  lastButtonState = reading;
  // END of the switch Debouncing code
  // --------------------------------------

  return hasChanged;
}

void loop()
{

  if (hasChanged == true)
  {
    hasChanged = false;
    switch (WhichSequence)
    {
      case 1:
        {
          firstSequence();
        }
        break;

      case 2:
        {
          secondSequence();
        }
        break;

      case 3:
        {
          thirdSequence();
        }
        break;

      case 4:
        {
          fourthSequence();
        }
        break;

      case 5:
        {
          fifthSequence();
        }
        break;

      case 6:
        {
          sixthSequence();
        }
        break;
      case 0:
        {
        }
        break;
    }
  }

  checkButton();

  if (digitalRead(sensorPin) == LOW)
  {
    performSequence(WhichSequence);
  }
}

void sequence1_actions(int position)
{
  switch (position)
  {
    case 1: 
      // Perform actions for position 1
      digitalWrite (output1Pin, 1);
      digitalWrite (output2Pin, 0);
      digitalWrite (output3Pin, 0);
      break;

   case 2: 
      // Perform actions for position 2
      digitalWrite (output2Pin, 1);
      break;

    case 3: 
      // Perform actions for position 3
      digitalWrite (output3Pin, 1);
      break;

    default:
      // No action required
      break;
  }
}

void sequence2_actions(int position)
{
  switch (position)
  {
    case 1: 
      // Perform actions for position 1
      break;

   case 2: 
      // Perform actions for position 2
      break;

    case 3: 
      // Perform actions for position 3
      break;

    default:
      // No action required
      break;
  }
}

void sequence3_actions(int position)
{
  switch (position)
  {
    case 1: 
      // Perform actions for position 1
      break;

   case 2: 
      // Perform actions for position 2
      break;

    case 3: 
      // Perform actions for position 3
      break;

    default:
      // No action required
      break;
  }
}

void sequence4_actions(int position)
{
  switch (position)
  {
    case 1: 
      // Perform actions for position 1
      break;

   case 2: 
      // Perform actions for position 2
      break;

    case 3: 
      // Perform actions for position 3
      break;

    default:
      // No action required
      break;
  }
}

void sequence5_actions(int position)
{
  switch (position)
  {
    case 1: 
      // Perform actions for position 1
      break;

   case 2: 
      // Perform actions for position 2
      break;

    case 3: 
      // Perform actions for position 3
      break;

    default:
      // No action required
      break;
  }
}

void sequence6_actions(int position)
{
  switch (position)
  {
    case 1: 
      // Perform actions for position 1
      break;

   case 2: 
      // Perform actions for position 2
      break;

    case 3: 
      // Perform actions for position 3
      break;

    default:
      // No action required
      break;
  }
}

void performSequence(int sequence)
{
  int count;
  switch (sequence)
  {
    case 1:
      count = 4;
      break;
      //todo: other cases
  }

  int position = 1;

  while (!hasChanged)
  {
    lcd.setCursor(0, 1);
    lcd.print("  Position ");
    lcd.print(position);

    // we've reached position n
    switch (sequence)
    {
      case 1:
        sequence1_actions(position);
        break;
      case 2:
        sequence2_actions(position);
        break;
      case 3:
        sequence3_actions(position);
        break;
      case 4:
        sequence4_actions(position);
        break;
      case 5:
        sequence5_actions(position);
        break;
      case 6:
        sequence6_actions(position);
        break;
      default:
        //invalid sequence?
        break;
    }


    while ((digitalRead(sensorPin) == LOW) && !checkButton())
    {
    }

    while ((digitalRead(sensorPin) == HIGH) && !checkButton())
    {
    }

    position++;
    if (position > count)
      position = 1;
  }
}

void firstSequence()
{
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("  Sequence 1");
  lcd.setCursor(0, 1);
  lcd.print("  Count to 4");
}
void secondSequence()
{
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("Sequence 2");
  lcd.setCursor(0, 1);
  lcd.print("Count to 5");
}
void thirdSequence()
{
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("  Sequence 3");
  lcd.setCursor(0, 1);
  lcd.print("  Count to 6");
}
void fourthSequence()
{
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("Sequence 4");
  lcd.setCursor(0, 1);
  lcd.print("Count to 7");
}
void fifthSequence()
{
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("  Sequence 5");
  lcd.setCursor(0, 1);
  lcd.print("  Count to 8");
}
void sixthSequence()
{
  lcd.clear();
  lcd.setCursor(0, 0);  // Column, line
  lcd.print("Sequence 6");
  lcd.setCursor(0, 1);
  lcd.print("Count to 9");
}

A general note on code vs tables. Using data tables is more compact than code, and is generally better than repeating almost identical code. However, code is more flexible and depending on author may be easier to maintain. Updating and extending functionality in a table could be easier or harder, but that depends on situation.

For efficiency and other reasons I would tend towards use of tables, but I would leave it as a matter of personal preference and situation.

Thanks so much Bob, we have finally cracked the code (excuse the pun). This is exactly how I need it to work, I am so pleased.
Thanks everyone for your input.

Just like in Highlander