Using buttons while reading from serial

Hi awesome community.

I am running in to some programming/logic question that I am pretty sure you can help me fix. I don’t have the code at hand but I will make sure to post it as soon as I do. :slight_smile:

I am building a menu with 3 button selections. Up, Down, Select while reading a value from a HX711 module BUT:

As soon as I start reading the HX711 the buttons become slow and unresponsive. If I disable the serial reading everything runs smooth.

I’ve been looking in to interrupts but the nano I’m using only supports 2. I need 3 for navigating my menu.

I also suspect this to be more of a logic question that hardware.

How do I approach this issue?

Thank you so much.

rasmusnielsendk:
How do I approach this issue?

by looking at the code. don't forget to post it within code tags, the "</>" top left

would be guessing otherwise

Generally, you do not need 3 separate interrupts just to be able to read 3 button switches. The processor is plenty fast enough to read any number of switches, using polling in loop(). If your switches respond sluggishly, it is because the HX711 code is flawed.

OK, Let's get a little more concrete. This is my code. I know it's a little bit sloppy but I'm not a programmer by trade.

Thanks again.

//Compatible with the Arduino IDE 1.0
//Library version:1.1
#include <Wire.h>
#include <HX711.h>

#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>

// HX711 circuit wiring
const int LOADCELL_DOUT_PIN = 2;
const int LOADCELL_SCK_PIN = 3;

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define NAVBAR_X_COORD 57 // X position for navbar locataion

#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
HX711 scale;

const int LeftButtonPin = 6;hhhhhh6
const int CenterButtonPin = 7;
const int RightButtonPin = 5;

int LeftButtonState = 0; 
int CenterButtonState = 0; 
int RightButtonState = 0; 

int brewTime = 27;
int targetGrams = 31;

int currentScreen = 1;
// 1 = Menu     2 = Brew    3 = Time settings     4 = Tare Done      5 = End

int menuItem = 1;  

void setup()
{
  //initiate buttons
  pinMode(LeftButtonPin, INPUT);
  pinMode(CenterButtonPin, INPUT);
  pinMode(RightButtonPin, INPUT);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for OLED
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  display.clearDisplay();
  display.display();

  // listen on serial for scale HX711
  Serial.begin(57600);
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);

  // Calibrate the scale
  scale.set_scale(1860); 
  scale.tare();
}

void loop()
{

  //if on menu and left is pressed
  if (LeftButtonState == HIGH && currentScreen == 1) {
      if (menuItem == 1){
        menuItem = 3;
        } else {
      menuItem = menuItem-1;
      }
      display.clearDisplay();
  }

  //if on menu and center is pressed
  if (CenterButtonState == HIGH && currentScreen == 1) {
   if (menuItem == 3){
        menuItem = 1;
        } else {
       menuItem = menuItem+1;
     }
      display.clearDisplay();
  }

  // if on brew menu and pressed
  if (RightButtonState == HIGH && menuItem == 1) {
      currentScreen = 2;
      display.clearDisplay();
  }
    
  //read button states
  LeftButtonState = digitalRead(LeftButtonPin);
  CenterButtonState = digitalRead(CenterButtonPin);
  RightButtonState = digitalRead(RightButtonPin);

  // MENU SCREEN
  if (currentScreen == 1){
    
    readFromScale();
    
    if (menuItem == 1){
      drawMainMenu(1);
      drawNavMenu();
    }
    if (menuItem == 2){
      drawMainMenu(2);
      drawNavMenu();
    }
    if (menuItem == 3){
      drawMainMenu(3);
      drawNavMenu();
    }
  }
  
  // BREWING
  if (currentScreen == 2){
    display.setCursor(1,1);
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.print("Brewing");
    
    display.setTextSize(1);
    display.setCursor(110,6);
    display.print(targetGrams);
  }

  display.display();
  delay(100);

}

void readFromScale(){
 
 if (scale.is_ready()) {
    long reading = scale.read();
    Serial.print("HX711 reading:");
    Serial.println(reading);

    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(90,24);
    display.print(scale.get_units(10));      
  }    
}

void Brew(){

}

void Tare(){

}

void RedrawScreen(){
    display.clearDisplay();
    display.display();
}

void drawMainMenu(int no){

    if (no == 1){
      display.fillRect(0,0, SCREEN_WIDTH, 16, WHITE);
    }

    if (no == 2){
      display.fillRect(0,19, SCREEN_WIDTH, 16, WHITE);
    }

    if (no == 3){
      display.fillRect(0,38, SCREEN_WIDTH, 16, WHITE);
    }

    
    display.setTextSize(2);
    if (no == 1){ display.setTextColor(BLACK);} else {display.setTextColor(WHITE);}
    display.setCursor(1,1);
    display.print("Brew");

    display.setTextSize(2);
    if (no == 2){ display.setTextColor(BLACK);} else {display.setTextColor(WHITE);}
    display.setCursor(1,20);
    display.print("Tare");

    display.setTextSize(2);
    if (no == 3){ display.setTextColor(BLACK);} else {display.setTextColor(WHITE);}
    display.setCursor(1,39);
    display.print("Time");    
}

