/**************************************************************************
  Conversation face
  Markus Opitz 2021
  instructables.com
 **************************************************************************/


#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display1(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_SSD1306 display2(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// eyes = = = = = = = = = = = = = = = = = = = =
uint8_t
 blinkCountdown = 30, // Countdown to next blink (in frames)
  gazeCountdown  =  20, // Countdown to next eye movement
  gazeFrames     =  10; // Duration of eye movement (smaller = faster)
  int8_t
  eyeX = 64, eyeY = 29,   // Current eye position
  newX = 64, newY = 29,   // Next eye position
  dX   = 0, dY   = 0;   // Distance from prior to new position 

// mouth = = = = = = = = = = = = = = = = = = = =
int soundPin = A0;
long sum;
int grid0 = 0;
int grid1 = 36;
int grid2 = 50;
int grid3 = 64;
int grid4 = 78;
int grid5 = 92;
int grid6 = 128;

int uy0, uy1, uy2, uy3, uy4, uy5, uy6;
int ly0, ly1, ly2, ly3, ly4, ly5, ly6;

#define mic A0
int sampleFrame = 10; 
float sample_old1, sample_old2, sample_old3, sample_old4;


void setup() {
  pinMode(mic, INPUT );
  Serial.begin(9600);
  Serial.println("start");

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display1.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32 (eyes)
    //Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display2.begin(SSD1306_SWITCHCAPVCC, 0x3D)) { // Address 0x3D for 128x32 (mouth)
    //Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
display1.clearDisplay();
display2.clearDisplay();
// center of the displays: (64, 32)

}



void loop() {

mouthmove();

// eyes = = = = = = = = = = = = = = = = = = = =
       display1.clearDisplay();
       display1.fillRect(0, 0, 128, 64, WHITE);
       // Decrement blink counter.  At end, set random time for next blink.
     
        // Add a pupil (fillCircle) atop the blinky eyeball bitmap.
        // Periodically, the pupil moves to a new position...
        if(--gazeCountdown <= gazeFrames) {
          // Eyes are in motion - draw pupil at interim position
          display1.fillCircle(
          newX - (dX * gazeCountdown / gazeFrames),
          newY - (dY * gazeCountdown / gazeFrames),
          11, BLACK);
          display1.drawCircle(
          newX - (dX * gazeCountdown / gazeFrames),
          newY - (dY * gazeCountdown / gazeFrames),
          34, BLACK);
          display1.drawCircle(  // 2nd line to make it more visible = = = 
          newX - (dX * gazeCountdown / gazeFrames),
          newY - (dY * gazeCountdown / gazeFrames),
          33, BLACK);
          
          display1.fillRect(0, 0, 128, 10, WHITE);
      
          if(gazeCountdown == 0) {    // Last frame?
            eyeX = newX; 
            eyeY = newY; // Yes.  What's new is old, then...
            do { // Pick random positions until one is within the eye circle
              newX = random(35,120); 
              newY = random(16,36);
              dX   = newX-30;  
              dY   = newY-30;
            } 
            while((dX * dX + dY * dY) >= 3900);      // Thank you Pythagoras!
            dX            = newX - eyeX;             // Horizontal distance to move
            dY            = newY - eyeY;             // Vertical distance to move
            gazeFrames    = random(8, 55);           // Duration of eye movement(smaller = faster)
            gazeCountdown = random(gazeFrames, 30);  // Count to end of next movement
          }
          display1.fillRect(0, 0, 128, 10, WHITE);
        }
        else {
          // Not in motion yet -- draw pupil at current static position
          display1.fillCircle(eyeX, eyeY, 11, BLACK);
          display1.drawCircle(eyeX, eyeY, 34, BLACK);
          display1.drawCircle(eyeX, eyeY, 33, BLACK);  // 2nd line
          display1.fillRect(0, 0, 128, 10, WHITE);
        }
      // draw display
      display1.display();
      
      if(--blinkCountdown == 0) {
          blinkCountdown = random(300, 7000);
          blinklid();
      }
}




void blinkeye(void){
  //display1.clearDisplay();
  for (int i= 0; i <51; i= i+10){
    display1.fillRect(0, 0, 128, i, WHITE);
    display1.display();
  }
}


void blinklid(void){
  //display1.clearDisplay();
  for (int i= 0; i <61; i= i+15){
    int r = i-90;
    display1.fillCircle(64, r, 95, WHITE);
    display1.display();
  }
  for (int i= 60; i >0; i= i-15){
    int r = i-90;
    display1.fillCircle(64, r, 95, WHITE);
    display1.display();
  }
}
  

