//ATmega2560
#include <avr/io.h> //definições do componente especificado
#include <util/delay.h> //biblioteca para o uso das rotinas de _delay_ms e _delay_us(), biblioteca de atraso incluída no AVR Libc
#include <avr/pgmspace.h> //biblioteca para o uso dos DDRAM e DDROM

//===============================================//
//               DEFINICAO                       //
//===============================================//

//Atmega 2560 clk
#define F_CPU 16000000UL  // Frequência do clock do microcontrolador (16 MHz para Arduino Mega)

//POTENCIOMENTRO
#define knob_POTENCIOMETRO A0 //porta analogica

//LCD -> CONTROLE e DADOS
#define CONTR_LCD PORTA //PORT com os pinos de controle do LCD (pino R/W em 0).
#define RS PA1 //pino para informar se o dado é uma instrução ou caractere
#define RW PA3 //pino de habilitação do LCD (RW)
#define E PA5 //pino de habilitação do LCD (enable)
#define DADOS_LCD PORTC //4 bits de dados do LCD no PORTa
#define nibble_dados 1 
#define LCD_ANODO PH3

//LED VERDE
#define LED_VERDE_PIN PJ1

//BUZZER
#define NOTAS_POR_OITAVA 12 // Definindo a quantidade de notas em uma oitava
#define NUM_OITAVAS 9// Número de oitavas
#define BUZZER_PIN PE3

//DHT
#define DHT_PIN PB7      // Pino ao qual o sensor DHT22 está conectado
#define DHT_TYPE DHT22   // Tipo do sensor DHT (DHT11 ou DHT22)

//Botoes -> Modos
#define botao_TEMPERATURA_INST PD0
#define botao_TEMPERATURA_MED PD1
#define botao_TEMPERATURA_CLR PD2
#define botao_TEMPERATURA_minMAX PD3
#define botao_On_Off PE4

//eeprom
#define temp_max 10 // Número máximo de temperaturas a serem consideradas

//sinal de habilitação para o LCD
#define pulso_enable() _delay_us(1); set_bit(CONTR_LCD, E); _delay_us(1); clr_bit(CONTR_LCD, E); _delay_us(45);

//Definições de macros para o trabalho com bits
#define set_bit(y,bit) (y|=(1<<bit)) //coloca em 1 o bit x da variável Y
#define clr_bit(y,bit) (y&=~(1<<bit)) //coloca em 0 o bit x da variável Y
#define cpl_bit(y,bit) (y^=(1<<bit)) //troca o estado lógico do bit x da variável Y
#define tst_bit(y,bit) (y&(1<<bit)) //retorna 0 ou 1 conforme leitura do bit 
//------------------------------------------------------------------------------------

//===============================================//
//               NUMERACAO                       //
//===============================================//

//dht
enum tipo_sensor{
  DHT22,
  DHT11
};

//eeprom
enum tipo_dados{
  Temperatura,
  Umidade
};

//buzzer
enum tom{
  NOTA_DO = 0 ,
  NOTA_DO_SUSTENIDO = 1,
  NOTA_RE = 2,
  NOTA_RE_SUSTENIDO = 3,
  NOTA_MI = 4,
  NOTA_FA = 5,
  NOTA_FA_SUSTENIDO = 6,
  NOTA_SOL = 7,
  NOTA_SOL_SUSTENIDO = 8,
  NOTA_LA = 9,
  NOTA_LA_SUSTENIDO = 10,
  NOTA_SI = 11
};

//buzzer
enum faixa{
  ligando,
  beep,
  alarme,
  desligando
};

//LCD
enum modo{
  modo_PADRAO, modo_TEMPERATURA_INST, modo_TEMPERATURA_MED, modo_TROCAR_ESCALA, modo_TEMPERATURA_MinMax
};
//---------------------------------------------------------------------------

//===============================================//
//               VARIAVEIS GLOBAIS               //
//===============================================//
volatile float temperatura;
volatile float umidade;
volatile enum modo m; // Controle de modos
volatile bool flag_temperatura = true; //se a temperatura chegou na faixa entao false, se nao true
volatile uint8_t cont_bip;
volatile uint16_t endereco = 0;
volatile float media_temp = 0;
volatile float inst_temp = 0;
volatile float min_temp = 6000.00;
volatile float max_temp = -6000.00;
volatile char tempString[16];
volatile char mediaString[16];
volatile char minString[16];
volatile char maxString[16];
volatile uint16_t valor_ADC;
volatile bool timer_flag = false;
volatile bool init_ = true;
volatile bool OnOff = false;
volatile float FREQUENCIA_PWM; // Frequência desejada do PWM em Hz
//------------------------------------------------

