How To Structure a Program?

Hello All

This is my first post here and I’m new to the arduino too, so apologies for any rookie errors.

I’m trying to write a program that uses an Arduino Mega, a joystick, LCD display and 3 buttons to automate some outputs.

For example, knock the joystick left it will turn on output x for 3 seconds, then output y for 2 seconds.
Knock it right and it’ll do output z for 3 seconds then output x for 6 seconds etc etc. So I’m using it as 4 way switch, plus the joystick press as a 5th option.
I’m also using the 3 buttons to access a menu system on the LCD. I.e. press left button to access menu, then the iddle button as a “+” and the right button as a “-”. Then the left button as a “Select” to allow me to scroll through and alter the respective timers.

I found this link

about planning a project, and have worked through, looking at a few tutorials and examples. Everything was going well, but now it is saying ‘inputs’ was not declared in this scope’ when I try and upload. Am I trying to put too much into one sketch?
I read somewhere that the function needs declaring before it’s called - but how do I do this when all the functions call each other?

I’m guessing there is a better way to implement all of this?

// Define joystick axes
#define joyX A0
#define joyY A1

//Include library code
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

//Setup joystick button
int joybut=2;
int joybutState = 0;
int joybutState1 = 0;

//Setup menu buttons
int lbut=3;
int lbutState = 0;
int mbut=4;
int mbutState = 0;
int rbut=5;
int rbutState= 0;

void setup() {
  //Setup joystick
  pinMode(7,OUTPUT);
  pinMode(joybut,INPUT);
  pinMode(lbut,INPUT);
  pinMode(mbut,INPUT);
  pinMode(rbut,INPUT);
  digitalWrite(joybut, HIGH);
  digitalWrite(lbut, HIGH);
  digitalWrite(mbut, HIGH);
  digitalWrite(rbut, HIGH);
  Serial.begin(9600);
    // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("Control System!");
  lcd.setCursor(0,1);
}

void loop() {
  menuCheck();
}

void menuCheck() {
  lbutState = digitalRead(lbut);

  if (lbutState == LOW) {
    lbutState = HIGH;
    lcd.setCursor(0,0);
    lcd.print("Main Menu            ");
    lcd.setCursor(0,1);
    lcd.print("Exit             ");
    delay(300);
    exitMenu();
  }
  
  else {
    inputs();
  }
}

void exitMenu() {
    lbutState = digitalRead(lbut);
    mbutState = digitalRead(mbut);
    rbutState = digitalRead(rbut);

    if (rbutState == LOW) {
    rbutState = HIGH;
    lcd.setCursor(0,1);
    lcd.print("Timers             ");
    delay(300);
      timerMenu();

    if (lbutState == LOW) {
    lbutState = HIGH;
    lcd.setCursor(0,0);
    lcd.print("Control System");
    lcd.setCursor(0,1);
    delay(300);
    inputs();
    }
    exitMenu();
}

void timerMenu() {
    lbutState = digitalRead(lbut);
    mbutState = digitalRead(mbut);
    rbutState = digitalRead(rbut);
    

    if (rbutState == LOW) {
    rbutState = HIGH;
    lcd.setCursor(0,1);
    lcd.print("Exit             ");
    delay(300);
      exitMenu();
    }
    timerMenu();
}

void inputs() {
 int xValue = analogRead(joyX);
 int yValue = analogRead(joyY);
  
  joybutState = digitalRead(joybut);
  
  if (xValue>=500 && yValue<=200)
  {
    lcd.print ("LEFT                   ");
    delay (1000);
    lcd.print ("             ");
  }

  if (xValue<=200 && yValue>=500)
  {
    lcd.print("UP                ");
  }

  if (xValue>=1000 && yValue>=500)
  {
    lcd.print("DOWN                 ");
  }

  if (xValue>=200 && yValue>=1000)
  {
    lcd.print("RIGHT                    ");
  }

  if (xValue>=1000 && yValue>=1000)
  {
    Serial.write("oops!");
  }

  if (joybutState == LOW)
  {
    lcd.print("Joystick click          ");
  }
}

You are missing a } in your exitMenu() function. Maybe it should be on line 77.

If you use the AutoFormat tool it will make that sort of thing more obvious.

...R

Thankyou!

Do you think my current approach will work for the program I have in mind?

A couple of comments ...

I would have a function that is called from loop() and which reads all the buttons and joystick values and saves them for use by the other functions. That way everything in a single iteration of loop() uses the same values.

I would not use delay() anywhere because it prevents loop() from repeating quickly and makes the program unresponsive. Use millis() to manage the timing without blocking as illustrated in Several Things at a Time and in Planning and Implementing a Program

...R

The functions exitMenu() and timerMenu() call themselves recursively. This can kill the program if the stack overflows. Eventually tail call elimination will prevent that error, but i would not rely on such compiler dependencies.

Robin2:
A couple of comments ...

I would have a function that is called from loop() and which reads all the buttons and joystick values and saves them for use by the other functions. That way everything in a single iteration of loop() uses the same values.

I would not use delay() anywhere because it prevents loop() from repeating quickly and makes the program unresponsive. Use millis() to manage the timing without blocking as illustrated in Several Things at a Time and in Planning and Implementing a Program

...R

Thanks for your input, I'll have another read!

DrDiettrich:
The functions exitMenu() and timerMenu() call themselves recursively. This can kill the program if the stack overflows. Eventually tail call elimination will prevent that error, but i would not rely on such compiler dependencies.

Is there a better way to keep looping through 'timerMenu' then, so the controls won't work until the main screen is accessed again?

Andrew1987:
Is there a better way to keep looping through ‘timerMenu’ then, so the controls won’t work until the main screen is accessed again?

One function reads the buttons.

Another function acts on those buttons. It will do different things for the ‘left’ button depending on what menu you’re currently in. Maybe in some menus or modes, the button does nothing.

For simple buttons, reading and acting can be combined into one function.

Use global variables to indicate which subroutine (task) should actually run and which should not. See the Several Things...

MorganS:
One function reads the buttons.

Another function acts on those buttons. It will do different things for the 'left' button depending on what menu you're currently in. Maybe in some menus or modes, the button does nothing.

For simple buttons, reading and acting can be combined into one function.

DrDiettrich:
Use global variables to indicate which subroutine (task) should actually run and which should not. See the Several Things...

Thank you! Didn't think of that, I'll have a look!

GO HERE