void draweye(void){
  display1.clearDisplay();
  display1.drawCircle(eyeX, eyeY, 34, BLACK);
  display1.fillCircle(eyeX, eyeY, 11, BLACK);
  display1.fillRect(0, 0, 128, 10, WHITE);
  display1.display();
}


void moveeye(void){
 for (int i= 30; i < 98; i++) {
  display1.clearDisplay();
  eyeX = i;
  display1.drawCircle(eyeX, eyeY, 34, BLACK);
  display1.fillCircle(eyeX, eyeY, 11, BLACK);
  display1.fillRect(0, 0, 128, 10, WHITE);
  display1.display();
  }
  for (int i= 98; i > 30; i--) {
  display1.clearDisplay();
  eyeX = i;
  display1.drawCircle(eyeX, eyeY, 34, BLACK);
  display1.fillCircle(eyeX, eyeY, 11, BLACK);
  display1.fillRect(0, 0, 128, 10, WHITE);
  display1.display();
 }
}


void mouthmove(void){

// variables for mouth coordinates
uy0 = 32;
uy1 = 34;
uy2 = 32;
uy3 = 34;
uy4 = uy2;
uy5 = uy1;
uy6 = uy0;

ly0 = 33;
ly1 = 36;
ly3 = 39;
ly5 = ly1;
ly6 = ly0;
int i;

// read microphone * * * * * * * * * * * * * * *
  float sample = analogRead(mic);
  sample = sample / 20; 

  if(sample<90) sample=(sample+(90-sample));  //eliminates all lower than 90

  sample = ((sample_old1 + sample_old2 + sample_old3 + sample_old4 + (sample*1.1))/5); // smoothing the wave

  Serial.println(sample);
  sample_old4 = sample_old3; // store old values
  sample_old3 = sample_old2;
  sample_old2 = sample_old1;
  sample_old1 = sample;
  delay(10);
    
    display2.clearDisplay();


  // if face should be silent when you talk * * * * * * * * * *
    if (sample>= 100.45){    // change sound tolerance here
        i = 0;
    }
    if (sample< 100.45){     // change sound tolerance here
        i = random(0, 15);
    }
    //   * * * * * * * * * * * * * * * * * * * * * * * * * 

/*

  // if face should talk when you talk = = = = = = = = = 
    if (sample>= 100.6){     // change sound tolerance here
        i = random(4, 15);
    }
    if (sample< 100.6){      // change sound tolerance here
        i = 0;
    }
    //  = = = = = = = = = = = = = = = = = = = = = = = = = 
*/  


if (i>=8) {      //narrow the corners of the mouth
  grid0 = 7;
  grid6 = 121;
}
if (i>=11) {
  grid0 = 11;
  grid6 = 117;
}
display2.clearDisplay();

display2.fillRect(0, 0, 128, 64, WHITE);   //white background
//upper lip
display2.drawLine(grid0, uy0, grid1, uy1-i, BLACK);
display2.drawLine(grid1, uy1-i, grid2, uy2-i, BLACK);
display2.drawLine(grid2, uy2-i, grid3, uy3-i, BLACK);
display2.drawLine(grid3, uy3-i, grid4, uy4-i, BLACK);
display2.drawLine(grid4, uy4-i, grid5, uy5-i, BLACK);
display2.drawLine(grid5, uy5-i, grid6, uy6, BLACK);

//upper lip 2nd line
display2.drawLine(grid0, uy0-1, grid1, uy1-i-1, BLACK);
display2.drawLine(grid1, uy1-i-1, grid2, uy2-i-1, BLACK);
display2.drawLine(grid2, uy2-i-1, grid3, uy3-i-1, BLACK);
display2.drawLine(grid3, uy3-i-1, grid4, uy4-i-1, BLACK);
display2.drawLine(grid4, uy4-i-1, grid5, uy5-i-1, BLACK);
display2.drawLine(grid5, uy5-i-1, grid6, uy6-1, BLACK);

//lower lip
display2.drawLine(grid0, ly0, grid1, ly1+i, BLACK);
display2.drawLine(grid1, ly1+i, grid3, ly3+i, BLACK);
display2.drawLine(grid3, ly3+i, grid5, ly5+i, BLACK);
display2.drawLine(grid5, ly5+i, grid6, ly6, BLACK);

//lower lip 2nd line
display2.drawLine(grid0, ly0+1, grid1, ly1+i+1, BLACK);
display2.drawLine(grid1, ly1+i+1, grid3, ly3+i+1, BLACK);
display2.drawLine(grid3, ly3+i+1, grid5, ly5+i+1, BLACK);
display2.drawLine(grid5, ly5+i+1, grid6, ly6+1, BLACK);
delay(2);

grid0 = 0;
grid6 = 128;
display2.display();
}
