#include <util/delay.h>
#include <avr/io.h>
enum DHT_Type {
DHT22,
DHT11
};
// Escolher o tipo de sensor no circuito
#define _type DHT22
// Botões e led
#define BOTAO_BLACK PD1
#define LED PG2
volatile int tem_led = 0;
int med[10];
int index = 0;
int sum = 0;
int average = 0;
// ========================================================================
// PINOS LCD
// ========================================================================
// LCD - Pinos de controle
#define CONTR_LCD PORTA
#define RS PA1
//#define RW PA3
#define E PA5
// LCD - Pinos de dados (8 bits)
#define DADOS_LCD PORTC
#define nibble_dados 1
// Definição do Sensor DHT
#define DHT_PIN PH5
#define pulse_enable() _delay_us(1); set_bit(CONTR_LCD, E); _delay_us(1); clr_bit(CONTR_LCD, E); _delay_us(45);
// ========================================================================
// 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
char* itoa(int value, char* str, int base);
// Ler a temperatura do sensor
uint8_t dht_read(uint8_t dht, enum DHT_Type type);
// Ligar/desligar led
void led_control(int temperature);
// REVER AONDE ESTÁ ESSA FUNÇÃO
uint8_t dht_mount_byte()
{
uint8_t b = 0x0;
for (uint8_t i = 0; i < 8; i++)
{
while(~(PINH >> DHT_PIN) & 0x1);
switch(_type)
{
case DHT22: // DHT22 espera 50us
_delay_us(50);
case DHT11: // DHT11 espera 30us
_delay_us(30);
}
if ((PINH >> DHT_PIN) & 0x1) b = (b<<1)|(0x01);
else b = (b<<1);
while((PINH >> DHT_PIN) & 0x1);
}
return b;
}
// ========================================================================
// LCD - Enviar caracteres/instruções
// ========================================================================
void lcd_cmd(unsigned char c, char cd)
{
// cd : indica se instrução (0) ou caractere (1)
if(cd == 0)
clr_bit(CONTR_LCD,RS);
else
set_bit(CONTR_LCD,RS);
// 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
pulse_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
pulse_enable();
// Se for instrução de retorno ou limpeza espera LCD estar pronto
if((cd == 0) && (c < 4)) _delay_ms(2);
}
// ========================================================================
// LCD - Inicializacao do LCD com via de dados de 4 Bits
// ========================================================================
void lcd_init_4bits()
{
// Como o LCD será só escrito, 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
// Tempo para estabilizar a tensão do LCD, após VCC ultrapassar 4.5 V (na prática pode ser maior)
_delay_ms(20);
// Interface de 8 bits
#if (nibble_dados)
DADOS_LCD = (DADOS_LCD & 0x0F) | 0x30;
#else
DADOS_LCD = (DADOS_LCD & 0xF0) | 0x03;
#endif
pulse_enable(); // Habilitação respeitando os tempos de resposta do LCD
_delay_ms(5);
pulse_enable();
_delay_us(200);
pulse_enable();
// 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
pulse_enable();
// Interface de 4 bits 2 linhas
// Aqui se habilita as 2 linhas
lcd_cmd(0x28,0);
// São enviados os 2 nibbles (0x2 e 0x8)
lcd_cmd(0x08,0); // Desliga o display
lcd_cmd(0x01,0); // Limpa todo o display
lcd_cmd(0x0C,0); // Mensagem aparente cursor inativo não piscando
lcd_cmd(0x80,0); // Inicializa cursor na primeira posição a esquerda
}
// ========================================================================
// LCD - Escrita no LCD
// ========================================================================
void lcd_write(char *c)
{
for (; *c!=0;c++) lcd_cmd(*c,1);
}
// Protótipo da função itoa()
char* itoa(int value, char* str, int base)
{
// Verifica se a base é suportada (apenas base 10 é implementada aqui)
if (base != 10) {
*str = '\0';
return str;
}
// Inicializa o índice para a string resultante
int i = 0;
// Armazena o sinal do número (positivo ou negativo)
int is_negative = 0;
// Caso o número seja zero, adiciona '0' à string e encerra
if (value == 0) {
str[i++] = '0';
str[i] = '\0';
return str;
}
// Se o número for negativo, armazena o sinal e transforma em positivo
if (value < 0) {
is_negative = 1;
value = -value;
}
// Loop para converter o número em string reversa
while (value != 0) {
int rem = value % 10; // Calcula o dígito menos significativo
str[i++] = rem + '0'; // Converte o dígito em caractere e armazena
value /= 10; // Remove o dígito menos significativo
}
// Adiciona o sinal '-' se o número for negativo
if (is_negative) {
str[i++] = '-';
}
// Adiciona o terminador de string
str[i] = '\0';
// Inverte a string resultante
int start = 0;
int end = i - 1;
while (start < end) {
char temp = str[start];
str[start] = str[end];
str[end] = temp;
start++;
end--;
}
return str;
}
int main()
{
// Configurar portas de saida do LCD
DDRA = 0xFF; // Configura todas as portas A como saída para o LCD
DDRC = 0xFF; // Configura todas as portas C como saída para o LCD
DDRG |= (1 << LED); // Configura o pino PG2 como saída para o LED
DDRD &= ~(1 << BOTAO_BLACK); // Define o pino do botão como entrada
PORTD |= (1 << BOTAO_BLACK); // Ativa o pull-up interno do botão
// Configuração de interrupção externa INT1 (PD1) - Botão de Zerar
EICRA |= (1 << ISC10); // Configura a interrupção para acontecer na borda de descida
EIMSK |= (1 << INT1); // Habilita a interrupção INT1
// Inicializar array de medições
for(int i = 0; i < 10; i++) {
med[i] = 0; // Inicializa todas as posições com 0
}
// Configuração do Timer1 para modo CTC (Clear Timer on Compare Match)
TCCR1B |= (1 << WGM12); // Ativa modo CTC
OCR1A = 1562; // Valor de comparação para 1 segundo (frequência de 16 MHz)
TIMSK1 |= (1 << OCIE1A); // Habilita interrupção do comparador A
TCCR1B |= (1 << CS12) | (1 << CS10); // Configura prescaler para 1024 e inicia a contagem
sei(); // Habilita todas as interrupções globais
// Inicializar LCD
lcd_init_4bits();
// Buffer para armazenar a temperatura como texto
// char temperature_text[16];
// Mostrar a mensagem "Olá Mundo"
// lcd_cmd(0x80, 0);
// lcd_write("Olá mundo");
//int led_on = 0; // Estado inicial do led (desligado)
// int tem_led = 0;
while (1)
{
int result = dht_read(DHT_PIN, _type, tem_led);
if (result != 0)
{
lcd_cmd(0x01, 0); // Limpa o display
_delay_ms(2);
lcd_cmd(0x80, 0); // Posiciona o cursor na primeira linha
lcd_write("Erro: ");
char buffer[2];
itoa(result, buffer, 10);
lcd_write(buffer);
}
_delay_ms(1000); // Aguarda 1 segundo antes de fazer a próxima leitura
// Se botão é pressionado
// SÓ VIRA ON SE FICAR SEGURANDO O BOTÃO, AJEITAR
/*if (!(PIND & (1 << BOTAO_BLACK)))
{
lcd_cmd(0xC0, 0); // Posiciona o cursor na segunda linha
lcd_write("LED: ON");
tem_led = 1;
} else
{
lcd_cmd(0xC0, 0); // Posiciona o cursor na segunda linha
lcd_write("LED: OFF");
tem_led = 0;
}*/
}
}
ISR(INT1_vect) {
_delay_ms(50);
// Se botão for pressionado
if (!(PIND & (1 << BOTAO_BLACK))) {
// Inverte se vai ter ou não led
// Se era 1, vira 0
// Se era 0, vira 1
tem_led = !tem_led;
}
}
ISR(TIMER1_COMPA_vect)
{
// Deslocamento circular para a esquerda
for(int i = 0; i < 9; i++) {
med[i] = med[i + 1];
}
med[9] = dht_read(DHT_PIN, _type, tem_led);
// Recalcula a média das 10 últimas medições
sum = 0;
for(int i = 0; i < 10; i++) {
sum += med[i];
}
average = sum / 10;
// Atualiza o LCD com a média
lcd_cmd(0xCA, 0); // Posiciona o cursor na segunda linha
lcd_write("M: ");
char buffer[4];
itoa(average, buffer, 10);
lcd_write(buffer);
lcd_write(" C");
}
// Função para ler o valor digital do sensor DHT22
uint8_t dht_read(uint8_t dht, enum DHT_Type type, int tem_led)
{
uint8_t dht_data[5] = {0, 0, 0, 0, 0};
// Configurando o pino como saída
DDRH |= (1 << dht);
// Enviando sinal de inicialização
PORTH &= ~(1 << dht);
_delay_ms(18);
// switch(_type)
// {
// case DHT22:
// _delay_us(1000);
// break;
// case DHT11:
// _delay_us(18);
// break;
// }
PORTH |= (1 << dht);
_delay_us(1);
// switch(_type)
// {
// case DHT22:
// _delay_us(40);
// break;
// case DHT11:
// _delay_us(20);
// break;
// }
DDRH &= ~(1 << dht); // Configurando o pino como entrada
// Esperando resposta do sensor
uint8_t timeout_counter = 0;
while (PINH & (1 << dht))
{
if (++timeout_counter > 100)
{
return 1; // Erro de timeout
}
_delay_us(1);
}
timeout_counter = 0;
while (!(PINH & (1 << dht)))
{
if (++timeout_counter > 100)
{
return 2; // Erro de timeout
}
_delay_us(1);
}
timeout_counter = 0;
while (PINH & (1 << dht))
{
if (++timeout_counter > 100)
{
return 3; // Erro de timeout
}
_delay_us(1);
}
// Lendo os dados do sensor
for (uint8_t i = 0; i < 5; ++i)
{
uint8_t byte = 0;
for (uint8_t j = 0; j < 8; ++j)
{
timeout_counter = 0;
while (!(PINH & (1 << dht)))
{
if (++timeout_counter > 100)
{
return 4; // Erro de timeout
}
_delay_us(1);
}
_delay_us(30); // Espera para determinar se o bit é 0 ou 1
if (PINH & (1 << dht))
{
byte |= (1 << (7 - j)); // Bit é 1
timeout_counter = 0;
while (PINH & (1 << dht))
{
if (++timeout_counter > 100)
{
return 5; // Erro de timeout
}
_delay_us(1);
}
}
}
dht_data[i] = byte;
}
// Verificando a soma de verificação
if ((uint8_t)(dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) != dht_data[4])
{
return 6; // Erro de checksum
}
uint16_t temperature;
uint8_t temperature_decimal;
// Calculando temperatura e umidade
switch (type)
{
case DHT22:
// Unir parte inteira e decimal
temperature = ((uint16_t)dht_data[2] & 0x7f) << 8 | dht_data[3];
// humidity = ((uint16_t)dht_data[0] << 8) | dht_data[1];
// Atribuir aos decimais
temperature_decimal = temperature / 10;
// humidity_decimal = humidity / 10;
// Corrigir sinal da temperatura
if ((uint16_t)dht_data[2] & 0x80) temperature_decimal *= -1;
break;
case DHT11:
// Unir parte inteira e decimal
temperature = ((uint16_t)dht_data[2]);
// humidity = (uint16_t)dht_data[0] + dht_data[1] * 0.1;
// Atribuir aos decimais
temperature_decimal = ((float)temperature);
// humidity_decimal = ((float)humidity);
// Corrigir sinal da temperatura
if (dht_data[3] & 0x80)
{
temperature = -1-temperature;
}
temperature += (dht_data[3] & 0x0f) * 0.1;
break;
}
// Exibindo os valores lidos
lcd_cmd(0x01, 0); // Limpa o display
_delay_ms(2);
lcd_cmd(0x80, 0); // Posiciona o cursor na primeira linha
lcd_write("Temp: ");
//lcd_cmd(0x86, 0); // Posiciona o cursor após "Temp: "
char buffer[8];
itoa(temperature / 10, buffer, 10);
lcd_write(buffer);
lcd_write(".");
itoa(temperature % 10, buffer, 10);
lcd_write(buffer);
lcd_write(" C");
if(tem_led==1){
lcd_cmd(0xC0, 0); // Posiciona o cursor na segunda linha
lcd_write("LED: ON");
led_control(temperature_decimal);
}else{
PORTG &= ~(1 << LED);
lcd_cmd(0xC0, 0); // Posiciona o cursor na segunda linha
lcd_write("LED: OFF");
}
/*
// Deslocamento circular para a esquerda
for(int i = 0; i < 9; i++) {
med[i] = med[i + 1];
}
med[9] = temperature;
// Calcula a média das 10 últimas medições
int sum = 0;
for(int i = 0; i < 10; i++) {
sum += med[i];
}
int average = sum / 10;
// Exibe a média no LCD
lcd_cmd(0xC0, 0); // Posiciona o cursor na segunda linha
lcd_write("Media: ");
char buffer[8];
itoa(average, buffer, 10);
lcd_write(buffer);
lcd_write(" C");
*/
return 0; // Sucesso
}
// Função para controlar o LED com base na temperatura
void led_control(int temperature)
{
if (temperature < 34 || temperature > 45) {
// Temperatura fora do intervalo, liga o LED
PORTG |= (1 << LED);
} else {
// Temperatura dentro do intervalo, desliga o LED
PORTG &= ~(1 << LED);
}
}