/**
 * Esempio 3: Lampeggio 3 LED - INTERRUPT HARDWARE (TIMER)
 * https://wokwi.com/projects/445615631754931201
 * 
 * Questo esempio usa Timer Interrupt per temporizzare in modo PRECISO.
 * Il Timer Hardware genera interrupt a intervalli regolari.
 * La CPU può andare in SLEEP tra gli interrupt (risparmio energia).
 * 
 * Richiede libreria: TimerOne
 * (Installare da Library Manager: Sketch -> Include Library -> Manage Libraries -> cerca "TimerOne")
 * 
 * Hardware:
 * - LED1 connesso al pin 8 (lampeggia ogni 1000ms)
 * - LED2 connesso al pin 9 (lampeggia ogni 500ms)
 * - LED3 connesso al pin 10 (lampeggia ogni 200ms)
 * - Resistenze da 220Ω in serie ad ogni LED
 * 
 * NOTA: Questo esempio è specifico per Arduino Uno/Mega (ATmega328P/2560)
 * Per altri board, adattare il timer usato.
 */
#include <TimerOne.h>  // Timer1 (16-bit)
// Pin dei LED
const int LED1_PIN = 8;
const int LED2_PIN = 9;
const int LED3_PIN = 10;
// Intervalli di lampeggio (millisecondi)
const unsigned long LED1_INTERVAL = 1000;
const unsigned long LED2_INTERVAL = 500;
const unsigned long LED3_INTERVAL = 200;
// Contatori per divisione frequenza
volatile unsigned long led1_counter = 0;
volatile unsigned long led2_counter = 0;
volatile unsigned long led3_counter = 0;
// Stati LED (volatile perché modificati in ISR)
volatile int led1_state = LOW;
volatile int led2_state = LOW;
volatile int led3_state = LOW;
// Periodo base dell'interrupt (microscondi)
// Usiamo 1ms (1000us) come base comune
const unsigned long ISR_PERIOD_US = 1000;  // 1ms = 1000us
// Calcola quanti tick servono per ogni LED
const unsigned long LED1_TICKS = LED1_INTERVAL;      // 1000ms / 1ms = 1000 tick
const unsigned long LED2_TICKS = LED2_INTERVAL;      // 500ms / 1ms = 500 tick
const unsigned long LED3_TICKS = LED3_INTERVAL;      // 200ms / 1ms = 200 tick
// Statistiche (volatile per accesso da ISR)
volatile unsigned long led1_toggles = 0;
volatile unsigned long led2_toggles = 0;
volatile unsigned long led3_toggles = 0;
volatile unsigned long isr_count = 0;
void setup() {
  // Inizializza i pin come output
  pinMode(LED1_PIN, OUTPUT);
  pinMode(LED2_PIN, OUTPUT);
  pinMode(LED3_PIN, OUTPUT);
  
  // Inizializza la comunicazione seriale
  Serial.begin(9600);
  Serial.println("=== Esempio 3: LED con Timer Interrupt ===");
  Serial.println("Timer Hardware controlla i LED!");
  Serial.println("MASSIMA PRECISIONE - CPU può dormire.\n");
  
  // Mostra configurazione
  Serial.print("ISR period: ");
  Serial.print(ISR_PERIOD_US);
  Serial.println(" us (1ms)");
  Serial.print("LED1 toggle ogni ");
  Serial.print(LED1_TICKS);
  Serial.println(" tick");
  Serial.print("LED2 toggle ogni ");
  Serial.print(LED2_TICKS);
  Serial.println(" tick");
  Serial.print("LED3 toggle ogni ");
  Serial.print(LED3_TICKS);
  Serial.println(" tick\n");
  
  // Inizializza Timer1
  Timer1.initialize(ISR_PERIOD_US);  // 1ms
  Timer1.attachInterrupt(timerISR);  // Associa ISR
  
  Serial.println("Timer interrupt attivo!\n");
}
/**
 * ISR (Interrupt Service Routine)
 * 
 * Chiamata automaticamente ogni 1ms dal Timer Hardware.
 * ATTENZIONE: Deve essere VELOCE! No delay(), no Serial.print() (lento).
 * Usare solo operazioni atomiche e veloci.
 */