//===============================================//
//                    EEPROM                     //
//===============================================//
void escrita_EEPROM_byte(uint16_t uiEndereco, uint8_t ucDado){

  while(EECR & (1<<EEPE)); //espera completar um escrita prévia
  EEAR = uiEndereco; //carrega o endereço para a escrita EEAR =>
  EEDR = ucDado; //carrega o dado a ser escrito EEDR -> EEPROM Data Register
  EECR |= (1<<EEMPE); //permite a escrita
  EECR |= (1<<EEPE); //inicia a escrita ativando EEPE

}

unsigned char leitura_EEPROM_byte(uint16_t uiEndereco){

  while(EECR & (1<<EEPE)); //espera completar um escrita prévia
  EEAR = uiEndereco; //escreve o endereço de leitura
  EECR |= (1<<EERE); //inicia a operação de leitura da EEPROM
  return EEDR; //retorna o valor lido do registrador de dados
} 

//tratamento dos dados de temperatura por casting
void escrita_EEPROM_float(uint16_t uiEndereco, float valor) {
  byte* ptr = (byte*) &valor; // Ponteiro para o valor float
  
  //Serial.println("Enderecos e bytes escritos:");
  
  for (int i = 0; i < sizeof(float); i++) {
    uint16_t endereco_atual = uiEndereco + i;
    
    // Imprimir o endereço atual e o byte correspondente
    //Serial.print("Endereco ");
    //Serial.print(endereco_atual);
    //Serial.print(": ");
    //Serial.println(*(ptr + i));
      
    // Escrever o byte na EEPROM
    escrita_EEPROM_byte(endereco_atual, *(ptr + i)); 
  }
  //Serial.print("Escrita da temperatura: ");
  //Serial.println(valor);
}

float leitura_EEPROM_float(uint16_t uiEndereco) {
  float valor;
  byte* ptr = (byte*) &valor; // Ponteiro para o valor float

  //Serial.println("Enderecos e bytes lidos:");
  
  // Lê os bytes da EEPROM na ordem correta
  for (int i = 0; i < sizeof(float); i++) {

    // Ler o byte da EEPROM e imprimir
    uint16_t endereco_atual = 4 * uiEndereco + i;
    // Imprimir o endereço atual
    //Serial.print("Endereco ");
    //Serial.print(endereco_atual);
    //Serial.print(": ");
    
    // Ler o byte da EEPROM e imprimir
    *(ptr + i) = leitura_EEPROM_byte(endereco_atual);
    //Serial.println(*(ptr + i));

  }

  //Serial.print("Endereco da leitura: ");
  //Serial.println(uiEndereco);

  //Serial.print("Leitura da temperatura: ");
  //Serial.println(valor);
  //Serial.println("=======================================");

  return valor;
}

void armazenar_EEPROM(float temperatura){

  escrita_EEPROM_float(4*endereco, temperatura);

}

void circular_EEPROM(){
  
  //temperatura armazenada circularmente
  if(4 * endereco <= 32){
    endereco++;
  }
  else{
    endereco = 0;

  }
}

float media() {
  float somaAfericoes = 0.0; // Variável para armazenar a soma das aferições
  int numAfericoesValidas = 0; // Contador para o número de aferições válidas

  // Loop para ler as aferições armazenadas no buffer circular da EEPROM
  for(int i = 0; i < 10; i++) {
    float afericao = leitura_EEPROM_float(i); // Lê a aferição na posição de memória
      
    // Verifica se a aferição é válida (não é NaN)
    if (!isnan(afericao)) {
      somaAfericoes += afericao; // Adiciona a aferição à soma
      numAfericoesValidas++; // Incrementa o contador de aferições válidas
    }
  }

  //Serial.print("Na media aparece:");
  //Serial.println(somaAfericoes / numAfericoesValidas);
  //Serial.println(" ");

  // Verifica se há pelo menos uma aferição válida antes de calcular a média
  if (numAfericoesValidas > 0) {
    return somaAfericoes / numAfericoesValidas; // Retorna a média calculada
  } else {
    return NAN; // Se não houver aferições válidas, retorna NaN
  }
}

//ler na memoria EEPROM as ultimas 10 medicoes para escolher os tronos
void min_max_TEMPERATURA(float temp){

  if(temp > max_temp){
    max_temp = temp;
  }

  if(temp < min_temp){
    min_temp = temp;
  }
}

//--------------------------------------------------

//================================================//
//                  SENSOR DHT22                  //
//================================================//

/*
  The data from the DHT11 and DHT22 sensor consists of 40 bits and the format is as follows:

  8 – Bit data for integral part of RH value,             bt[0] -> high
  8 – Bit data for decimal part of RH value,              bt[1] -> low
  8 – Bit data for integral part of Temperature value,    bt[2] -> high
  8 – Bit data for decimal part of Temperature value and  bt[3] -> low
  8 – Bit data for checksum.
*/

