/**************************************************************************

Timer full info
per far funzionare il semaforo occorre tener premuto il pulsante

•	Timer 0 - 8 bit e gestisce delay(), millis(), micros(), le porte PWM 5 e 6
•	Timer 1 – 16 bit (65536) e gestisce le librerie servo e le porte PWM 9 e 10
•	Timer 2 – 8 bit e gestisce le funzioni tone(), le porte PWM 3 e 11,  
            il pin 11 è usato anche per il MOSI dell’interfaccia SPI
Manually setting up a timer will stop analogWrite() from working.


Trafic light is enabled if the button stay pushed

  https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts
  FreeMono12pt7b.h		  FreeSansBoldOblique12pt7b.h FreeMono18pt7b.h		        FreeSansBoldOblique18pt7b.h
  FreeMono24pt7b.h		  FreeSansBoldOblique24pt7b.h FreeMono9pt7b.h			        FreeSansBoldOblique9pt7b.h
  FreeMonoBold12pt7b.h		  FreeSansOblique12pt7b.h FreeMonoBold18pt7b.h		    FreeSansOblique18pt7b.h
  FreeMonoBold24pt7b.h		  FreeSansOblique24pt7b.h FreeMonoBold9pt7b.h		      FreeSansOblique9pt7b.h
  FreeMonoBoldOblique12pt7b.h	  FreeSerif12pt7b.h   FreeMonoBoldOblique18pt7b.h	FreeSerif18pt7b.h
  FreeMonoBoldOblique24pt7b.h	   FreeSerif24pt7b.h  FreeMonoBoldOblique9pt7b.h	FreeSerif9pt7b.h
  FreeMonoOblique12pt7b.h		  FreeSerifBold12pt7b.h FreeMonoOblique18pt7b.h		  FreeSerifBold18pt7b.h
  FreeMonoOblique24pt7b.h		  FreeSerifBold24pt7b.h FreeMonoOblique9pt7b.h		  FreeSerifBold9pt7b.h
  FreeSans12pt7b.h		  FreeSerifBoldItalic12pt7b.h FreeSans18pt7b.h		        FreeSerifBoldItalic18pt7b.h
  FreeSans24pt7b.h		  FreeSerifBoldItalic24pt7b.h FreeSans9pt7b.h			        FreeSerifBoldItalic9pt7b.h
  FreeSansBold12pt7b.h		  FreeSerifItalic12pt7b.h FreeSansBold18pt7b.h		    FreeSerifItalic18pt7b.h
  FreeSansBold24pt7b.h		  FreeSerifItalic24pt7b.h FreeSansBold9pt7b.h		      FreeSerifItalic9pt7b.h
  
 **************************************************************************/

//#include <SPI.h> // protocollo SPI alternativo al i2c
#include <Wire.h> // protocollo i2c per settare ad esempio la velocità
#include <Adafruit_GFX.h> // font
#include <Adafruit_SSD1306.h>
#include <stdio.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 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

int  start_h=15, start_m=04, clk=53;
char outstr[30];

  #define led1 	12 // led 1 
  #define led2 	11 // led 2 
  #define led3  10
  #define button 3 // pulsante sulla porta 3
  
  //volatile bool buttonState;
  volatile bool state=false;
  volatile long counter=0L,start=0L,stop=0L;
  
  void rising_count() {
    // interrupt
    stop=millis();   
    attachInterrupt(digitalPinToInterrupt(button),count,FALLING);  
    TIMSK1 &=0b11111000; // disabilita i tre interrupt
  }
  
  void count() {
    // interrupt
    start=millis();   
    attachInterrupt(digitalPinToInterrupt(button),rising_count,RISING);
    counter++;
    /*
    TIMSK1 |= (1 << OCIE1B); // enable Timer1 low value interrupt  TIMSK1  |=   B00000100;
    TIMSK1 |= (1 << OCIE1A); // enable Timer1 high value interrupt TIMSK1  |=   B00000010;
    
    TIMSK1 |= (1 << TOIE1); // enable Timer1 overflow interrupt    TIMSK1  |=   B00000001;
                            TCNT1=N; il restar value deve essere messo sempre nel 
                            OVF interrupt function
    */
    TIMSK1 |=0b00000111; // abilita i tre interrupt
  }
  
