LCD and Millis

Hello! I am working on a project, and three of the main components are a three way switch, a pressure sensor and a LCD screen. How it works is that depending on the setting of the switch, the LCD screen displays certain things.

Switch is up - displays certain set of instructions
Switch is middle - shows certain screen (this is good, it's the other two options that do not work)
Switch is down - displays a different set of instructions

There are a few problems with my current code:

If you switch the switch to the "up" position, the screen will not update until it cycles through completely (current code = 20 seconds). The pressure applied needs to update every half second, and the screen needs to be able to change instantaneously on the flick of the switch, and not be delayed 20 seconds. You can take a look at the code I have. I know I need to use millis for the LCD instructions, I'm just not sure how I should go about implementing it.

//LIBRARIES
#include <LiquidCrystal_I2C.h>
#include <Wire.h>  
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); 

//VARIABLES
int light = 5; //LED that lights up when too much pressure is applied
int up = 6; //switch pin
int down = 7; //switch pin
int pot = A1; //potentiometer pin
int potval; //value of poteniometer
const int maxpressure = 80; //Analog reading of most pressure allowed to be applied 

void setup() {
  
  //START SERIAL
  Serial.begin(9600);
  
  //DECLARATIONS
  pinMode(light, OUTPUT);
  pinMode(up, INPUT);
  pinMode(down, INPUT);
  
  //INITIAL STATES
  digitalWrite(up, HIGH);
  digitalWrite(down, HIGH);
  
  //INITIAL LCD DISPLAY
  lcd.begin(20,4);
  lcd.setCursor(0,0); 
  lcd.print("Hello");
  delay(5000);
}

void loop() {
  
  //PRINT POTENTIOMETER READING (initial testing, to be changed)   
  potval = analogRead(pot); 
  potval = map(potval,0,1023,0,100);
  Serial.println("Potentiometer Reading: "); 
  Serial.println(potval); 
  
  //DECLARE PRESSURE READINGS
  int pressure = analogRead(A0);
  pressure = map(pressure,0,1023,0,100);
  Serial.println("Pressure Reading: "); 
  Serial.println(pressure);
  
  //LED TURNS ON IF TOO MUCH PRESSURE
  if(pressure>maxpressure)
  {
    digitalWrite(light, HIGH);
  }
  else
  {
    digitalWrite(light,LOW);
  }

//GOING UP
lcd.clear();
  if(digitalRead(up)==LOW)
  {
    lcd.backlight();
    lcd.setCursor(0,0);
    lcd.print("Instructions for");
    lcd.setCursor(0,1); 
    lcd.print("going up");  
    lcd.setCursor(0,3);
    lcd.print("Pressure:"); 
    lcd.setCursor(10,3);
    lcd.print(pressure);
    lcd.setCursor(13,3);
    lcd.print("/");
    lcd.setCursor(14,3);
    lcd.print(maxpressure); 
    delay(5000);
    
      lcd.setCursor(0,0);
      lcd.print("                    ");
      lcd.setCursor(0,1); 
      lcd.print("                    ");
      lcd.setCursor(0,0);
      lcd.print("Place strong foot");
      lcd.setCursor(0,1); 
      lcd.print("on next stair");    
      delay(5000);
      
        lcd.setCursor(0,0);
        lcd.print("                    ");
        lcd.setCursor(0,1); 
        lcd.print("                    ");
        lcd.setCursor(0,0);
        lcd.print("Place weak foot");
        lcd.setCursor(0,1); 
        lcd.print("on next stair"); 
        delay(5000);
        
          lcd.setCursor(0,0);
          lcd.print("                    ");
          lcd.setCursor(0,1); 
          lcd.print("                    ");
          lcd.setCursor(0,0);
          lcd.print("Place cane");
          lcd.setCursor(0,1); 
          lcd.print("on next stair");     
          delay(5000);
  }

//GOING DOWN
lcd.clear();
  if(digitalRead(down)==LOW)
  {
    lcd.backlight();
    lcd.setCursor(0,0);
    lcd.print("Instructions for");
    lcd.setCursor(0,1); 
    lcd.print("Going down");  
    lcd.setCursor(0,3);
    lcd.print("Pressure:"); 
    lcd.setCursor(10,3);
    lcd.print(pressure);
    lcd.setCursor(13,3);
    lcd.print("/");
    lcd.setCursor(14,3);
    lcd.print(maxpressure); 
    delay(5000);
    
      lcd.setCursor(0,0);
      lcd.print("                    ");
      lcd.setCursor(0,1); 
      lcd.print("                    ");
      lcd.setCursor(0,0);
      lcd.print("Place cane");
      lcd.setCursor(0,1); 
      lcd.print("on next stair");    
      delay(5000);
      
        lcd.setCursor(0,0);
        lcd.print("                    ");
        lcd.setCursor(0,1); 
        lcd.print("                    ");
        lcd.setCursor(0,0);
        lcd.print("Place weak foot");
        lcd.setCursor(0,1); 
        lcd.print("on next stair"); 
        delay(5000);
        
          lcd.setCursor(0,0);
          lcd.print("                    ");
          lcd.setCursor(0,1); 
          lcd.print("                    ");
          lcd.setCursor(0,0);
          lcd.print("Place strong foot");
          lcd.setCursor(0,1); 
          lcd.print("on next stair");     
          delay(5000);
  }
  
//MIDDLE STATE
lcd.clear();
  if((digitalRead(up)==HIGH)&&(digitalRead(down)==HIGH))
  {
    lcd.setCursor(0,0);
    lcd.print("Pressure:");
    lcd.setCursor(10,0);
    lcd.print(pressure); 
    lcd.setCursor(13,0);
    lcd.print("/");
    lcd.setCursor(14,0);
    lcd.print(maxpressure); 
    lcd.setCursor(0,1);
    lcd.print("1st # = Pressure Now");
    lcd.setCursor(0,2);
    lcd.print("2nd # = Max Pressure");
    lcd.setCursor(0,3);
    lcd.print("Do not exceed 2nd #!");
    delay(800);
    //lcd.noBacklight();
  }
}
[code]