void sinc_MCU_() {

  DDRB |= (1 << DHT_PIN); // Configura o pino de comunicação como saída

  switch(DHT_TYPE) {
    case DHT11:
      PORTB &= ~(1 << DHT_PIN); 
      _delay_ms(18);
      PORTB |= (1 << DHT_PIN);
      _delay_us(20);
    break;
    case DHT22:
      PORTB &= ~(1 << DHT_PIN); 
      _delay_us(1000);
      PORTB |= (1 << DHT_PIN);
      _delay_us(40);
    break;
  }
}

void sinc_DHT() {

  DDRB &= ~(1 << DHT_PIN); // Configura o pino de comunicação como entrada

  while(PINB & (1 << DHT_PIN)); // Aguarda o pino ficar alto
  while(!(PINB & (1 << DHT_PIN))); // Aguarda o pino ficar baixo
  while(PINB & (1 << DHT_PIN)); // Aguarda o pino ficar alto
}

//funcao secundaria
float calcular_temperatura(uint8_t dados[]) {

  float temperatura = 0.0;

  switch(DHT_TYPE) {
    case DHT22:
      temperatura = ((dados[2] & 0x7F) << 8 | dados[3]) / 10.0;
      if (dados[2] & 0x80) {
        temperatura *= -1;
      }
    break;
    case DHT11:
      temperatura = dados[2];
      if (dados[2] & 0x80) temperatura = -1-temperatura;
      temperatura += (dados[3] & 0x0f) * 0.1; 
    break;
  }
  return temperatura;
}

//funcao pai
float DHT(enum tipo_dados dados, enum tipo_sensor sensor){

  sinc_MCU_();
  sinc_DHT();

  uint8_t bt[5] = {0};

  for(uint8_t j = 0; j < 5; j++) {

    uint8_t b = 0x0; //bits tudo zerado

    for(uint8_t i = 0; i < 8; i++) {

      while(!(PINB & (1 << DHT_PIN))); //enquanto  baixo
      switch(sensor){

        case DHT22:
          _delay_us(50);
        break;
        case DHT11:
          _delay_us(30);
        break;
      }

      if(PINB & (1 << DHT_PIN)) b |= (1 << (7 - i));

      while(PINB & (1 << DHT_PIN));

    }

    bt[j] = b;

  }

  if(dados == Temperatura) {

    float temp = calcular_temperatura(bt);
    return temp;
  }

  return 0.0; // Retorno padrão caso o tipo de dado não seja suportado
}

//----------------------------------------------------------------------------------

//================================================================================ //
//              ACIONANDO UM DISPLAY DE CRISTAL LIQUIDO DE 16x2                    //
//                    Interface de dados de 4 bits                                 //
//================================================================================ //

//c é o dado e cd indica se é instrução ou caractere (0 ou 1)
void cmd_LCD(unsigned char c, char cd){

  if(cd==0) 
    clr_bit(CONTR_LCD,RS); //instrução
  else 
    set_bit(CONTR_LCD,RS); //caractere

  //primeiro nibble de dados - 4 MSB
  #if (nibble_dados) //compila o código para os pinos de dados do LCD nos 4 MSB do PORT
    DADOS_LCD = (DADOS_LCD & 0x0F)|(0xF0 & c);
  #else 
    //compila o código para os pinos de dados do LCD nos 4 LSB do PORT
    DADOS_LCD = (DADOS_LCD & 0xF0)|(c>>4);
  #endif
  pulso_enable();

  //segundo nibble de dados - 4 LSB
  #if (nibble_dados) 
    //compila o código para os pinos de dados do LCD nos 4 MSB do PORT
    DADOS_LCD = (DADOS_LCD & 0x0F) | (0xF0 & (c<<4));
  #else 
    //compila o código para os pinos de dados do LCD nos 4 LSB do PORT
    DADOS_LCD = (DADOS_LCD & 0xF0) | (0x0F & c);
  #endif
  pulso_enable();

  if((cd==0) && (c<4)) _delay_ms(2); //se for instrução de retorno ou limpeza espera LCD estar pronto
  
}