void drawNavMenu(){
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0,NAVBAR_X_COORD);
    display.print("UP");

    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(45,NAVBAR_X_COORD);
    display.print("DOWN");

    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(90,NAVBAR_X_COORD);
    display.print("SELECT");    
}

The way your code is written it's very hard to figure out what depends on what as far as timing is concerned - in other words to see what is the critical piece of code.

In your readFromScale() function you have code to print and display stuff - that always slows things down.

Also it looks as if the readFromScale() function may be called more often than necessary.

...R

in general, do you need to update the display if nothing has changed and similarly do you need to report the HX711 value if it hasn't changed.

why do you call clearDisplay() when the button is de-pressed but update the display elsewhere. you call clearDisplay() in various locations in the code. why not have the display for each menu updated in just one place

for screen 1, readFromScale() updates the display and then depending on menuItem the display is again updated.

in readFromScale(), is it the reading, the serial prints or the clearing of the display that is time consuming?

Alright. Thanks for the answers so far. The code is just hacked together very quickly.

So consensus is to streamline the code more. Only update the display when needed, Slow down the reading from the HX711 but not to use interrups at all?

rasmusnielsendk:
but not to use interrups at all?

Yes

Interrupts are only necessary to capture short lived events or unpredictable events - mainly short lived events.

...R

rasmusnielsendk:
So consensus is to streamline the code more. Only update the display when needed, Slow down the reading from the HX711 but not to use interrupts at all?

Interrupts are rarely, if ever, of any use when reading buttons. About their only valid use case it to wake a device from sleep, after which standard keyboard scanning (non-interrupt based) determines the key that was actually pressed.

Besides, suppose you get an interrupt - what are you going to do if your mainline code is busy calling scale.read()? You certainly shouldn't update the LCD from an ISR...

I suppose you can write the key presses to a keyboard buffer (which will stop you missing key presses), but it won't address the responsiveness issue of nothing apparently happening when a button is pressed until whatever foreground process is over.

First step: Work out where your delay is and how much it is.
Is it scale.read(), or the Serial.print() lines, or something else?

Only when you know that can decide how best to address it. For example if the problem is that the library uses long internal delays, better to fix the library than attempt to apply sticking plaster to the key press detection (IMHO).

Thanks again for the answers. I've been working on the code trying to streamline it even more.

To answer your questions - I found out the huge delay came from the HX711. When I remove scale.get_units(10); everything runs smooth.

For now my only fix was to limit the read from the HX711 by wrapping it inside a counter.

It's not in any sense perfect - So i'm still very open to ideas and suggestions.

I've attached code below.

//Compatible with the Arduino IDE 1.0
//Library version:1.1
#include <Wire.h>
#include <HX711.h>

#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>

// HX711 circuit wiring
const int LOADCELL_DOUT_PIN = 2;
const int LOADCELL_SCK_PIN = 3;

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define NAVBAR_X_COORD 57 // X position for navbar locataion

#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
HX711 scale;

const int LeftButtonPin = 6;
const int CenterButtonPin = 7;
const int RightButtonPin = 5;

int LeftButtonState = 0; 
int CenterButtonState = 0; 
int RightButtonState = 0; 

double currentbrewTime = 0;
double brewTime = 27;
float getpercent;
float barwidth;

float currentGrams;
float targetGrams = 308.0f;
float gramsGetPercent;
float gramsBarWidth;

float readingfromscale;

int currentScreen = 1;
// 1 = Menu     2 = Brew    3 = Time settings     4 = Tare Done      5 = End

int menuItem = 1;  
int countdown = 1;
int tempcountdown;

void setup()
{
  //initiate buttons
  pinMode(LeftButtonPin, INPUT);
  pinMode(CenterButtonPin, INPUT);
  pinMode(RightButtonPin, INPUT);

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for OLED
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  display.clearDisplay();
  drawScreens();
  display.display();

  // listen on serial for scale HX711
  Serial.begin(57600);
  scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);

  // Calibrate the scale
  scale.set_scale(1860); 
  scale.tare();
}

void loop()
{
 //read button states
  LeftButtonState = digitalRead(LeftButtonPin);
  CenterButtonState = digitalRead(CenterButtonPin);
  RightButtonState = digitalRead(RightButtonPin);

  
  buttonLogic();

if (currentScreen == 2) {
  display.clearDisplay();
  drawScreens();
  display.display();
}

if (scale.wait_ready_timeout(1000)) {
  countdown = countdown-1;
  
  if (countdown == 0) {
    countdown = 60;
    readingfromscale = scale.get_units(10);
    display.clearDisplay();
    drawScreens();
    display.display();
  }
}
  delay(100);
}

