A small arduino library to debounce a 94H Rotary DIP Switch

A small Arduino library to debounce a 94H Rotary DIP Switch

Here is a small C++ class to debounce a 94H rotary DIP switch for the Arduino.

// 94H Rotary DIP Switch
//
// Debounced 94H front end - does not block your code.
//
// http://datasheet.octopart.com/94HBB08T-Grayhill-datasheet-598324.pdf

class RotaryDipSwitch_94H {
public:

  // Constructors - four bytes for Hex switch, three bytes for Octal switch.
  RotaryDipSwitch_94H(byte, byte, byte);
  RotaryDipSwitch_94H(byte, byte, byte, byte);

  // Returns current position of the switch determined to be correct
  byte position(void);

  // Call this as often as possible.
  void pole(void);
  
  // Returns true if switch position has changed.
  bool changed(void);
  
  // Amount of time in ms to wait before deciding position read is the
  // correct one - the physical switch returns incorrect values over pins
  // as it turns. The higher this value, the more acurate the answer to
  // Position() will be. Default value is 250ms.
  void threshold(int);

private:
  // Physical position of rotary dial - could be what we want, probably isn't!
  byte get_pos(void);

  // 4 pins assigned to rotary dial output.
  byte pins[4];
  
  // Previous known position of dial. Will be updated only after 'threshold' ms have passed.
  byte correct_pos;

  // Amount of time to wait until 'Pole' decides what we are receiving from the rotary dial
  // is the correct answer.
  int threshold;
  
  // previous position of dial while checking in pole() if correct answer 
  //(-1 when not waiting to see if dial changes).
  byte pole_pos; 

  // end of check, reset to millis()+threshold every time dial returns a different value.
  long end_millis;

  // "correct" value has changed or not.
  bool value_changed;
};

// Physical value returned by dial - may not be "correct" answer we want.
byte RotaryDipSwitch_94H::get_pos(void) {
  return digitalRead(pins[0]) + (digitalRead(pins[1])<<1) 
   + (digitalRead(pins[2])<<2) + (pins[3]>=0 ? (digitalRead(pins[3])<<3) : 0);
}

// Hex or BCD switch - 10 or 16 positions
// a, b, c & d are the pin numbers on the AVR. Ex.: 3, 4, 5 and 6 etc.
// a = 94H PIN 1
// b = 94H PIN 2
// c = 94H PIN 4
// d = 94H PIN a
RotaryDipSwitch_94H::RotaryDipSwitch_94H(byte a, byte b, byte c, byte d) {
  pins[0] = a;
  pins[1] = b;
  pins[2] = c;
  pins[3] = d;

  threshold = 200;

  for(byte i=0; i<(pins[3]>=0 ? 4:3); i++)
    pinMode(pins[i], INPUT);

  end_millis = 0;
  pole_pos = -1;
  value_changed = false;

  // Set Position() to current value at startup.
  correct_pos = get_pos();
}

// Octal switch - 8 positions.
// a, b & c are the pin numbers on the AVR. Ex.: 3, 4 and 5 etc.
// a = PIN 1 on 94H
// b = PIN 2 on 94H
// c = PIN 4 on 94H
RotaryDipSwitch_94H::RotaryDipSwitch_94H(byte a, byte b, byte c) {
  RotaryDipSwitch_94H(a, b, c, -1);
}

// Threshold to delay output from Changed() and Position().
void RotaryDipSwitch_94H::Threshold(int ms) {
  threshold = ms;
}

// Dial position has changed, set to false when called.
bool RotaryDipSwitch_94H::changed(void) {
  if (value_changed == true) {
    value_changed = false;
    return true;
  } else {
    return false;
  }
}

// Poling function. Call as often as possible.
void RotaryDipSwitch_94H::pole(void) {
  long cur_millis;
  byte cur_pos;
  
  // get current position of rotary dial - may not be the "correct"
  // value we want.
  cur_pos = get_pos();
  
  // if position returned from rotary dial is the same
  // as the last position we decided is "correct", just return as
  // the dial has not been turned.
  if (cur_pos == correct_pos) {
    return;
  }

  // While the value returned from the rotary dial changes within 'threshold'
  // ms, keep checking until it doesn't change. 
  // (first time around, pole_pos is -1, forcing a new check for 'threshold'
  // ms.)
  cur_millis = millis();
  if ( cur_pos != pole_pos) {
    end_millis = cur_millis + threshold;
  }
 
  // If we have reached the end of our waiting period (threshold ms) and no
  // additional position changes are reported from the rotary encoder, then 
  // assume this is the new "correct" position. 
  if (cur_millis >= end_millis) {
    correct_pos = cur_pos;
    pole_pos = -1;
    value_changed = true;
  }

  // Remember current received position for next time we call Pole().
  pole_pos = cur_pos;
}

// Returns current position of rotary dial we have decided is "correct".
// Value will lag the physical position of the dial by 'threshold' ms.
byte RotaryDipSwitch_94H::position(void) {
  return correct_pos;
}

And he is an example using it:

// Wire up the 16 digit HEX rotary switch:
// AVR digital pin 3 to 94H rotary switch PIN 1
// AVR digital pin 4 to 94H rotary switch PIN 2
// AVR digital pin 5 to 94H rotary switch PIN 4
// AVR digital pin 6 to 94H rotary switch PIN a
// 5V & GND to C pins on rotary switch.
//
// www.sparkfun.com/datasheets/Components/Buttons/B-2124.pdf
static RotaryDipSwitch_94H dip_switch(3, 4, 5, 6);

void setup() {
  Serial.begin(9600);

  // Only update changed() and position() below every 1 second.
  dip_switch.threshold(1000);
}

void loop() {

  // Call as often as possible. Will pole the current position
  // of the rotary dial and decide if it is a useful "correct"
  // value or not.
  dip_switch.pole();

  // If the dial was turned print it to serial. (with a 1 second
  // (none blocked) delay - as we set threshold() to 1 second above.)
  if (dip_switch.changed()) {
    Serial.println(dip_switch.position());
  }

  // For stability
  delay(1);
}

Leave a Reply

Your email address will not be published. Required fields are marked *