// CLIQUE UMA VEZ PARA TROCAR MANUALMENTE O VALOR MOSTRADO NO DISPLAY
// CLIQUE DUAS VEZES NO BOTÃO PARA PAUSAR A TROCA AUTOMÁTICA DE VALORES MEDIDOS
#include <stdio.h>
#include <stdarg.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_timer.h"
#include "rom/ets_sys.h"
// INÍCIO DA BIBLIOTECA DO LCD -------------------------------------------
// PINOS DO PCF8475
#define RS 0
#define RW 1
#define EN 2
#define BL 3
// MEDIDAS DOS DISPLAYS
#define DISPLAY_16X02 0
#define DISPLAY_20X04 1
// INSTRUÇÕES DO DISPLAY LCD
#define CLEAR_DISPLAY 0x01
#define RETURN_HOME_UNSHIFT 0x02
#define CURSOR_RIGHT_NO_SHIFT 0x04
#define CURSOR_RIGHT_SHIFT 0x05
#define CURSOR_RIGHT_NO_SHIFT_LEFT 0x06
#define CURSOR_RIGHT_SHIFT_LEFT 0x07
#define DISPLAY_OFF 0x08
#define DISPLAY_ON_CURSOR_OFF 0x0C
#define DISPLAY_ON_CURSOR_ON_STEADY 0x0E
#define DISPLAY_ON_CURSOR_ON_BLINK 0x0F
#define RETURN_HOME 0x80
#define SHIFT_CURSOR_LEFT 0x10
#define SHIFT_CURSOR_RIGHT 0x14
#define SHIFT_DISPLAY_LEFT 0x18
#define SHIFT_DISPLAY_RIGHT 0x1C
#define SET_4BIT_MODE 0x28
typedef struct {
uint8_t address;
uint8_t num;
uint8_t backlight;
uint8_t size;
} lcd_i2c_handle_t;
void i2c_write_byte(lcd_i2c_handle_t * lcd, uint8_t data) {
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (lcd->address << 1) | I2C_MASTER_WRITE, 1);
i2c_master_write_byte(i2c_cmd, data, 1);
i2c_master_stop(i2c_cmd);
i2c_master_cmd_begin(lcd->num, i2c_cmd, 10 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(i2c_cmd);
}
void lcd_i2c_write(lcd_i2c_handle_t * lcd, char rs_flag, char data_byte) {
uint8_t buffer_byte = ((1 << RS) * rs_flag) | ((1 << BL) * lcd->backlight);
buffer_byte |= (buffer_byte & 0x0F) | (0xF0 & data_byte);
buffer_byte |= (1 << EN);
i2c_write_byte(lcd, buffer_byte);
ets_delay_us(10);
buffer_byte &= ~(1 << EN);
i2c_write_byte(lcd, buffer_byte);
ets_delay_us(50);
buffer_byte = (buffer_byte & 0x0F) | (data_byte << 4);
buffer_byte |= (1 << EN);
i2c_write_byte(lcd, buffer_byte);
ets_delay_us(10);
buffer_byte &= ~(1 << EN);
i2c_write_byte(lcd, buffer_byte);
ets_delay_us(50);
if (data_byte == CLEAR_DISPLAY) {
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void lcd_i2c_init(lcd_i2c_handle_t * lcd) {
vTaskDelay(10 / portTICK_PERIOD_MS);
lcd_i2c_write(lcd, 0, RETURN_HOME_UNSHIFT);
lcd_i2c_write(lcd, 0, SET_4BIT_MODE);
lcd_i2c_write(lcd, 0, DISPLAY_ON_CURSOR_OFF);
lcd_i2c_write(lcd, 0, CURSOR_RIGHT_NO_SHIFT_LEFT);
lcd_i2c_write(lcd, 0, CLEAR_DISPLAY);
vTaskDelay(10 / portTICK_PERIOD_MS);
}
void lcd_i2c_cursor_set(lcd_i2c_handle_t * lcd, uint8_t column, uint8_t row) {
if (lcd->size == DISPLAY_16X02) {
if (row) lcd_i2c_write(lcd, 0, 0x80 + 0x40 + column);
else lcd_i2c_write(lcd, 0, 0x80 + column);
}
else if (lcd->size == DISPLAY_20X04) {
switch (row) {
case 0:
lcd_i2c_write(lcd, 0, 0x80 + column);
break;
case 1:
lcd_i2c_write(lcd, 0, 0x80 + 0x40 + column);
break;
case 2:
lcd_i2c_write(lcd, 0, 0x80 + 0x14 + column);
break;
case 3:
lcd_i2c_write(lcd, 0, 0x80 + 0x54 + column);
break;
default:
break;
}
}
}
void lcd_i2c_custom_char(lcd_i2c_handle_t * lcd, char char_address, const char * pixels) {
lcd_i2c_write(lcd, 0, 0x40 | (char_address << 3));
for (uint8_t i = 0; i < 8; i++) {
lcd_i2c_write(lcd, 1, pixels[i]);
}
lcd_i2c_write(lcd, 0, RETURN_HOME);
}
void lcd_i2c_print(lcd_i2c_handle_t * lcd, const char * format_string, ...) {
uint16_t i = 0;
char buffer_string[128];
va_list arguments;
va_start(arguments, format_string);
vsnprintf(buffer_string, sizeof(buffer_string), format_string, arguments);
va_end(arguments);
while (buffer_string[i] != '\0') {
lcd_i2c_write(lcd, 1, buffer_string[i]);
i++;
}
}
// FIM DA BIBLIOTECA DO LCD ----------------------------------------------
// INÍCIO DA BIBLIOTECA DO DHT -------------------------------------------
#define DHT_OK 0
#define DHT_TIMEOUT 1
#define DHT_CHECKSUM_FAIL 2
#define DHT_NOT_READY 3
typedef struct {
uint8_t gpio;
uint64_t time;
uint64_t buffer;
} dht_handle_t;
uint8_t dht_get_raw(dht_handle_t * dht) {
uint8_t count = 0;
uint64_t pulse_start;
uint8_t checksum;
if (dht->time > esp_timer_get_time()) {
dht->time = esp_timer_get_time();
}
if (esp_timer_get_time() - dht->time > 2e6) {
dht->buffer = 0;
gpio_set_direction(dht->gpio, GPIO_MODE_OUTPUT);
gpio_set_level(dht->gpio, 0);
vTaskDelay(20 / portTICK_PERIOD_MS);
gpio_set_level(dht->gpio, 1);
gpio_set_direction(dht->gpio, GPIO_MODE_INPUT);
gpio_pullup_en(dht->gpio);
dht->time = esp_timer_get_time();
while (count < 41) {
while (!gpio_get_level(dht->gpio)) {
if (esp_timer_get_time() - dht->time > 12000) {
return DHT_TIMEOUT;
}
}
pulse_start = esp_timer_get_time();
while (gpio_get_level(dht->gpio)) {
if (esp_timer_get_time() - dht->time > 12000) {
return DHT_TIMEOUT;
}
}
if (count && (esp_timer_get_time() - pulse_start > 60)) {
dht->buffer |= (1LL) << (40 - count);
}
count++;
}
checksum = 0;
for (uint8_t i = 8; i < 40; i += 8) {
checksum += (dht->buffer >> i) & 0xFF;
}
if (checksum == (dht->buffer & 0xFF)) {
return DHT_OK;
}
else {
return DHT_CHECKSUM_FAIL;
}
dht->time = esp_timer_get_time();
}
return DHT_NOT_READY;
}
float dht_get_temperature(dht_handle_t * dht) {
if ((dht->buffer >> 16) & 0x80) {
return ((dht->buffer >> 8) & 0x7FFF) / -10.0;
}
else {
return ((dht->buffer >> 8) & 0x7FFF) / 10.0;
}
}
float dht_get_humidity(dht_handle_t * dht) {
dht_get_raw(dht);
return ((dht->buffer >> 24) & 0xFFFF) / 10.0;
}
// FIM DA BIBLIOTECA DO DHT ----------------------------------------------
// INÍCIO DA BIBLIOTECA DO LDR -------------------------------------------
#define ADC_1 1
#define ADC_2 2
typedef struct {
uint8_t adc_device;
uint8_t adc_channel;
float k1;
float k2;
} ldr_handle_t;
float ldr_get_illumination(ldr_handle_t * ldr) {
if (ldr->adc_device == ADC_2) {
int buffer;
adc2_get_raw(ldr->adc_channel, ADC_WIDTH_BIT_12, &buffer);
return ldr->k1 + ldr->k2 * log((4096.0 / buffer) - 1);
}
else {
return ldr->k1 + ldr->k2 * log((4096.0 / adc1_get_raw(ldr->adc_channel)) - 1);
}
}
// FIM DA BIBLIOTECA DO LDR ----------------------------------------------
// INÍCIO DA BIBLIOTECA DO BOTÃO -----------------------------------------
#define SINGLE_CLICK 4
#define DOUBLE_CLICK 7
#define LONG_PRESS 9
typedef struct {
uint8_t pin;
uint8_t active_level;
uint16_t debounce_time;
uint16_t double_click_timeout;
uint16_t long_press_time;
uint8_t state;
uint64_t time;
} button_handle_t;
void button_begin(button_handle_t * button,
uint8_t pin,
uint8_t active_level,
uint16_t debounce_time,
uint16_t double_click_timeout,
uint16_t long_press_time) {
button->pin = pin;
button->active_level = active_level;
button->debounce_time = debounce_time;
button->double_click_timeout = double_click_timeout;
button->long_press_time = long_press_time;
gpio_set_direction(button->pin, GPIO_MODE_INPUT);
if (button->active_level) gpio_set_pull_mode(button->pin, GPIO_PULLDOWN_ONLY);
else gpio_set_pull_mode(button->pin, GPIO_PULLUP_ONLY);
}
uint8_t button_tick(button_handle_t * button) {
switch (button->state) {
case 0:
if (gpio_get_level(button->pin) == button->active_level) {
button->time = esp_timer_get_time() / 1000;
button->state = 1;
}
break;
case 1:
if (esp_timer_get_time() / 1000 - button->time > button->debounce_time) {
if (gpio_get_level(button->pin) == button->active_level) {
button->state = 2;
button->time = esp_timer_get_time() / 1000;
}
else button->state = 0;
}
break;
case 2:
if (gpio_get_level(button->pin) != button->active_level) {
button->state = 3;
button->time = esp_timer_get_time() / 1000;
}
if (esp_timer_get_time() / 1000 - button->time > button->long_press_time) {
button->state = 9;
button->time = esp_timer_get_time() / 1000;
};
break;
case 3:
if ((esp_timer_get_time() / 1000 - button->time > button->debounce_time)) {
if (gpio_get_level(button->pin) == button->active_level) {
button->state = 5;
button->time = esp_timer_get_time() / 1000;
}
}
if (esp_timer_get_time() / 1000 - button->time > button->double_click_timeout) {
if (gpio_get_level(button->pin) != button->active_level) button->state = 4;
}
break;
case 4:
button->state = 0;
break;
case 5:
if (esp_timer_get_time() / 1000 - button->time > button->debounce_time) {
if (gpio_get_level(button->pin) == button->active_level) button->state = 6;
else button->state = 0;
}
break;
case 6:
if (gpio_get_level(button->pin) != button->active_level) {
button->state = 7;
button->time = esp_timer_get_time() / 1000;
}
break;
case 7:
button->state = 8;
break;
case 8:
if ((esp_timer_get_time() / 1000 - button->time > button->debounce_time)) {
button->state = 0;
}
break;
case 9:
button->state = 10;
break;
case 10:
if ((esp_timer_get_time() / 1000 - button->time > button->debounce_time)) {
if (gpio_get_level(button->pin) != button->active_level) button->state = 0;
}
break;
default:
break;
}
return button->state;
}
// FIM DA BIBLIOTECA DO BOTÃO --------------------------------------------
// INÍCIO DO CÓDIGO ESPECÍFICO DO PROJETO --------------------------------
#define BTN_UP 0
#define BTN_DN 1
typedef struct {
lcd_i2c_handle_t * display;
dht_handle_t * dht;
ldr_handle_t * ldr;
button_handle_t * button;
SemaphoreHandle_t semaphore;
uint8_t count;
} task_struct;
void animate_task(void * parameters) {
task_struct * data = (task_struct *)parameters;
while (!xSemaphoreTake(data->semaphore, 100 / portTICK_PERIOD_MS));
lcd_i2c_cursor_set(data->display, 0, 1);
lcd_i2c_write(data->display, 1, BTN_UP);
lcd_i2c_cursor_set(data->display, 10, 1);
lcd_i2c_write(data->display, 1, BTN_UP);
xSemaphoreGive(data->semaphore);
while (1) {
while (!xSemaphoreTake(data->semaphore, 100 / portTICK_PERIOD_MS));
lcd_i2c_cursor_set(data->display, 0, 1);
lcd_i2c_write(data->display, 1, BTN_UP);
xSemaphoreGive(data->semaphore);
vTaskDelay(100 / portTICK_PERIOD_MS);
while (!xSemaphoreTake(data->semaphore, 100 / portTICK_PERIOD_MS));
lcd_i2c_cursor_set(data->display, 0, 1);
lcd_i2c_write(data->display, 1, BTN_DN);
xSemaphoreGive(data->semaphore);
vTaskDelay(100 / portTICK_PERIOD_MS);
while (!xSemaphoreTake(data->semaphore, 100 / portTICK_PERIOD_MS));
lcd_i2c_cursor_set(data->display, 0, 1);
lcd_i2c_write(data->display, 1, BTN_UP);
xSemaphoreGive(data->semaphore);
vTaskDelay(1100 / portTICK_PERIOD_MS);
while (!xSemaphoreTake(data->semaphore, 100 / portTICK_PERIOD_MS));
lcd_i2c_cursor_set(data->display, 10, 1);
lcd_i2c_write(data->display, 1, BTN_UP);
xSemaphoreGive(data->semaphore);
vTaskDelay(100 / portTICK_PERIOD_MS);
while (!xSemaphoreTake(data->semaphore, 100 / portTICK_PERIOD_MS));
lcd_i2c_cursor_set(data->display, 10, 1);
lcd_i2c_write(data->display, 1, BTN_DN);
xSemaphoreGive(data->semaphore);
vTaskDelay(100 / portTICK_PERIOD_MS);
while (!xSemaphoreTake(data->semaphore, 100 / portTICK_PERIOD_MS));
lcd_i2c_cursor_set(data->display, 10, 1);
lcd_i2c_write(data->display, 1, BTN_UP);
xSemaphoreGive(data->semaphore);
vTaskDelay(100 / portTICK_PERIOD_MS);
while (!xSemaphoreTake(data->semaphore, 100 / portTICK_PERIOD_MS));
lcd_i2c_cursor_set(data->display, 10, 1);
lcd_i2c_write(data->display, 1, BTN_DN);
xSemaphoreGive(data->semaphore);
vTaskDelay(100 / portTICK_PERIOD_MS);
while (!xSemaphoreTake(data->semaphore, 100 / portTICK_PERIOD_MS));
lcd_i2c_cursor_set(data->display, 10, 1);
lcd_i2c_write(data->display, 1, BTN_UP);
xSemaphoreGive(data->semaphore);
vTaskDelay(1100 / portTICK_PERIOD_MS);
}
}
void display_task(void * parameters) {
task_struct * data = (task_struct *)parameters;
uint8_t last_count = 0;
while (1) {
while (!xSemaphoreTake(data->semaphore, 100 / portTICK_PERIOD_MS));
if (last_count != (data->count & 0x0F)) {
lcd_i2c_cursor_set(data->display, 0, 0);
if ((data->count & 0x0F) == 0) lcd_i2c_print(data->display, "Temp");
else if ((data->count & 0x0F) == 1) lcd_i2c_print(data->display, "Umid");
else if ((data->count & 0x0F) == 2) lcd_i2c_print(data->display, "Ilum");
last_count = data->count & 0x0F;
}
dht_get_raw(data->dht);
lcd_i2c_cursor_set(data->display, 5, 0);
if ((data->count & 0x0F) == 0) {
lcd_i2c_print(data->display, "%.1f%cC ", dht_get_temperature(data->dht), 0xDF);
}
else if ((data->count & 0x0F) == 1) {
lcd_i2c_print(data->display, "%.1f%% ", dht_get_humidity(data->dht));
}
else if ((data->count & 0x0F) == 2) {
lcd_i2c_print(data->display, "%.1f%% ", ldr_get_illumination(data->ldr));
}
xSemaphoreGive(data->semaphore);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void changer_task(void * parameters) {
task_struct * data = (task_struct *)parameters;
while (1) {
while (!xSemaphoreTake(data->semaphore, 100 / portTICK_PERIOD_MS));
if (!(data->count & 0xF0)) {
data->count++;
if ((data->count & 0x0F) > 2) data->count &= 0xF0;
}
xSemaphoreGive(data->semaphore);
vTaskDelay(10000 / portTICK_PERIOD_MS);
}
}
void button_task(void * parameters) {
task_struct * data = (task_struct *)parameters;
uint8_t command;
while (1) {
while (!xSemaphoreTake(data->semaphore, 10 / portTICK_PERIOD_MS));
command = button_tick(data->button);
if (command == SINGLE_CLICK) {
data->count++;
if ((data->count & 0xF) > 2) data->count &= 0xF0;
}
else if (command == DOUBLE_CLICK) {
data->count ^= (1 << 7);
if (data->count & 0xF0) {
lcd_i2c_cursor_set(data->display, 11, 1);
lcd_i2c_print(data->display, "Segue");
}
else {
lcd_i2c_cursor_set(data->display, 11, 1);
lcd_i2c_print(data->display, "Pausa");
}
}
xSemaphoreGive(data->semaphore);
vTaskDelay(20 / portTICK_PERIOD_MS);
}
}
void i2c_init() {
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = 23,
.sda_pullup_en = 1,
.scl_io_num = 22,
.scl_pullup_en = 1,
.master.clk_speed = 100000,
};
i2c_param_config(I2C_NUM_1, &i2c_config);
i2c_driver_install(I2C_NUM_1, I2C_MODE_MASTER, 0, 0, 0);
}
void display_start(lcd_i2c_handle_t * display) {
const char btn_up[8] = {0x00, 0x00, 0x0E, 0x0E, 0x0E, 0x1F, 0x00, 0x00};
const char btn_dn[8] = {0x00, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0x00, 0x00};
lcd_i2c_init(display);
lcd_i2c_custom_char(display, BTN_UP, btn_up);
lcd_i2c_custom_char(display, BTN_DN, btn_dn);
lcd_i2c_cursor_set(display, 4, 0);
lcd_i2c_print(display, ":");
lcd_i2c_cursor_set(display, 1, 1);
lcd_i2c_print(display, "Troca");
lcd_i2c_cursor_set(display, 11, 1);
lcd_i2c_print(display, "Pausa");
}
void app_main() {
esp_timer_early_init();
i2c_init();
lcd_i2c_handle_t display = {
.address = 0x27,
.num = I2C_NUM_1,
.backlight = 1,
.size = DISPLAY_16X02
};
display_start(&display);
dht_handle_t dht22 = {.gpio = 21};
ldr_handle_t ldr = {
.adc_device = ADC_1,
.adc_channel = ADC1_CHANNEL_7,
.k1 = 49.95095,
.k2 = 10.34034
};
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC1_CHANNEL_7, ADC_ATTEN_DB_11);
button_handle_t button = {
.pin = 2,
.active_level = 0,
.debounce_time = 10,
.double_click_timeout = 300,
.long_press_time = 750
};
task_struct sensor_data = {
.display = &display,
.dht = &dht22,
.ldr = &ldr,
.button = &button,
.semaphore = xSemaphoreCreateMutex(),
.count = 2
};
xSemaphoreGive(sensor_data.semaphore);
xTaskCreatePinnedToCore(&animate_task, "Animate", 4096, &sensor_data, 2, NULL, 1);
xTaskCreatePinnedToCore(&display_task, "Display", 8192, &sensor_data, 2, NULL, 1);
xTaskCreatePinnedToCore(&changer_task, "Changer", 4096, &sensor_data, 1, NULL, 1);
xTaskCreatePinnedToCore(&button_task, "Button", 4096, &sensor_data, 3, NULL, 1);
while (1) vTaskDelay(1000 / portTICK_PERIOD_MS);
}
// FIM DO CÓDIGO ESPECÍFICO DO PROJETO -----------------------------------