//sequência ditada pelo fabricando do circuito integrado HD44780
void inic_LCD_4bits(){ 

  //o LCD será só escrito. Então, R/W é sempre zero.
  clr_bit(CONTR_LCD,RS);//RS em zero indicando que o dado para o LCD será uma instrução
  clr_bit(CONTR_LCD,E);//pino de habilitação em zero
  _delay_ms(20); /*tempo para estabilizar a tensão do LCD, após VCC
                   ultrapassar 4.5 V (na prática pode ser maior).*/

  //interface de 8 bits
  #if (nibble_dados)
    DADOS_LCD = (DADOS_LCD & 0x0F) | 0x30;
  #else
    DADOS_LCD = (DADOS_LCD & 0xF0) | 0x03;
  #endif

  pulso_enable(); //habilitação respeitando os tempos de resposta do LCD
  _delay_ms(5);
  pulso_enable();
  _delay_us(200);
  pulso_enable(); //até aqui ainda é uma interface de 8 bits.

  //interface de 4 bits, deve ser enviado duas vezes (a outra está abaixo)
  #if (nibble_dados)
    DADOS_LCD = (DADOS_LCD & 0x0F) | 0x20;
  #else
    DADOS_LCD = (DADOS_LCD & 0xF0) | 0x02;
  #endif
  pulso_enable();

  cmd_LCD(0x28,0); 
  
  //interface de 4 bits 2 linhas (aqui se habilita as 2 linhas)
  //são enviados os 2 nibbles (0x2 e 0x8)
  cmd_LCD(0x08,0); //desliga o display
  cmd_LCD(0x01,0); //limpa todo o display
  cmd_LCD(0x0C,0); //mensagem aparente cursor inativo não piscando
  cmd_LCD(0x80,0); //inicializa cursor na primeira posição a esquerda - 1a linha

}

//escreve no LCD
void escreve_LCD(char *c){

  for (; *c!=0;c++) cmd_LCD(*c,1);

}

//informação para criar caracteres novos armazenada na memória flash
const unsigned char carac1[] PROGMEM = {0b00011,//GRAU
                                        0b00011,
                                        0b00000,
                                        0b00000,
                                        0b00000,
                                        0b00000,
                                        0b00000};

const unsigned char carac2[] PROGMEM = {0b00000,//M
                                        0b00000,
                                        0b10001,
                                        0b11011,
                                        0b10101,
                                        0b10001,
                                        0b10001}; 

const unsigned char carac3[] PROGMEM = {0b00000,//A
                                        0b00000,
                                        0b01110,
                                        0b10001,
                                        0b11111,
                                        0b10001,
                                        0b10001};
                                        
const unsigned char carac4[] PROGMEM = {0b00000,//X
                                        0b00000,
                                        0b10001,
                                        0b01010,
                                        0b00100,
                                        0b01010,
                                        0b10001};

const unsigned char carac5[] PROGMEM = {0b00000,//I
                                        0b00000,
                                        0b00100,
                                        0b00100,
                                        0b00100,
                                        0b00100,
                                        0b00100};

const unsigned char carac6[] PROGMEM = {0b00000,//N
                                        0b00000,
                                        0b10001,
                                        0b11001,
                                        0b10101,
                                        0b10011,
                                        0b10001};

const unsigned char carac7[] PROGMEM = {0b00000,
                                        0b00100,
                                        0b01010,
                                        0b10001,
                                        0b10101,
                                        0b01110,
                                        0b00000};

const unsigned char carac8[] PROGMEM = {0b00000,
                                        0b10101,
                                        0b01110,
                                        0b11011,
                                        0b01110,
                                        0b10101,
                                        0b00000};

void gerar_caracteres(){

  //Gera grau
  cmd_LCD(0x40,0); //endereço base para gravar novo segmento
  for(int k=0;k<7;k++) //grava 8 bytes na DDRAM começando no end. 0x40
    cmd_LCD(pgm_read_byte(&carac1[k]),1);
    cmd_LCD(0x00,1); /*apaga última posição do end. da CGRAM para evitar algum dado espúrio*/

  //gera M
  cmd_LCD(0x48,0); //endereço base para gravar novo segmento
  for(int k=0;k<7;k++) //grava 8 bytes na DDRAM começando no end. 0x48
    cmd_LCD(pgm_read_byte(&carac2[k]),1);
  cmd_LCD(0x00,1); 

  //Gera A
  cmd_LCD(0x50,0); //endereço base para gravar novo segmento
  for(int k=0;k<7;k++) //grava 8 bytes na DDRAM começando no end. 0x50
    cmd_LCD(pgm_read_byte(&carac3[k]),1);
  cmd_LCD(0x00,1); 

  //gera X
  cmd_LCD(0x58,0); //endereço base para gravar novo segmento
  for(int k=0;k<7;k++) //grava 8 bytes na DDRAM começando no end. 0x58
    cmd_LCD(pgm_read_byte(&carac4[k]),1);
  cmd_LCD(0x00,1);

  //gera I
  cmd_LCD(0x60,0); //endereço base para gravar novo segmento
  for(int k=0;k<7;k++) //grava 8 bytes na DDRAM começando no end. 0x60
    cmd_LCD(pgm_read_byte(&carac5[k]),1);
  cmd_LCD(0x00,1); 

  //Gera N
  cmd_LCD(0x68,0); //endereço base para gravar novo segmento
  for(int k=0;k<7;k++) //grava 8 bytes na DDRAM começando no end. 0x68
    cmd_LCD(pgm_read_byte(&carac6[k]),1);
  cmd_LCD(0x00,1); 

  //Gera fogo
  cmd_LCD(0x70,0); //endereço base para gravar novo segmento
  for(int k=0;k<7;k++) //grava 8 bytes na DDRAM começando no end. 0x68
    cmd_LCD(pgm_read_byte(&carac7[k]),1);
  cmd_LCD(0x00,1); 

  //Gera neve
  cmd_LCD(0x78,0); //endereço base para gravar novo segmento
  for(int k=0;k<7;k++) //grava 8 bytes na DDRAM começando no end. 0x68
    cmd_LCD(pgm_read_byte(&carac8[k]),1);
  cmd_LCD(0x00,1); 
  
  /*
  cmd_LCD(0x00,1); //apresenta segundo caractere 0x00 -> grau
  cmd_LCD(0x01,1); //apresenta segundo caractere 0x01 -> M
  cmd_LCD(0x02,1); //apresenta segundo caractere 0x02 -> A
  cmd_LCD(0x03,1); //apresenta segundo caractere 0x03 -> X
  cmd_LCD(0x04,1); //apresenta segundo caractere 0x04 -> I
  cmd_LCD(0x05,1); //apresenta segundo caractere 0x05 -> N
  ...
  */

}