void buttonLogic(){
   
//if on menu and left is pressed
  if (LeftButtonState == HIGH && currentScreen == 1) {
      if (menuItem == 1){
        menuItem = 3;
      } else {
        menuItem = menuItem-1;
      }
      
      display.clearDisplay();
      drawScreens();
      display.display();
  }

//if on menu and center is pressed
  if (CenterButtonState == HIGH && currentScreen == 1 && currentScreen == 1) {
   if (menuItem == 3){
        menuItem = 1;
     } else {
       menuItem = menuItem+1;
     }
     
      display.clearDisplay();
      drawScreens();
      display.display();
  }

// if on brew menu and pressed
  if (RightButtonState == HIGH  && currentScreen == 1 && menuItem == 1) {
      currentScreen = 2;
      display.clearDisplay();
      drawScreens();
      display.display();
  }

  // if on tare menu and pressed
  if (RightButtonState == HIGH && currentScreen == 1 && menuItem == 2) {
      scale.tare();
      readingfromscale = scale.get_units(10);      
      display.clearDisplay();
      drawScreens();
      display.display();
  }
}

void drawScreens(){
    // SCREEN MENU
  if (currentScreen == 1){
    
  if (menuItem == 1){
      drawMainMenu(1);
      drawNavMenu();
  }
  
  if (menuItem == 2){
      drawMainMenu(2);
      drawNavMenu();
  }
  
  if (menuItem == 3){
      drawMainMenu(3);
      drawNavMenu();
  }

   display.setTextSize(1);
   if (menuItem == 2){ display.setTextColor(BLACK);} else {display.setTextColor(WHITE);}
   display.setCursor(90,24);
   display.print(readingfromscale);
   display.display(); 
}
  
  // SCREEN BREWING
  if (currentScreen == 2){
    
    display.clearDisplay();
    display.setCursor(1,1);
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.print("Brewing");
    display.setTextSize(1);
    display.setCursor(105,6);
    display.print(targetGrams);

    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(45,NAVBAR_X_COORD);
    display.print("STOP");

    //draw bar

    currentGrams = readingfromscale;
    getpercent = currentGrams / targetGrams * 100;
    barwidth = 128.0f / 100.0f * getpercent;

    currentbrewTime = currentbrewTime +1;
    
    display.drawRect(0,19, SCREEN_WIDTH, 16, WHITE);
    display.fillRect(0,19, barwidth, 16, WHITE);
    
    display.setCursor(20,40);
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.print(currentbrewTime);  
    display.display();


    //if grams is the same as targetgrams
    if (currentGrams >= targetGrams) {
      currentScreen = 5;
      display.clearDisplay();
      drawScreens();
      display.display();
    } 
  
}

  // SCREED  DONE
  if (currentScreen == 5){
    display.setCursor(1,1);
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.print("Complete");

    display.setCursor(1,20);
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.print(currentbrewTime);

    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(45,NAVBAR_X_COORD);
    display.print("MENU");
  }
}

void drawMainMenu(int no){

    if (no == 1){
      display.fillRect(0,0, SCREEN_WIDTH, 16, WHITE);
    }

    if (no == 2){
      display.fillRect(0,19, SCREEN_WIDTH, 16, WHITE);
    }

    if (no == 3){
      display.fillRect(0,38, SCREEN_WIDTH, 16, WHITE);
    }

    
    display.setTextSize(2);
    if (no == 1){ display.setTextColor(BLACK);} else {display.setTextColor(WHITE);}
    display.setCursor(1,1);
    display.print("Brew");

    display.setTextSize(2);
    if (no == 2){ display.setTextColor(BLACK);} else {display.setTextColor(WHITE);}
    display.setCursor(1,20);
    display.print("Tare");

    display.setTextSize(2);
    if (no == 3){ display.setTextColor(BLACK);} else {display.setTextColor(WHITE);}
    display.setCursor(1,39);
    display.print("Time");    
}

void drawNavMenu(){
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0,NAVBAR_X_COORD);
    display.print("UP");

    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(45,NAVBAR_X_COORD);
    display.print("DOWN");

    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(90,NAVBAR_X_COORD);
    display.print("SELECT");    
}

Look at the library code for scale.get_units(10);
Work out what it’s doing and why it takes so long.

Or..... find a better, non-blocking, library (if there is one).

rasmusnielsendk:
To answer your questions - I found out the huge delay came from the HX711. When I remove scale.get_units(10); everything runs smooth.

What does scale.get_units() do? It sounds like something that only needs to be done once in setup().

Maybe there is a function that allows you to ask the scale to start a reading and another function you can call later to get the new value without having to wait while the reading takes place.

...R