The UP and DOWN sections need some work. Print pressure needs to update every  second and the delays need to be replaced so that screen can change instantly. I can supply anything else you might need!

Looking in my crystal ball I can see a state machine in your near future.

state machines should help.

Would that solve the problem of millis vs. delay I'm having though?

Yes. The program will be in one of several states at any one time. Some of these states will involve waiting but the trick is to use millis() to determine whether the required period has passed in that state and if not go round loop() again, checking for button presses each time. If a button press is detected change the state variable and different code will be executed straight away rather than waiting for the delay() to elapse or a for loop to end.

Do you think you can help me by writing an example? I'm pretty new at this, and I've never worked with cases before.

If you can please write like one example code I'll be able to adopt it and figure it out for the rest of the parts

Thanks for your help!

k

Hold on a little !

Here is an example I wrote at some time. Read the comments about what each state does. You may need to change the logic that reads the pins and turns the LED on and off to suit your setup.

byte state = 0;
unsigned long waitStart;
unsigned long currentMillis;
unsigned long waitPeriod = 10000;
const byte inputPin = A1;
const byte resetPin = A2;
const byte ledPin = 13;
byte currentButtonState = HIGH;
byte previousButtonState = HIGH;

void setup()
{
  Serial.begin(115200);
  pinMode(inputPin, INPUT_PULLUP);
  pinMode(resetPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);  //start with LED off
  Serial.println(F("Starting in state 0.  Waiting for button 1 to be pressed"));
}

void loop()
{
  switch (state)
  {
    case 0:    //wait for button 1 to be pressed
      currentButtonState = digitalRead(inputPin);
      if (currentButtonState != previousButtonState && currentButtonState == LOW) //button has become pressed
      {
        state = 1;
        Serial.println(F("Moving to state 1.  Waiting 10 seconds unless button 1 is pressed"));
        waitStart = millis();
        previousButtonState = HIGH;  //ready to test for a button press in  state 2
      }
      previousButtonState = currentButtonState;
      break;

    case 1:  //wait 10 seconds unless button 1 is pressed
      currentButtonState = digitalRead(inputPin);
      if (currentButtonState != previousButtonState && currentButtonState == LOW) //button has become pressed
      {
        state = 2;
        Serial.println(F("Moving to state 2.  Waiting 10 seconds with LED on"));
        waitStart = millis();
        digitalWrite(ledPin, LOW);    //LED on for state 2
      }
      previousButtonState = currentButtonState;

      currentMillis = millis();
      if (currentMillis - waitStart >= waitPeriod)
      {
        state = 0;  //time's up.  Back to state 0
        digitalWrite(ledPin, HIGH);
        Serial.println(F("Time's up.  Moving to state 0.  Waiting for button 1 to be pressed"));
      }
      break;

    case 2:  //wait 10 seconds with LED on
      currentMillis = millis();
      if (currentMillis - waitStart >= waitPeriod)
      {
        state = 0;  //time's up.  Back to state 0
        Serial.println(F("LED turned off.  Moving to state 0"));
        digitalWrite(ledPin, HIGH);
      }
      break;
  }

  //any code you put here will be executed each time through loop()
  if (digitalRead(resetPin) == LOW)
  {
    state = 0;    //unconditional return to state 0
    Serial.println(F("Reset button pressed.  Moving to state 0"));  //this message will repeat all the time button 2 is held down
  }
}

Ok ill try to figure that out

Any other solutions, anyone?

AYJCane, you are quite right to dismiss the above suggestions as they don't solve your immediate problem. However they are absolutely right and a little reading on your behalf (The Nick Gammon site is one of the best explanations) will give you the knowledge you need to finish your next project.

Your immediate problem is the delays scattered throughout your code. You need to change your approach. Think about having the main loop working thousands of times per second, checking the inputs and updating the outputs when required.

For a slightly shorter, simpler answer, have a look at the code I wrote today in this post here. See if you can work out how I scheduled the sensor to operate 10 times per second yet the 'other code' at the bottom is running so fast that switching a switch will appear to be instantaneous.

Then look at how I'm using the PreviousVal variable. This is my state machine because that problem had two states: object detected and object not detected.

@Morgan

That solves my pressure updating issue, but it makes the instructions on the LCD screen display too quickly

I need each instruction to be displayed for 5 seconds (4 different instructions, looping) on the top 2 lines of the LCD screen while the 4th line is updated constantly

Is that possible..?

For reference, this code works as I want it to - but it is without the instructions changing on the LCD screen. Pressure updates on line 4 and switch changes instantaneously. If you can help me find a way to make it so that the top 2 lines "Instructions for going up" can change to display "Going up the stairs" after 5 seconds, that'd be awesome.

//LIBRARIES
#include <LiquidCrystal_I2C.h>
#include <Wire.h>  
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); 

/* 
LCD Guidelines:

GND - GND
VCC - 5V
SDA - Pin A4
SCL - Pin A5
*/

//VARIABLES
int light = 5; //LED that lights up when too much pressure is applied
int up = 6; //switchpin
int down = 7; //switchpin
const int maxpressure = 80; //Analog reading of most pressure allowed to be applied 


void setup() {
  
  //START SERIAL
  Serial.begin(9600);
  
  //DECLARATIONS
  pinMode(light, OUTPUT);
  pinMode(up, INPUT);
  pinMode(down, INPUT);
  
  //INITIAL STATES
  digitalWrite(up, HIGH);
  digitalWrite(down, HIGH);
  
  //INITIAL LCD DISPLAY
  lcd.begin(20,4);
  lcd.setCursor(0,0); 
  lcd.print("AYJ Corporation");
  lcd.setCursor(0,2); 
  lcd.print("Welcome to the");
  lcd.setCursor(0,3); 
  lcd.print("Cane Enable (v1.0)");
  delay(5000);
}

void loop() {

  //DECLARE PRESSURE READINGS
  int pressure = analogRead(A0);
  pressure = map(pressure,0,1023,0,100); //float voltage = pressure * (5.0 / 1023.0); (If you want the reading in voltage, use this)
  Serial.println(pressure);
  
  //LED TURNS ON IF TOO MUCH PRESSURE
  if(pressure>maxpressure)
  {
    digitalWrite(light, HIGH);
  }
  else
  {
    digitalWrite(light,LOW);
  }
    
//GOING UP THE STAIRS
lcd.clear();
  if(digitalRead(up)==LOW)
  {
    lcd.backlight();
    lcd.setCursor(0,0);
    lcd.print("Instructions for");
    lcd.setCursor(0,1); 
    lcd.print("Going up");  
    lcd.setCursor(0,3);
    lcd.print("Pressure:"); 
    lcd.setCursor(10,3);
    lcd.print(pressure);
    lcd.setCursor(13,3);
    lcd.print("/");
    lcd.setCursor(14,3);
    lcd.print(maxpressure); 
    delay(800);
  }

//GOING DOWN THE STAIRS
lcd.clear();
  if(digitalRead(down)==LOW)
  {
    lcd.backlight();
    lcd.setCursor(0,0);
    lcd.print("Instructions for");
    lcd.setCursor(0,1); 
    lcd.print("Going down");
    lcd.setCursor(0,3);
    lcd.print("Pressure:");
    lcd.setCursor(10,3);
    lcd.print(pressure); 
    lcd.setCursor(13,3);
    lcd.print("/");
    lcd.setCursor(14,3);
    lcd.print(maxpressure); 
    delay(800);
  }
  
//MIDDLE STATE
lcd.clear();
  if((digitalRead(up)==HIGH)&&(digitalRead(down)==HIGH))
  {
    lcd.setCursor(0,0);
    lcd.print("Pressure:");
    lcd.setCursor(10,0);
    lcd.print(pressure); 
    lcd.setCursor(13,0);
    lcd.print("/");
    lcd.setCursor(14,0);
    lcd.print(maxpressure); 
    lcd.setCursor(0,1);
    lcd.print("1st # = Pressure Now");
    lcd.setCursor(0,2);
    lcd.print("2nd # = Max Pressure");
    lcd.setCursor(0,3);
    lcd.print("Do not exceed 2nd #!");
    delay(800);
    //lcd.noBacklight();
  }
}

Hey as I told you need 1 startTime variable for each function. Now you have 2 if statements using same startTime and startTime gets updated all the time resulting some code never gets run.

As I told:

uint64_t start1time, start2time, current1time, current2time;
//These can be used in 2 if statements, if more if statements you need to add counter variables more, and more.

Some code works only single currentTime, but you will lose precision..

Also to make your app solid and fast you need boolian variables. Best method in is to just count 5sec make bool false, turn of something acording to the bool value. If bool is true turn up and so on. Use millis() to controll your booleans to make your display work like you want.