void modo_LCD(enum modo m){

  //if(m != modo_PADRAO){

    //brilho LCD
    ler_potenciometro(); //para o brilho LCD
    lcd_brilho_potenciometro(valor_ADC); //brilho LCD

    //Lê a temperatura em Celsius
    temperatura = DHT(Temperatura, DHT22);

    //Adicionar novos caracteres na memoria DDRAM
    gerar_caracteres();
    
    //armazenamento
    armazenar_EEPROM(temperatura); 

    //passagem das funcoes
    inst_temp = leitura_EEPROM_float(endereco);
    min_max_TEMPERATURA(inst_temp);
    media_temp = media();

    // Buffer converte os valores de temperatura e umidade para strings
    dtostrf(inst_temp, 6, 2, tempString);
    dtostrf(media_temp, 6, 2, mediaString);
    dtostrf(min_temp, 6, 2, minString);
    dtostrf(max_temp, 6, 2, maxString);

    //circulando
    circular_EEPROM();

  //}

  // Limpar display
  cmd_LCD(0x01,0);

  switch(m){

    //Modo padrao
    case modo_PADRAO:
      cmd_LCD(0x81, 0);  // Posiciona o cursor na segunda linha, terceira coluna
      escreve_LCD("AquaWatch Kids");// Escreve no LCD
      cmd_LCD(0xC2, 0);  
      escreve_LCD("JK Care LTDA");
    break;

    //Modo temperatura instantenea
    case modo_TEMPERATURA_INST:
      cmd_LCD(0x80, 0);  // Posiciona o cursor na segunda linha, terceira coluna
      escreve_LCD("Temperatura Inst."); // Escreve no LCD
      cmd_LCD(0xC4, 0);  
      escreve_LCD(tempString);
      cmd_LCD(0xCB, 0);  // Posiciona o cursor na segunda linha, terceira coluna
      escreve_LCD("C"); // Escreve no LCD
      cmd_LCD(0xCA, 0); 
      cmd_LCD(0x00,1);  //apresenta primeiro caractere 0x00
    break;

    //Modo temperatura min/max
    case modo_TEMPERATURA_MinMax:

      cmd_LCD(0x81, 0);
      cmd_LCD(0x06,1); 
      cmd_LCD(0x83, 0);   
      escreve_LCD("T"); // Escreve no LCD
      cmd_LCD(0x01,1); //apresenta segundo caractere 0x01 -> M
      cmd_LCD(0x02,1); //apresenta segundo caractere 0x02 -> A
      cmd_LCD(0x03,1); //apresenta segundo caractere 0x03 -> X

      cmd_LCD(0xC1, 0);
      cmd_LCD(0x07,1); 
      cmd_LCD(0xC3, 0);  
      escreve_LCD("T"); // Escreve no LCD
      cmd_LCD(0x01,1); //apresenta segundo caractere 0x01 -> M
      cmd_LCD(0x04,1); //apresenta segundo caractere 0x02 -> I
      cmd_LCD(0x05,1); //apresenta segundo caractere 0x03 -> N

      //MAXIMA
      cmd_LCD(0x87, 0); 
      escreve_LCD(maxString);
      cmd_LCD(0x00,1);  //apresenta primeiro caractere 0x00
      escreve_LCD("C");

      //MINIMA
      cmd_LCD(0xC7, 0); 
      escreve_LCD(minString);
      cmd_LCD(0x00,1);    //apresenta primeiro caractere 0x00
      escreve_LCD("C");   // Escreve no LCD
    break;

    //Modo temperatura media
    case modo_TEMPERATURA_MED:
      cmd_LCD(0x80, 0);           // Posiciona o cursor na segunda linha, terceira coluna
      escreve_LCD("Temperatura Med"); // Escreve no LCD
      cmd_LCD(0xC4, 0);  
      escreve_LCD(mediaString);
      cmd_LCD(0xCB, 0);           // Posiciona o cursor na segunda linha, terceira coluna
      escreve_LCD("C"); // Escreve no LCD
      cmd_LCD(0xCA,0);    //endereça a posição para escrita dos caracteres
      cmd_LCD(0x00,1);    //apresenta primeiro caractere 0x00
    break;
  }

  //bip se temperatura 36<t<37
  beep_beep_beep_buzzer();

}
//---------------------------------------------------------------------------

//================================================================================ //
//              Acionamento do POTENCIOMETRO                                       //
//================================================================================ //
void ler_potenciometro(){ //POTENCIOMETRO

  ADCSRA |= (1 << ADSC);
  while (ADCSRA & (1 << ADSC)); // Aguardar a conversão ser concluída
  valor_ADC = ADC; //Novo valor capturado
  //Serial.print("Potenciometro: ");
  //Serial.println(valor_ADC); // Imprimir o valor capturado do potenciômetro

}

void lcd_brilho_potenciometro(uint16_t val_adc){ 

  DDRH |= (1 << LCD_ANODO); //saida

  if(val_adc <= 500 & OnOff == true){
    PORTH &= ~(1 << LCD_ANODO);
  }
  else{
    PORTH |= (1 << LCD_ANODO); 
  }

}
//---------------------------------------------------------------------------

//================================================================================ //
//              Acionamento do BUZZER                                              //
//================================================================================ //

 /*tipos de audio
    tocar_faixa_musical(ligando);
    
    tocar_faixa_musical(beep);
   
    alarme_buzzer();
  
    tocar_faixa_musical(desligando);
  */

// Frequências das notas em Hz para a oitava 0 até a oitava 8 (Universal)
float frequencias_notas[NUM_OITAVAS][NOTAS_POR_OITAVA] = {
    // Oitava 0
    {16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96, 27.50, 29.14, 30.87},
    // Oitava 1
    {32.70, 34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 49.00, 51.91, 55.00, 58.27, 61.74},
    // Oitava 2
    {65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.83, 110.00, 116.54, 123.47},
    // Oitava 3
    {130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 233.08, 246.94},
    // Oitava 4
    {261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88},
    // Oitava 5
    {523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880.00, 932.33, 987.77},
    // Oitava 6
    {1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760.00, 1864.66, 1975.53},
    // Oitava 7
    {2093.00, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520.00, 3729.31, 3951.07},
    // Oitava 8
    {4186.01, 4434.92, 4698.64, 4978.03, 5274.04, 5587.65, 5919.91, 6271.93, 6644.88, 7040.00, 7458.62, 7902.13}
}; //obs: o buzzer opera em 32hz-10khz

//init buzzer
void inicializar_pwm_buzzer(){

  //se o dispositivo estiver ligado, entao pode tocar o som dj
  if(OnOff == true){

    // Configura o pino 13 (ou qualquer pino que deseje) como saída
    DDRE |= (1 << BUZZER_PIN); // PB5 corresponde ao pino 13 no Arduino Mega
    //PORTB |= (1 << PB5); //SAIDA
    
    // Configura o Timer1 para PWM
    TCCR3A |= (1 << COM3A1) | (1 << WGM31); // Configura o modo de saída não invertido (OC2A) e o modo de contagem PWM de 10 bits
    TCCR3B |= (1 << WGM33) | (1 << WGM32) | (1 << CS31) | (1 << CS30); // Configura o pré-scaler para 64 (frequência do clock / 64)
    ICR3 = F_CPU / 64 / FREQUENCIA_PWM - 1; // Configura o TOP para obter a frequência desejada

    // O PWM está configurado, você pode ajustar o ciclo de trabalho alterando o valor do registrador OCR1A
    // Exemplo: 50% de ciclo de trabalho (eficiente e audivel)
    OCR3A = ICR3 / 2; //duty cyle (INMPORTANTE)

  }
}

//1 beep -> PADRAO para qualquer som, entao se a frequencia muda -> a freq. do pwm deve mudar em ICR3 (TOP)
void beep_buzzer(int oitava, enum tom nota){

  FREQUENCIA_PWM = frequencias_notas[oitava][nota];
  ICR3 = F_CPU / 64 / FREQUENCIA_PWM - 1; // valor limite do temporizado TCN1 -> (TOP)

}

//padrao duracao = 250
void tocar_faixa_musical(enum faixa fx) {

  //inicializa os registradores para fast pwm 
  inicializar_pwm_buzzer();

  // Duração de cada nota em milissegundos
  int duracao_nota = 250; // 0.250 segundos

  // Sequência de notas para uma música simples (por exemplo)
  enum tom notas_musica[] = {NOTA_DO, NOTA_RE, NOTA_MI};

  //ligando
  if(fx == ligando){

    // Tocar cada nota na sequência
    for (int i = 0; i < sizeof(notas_musica) / sizeof(notas_musica[0]); i++) {
      beep_buzzer(5, notas_musica[i]); // Toca a nota
      _delay_ms(300); // Aguarda a duração da nota
    }
  }

  //beep
  if(fx == beep){

    // Duração de cada nota em milissegundos
    beep_buzzer(5, NOTA_FA);
    _delay_ms(333);

  }

  //alarme
  if(fx == alarme){

    beep_buzzer(7, NOTA_SI); // Toca a nota
    _delay_ms(duracao_nota); // Aguarda a duração da nota

  }

  //desligando
  if(fx == desligando){

    // Tocar cada nota na sequência
    for (int i = 3; i >= 0; i--) {
      beep_buzzer(5, notas_musica[i]); // Toca a nota
      _delay_ms(duracao_nota); // Aguarda a duração da nota
    }
  }


  //parar a musica e PWM no pino
  parar_musica();
}

//parar pwm basicamente
void parar_musica() {

  // Desativa o timer para parar completamente o sinal PWM
  TCCR3B = 0x00; // Desativa o clock do timer

  // Desativa a saída PWM
  TCCR3A = 0x00;

  // Zera o registrador de comparação para manter o pino em nível baixo
  OCR3A = 0;

  // Define o pino como entrada
  DDRE |= (0 << BUZZER_PIN); 
}

//2 beeps
void alarme_buzzer(){
  tocar_faixa_musical(alarme);
  _delay_ms(333);
  tocar_faixa_musical(alarme);
  _delay_ms(333);
  tocar_faixa_musical(alarme);
  _delay_ms(666);
}

//3 sequecias de beeps e tratamentos
void beep_beep_beep_buzzer(){
  
  if (media_temp >= 35.00 && media_temp <= 37.00 && m != modo_PADRAO){

    if(cont_bip < 1 && flag_temperatura == false){
      PORTJ |= (1 << LED_VERDE_PIN);
      alarme_buzzer();
      cont_bip++;
    }
  }

  else{

    flag_temperatura = true;
    PORTJ &= ~(1 << LED_VERDE_PIN);

    if(media_temp < 35.00 or media_temp > 37.00){
      flag_temperatura = false;
      cont_bip = 0;
    }
  }
}

//---------------------------------------------------------------------------

//================================================================================ //
//                                  WATCHDOG                                       //
//================================================================================ //

void reset() {

  // Desabilitar interrupções globalmente
  cli();

  // Configurar o registrador WDTCSR para habilitar o watchdog (WDTCSR == WDRCR)
  WDTCSR |= ( 1 << WDCE) | (1 << WDE);

  // Configurar o watchdog para um tempo de reinicialização curto
  WDTCSR = (1 << WDE);

  // Entrar em loop infinito até o reinício ocorrer
  while(1);
}
//---------------------------------------------------------------------------