void timerISR() {
  isr_count++;
  
  // ========== GESTIONE LED1 ==========
  led1_counter++;
  if (led1_counter >= LED1_TICKS) {
    led1_counter = 0;
    led1_state = !led1_state;
    digitalWrite(LED1_PIN, led1_state);
    led1_toggles++;
  }
  
  // ========== GESTIONE LED2 ==========
  led2_counter++;
  if (led2_counter >= LED2_TICKS) {
    led2_counter = 0;
    led2_state = !led2_state;
    digitalWrite(LED2_PIN, led2_state);
    led2_toggles++;
  }
  
  // ========== GESTIONE LED3 ==========
  led3_counter++;
  if (led3_counter >= LED3_TICKS) {
    led3_counter = 0;
    led3_state = !led3_state;
    digitalWrite(LED3_PIN, led3_state);
    led3_toggles++;
  }
}
void loop() {
  // Il loop() è LIBERO di fare altro!
  // I LED sono gestiti completamente dall'interrupt hardware.
  
  // Mostra statistiche ogni 10 secondi
  static unsigned long lastStatsTime = 0;
  unsigned long currentMillis = millis();
  
  if (currentMillis - lastStatsTime >= 10000) {
    lastStatsTime = currentMillis;
    
    // Disabilita interrupt durante lettura (atomic read)
    noInterrupts();
    unsigned long isr_copy = isr_count;
    unsigned long led1_copy = led1_toggles;
    unsigned long led2_copy = led2_toggles;
    unsigned long led3_copy = led3_toggles;
    interrupts();
    
    Serial.println("\n=== STATISTICHE (10s) ===");
    Serial.print("ISR chiamate: ");
    Serial.println(isr_copy);
    Serial.print("LED1 toggle: ");
    Serial.println(led1_copy);
    Serial.print("LED2 toggle: ");
    Serial.println(led2_copy);
    Serial.print("LED3 toggle: ");
    Serial.println(led3_copy);
    Serial.println("Loop: CPU LIBERA per altri task!\n");
  }
  
  // Simula altro lavoro (o vai in sleep per risparmiare energia)
  simulaAltroLavoro();
  
  // OPZIONALE: Abilita sleep mode per risparmio energetico
  // #include <avr/sleep.h>
  // set_sleep_mode(SLEEP_MODE_IDLE);
  // sleep_mode();  // CPU dorme fino al prossimo interrupt
}
void simulaAltroLavoro() {
  // La CPU può fare lavoro complesso qui.
  // I LED continuano a lampeggiare PERFETTAMENTE grazie all'interrupt!
  
  static unsigned long counter = 0;
  counter++;
  
  // Ogni 500.000 iterazioni, simula operazione lenta
  if (counter % 500000 == 0) {
    Serial.println("[INFO] Loop sta facendo lavoro pesante...");
    
    // Simula operazione che richiede tempo
    // (I LED continuano a lampeggiare SENZA PROBLEMI!)
    unsigned long sum = 0;
    for (int i = 0; i < 10000; i++) {
      sum += i;
    }
    
    Serial.print("[INFO] Calcolo completato: ");
    Serial.println(sum);
  }
  
  // Piccolo delay per non saturare seriale
  delay(10);  // I LED NON sono influenzati da questo delay!
}
/**
 * VANTAGGI INTERRUPT:
 * 
 * 1. PRECISIONE MASSIMA: Timer hardware è preciso al microsecondo
 * 2. NON BLOCCANTE: ISR esegue indipendentemente dal loop()
 * 3. RISPARMIO ENERGIA: CPU può andare in sleep tra interrupt
 * 4. DETERMINISMO: Timing garantito anche con loop() lento
 * 5. PRIORITÀ: Interrupt ha priorità su codice normale
 * 
 * SVANTAGGI:
 * 
 * 1. COMPLESSITÀ: Richiede comprensione interrupt e ISR
 * 2. DEBUGGING: Difficile debug codice ISR
 * 3. RISORSE: Usa timer hardware (risorsa limitata)
 * 4. VINCOLI ISR: ISR deve essere veloce, no blocking calls
 * 5. RACE CONDITION: Serve sincronizzazione variabili condivise
 * 
 * QUANDO USARLO:
 * - Quando serve timing PRECISO
 * - Per applicazioni real-time hard
 * - Quando serve risparmio energetico (sleep mode)
 * - Per sistemi embedded professionali
 * - Quando il polling non basta
 * 
 * BEST PRACTICES ISR:
 * 
 * 1. ISR deve essere VELOCE (< 50us idealmente)
 * 2. NO delay() in ISR
 * 3. NO Serial.print() in ISR (troppo lento)
 * 4. Variabili condivise DEVONO essere volatile
 * 5. Usa noInterrupts()/interrupts() per accesso atomico
 * 6. Minimizza operazioni in ISR (solo essenziale)
 * 
 * TIMER DISPONIBILI (Arduino Uno):
 * - Timer0: 8-bit, usato da millis()/delay() - NON TOCCARE!
 * - Timer1: 16-bit, libero (usato qui con TimerOne library)
 * - Timer2: 8-bit, libero
 * 
 * ALTERNATIVE:
 * - MsTimer2 library (usa Timer2)
 * - FlexiTimer2 library
 * - Direct register manipulation (avanzato)
 * 
 * NOTA ARCHITETTURALE:
 * Questo esempio dimostra programmazione INTERRUPT-DRIVEN,
 * paradigma fondamentale nei sistemi embedded real-time.
 * La CPU risponde a EVENTI (interrupt) invece di fare polling continuo.
 */
/**
 * CONFRONTO TRE APPROCCI:
 * 
 * | Aspetto          | Sequenziale | Polling    | Interrupt    |
 * |------------------|-------------|------------|--------------|
 * | Precisione       | Bassa       | Media      | Alta         |
 * | CPU Libera       | NO          | Parziale   | SI           |
 * | Complessità      | Bassa       | Media      | Alta         |
 * | Risparmio Energia| NO          | NO         | SI (sleep)   |
 * | Scalabilità      | Pessima     | Buona      | Ottima       |
 * | Debug            | Facile      | Medio      | Difficile    |
 * | Uso Risorse      | Minimo      | Minimo     | Timer HW     |
 * 
 * SCELTA APPROCCIO:
 * - Sequenziale: Solo per sketch semplicissimi
 * - Polling: Per la maggior parte delle applicazioni hobbistiche
 * - Interrupt: Per applicazioni professionali, real-time, low-power
 */