Pages: [1]   Go Down
Author Topic: A strange Case  (Read 264 times)
0 Members and 1 Guest are viewing this topic.
London-UK
Offline Offline
Full Member
***
Karma: 1
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am trying to iron out some bugs in my code. Its a small menu but what is happening after 10 seconds the LCD displays "top line" and "bottom line". When the "BUTTON" is pressed the LCD displays  "Turn a knob to select an option".

When I turn the encoder and select "Change units" by pressing "BUTTONR" the LCD then prints "select unit", I then press "BUTTONR" again but nothing happens, it just hangs till I reset the board.

If I select "Backlight on off" it hangs without giving the options in the switch cases.

Code:
/*
 Switch case menu system using a rotary encoder with built in push button and one separte push button
 */

// Include Standard Library
#include <stdlib.h>

// Include LCD Library Code
#include <LiquidCrystal.h>

// Include Rotary Encoder Library
#include <QuadEncoder.h>

#include <Button.h>

// control
#define ROTENCA 43 //encoder pin A
#define ROTENCB 45 //encoder pin B
#define BUTTONR 2 // Intr 0 = Pin 2
#define BUTTON  1 // Intr 1 = Pin 3

LiquidCrystal lcd( 7, 8, 9, 10, 11, 12 );

// initialize the encoder
QuadEncoder encoder(ROTENCA,ROTENCB); // initialize the encoder
// has the encoder moved on this loop?
boolean moved = false;
Button buttonR(BUTTONR);
unsigned long timeSinceLastDisplay = 0;

void setup() {
  // setup interrupts
  attachInterrupt(BUTTON, displayMenu, RISING);

  // set the LCD's number of cols and lines
  lcd.begin(16, 2);

  Serial.begin(115200);
  lcd.print("initialising");


}

void loop() {

  displayData();
}

// Displays the current values on the LCD YET TO BE EDITED
void displayData() {
  if (displayOk()) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("top line");
    lcd.setCursor(0,1);
    lcd.print("bottom line");
  }
}


void displayMenu() {

  int selected = 0;// 0 = back light, 1 = logging on and off, 2 = exit settings, 3 = change units

  lcd.setCursor(0,0);
  lcd.clear();
  lcd.print("Turn the knob to");
  lcd.setCursor(0,1);
  lcd.print("select an option");

  // display options
  while (!buttonR.isPressed()) {
    selected += readEncoder();
    if (selected<0) selected=3;// less than
    if (selected>3) selected=0;//more than
    if (moved){
      lcd.clear();
      lcd.setCursor(0,0);
      switch(selected){
      case 0:
        lcd.print("Change units");
        break;
      case 1:
        lcd.print("Backlight On/Off");
        Serial.println("Backlight On/Off");
        break;
      case 2:
        lcd.print("Logging On/Off  ");
        Serial.println("Logging On/Off  ");
        break;
      case 3:
        lcd.print("Exit Settings   ");
        Serial.println("Exit Settings   ");
        break;
      }
    }
  }

  // when button is isPressed...
  lcd.clear();
  switch(selected){
  case 0:
    settingsChangeUnits();
    Serial.println("jump to step 2");
    break;
  case 1:
    settingsChangeBacklight();
    break;
  case 2:
    displayData();
    break;
  }
}

void settingsChangeUnits() {

  int selected = 2;

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Select Unit     ");
  while (!buttonR.isPressed()) {
    selected += readEncoder();
    if (selected<0) selected=2;
    if (selected>2) selected=0;
    if (moved) {
      lcd.clear();
      lcd.setCursor(0,1);
      switch(selected) {
      case 0:
        lcd.print("Celcius         ");
        Serial.print("step 3");
        break;
      case 1:
        lcd.print("Fahrenheit      ");
        Serial.print("step 3b");
        break;
      case 2:
        lcd.print("Cancel          ");
        Serial.print("step 3c");
        break;
      }
    }
  }
  // when button isPressed...
  switch(selected) {
    lcd.clear();
  case 0:
    lcd.print("temperature c");
    Serial.print("step 4");
    break;
  case 1:
    lcd.print("temperature c");
    Serial.print("step 4a");
    break;
  }
}

void settingsChangeBacklight() {

  int selected = 2;

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Backlight On/Off");
  while (!buttonR.isPressed()) {
    selected += readEncoder();
    if (selected<0) selected=2;
    if (selected>2) selected=0;
    if (moved) {
      lcd.setCursor(0,1);
      lcd.print("On  Off  Cancel");
      switch(selected) {
      case 0:
        lcd.setCursor(0,1);
        ledOn();
        break;
      case 1:
        lcd.setCursor(4,1);
        ledOff();
        break;
      case 2:
        lcd.setCursor(9,1);
        lcd.print("ledOn : ledOff");
        break;
      }

    }
  }
}

boolean displayOk() {
  unsigned long now = millis();
  if (now - timeSinceLastDisplay >= 10000) {
    timeSinceLastDisplay = now;
    return true;
  }
  else return false;
}


// returns 1 for right, -1 for left, or 0 for no movement
int readEncoder() {
  moved = true;
  switch(encoder.hb()) {
  case '>':
    return 1;
  case '<':
    return -1;
  }
  moved = false;
  return 0;
}

// turns on the backlight
void ledOn() {
  lcd.print("led on");
  Serial.print("led on");
}

// turns off the backlight
void ledOff() {
  lcd.print("led off");
  Serial.print("led off");
}


and a final thought

