#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#include "esp_err.h"
#include "esp_adc/adc_oneshot.h"
#define TAG_I "TEMP_CTRL_INFO"
#define TAG_E "TEMP_CTRL_ERROR"
#define CONFIG_LOG_DEFAULT_LEVEL_VERBOSE 1
#define CONFIG_LOG_MAXIMUM_LEVEL 5
#define ADC_CHANNEL ADC_CHANNEL_4 // Канал АЦП для потенциометра
#define DHT_PIN (12) // GPIO, подключенный к DHT22
#define LED_HEAT GPIO_NUM_21 // GPIO для красного светодиода
#define LED_COOL GPIO_NUM_22 // GPIO для зелёного светодиода
#define ADC_MAX_VALUE 4095
#define TEMP_MIN -40
#define TEMP_MAX 80
// Объявляем глобальную переменную для хэндла очереди для передачи данных
static QueueHandle_t adc_queue = NULL;
// Объявляем глобальную переменную для хэндла АЦП
static adc_oneshot_unit_handle_t adc1_handle;
// Функция для инициализации АЦП
static void init_adc(void) {
// Конфигурация блока АЦП (ADC1)
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = ADC_UNIT_1,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
// Конфигурация канала АЦП
adc_oneshot_chan_cfg_t config = {
.bitwidth = ADC_BITWIDTH_DEFAULT,
.atten = ADC_ATTEN_DB_12, // 12 дБ для диапазона напряжения до 3.3 В
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC_CHANNEL, &config));
ESP_LOGI(TAG_I, "ADC initialized successfully");
}
// возсожные значения статуса работы с dht
typedef enum {
DHT_OK,
DHT_TIMEOUT,
DHT_CHECKSUM_FAIL,
DHT_NOT_READY,
} dht_state_t;
//необходимые составляющие хэндлера dht
typedef struct {
gpio_num_t gpio;
uint64_t time;
uint64_t buffer;
} dht_handle_t;
//функция получения данных с датчика температур
dht_state_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(pdMS_TO_TICKS(20));
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) {
ESP_LOGE(TAG_E, "DHT Raw Data 1 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) {
ESP_LOGE(TAG_E, "DHT Raw Data 2 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 {
ESP_LOGE(TAG_E, "DHT Raw Data !(checksum == (dht->buffer & 0xFF))");
return DHT_CHECKSUM_FAIL;
}
dht->time = esp_timer_get_time();
}
ESP_LOGW(TAG_E, "DHT Raw Data DHT_NOT_READY");
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;
}
}
static void init_leds(void) {
gpio_set_direction(LED_HEAT, GPIO_MODE_OUTPUT);
gpio_set_direction(LED_COOL, GPIO_MODE_OUTPUT);
gpio_set_level(LED_HEAT, 0);
gpio_set_level(LED_COOL, 0);
ESP_LOGI(TAG_I, "LEDs initialized successfully");
}
// Функция масштабирования значения потенциометра в диапазон температуры
static float scale_adc_to_temp(int adc_value) {
return TEMP_MIN + ((float)adc_value / ADC_MAX_VALUE) * (TEMP_MAX - TEMP_MIN);
}
// ADC task to read data and push to the queue
void adc_task(void *arg) {
while (1) {
int adc_raw = 0;
ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, ADC_CHANNEL, &adc_raw));
// добавляем значение адс в очередь
if (xQueueSend(adc_queue, &adc_raw, portMAX_DELAY) != pdPASS) {
ESP_LOGE(TAG_E, "Failed to send ADC value to queue");
}
vTaskDelay(pdMS_TO_TICKS(100));
}
ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle));
}
// Задача для считывания данных с DHT22 и сравнения с целевой температурой
void temp_control_task(void *arg) {
dht_handle_t dht22 = {
.gpio = DHT_PIN,
.time = 0,
};
int before = 0;
while (1) {
float temperature = 0;
int adc_raw = 0;
float current_temp = 0;
// ждём данные от ацп
if (xQueueReceive(adc_queue, &adc_raw, portMAX_DELAY) == pdPASS) {
// Чтение данных с DHT22
if (dht_get_raw(&dht22) == DHT_OK) {
temperature = dht_get_temperature(&dht22);
ESP_LOGI(TAG_I, "Target Temperature: %.1f C", temperature);
}
if (before == adc_raw) {
continue;
}
before = adc_raw;
current_temp = scale_adc_to_temp(adc_raw);
ESP_LOGI(TAG_I, "Current Temperature: %.1f C", current_temp);
// Логика управления на основе сравнения температуры
if (current_temp < temperature) {
ESP_LOGI(TAG_I, "Current temperature is below target. Heating ON");
gpio_set_level(LED_HEAT, 1);
gpio_set_level(LED_COOL, 0);
} else {
ESP_LOGI(TAG_I, "Current temperature is above target. Cooling ON");
gpio_set_level(LED_HEAT, 0);
gpio_set_level(LED_COOL, 1);
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void app_main(void) {
esp_log_level_set(TAG_E, ESP_LOG_ERROR);
esp_log_level_set(TAG_I, ESP_LOG_INFO);
ESP_LOGI(TAG_I, "Starting Temperature Control Application");
// Инициализация АЦП
init_adc();
init_leds();
// создаём очередь для омена данными
adc_queue = xQueueCreate(10, sizeof(int));
if (adc_queue == NULL) {
ESP_LOGE(TAG_E, "Failed to create queue");
return;
}
// Задача для считывания с АЦП
if (xTaskCreate(adc_task, "ADC Task", 2048, NULL, 5, NULL) != pdPASS) {
ESP_LOGE(TAG_E, "Error creating ADC task");
}
// Задача контроля температуры
if (xTaskCreate(temp_control_task, "Temp Control Task", 4096, NULL, 5, NULL) != pdPASS) {
ESP_LOGE(TAG_E, "Error creating Temp Control task");
}
}