#include <avr/io.h>
#include <avr/interrupt.h>

// Definizione dei comandi (pulsanti)
#define READ_ROM 0x33
#define READ_SCRATCHPAD 0xBE

// Pin utilizzato per il bus 1-Wire
#define ONEWIRE_PIN PD4
#define ONEWIRE_PORT PORTD
#define ONEWIRE_DDR DDRD
#define ONEWIRE_PIN_INPUT PIND

#define F_CPU 16000000UL // Frequenza di clock della CPU
#define TIMER_INTERVAL_MS 1 // Intervallo del timer in millisecondi

// Variabili globali per i comandi
volatile uint8_t command = 0;
volatile uint16_t timer_counter = 0;

bool rom_received = false;

void setup() {
    DDRD &= ~((1<<PD2) | (1<<PD3)); // Imposta PD2 e PD3 come input (pulsanti)
    PORTD |= (1<<PD2) | (1<<PD3); // Attiva pull-up interni per PD2 e PD3

    ONEWIRE_DDR |= (1<<ONEWIRE_PIN);    // Imposta il pin PD4 come output
    ONEWIRE_PORT |= (1<<ONEWIRE_PIN);   // Attiva pull-up interno per PD4

    adc_init(); // Inizializza ADC (Analogic-to-Digital Converter)

    EIMSK |= (1<<INT0) | (1<<INT1); // Abilita INT0 e INT1
    EICRA |= (1<<ISC01) | (1<<ISC11); // Falling edge triggers

    timer1_init(); // Inizializza Timer1
    
    SREG |= (1 << 7); // Abilita gli interrupt globali
}

void loop() {
    if (command == READ_ROM) {
        command = 0;
        uint8_t rom_code[8] = {0x28, 0xFF, 0x6C, 0x50, 0x91, 0x16, 0x04, 0x04};
        uint8_t crc = crc8(rom_code, 7);
        rom_code[7] = crc;
        for (int8_t i = 7; i >= 0; i--) {
            onewire_write_byte(rom_code[i]);
        }
        rom_received = true; // Imposta il flag a true dopo aver ricevuto il comando READ_ROM
    } else if (command == READ_SCRATCHPAD && rom_received) {
        command = 0;
        volatile uint16_t scratchpad = 0;
        uint16_t adc_value = adc_read(0); // Legge il valore del potenziometro
        scratchpad = adc_value & 0x3FF; // Mantieni solo i 10 bit meno significativi

        // Spezza il valore in due parti: 2 bit più significativi e 8 bit meno significativi
        uint8_t msb = (scratchpad >> 8) & 0x03; // Ottieni i 2 bit più significativi
        uint8_t lsb = scratchpad & 0xFF; // Ottieni gli 8 bit meno significativi

        onewire_write_byte(lsb); // Invia gli 8 bit meno significativi
        onewire_write_bits(msb, 2); // Invia i 2 bit più significativi
        
    }
}

// ISR per i pulsanti
ISR(INT0_vect) {
    command = READ_ROM;
}

ISR(INT1_vect) {
    command = READ_SCRATCHPAD;
}

// ISR per il Timer1
ISR(TIMER1_COMPA_vect) {
    timer_counter++;
}

// Funzione per inizializzare l'ADC
void adc_init() {
    ADMUX = (1<<REFS0); // AVcc con condensatore su AREF
    ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1); // Abilita ADC e prescaler a 64
}

// Funzione per leggere il valore dall'ADC
uint16_t adc_read(uint8_t ch) {
    ch &= 0b00001111; // Canale deve essere tra 0 e 8
    ADMUX = (ADMUX & 0xF0) | ch; // Seleziona canale
    ADCSRA |= (1<<ADSC); // Avvia conversione
    while(ADCSRA & (1<<ADSC)); // Aspetta fine conversione
    return (ADC);
}

void timer1_init() {
    TCCR1A = 0; // Modalità normale
    TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10); // CTC mode, prescaler 64
    OCR1A = (F_CPU / 64 / 1000) * TIMER_INTERVAL_MS - 1; // Imposta il valore di confronto per 1ms
    TIMSK1 |= (1 << OCIE1A); // Abilita l'output compare interrupt per Timer1
}

void delay_ms(uint16_t ms) {
    uint16_t start_time = timer_counter;
    while ((timer_counter - start_time) < ms) {
        // Attendi per il tempo dato in input
    }
}

void onewire_write_byte(uint8_t byte) {
    for (uint8_t i = 0; i < 8; i++) {
        onewire_write_bit(byte & 0x01); // Trasmetti il bit meno significativo
        byte >>= 1; // Shifta il byte per preparare il prossimo bit
    }
}

void onewire_write_bits(uint8_t data, uint8_t num_bits) {
    for (uint8_t i = 0; i < num_bits; i++) {
        onewire_write_bit(data & 0x01); // Trasmetti il bit meno significativo
        data >>= 1; // Shifta l'input a destra per preparare il prossimo bit
    }
}

void onewire_write_bit(uint8_t bit) {
    if (bit) {
        // Scrittura di un bit '1'
        ONEWIRE_DDR |= (1<<ONEWIRE_PIN);    // Imposta il pin come output
        ONEWIRE_PORT &= ~(1<<ONEWIRE_PIN);  // Porta bassa
        delay_ms(8);                       // Attendi 6 µs
        ONEWIRE_PORT |= (1<<ONEWIRE_PIN);   // Porta alta
        delay_ms(56);                      // Attendi 64 µs
    } else {
        // Scrittura di un bit '0'
        ONEWIRE_DDR |= (1<<ONEWIRE_PIN);    // Imposta il pin come output
        ONEWIRE_PORT &= ~(1<<ONEWIRE_PIN);  // Porta bassa
        delay_ms(60);                      // Attendi 60 µs
        ONEWIRE_PORT |= (1<<ONEWIRE_PIN);   // Porta alta
        delay_ms(4);                      // Attendi 10 µs
    }
}

// Funzione per calcolare CRC8
uint8_t crc8(const uint8_t *data, uint8_t len) {
    uint8_t crc = 0;
    while (len--) {
        uint8_t inbyte = *data++;
        for (uint8_t i = 8; i; i--) {
            uint8_t mix = (crc ^ inbyte) & 0x01;
            crc >>= 1;
            if (mix) crc ^= 0x8C;
            inbyte >>= 1;
        }
    }
    return crc;
}
D0D1D2D3D4D5D6D7GNDLOGIC