Fab Academy 2018 - Thierry Dassé
          /* 
           * e-bin firmware
           * Thierry Dassé - Fabacademy 2018
           * cc-by-nc-sa
 
          */
 
          
          #include "TinyWireM.h" // I2C library for ATtiny 25/45/85 24/44/84
          #include "LiquidCrystal_attiny.h" // LCD display library for ATtiny 25/45/85 24/44/84
          
          LiquidCrystal_I2C lcd(0x3F,16,2);  // set the LCD address to 0x30 for my LCD display
          
          #define encoderPinA 9 ///encoder pins
          #define encoderPinB 10
          #define pushPin 8
          
          #define potNumber 5 // number of potentiometer
          
          typedef struct param { //potentiometer parameters
            unsigned int pin;  //pin number
            unsigned int zero; //value when bin is empty
            unsigned int one; //value when bin contains 500g
            int level; //current level
          } potParam;
          
          unsigned short encoder = 0b000111; // encoder 6 bits state
          potParam potentiometer[potNumber] = {{0,0,0,0},{1,0,0,0},{2,0,0,0},{3,0,0,0},{5,0,0,0}}; //potentiometers pins number
          unsigned long date;
          bool lcdOn;
          int val;
          char strval[8];
          int menu = 0;
          int weight;
          
          void printAt(char *s,int col,int row) {
            // print a string at col and row position
            int i = 0;
            while (s[i] != 0) {
              lcd.setCursor(col+i,row);
              lcd.print(s[i]);
              i++;
            }
          }
          
          void print_weight() {
            // print total weight of all bins on lcd display first line
            int w = 0;
            for (int i=0;i<potNumber;i++) {
              w += map(potentiometer[i].level,potentiometer[i].zero,potentiometer[i].one,0,500);
            }
            if (w) {
              itoa(w,strval,10);
              printAt(strval,5,0);
            } else {
              printAt("e-bin",5,0);
            }
          }
          
          void print_bin(int i) {
            // print bin weight on lcd display second line
            printAt("bin",1,1);
            itoa(i+1,strval,10);
            printAt(strval,5,1);
            printAt(":",7,1);
            itoa(map(potentiometer[i].level,potentiometer[i].zero,potentiometer[i].one,0,500),strval,10);
            printAt(strval,9,1);
          }
          
          void setup() { 
            // setup function, executed one time at the begining
  
            pinMode(encoderPinA, INPUT_PULLUP); //encoder pins are in input mode using pull up resistor
            pinMode(encoderPinB, INPUT_PULLUP);
            pinMode(pushPin, INPUT_PULLUP);
          
            date = millis();
            delay(1000); // wait lcd display ready before sending commands
            TinyWireM.begin();
            lcd.init();
            lcd.backlight();
            printAt("e-bin",6,0);
            delay(1000);
            lcd.clear();
            lcdOn = true;
          }
          
          void loop() {
            // main infinite loop
  
            unsigned short readPin = (unsigned short) digitalRead(encoderPinA); //read encoder pins
            encoder = (encoder & 0b110111) | (readPin << 3); //write encoderA state in the fourth digit
          
            readPin = (unsigned short) digitalRead(encoderPinB);
            encoder = (encoder & 0b101111) | (readPin << 4); //write encoderB state in the fifth digit
          
            readPin = (unsigned short) digitalRead(pushPin);
            encoder = (encoder & 0b011111) | (readPin << 5); //write push state in the sixth digit
          
            if (encoder == 0b110111) { //turn clockwise
              date = millis();
              if (lcdOn) {
                menu = (menu + 1 ) % (1 + potNumber);
                lcd.clear();
                if (menu) {
                  if (potentiometer[menu-1].zero != potentiometer[menu-1].one) {
                    print_bin(menu-1);
                  } else {
                    printAt("bin",5,0);
                    itoa(menu,strval,10);
                    printAt(strval,9,0);
                    printAt("click to setup",1,1);
                  }
                } else {
                  print_weight();
                }
              }
            } else if (encoder == 0b101111) { //turn counterclockwise
              date = millis();
              if (lcdOn) {
                menu = (menu - 1 ) % (1 + potNumber);
                lcd.clear();
                if (menu) {
                  if (potentiometer[menu-1].zero != potentiometer[menu-1].one) {
                    print_bin(menu-1);
                  } else {
                    printAt("bin",5,0);
                    itoa(menu,strval,10);
                    printAt(strval,9,0);
                    printAt("click to setup",1,1);
                  }
                } else {
                  print_weight();
                }
              }
            } else if (encoder == 0b011111) { //push on encoder
              date = millis();
              if (lcdOn && menu) {
                lcd.clear();
                printAt("bin",5,0);
                itoa(menu,strval,10);
                printAt(strval,9,0);
                printAt("click when empty",0,1);
                do {
                  encoder = (encoder >> 3) | 0b011000;
                  readPin = (unsigned short) digitalRead(pushPin);
                  encoder = (encoder & 0b011111) | (readPin << 5);
                } while (encoder != 0b011111);
                potentiometer[menu-1].zero  = analogRead(potentiometer[menu-1].pin);
                printAt("click with 500g",0,1);
                do {
                  encoder = (encoder >> 3) | 0b011000;
                  readPin = (unsigned short) digitalRead(pushPin);
                  encoder = (encoder & 0b011111) | (readPin << 5);
                } while (encoder != 0b011111);
                potentiometer[menu-1].one  = analogRead(potentiometer[menu-1].pin);
                lcd.clear();
                if (potentiometer[menu-1].zero != potentiometer[menu-1].one) {
                  printAt("done",0,1);
                } else {
                  printAt("failed",0,1);
                }
              }
            } 
            encoder = encoder >> 3; // write encoder current values to encoder previous values
            
            bool change = false;
            for (int i= 0;i<potNumber;i++) {
              if (potentiometer[i].zero != potentiometer[i].one) {
                  val = analogRead(potentiometer[i].pin);
                  if (abs(val-potentiometer[i].level) > 2) {
                    potentiometer[i].level = val;
                    change = true;
                    if (!lcdOn) {
                      lcdOn = true;
                      lcd.setBacklight(255);
                    }
                    lcd.clear();
                    print_bin(i);
                    date = millis();
                  }
              }
            }
            if (change) {
              print_weight();
            }
          
            if ((millis()-date > 5000) && lcdOn)  { //if nothing happen during last 5s, turn display off
              lcdOn = false;
              lcd.setBacklight(0);
            } else if ((millis()-date < 5000) && !lcdOn)  { 
              lcdOn = true;
              lcd.setBacklight(255);
            }  
          }