void setup() {
  Serial.begin(9600);
  //Wire.begin();
  //Wire.setClock(400000L); // velocità dell' i2c

  // Display setup
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.clearDisplay();              // cancella il logo iniziale di Adafruit
  
  cli(); // disable global interrupts = noInterrupts()

  pinMode(led1,OUTPUT);
  pinMode(led2,OUTPUT);
  pinMode(led3,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(button,INPUT_PULLUP);
  
  
  // START
  digitalWrite(led1, HIGH);
  digitalWrite(led2, HIGH);
  digitalWrite(led3, HIGH);
  delay(1600);
  digitalWrite(led2, LOW);
  digitalWrite(led3, LOW);
  // timer setup

  // TCCR mode setup

  // reset
  TCCR1A = 0;
  TCCR1B = 0;

/* Mode TCCR1A
   TCCR1A |= (1 << WGM11) | (1 << WGM10);
   pwm set
   TCCR1A |= (1 << COM1B1)| (1 << COM1B0) ; //PIN10
   TCCR1A |= (1 << COM1A1)| (1 << COM1A0) ; //PIN9
*/
TCCR1A |= (1 << COM1A0); //| (1 << COM1A0) ; //PIN9

/* Mode TCCR1B
   
   CTC Consente di generare solo forme d'onda quadre (duty cycle 50%)
   TCCR1B |= (1 << WGM13); // | (1 << WGM12);
*/

/* prescaler set
  1-8-64-256-1024
  •	Timer 0 CS02 CS01 CS00
  •	Timer 1 CS12 CS11 CS10
  •	Timer 2 CS22 CS21 CS20
  //(1 << CS10); 1
  //(1 << CS11); 8
  //(1 << CS11)|(1 << CS10); 64
  //(1 << CS12); 256
  //(1 << CS12)|(1 << CS10); 1024 // Solo Timer1
*/
  TCCR1B |= (1 << CS12)|(1 << CS10); //1024 solo Timer1
  
/* compare level set
  compare match register = [ 16,000,000Hz/ (prescaler * desired interrupt frequency) ] - 1
  Timer1 max 65535 else 255
  periodo massimo 4 secondi e 190 centesimi

*/
  OCR1A = 55000; 
  OCR1B = 50000; 

  // interrupt set
    /* enable is in pushbutton interrupt function "Push to RUN"
    TIMSK1 |= (1 << OCIE1B); // enable Timer1 low value interrupt  TIMSK1  |=   B00000100;
    TIMSK1 |= (1 << OCIE1A); // enable Timer1 high value interrupt TIMSK1  |=   B00000010;
    
    TIMSK1 |= (1 << TOIE1); // enable Timer1 overflow interrupt    TIMSK1  |=   B00000001;
                            TCNT1=N; // Timer1 restar value da inserire in OVF interrupt function
    */
  
   /* input capture 1 (ICP1) interrupt : solo con Timer1 PIN8
      occorre settare 
          prescaler           TCCR1B |=0b00000xxx;
          noise cancel        TCCR1B |=0b10000000;
          rising / falling    TCCR1B |=0b01000000;
      abilitazione interrupt ISR(TIMERx_CAPT_vect) 
          TIMSK1 |= 0b00100000; 
          TCNT1=0;
      Viene salvato il valore corrente del Timer in ICR1 ogni volta che un evento 
      esterno (rising o falling) interessa il PIN8 di Arduino1.
      il Timer lancia la routine di interrupt ISR(TIMERx_CAPT_vect) durante la quale
      va letto il valore di ICR1

      https://www.youtube.com/watch?v=N5VQZTDNcj0 
      Allegato II : Input Capture Mode  
   */
 
  sei(); // enable global interrupts = interrupts()
  attachInterrupt(digitalPinToInterrupt(button),count,FALLING); 
  TCNT1=0; // Timer1 restar value va reinpostato con OVF

  display.setTextSize(2);              // 2:1 pixel scale
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(36, 22);           // top-left corner
  display.cp437(true);                 // Use full 256 char 'Code Page 437' font
  display.write("START");
  display.display();
  delay(300);
  display.clearDisplay();
  
  // READY
  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setCursor(6,0);
  display.print("COUNTER");
  display.setCursor(80, 24);
  display.setTextColor(SSD1306_WHITE);
  display.print("00000");
  reset_watch();      
}

ISR(TIMER1_COMPB_vect) {
    state =2;
    //digitalWrite(led1,LOW); 
    //digitalWrite(led2,HIGH); 
    //digitalWrite(led3,LOW); 
    PORTB |= B00001000; 
    PORTB &= B11101011; 
}

ISR(TIMER1_COMPA_vect) {
    state =3;
    //digitalWrite(led1,LOW); 
    //digitalWrite(led2,LOW);
    //digitalWrite(led3,HIGH);  
    PORTB |= B00000100; 
    PORTB &= B11100111;      
}

ISR(TIMER1_OVF_vect) {
    state =1;
    //digitalWrite(led1,HIGH); 
    //digitalWrite(led2,LOW);
    //digitalWrite(led3,LOW);
    PORTB |= B00010000; 
    PORTB &= B11110011;     
    /* assegnando a TCNT1 un valore maggiore di zero
       DOPO IL RESET di overflow NON RIPARTE DA ZERO
       ma dal valore assegnato a TCNT1 */
    TCNT1=40000;    
  }


void reset_watch (void){
  // simula inizializzazione del RTC
  start_h=17;
  start_m=59;
  clk=53;
}

void clock(void)
{ 
  
  // cancella vecchio orario
  display.setCursor(80, 0);
  display.setTextColor(SSD1306_BLACK);
  sprintf(outstr,"%02d:%02d:%02d",start_h,start_m,clk);
  display.print(outstr);
  
  // simula RTC
  if(clk==59) {
    
    if(start_m==59){
      if(start_h==23) start_h=0;
      else start_h++;
      start_m=-1;
    }
    start_m++;
    clk=-1;  
  }
  
  clk++;
    
  // stampa orario aggiornato
  display.setCursor(80, 0);
  display.setTextColor(SSD1306_WHITE);
  sprintf(outstr,"%02d:%02d:%02d",start_h,start_m,clk);
  display.print(outstr);

  display.display();

}

void loop() {
  /*
  static long now,last,old_counter=0;
  static int k=0;
  
  if((stop-start)>1200)
  { //RESET Counter
    stop=0L;
    start=0L;
    counter=0L;
  }
  if(counter!=old_counter)
  { // print counter 
    display.setCursor(80, 24);
    display.setTextColor(SSD1306_BLACK);
    sprintf(outstr,"%05d",old_counter);
    display.print(outstr);

    display.setCursor(80, 24);
    display.setTextColor(SSD1306_WHITE);
    sprintf(outstr,"%05d",counter);
    display.print(outstr);

    old_counter=counter;
     
    display.display();
  }
  // aggiorna orario sul display ogni 1000ms
  // meglio farlo con un timer
  now=millis(); 
  if( (now-last) > 1000L) 
  { clock(); 
    last=now; 
    k++;
    
    // funzione di debug
    if(k==900)
    { // ogni 15 minuti resetta l'orologio
      k=0;
      reset_watch();
} } */}