Code:
void displayMenu() {

  int selected = 0;// 0 = back light, 1 = logging on and off, 2 = exit settings, 3 = change units

if I change int selected = 1 then the switch case  goes to "back light" not "change units" even though case 1 is "change units" is that correct. I thought selected number would call the same case. I have commented the various selected positions
Logged

London
Offline Offline
Edison Member
*
Karma: 47
Posts: 1435
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am trying to iron out some bugs in my code. Its a small menu but what is happening after 10 seconds the LCD displays "top line" and "bottom line". When the "BUTTON" is pressed the LCD displays  "Turn a knob to select an option".

When I turn the encoder and select "Change units" by pressing "BUTTONR" the LCD then prints "select unit", I then press "BUTTONR" again but nothing happens, it just hangs till I reset the board.

If I select "Backlight on off" it hangs without giving the options in the switch cases.
Start by writing a list of all the possible states your program can be in.
Use switch case to move between states if the required conditions are met.
Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12631
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You're calling Serial.print() from within an interrupt context. That's a very bad idea, because the serial output is managed by interrupts and if your handler tries to write more output than can fit in the serial buffer it will hang waiting for the buffer to be cleared - but it will never be cleared, since interrupts are disabled within interrupt handlers. There's every possibility for that to happen since you have serial output within a loop within an interrupt handler that's executed without any debouncing.

I suggest you change your architecture to not use interrupts, and add switch debounce logic.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Offline Offline
God Member
*****
Karma: 17
Posts: 522
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here are some debounce functions that I wrote tonight. I don't claim they're the best in the world, but they work for me...

Code:
const byte defaultDebounceDelay = 10;                                  // default switch debounce delay time, if no other specified

// IsButtonPressedDebounce returns true or false depending on whether the input passed into pinNum
// is LOW (pressed) or HIGH (not-pressed). The algorithm implements a software debounce. The parameter
// wasPressed is used to avoid the blocking delay of the debounce by passing the button's previous
// pressed/not-pressed state in. If the button's current state is the same as its previous state, then
// no state change has been initiated and there is no need to perform any debounce. If the button's
// current state is different than its previous state, then the button's state may have changed, or
// it may be bouncing; in that case, debounce is performed.
//
// The debounce delay can either be passed in as a parameter (in millis) or it can be omitted, in which
// case a default value is used. This allows for tuning of the debounce algorithm to the individual
// switch's performance, if desired.

boolean isButtonPressedDebounce(byte pinNum, boolean wasPressed)
{
  return isButtonPressedDebounce(pinNum, wasPressed, defaultDebounceDelay);
}

// This version of the function performs a debounced read of the pin without needing information about
// the pin's previous state. This means that the debounce delay will always occur. This version could
// be useful for switches where edge detection is not occurring for whatever reason, and so the "previous"
// state of the button is not being tracked.
//
// The function works by reading the pin's current state and then passing the inverse of that state to
// the "real" debounce function as the pin's "previous" state. This ensures that the debounce function
// always believes that the button's state may have changed, and therefore always performs the debounce.

boolean isButtonPressedDebounce(byte pinNum)
{
  boolean b = digitalRead(pinNum) == LOW;
  return isButtonPressedDebounce(pinNum, !b, defaultDebounceDelay);
}

boolean isButtonPressedDebounce(byte pinNum, boolean wasPressed, byte debounceDelay)
{
  boolean preDebounce = digitalRead(pinNum) == LOW;               // read remote switch state

  if (preDebounce == wasPressed)                                  // if remote switch state is the same as before, then
    return wasPressed;                                            // indicate no change in button state
  else                                                            // otherwise, remote switch state MIGHT have changed (we'll see...)
  {
    delay(debounceDelay);                                         // wait to let bouncing settle
    boolean postDebounce = digitalRead(pinNum) == LOW;            // check post-debounce value

    if (preDebounce == postDebounce)                              // see if pre- and post-debounce values are in agreement
      return preDebounce;                                         // if switch state has REALLY changed, return the new switch state
    else
      return wasPressed;                                          // otherwise, indicate no change in button state
  }
}

This assumes the use of pullup resistors. If you are using pulldown resistors, the LOW/HIGH logic must be reversed.
Logged

Offline Offline
God Member
*****
Karma: 17
Posts: 522
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think this rotary encoder library is much better than the one you're using.

http://www.pjrc.com/teensy/td_libs_Encoder.html
Logged

Offline Offline
God Member
*****
Karma: 17
Posts: 522
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I've gone through your code trying to figure out where it might be going wrong, but I'm kind of lost. You really don't need to be using interrupts for this, and it's only confusing matters and making your code hard to read and debug. As PeterH pointed out, a very likely cause of your hang is that you are using Serial.print() from within an interrupt handler. Every time I have ever done this, it has hung my code. This is just one reason why interrupt handlers are hard to debug. The best advice I can give you is to figure out how to rewrite this code without interrupts.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 484
Posts: 18767
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
void loop() {

  displayData();
}

// Displays the current values on the LCD YET TO BE EDITED
void displayData() {
  if (displayOk()) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("top line");
    lcd.setCursor(0,1);
    lcd.print("bottom line");
  }
}

Not only should you definitely not be doing a Serial.print in the interrupt routine, it is likely to be interrupting an lcd.print.

Imaging you are halfway through doing:

Code:
    lcd.print("bottom line");

Then an interrupt occurs and it starts doing:

Code:
  lcd.setCursor(0,0);
  lcd.clear();
  lcd.print("Turn the knob to");

This is a very bad idea. The lcd routines are unlikely to be re-entrant.

Rework so that the interrupt routine just sets a flag.
Logged


London-UK
Offline Offline
Full Member
***
Karma: 1
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you all

I shall revisit my code with your suggestions. I used serial.print to debug but know now that just adds more bugs.

karma awarded.
Logged

Pages: [1]   Go Up
Jump to: