// Buzz Pot
// Eric Mika, 2009
// Provides tactile feedback for a potentiometer to denote changes
// from one value range to another. Ideal for situations where an unknown
// number of values must be set by a single potentiometer.
// To do:
// 1. Debounce the thresholds.
// 2. Handle fringe-case runtime channel count changes.

int vibrationPin = 9; // Turns the vibration motor on and off through a transistor.
int potPin = 0; // Reads the potentiometer.
int potValue = 0; // Stores the potentiometer value.

int channels = 10; // Number of values selectable by the pot.
int currentChannel = 0; // Starting value.
int lastChannel = 0;

int vibDuration = 200; // How long to turn the motor on when thresholds are crossed.
unsigned long vibStart = 0; // Keep track of time so we know when to turn off the motor.

// Map digital pins to their respective LEDs in the 7 segment display.
// Could use a shift register instead to save pins.
int dispA = 2;
int dispB = 3;
int dispC = 4;
int dispD = 5;
int dispE = 6;
int dispF = 7;
int dispG = 8;

void setup() {
  // Set up 7 segment display pins.
  pinMode(dispA, OUTPUT);
  pinMode(dispB, OUTPUT);
  pinMode(dispC, OUTPUT);
  pinMode(dispD, OUTPUT);
  pinMode(dispE, OUTPUT);
  pinMode(dispF, OUTPUT);
  pinMode(dispG, OUTPUT);

  // Set up vibration pin.
  pinMode(vibrationPin, OUTPUT);
}

void loop() {
  // Read the analog input into a variable, correct for value inversion.
  potValue = 1023 - analogRead(potPin);

  // If 10 channels, return a number between 0 and 9...
  // Constrain to catch rounding errors at the top end.
  currentChannel = constrain(potValue / (1023 / channels), 0, channels - 1);

  // Show the current channel number on the 7 segment display.
  displayDigit(currentChannel);

  // Vibrate if we change channels.
  if (lastChannel != currentChannel) {
    vibStart = millis();
  }

  // Keep vibrating for the full duration...
  if ((millis() - vibStart) <= vibDuration) {
    digitalWrite(vibrationPin, HIGH);
  }
  else {
     digitalWrite(vibrationPin, LOW);
  }

  lastChannel = currentChannel;
}

// Shows a number on the 7 segment display.
// It's a common anode model, so LOW is actually on.
void displayDigit(int digit) {
  switch (digit) {
  case 0:
    digitalWrite(dispA, LOW);
    digitalWrite(dispB, LOW);
    digitalWrite(dispC, LOW);
    digitalWrite(dispD, LOW);
    digitalWrite(dispE, LOW);
    digitalWrite(dispF, LOW);
    digitalWrite(dispG, HIGH);
    break;
  case 1:
    digitalWrite(dispA, HIGH);
    digitalWrite(dispB, LOW);
    digitalWrite(dispC, LOW);
    digitalWrite(dispD, HIGH);
    digitalWrite(dispE, HIGH);
    digitalWrite(dispF, HIGH);
    digitalWrite(dispG, HIGH);
    break;
  case 2:
    digitalWrite(dispA, LOW);
    digitalWrite(dispB, LOW);
    digitalWrite(dispC, HIGH);
    digitalWrite(dispD, LOW);
    digitalWrite(dispE, LOW);
    digitalWrite(dispF, HIGH);
    digitalWrite(dispG, LOW);
    break;
  case 3:
    digitalWrite(dispA, LOW);
    digitalWrite(dispB, LOW);
    digitalWrite(dispC, LOW);
    digitalWrite(dispD, LOW);
    digitalWrite(dispE, HIGH);
    digitalWrite(dispF, HIGH);
    digitalWrite(dispG, LOW);
    break;
  case 4:
    digitalWrite(dispA, HIGH);
    digitalWrite(dispB, LOW);
    digitalWrite(dispC, LOW);
    digitalWrite(dispD, HIGH);
    digitalWrite(dispE, HIGH);
    digitalWrite(dispF, LOW);
    digitalWrite(dispG, LOW);
    break;
  case 5:
    digitalWrite(dispA, LOW);
    digitalWrite(dispB, HIGH);
    digitalWrite(dispC, LOW);
    digitalWrite(dispD, LOW);
    digitalWrite(dispE, HIGH);
    digitalWrite(dispF, LOW);
    digitalWrite(dispG, LOW);
    break;
  case 6:
    digitalWrite(dispA, LOW);
    digitalWrite(dispB, HIGH);
    digitalWrite(dispC, LOW);
    digitalWrite(dispD, LOW);
    digitalWrite(dispE, LOW);
    digitalWrite(dispF, LOW);
    digitalWrite(dispG, LOW);
    break;
  case 7:
    digitalWrite(dispA, LOW);
    digitalWrite(dispB, LOW);
    digitalWrite(dispC, LOW);
    digitalWrite(dispD, HIGH);
    digitalWrite(dispE, HIGH);
    digitalWrite(dispF, HIGH);
    digitalWrite(dispG, HIGH);
    break;
  case 8:
    digitalWrite(dispA, LOW);
    digitalWrite(dispB, LOW);
    digitalWrite(dispC, LOW);
    digitalWrite(dispD, LOW);
    digitalWrite(dispE, LOW);
    digitalWrite(dispF, LOW);
    digitalWrite(dispG, LOW);
    break;
  case 9:
    digitalWrite(dispA, LOW);
    digitalWrite(dispB, LOW);
    digitalWrite(dispC, LOW);
    digitalWrite(dispD, LOW);
    digitalWrite(dispE, HIGH);
    digitalWrite(dispF, LOW);
    digitalWrite(dispG, LOW);
    break;
  default:
    digitalWrite(dispA, HIGH);
    digitalWrite(dispB, HIGH);
    digitalWrite(dispC, HIGH);
    digitalWrite(dispD, HIGH);
    digitalWrite(dispE, HIGH);
    digitalWrite(dispF, HIGH);
    digitalWrite(dispG, HIGH);
  }
}
