2026-06-05 00:38:34 +03:00

517 lines
18 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <stdint.h>
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/event_groups.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <driver/gpio.h>
#include <esp_event.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <lwip/err.h>
#include <lwip/sys.h>
#include "esp_http_server.h"
#include <stdio.h>
#include "freertos/projdefs.h"
#include "freertos/task.h"
#include "esp_log.h"
#include <stdatomic.h>
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
// ==================== НАСТРОЙКИ ТОЧКИ ДОСТУПА ====================
#define AP_SSID "ESP32_Hotspot" // Имя Wi-Fi сети
#define AP_PASS "12345678" // Пароль (минимум 8 символов)
#define AP_MAX_CONN 4 // Максимум клиентов
#define AP_CHANNEL 6 // Wi-Fi канал
#define ADC_CHAN0 ADC_CHANNEL_4
#define ADC_CHAN1 ADC_CHANNEL_5
#define ADC_ATTEN_DB ADC_ATTEN_DB_12
static adc_oneshot_unit_handle_t adc_handle;
static adc_cali_handle_t cali_handle;
static bool is_calibrated = false;
// ==================== ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ====================
static const char *TAG = "ESP32_AP_SERVER";
atomic_int low_treshhold = 0;
atomic_int up_treshhold = 0;
esp_err_t adc_init(void)
{
// Инициализация ADC
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = ADC_UNIT_1,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc_handle));
// Конфигурация каналов
adc_oneshot_chan_cfg_t config = {
.atten = ADC_ATTEN_DB,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHAN0, &config));
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHAN1, &config));
// Калибровка для ESP32 (Line Fitting)
adc_cali_line_fitting_config_t cali_config = {
.unit_id = ADC_UNIT_1,
.atten = ADC_ATTEN_DB,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
esp_err_t ret = adc_cali_create_scheme_line_fitting(&cali_config, &cali_handle);
if (ret == ESP_OK) {
is_calibrated = true;
ESP_LOGI(TAG, "ADC калибровка успешна");
} else if (ret == ESP_ERR_NOT_SUPPORTED) {
ESP_LOGW(TAG, "Калибровка не доступна (eFuse не записан)");
} else {
ESP_LOGE(TAG, "Ошибка калибровки");
}
return ESP_OK;
}
int adc_read_raw(uint8_t channel)
{
int raw_value = 0;
adc_channel_t adc_channel;
if (channel == 0) {
adc_channel = ADC_CHAN0;
} else if (channel == 1) {
adc_channel = ADC_CHAN1;
} else {
ESP_LOGE(TAG, "Неверный канал: %d", channel);
return -1;
}
esp_err_t ret = adc_oneshot_read(adc_handle, adc_channel, &raw_value);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Ошибка чтения ADC");
return -1;
}
return raw_value;
}
int adc_read_voltage(uint8_t channel)
{
int raw_value = adc_read_raw(channel);
if (raw_value < 0) return -1;
if (is_calibrated) {
int voltage_mv = 0;
esp_err_t ret = adc_cali_raw_to_voltage(cali_handle, raw_value, &voltage_mv);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Ошибка конвертации в напряжение");
return -1;
}
return voltage_mv;
} else {
// Приблизительный расчет без калибровки (12-bit ADC: 0-4095 -> 0-3300mV)
return (raw_value * 3300) / 4095;
}
}
// ==================== ОБРАБОТЧИКИ HTTP ЗАПРОСОВ ====================
// Обработчик главной страницы
static esp_err_t root_get_handler(httpd_req_t *req)
{
const char* response =
"<!DOCTYPE html>"
"<html>"
"<head>"
" <title>ESP32 Access Point Server</title>"
" <meta charset='utf-8'>"
" <meta name='viewport' content='width=device-width, initial-scale=1'>"
" <style>"
" * { margin: 0; padding: 0; box-sizing: border-box; }"
" body {"
" font-family: 'Segoe UI', Arial, sans-serif;"
" background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);"
" min-height: 100vh;"
" padding: 20px;"
" }"
" .container {"
" max-width: 800px;"
" margin: 0 auto;"
" background: white;"
" border-radius: 20px;"
" padding: 30px;"
" box-shadow: 0 20px 60px rgba(0,0,0,0.3);"
" }"
" h1 {"
" color: #667eea;"
" margin-bottom: 20px;"
" text-align: center;"
" }"
" .status-card {"
" background: #f0f0f0;"
" border-radius: 10px;"
" padding: 20px;"
" margin: 20px 0;"
" }"
" .info {"
" margin: 10px 0;"
" padding: 10px;"
" background: #e8f5e9;"
" border-left: 4px solid #4CAF50;"
" }"
" .button {"
" background: #667eea;"
" color: white;"
" border: none;"
" padding: 12px 24px;"
" border-radius: 8px;"
" cursor: pointer;"
" font-size: 16px;"
" margin: 5px;"
" transition: transform 0.2s;"
" }"
" .button:hover {"
" transform: scale(1.05);"
" }"
" .gpio-control {"
" margin: 20px 0;"
" padding: 15px;"
" background: #fff3e0;"
" border-radius: 10px;"
" }"
" .led-status {"
" display: inline-block;"
" width: 20px;"
" height: 20px;"
" border-radius: 50%;"
" margin-left: 10px;"
" }"
" .led-on { background: #4CAF50; box-shadow: 0 0 10px #4CAF50; }"
" .led-off { background: #f44336; }"
" </style>"
"</head>"
"<body>"
" <div class='container'>"
" <h1>🎯 ESP32 Точка Доступа</h1>"
" <div class='status-card'>"
" <h3>📡 Информация о сервере</h3>"
" <div class='info'>✅ HTTP сервер работает</div>"
" <div class='info'>🌐 IP адрес: 192.168.4.1</div>"
" <div class='info'>💾 Свободно памяти: <span id='heap'>0</span> байт</div>"
" <div class='info'>🕐 Время работы: <span id='uptime'>0</span> сек</div>"
" </div>"
" <div class='gpio-control'>"
" <h3>💡 Управление GPIO2 (встроенный LED)</h3>"
" <button class='button' onclick='controlGPIO(1)'>🔆 ВКЛ</button>"
" <button class='button' onclick='controlGPIO(0)'>💡 ВЫКЛ</button>"
" <span id='ledIndicator' class='led-status led-off'></span>"
" </div>"
" <div class='gpio-control'>"
" <h3>📊 Получить данные с датчика</h3>"
" <button class='button' onclick='getSensorData()'>📈 Обновить</button>"
" <div id='sensorData' class='info' style='margin-top:10px;'>Данные не загружены</div>"
" </div>"
" </div>"
" <script>"
" function controlGPIO(state) {"
" fetch('/gpio?state=' + state)"
" .then(response => response.json())"
" .then(data => {"
" const indicator = document.getElementById('ledIndicator');"
" if(data.status === 'on') {"
" indicator.className = 'led-status led-on';"
" } else {"
" indicator.className = 'led-status led-off';"
" }"
" });"
" }"
" function getSensorData() {"
" fetch('/sensor')"
" .then(response => response.json())"
" .then(data => {"
" document.getElementById('sensorData').innerHTML = "
" '📊 Значение: ' + data.value + '<br>' +"
" '📝 Сообщение: ' + data.message;"
" });"
" }"
" function updateStats() {"
" fetch('/stats')"
" .then(response => response.json())"
" .then(data => {"
" document.getElementById('heap').innerText = data.free_heap;"
" document.getElementById('uptime').innerText = data.uptime;"
" });"
" }"
" setInterval(updateStats, 2000);"
" getSensorData();"
" </script>"
"</body>"
"</html>";
httpd_resp_set_type(req, "text/html; charset=utf-8");
httpd_resp_send(req, response, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
// Управление GPIO
static esp_err_t gpio_handler(httpd_req_t *req)
{
char param[10];
char state_str[10];
// Получаем параметр state из URL
if (httpd_req_get_url_query_str(req, param, sizeof(param)) == ESP_OK) {
if (httpd_query_key_value(param, "state", state_str, sizeof(state_str)) == ESP_OK) {
int state = atoi(state_str);
// Настройка GPIO2 (встроенный LED на многих ESP32)
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << 2),
.mode = GPIO_MODE_OUTPUT,
.intr_type = GPIO_INTR_DISABLE,
.pull_down_en = 0,
.pull_up_en = 0,
};
gpio_config(&io_conf);
gpio_set_level(2, state);
char response[100];
snprintf(response, sizeof(response), "{\"status\":\"%s\"}", state ? "on" : "off");
httpd_resp_set_type(req, "application/json");
httpd_resp_send(req, response, strlen(response));
return ESP_OK;
}
}
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Missing state parameter");
return ESP_FAIL;
}
// Имитация датчика
static esp_err_t sensor_handler(httpd_req_t *req) {
int volt0 = adc_read_voltage(0);
int sensor_value = volt0;
char response[100];
snprintf(response, sizeof(response),
"{\"value\":%d,\"message\":\"Случайное значение датчика\"}",
sensor_value);
httpd_resp_set_type(req, "application/json");
httpd_resp_send(req, response, strlen(response));
return ESP_OK;
}
// Статистика системы
static esp_err_t stats_handler(httpd_req_t *req)
{
char response[200];
snprintf(response, sizeof(response),
"{\"free_heap\":%lu,\"uptime\":%d}",
esp_get_free_heap_size(),
(int)(xTaskGetTickCount() * portTICK_PERIOD_MS / 1000));
httpd_resp_set_type(req, "application/json");
httpd_resp_send(req, response, strlen(response));
return ESP_OK;
}
// ==================== ИНИЦИАЛИЗАЦИЯ ТОЧКИ ДОСТУПА ====================
void wifi_init_softap(void)
{
// Инициализация сетевого интерфейса
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_ap();
// Инициализация Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// Настройка точки доступа
wifi_config_t wifi_config = {
.ap = {
.ssid = AP_SSID,
.ssid_len = strlen(AP_SSID),
.password = AP_PASS,
.max_connection = AP_MAX_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK,
.channel = AP_CHANNEL,
},
};
// Если пароль не задан - открытая сеть
if (strlen(AP_PASS) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "=========================================");
ESP_LOGI(TAG, "📡 Точка доступа запущена");
ESP_LOGI(TAG, "📶 Имя сети (SSID): %s", AP_SSID);
ESP_LOGI(TAG, "🔑 Пароль: %s", strlen(AP_PASS) ? AP_PASS : "ОТКРЫТАЯ СЕТЬ");
ESP_LOGI(TAG, "🌐 IP адрес сервера: 192.168.4.1");
ESP_LOGI(TAG, "=========================================");
}
// void setup_adc() {
// // Настройка разрешения (9-12 бит)
// adc1_config_width(ADC_WIDTH_BIT_12);
//
// // Настройка аттенюации (диапазон входного напряжения)
// adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);
// }
//
// int read_mid_pressure(void) {
// int N = 5;
// int pause = 20;
//
// int sum = 0;
// for (int i = 0; i < N; i++) {
// sum += adc1_get_raw(ADC1_CHANNEL_0);
// vTaskDelay(pdMS_TO_TICKS(pause));
// }
//
// return sum /= N;
// }
// static void pressure_control_task(void *pvParameters) {
// gpio_config_t io_conf = {
// .pin_bit_mask = (1ULL << 2),
// .mode = GPIO_MODE_OUTPUT,
// .intr_type = GPIO_INTR_DISABLE,
// .pull_down_en = 1,
// .pull_up_en = 0,
// };
// gpio_config(&io_conf);
//
// while(1) {
// int low_treshhold_pressure = atomic_load(&low_treshhold);
// int up_treshhold_pressure = atomic_load(&up_treshhold);
//
// int pressure = read_mid_pressure();
//
// if (pressure < low_treshhold_pressure) {
// gpio_set_level(2, 1);
// } else if (pressure >= up_treshhold_pressure) {
// gpio_set_level(2, 0);
// }
//
// vTaskDelay(pdMS_TO_TICKS(400));
// }
// }
// ==================== ЗАДАЧА HTTP СЕРВЕРА ====================
static void http_server_task(void *pvParameters)
{
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.max_uri_handlers = 10;
config.stack_size = 8192;
// Запуск HTTP сервера
if (httpd_start(&server, &config) == ESP_OK) {
ESP_LOGI(TAG, "🚀 HTTP сервер запущен на порту %d", config.server_port);
// Регистрация URI обработчиков
httpd_uri_t root = {
.uri = "/",
.method = HTTP_GET,
.handler = root_get_handler,
.user_ctx = NULL
};
httpd_register_uri_handler(server, &root);
httpd_uri_t gpio = {
.uri = "/gpio",
.method = HTTP_GET,
.handler = gpio_handler,
.user_ctx = NULL
};
httpd_register_uri_handler(server, &gpio);
httpd_uri_t sensor = {
.uri = "/sensor",
.method = HTTP_GET,
.handler = sensor_handler,
.user_ctx = NULL
};
httpd_register_uri_handler(server, &sensor);
httpd_uri_t stats = {
.uri = "/stats",
.method = HTTP_GET,
.handler = stats_handler,
.user_ctx = NULL
};
httpd_register_uri_handler(server, &stats);
ESP_LOGI(TAG, "📝 Зарегистрированы обработчики:");
ESP_LOGI(TAG, " - GET / (главная страница)");
ESP_LOGI(TAG, " - GET /gpio (управление LED)");
ESP_LOGI(TAG, " - GET /sensor (чтение датчика)");
ESP_LOGI(TAG, " - GET /stats (статистика)");
} else {
ESP_LOGE(TAG, "❌ Ошибка запуска HTTP сервера");
}
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// ==================== ГЛАВНАЯ ФУНКЦИЯ ====================
void app_main(void)
{
// Инициализация NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
adc_init();
ESP_LOGI(TAG, "=========================================");
ESP_LOGI(TAG, "ESP32 Точка Доступа + HTTP Сервер");
ESP_LOGI(TAG, "=========================================");
// Инициализация точки доступа
wifi_init_softap();
// Небольшая задержка для стабилизации
vTaskDelay(pdMS_TO_TICKS(1000));
// Создание задачи HTTP сервера
xTaskCreate(http_server_task, "http_server", 8192, NULL, 5, NULL);
// Демонстрационная задача для имитации датчика
ESP_LOGI(TAG, "✅ Система готова к работе");
ESP_LOGI(TAG, "📱 Подключитесь к Wi-Fi: %s", AP_SSID);
ESP_LOGI(TAG, "🌐 Откройте браузер: http://192.168.4.1");
while (1) {
// Вывод информации о подключенных клиентах каждые 10 секунд
vTaskDelay(pdMS_TO_TICKS(10000));
wifi_sta_list_t sta_list;
memset(&sta_list, 0, sizeof(sta_list));
esp_wifi_ap_get_sta_list(&sta_list);
ESP_LOGI(TAG, "📊 Подключено клиентов: %d", sta_list.num);
ESP_LOGI(TAG, "💾 Свободно памяти: %d байт", esp_get_free_heap_size());
}
}