//================================================================================//
//                                  MAIN                                          //
//================================================================================//
int main(){

  cli(); //desabilita interrupcao global

  //alimentacao 5v
  DDRH |= (1 << PH5); //saida

  //Botoes
  DDRD &= ~(1 << botao_TEMPERATURA_INST); //Direcionando bit do DDRD como entrada
  DDRD &= ~(1 << botao_TEMPERATURA_MED); //Direcionando bit do DDRD como entrada
  DDRD &= ~(1 << botao_TEMPERATURA_minMAX); //Direcionando bit do DDRD como entrada
  DDRD &= ~(1 << botao_TEMPERATURA_CLR);
  DDRC &= ~(1 << knob_POTENCIOMETRO); // Configurar o pino A0 como entrada para o ADC
  PORTD |= (1 << botao_TEMPERATURA_INST) | (1 << botao_TEMPERATURA_MED) | (1 << botao_TEMPERATURA_minMAX) | (1 << botao_TEMPERATURA_CLR); //ATIVA O PULL UP INTERNO
  PORTC |= (1 << knob_POTENCIOMETRO); // ATIVA O PULL UP INTERNO
  PORTE |= (1 << botao_On_Off); //Ativa o pull up interno

  //LED controle
  DDRJ |= (1 << LED_VERDE_PIN); // saida

  //LCD 16x2
  DDRA = 0xFF; // PORTD como saída (CONTROLE)
  DDRC = 0xFF; // PORTB como saída (DADOS)
  inic_LCD_4bits();   // Inicializa o LCD

  //Interrupcoes (Externas)
  EICRA |= (0 << ISC00) | (0 << ISC01); //Tipo de descida do INT0 inst
  EICRA |= (0 << ISC10) | (0 << ISC11); //Tipo de descida do INT1 min/max
  EICRA |= (0 << ISC20) | (0 << ISC21); //Tipo de descida do INT2 clr
  EICRA |= (0 << ISC30) | (0 << ISC31); //Tipo de descida do INT3 med
  EICRB |= (0 << ISC40) | (0 << ISC41); //Tipo de descida do INT3 med
  EIMSK |= (1 << INT0); //habilita INT0
  EIMSK |= (1 << INT1); //habilita INT1
  EIMSK |= (1 << INT2); //habilita INT2
  EIMSK |= (1 << INT3); //habilita INT3
  EIMSK |= (1 << INT4); //habilita INT4

  //ADC
  ADCSRA |= (1 << ADEN); //ADC habilitado
  ADMUX |= (1 << REFS0); //AVCC -> 5V como referencia para o ADC
  ADMUX |= (0 << MUX0); //canal 0
  ADCSRA |= (1 << ADSC); //Comecanco a conversao (podemos mudar aqui para o modo)
  ADCSRA |= (1 << ADPS1) | (1 << ADPS2); //Prescale de 64
  ADCSRA |= (1 << ADIF); //Mudancas salva no registrador
  
  //Temporizador 1 (Interno)
  TCCR1B |= (1 << WGM12); // ativa o modo CTC
  OCR1A = 15624; // Define o valor de comparação para 1 segundo a uma frequência de 16MHz //OCR1A = 7812 para 0.5, 15624 para 1s
  TIMSK1 |= (1 << OCIE1A); // ativa a interrupção de comparação A
  TCCR1B |= (1 << CS10) | (1 << CS12); // Configura o prescaler para 1024

  sei(); //Habilita interrupcao global

  while(1){
    if(timer_flag==true & OnOff == true){
      timer_flag=false;
      modo_LCD(m); // Chama a função para atualizar o LCD
    }
  }

  return 0;

}
//---------------------------------------------------------------------------


//================================================================================ //
//                                  INTERRUPCOES                                   //
//================================================================================ //

//modo instantanea rotina
ISR(INT0_vect){

  // Limpar display
  cmd_LCD(0x01,0);
    _delay_ms(200);
  flag_temperatura = false; cont_bip = 0;
  m = modo_TEMPERATURA_INST;
  tocar_faixa_musical(beep);

}

//modo media rotina
ISR(INT1_vect){
  
  //Limpar display
  cmd_LCD(0x01,0);
  _delay_ms(200);
  flag_temperatura = false; cont_bip = 0;
  m = modo_TEMPERATURA_MED;
  tocar_faixa_musical(beep);
   
}

//modo clear (clr) rotina
ISR(INT2_vect){
 
  //Limpar display
  cmd_LCD(0x01,0);
  _delay_ms(200);
  endereco = 0; cont_bip = 0; flag_temperatura = false; media_temp = 0; inst_temp = 0; max_temp = -6000.00; min_temp = 6000.00;
  tocar_faixa_musical(beep);
 
}

//modo temperatura min/max rotina
ISR(INT3_vect){

  // Limpar display
  cmd_LCD(0x01,0);
  _delay_ms(200);
  if(init==true){
    max_temp = -6000.00; min_temp = 6000.00;
    init_ = false;
  }
  flag_temperatura = false; cont_bip = 0;
  m = modo_TEMPERATURA_MinMax;
  tocar_faixa_musical(beep);

}

//On e Off rotina
ISR(INT4_vect){

  if(OnOff == false){ //ligar
    valor_ADC = 0;
    _delay_ms(333);
    m = modo_PADRAO;
    PORTH |= (1 << PH5); //Ligar alimentacao de 5v
    modo_LCD(m);
    OnOff = true;
    tocar_faixa_musical(ligando);
  }

  else{
    tocar_faixa_musical(desligando);
    parar_musica(); //parar a musica e PWM no pino
    PORTH &= ~(1 << LCD_ANODO);
    OnOff = false;
    cmd_LCD(0x01,0); // Limpar display
    PORTH &= ~(1 << PH5); //desligar alimentacao de 5v
    reset(); // Resetar a placa por software
  }
  _delay_ms(500);
}

//atualizar a cada 1s rotina
ISR(TIMER1_COMPA_vect) {
  timer_flag = true;
}
//---------------------------------------------------------------------------

/*
  obs: pode ocorrer problema ao medir a temperatura negativa se o sensor for DHT11.
*/
$abcdeabcde151015202530fghijfghij
$abcdeabcde151015202530fghijfghij