Загрузка данных
// =====================================================================
// Сушилка для овощей на ESP8266 (Witty Cloud)
// Framework: ESP8266 Non-OS SDK (PlatformIO: framework = esp8266-nonos-sdk)
// - SSR + ПИД + медленный ШИМ (2000 мс)
// - 3 семисегментных индикатора через PCF8574 (I2C)
// - 3 кнопки: MODE, UP, DOWN
// - Таймер до 999 минут, температура 30..99 °C целыми градусами
// - Wi-Fi AP + веб-интерфейс
// - Сохранение уставок во flash
// - UART TX работает для отладки через os_printf
// =====================================================================
#include "ets_sys.h"
#include "osapi.h"
#include "gpio.h"
#include "os_type.h"
#include "user_interface.h"
#include "mem.h"
#include "ip_addr.h"
#include "espconn.h"
#include "eagle_soc.h"
// ==================== ПИНЫ ====================
#define SSR_PIN 12
#define DS18B20_PIN 2
#define BTN_UP_PIN 4
#define BTN_DOWN_PIN 5
#define BTN_MODE_PIN 0 // ТРЕБУЕТ внешнего pullup 10 кОм
#define I2C_SDA_PIN 14
#define I2C_SCL_PIN 13
#define PCF8574_ADDR 0x20 // A0=A1=A2=GND
#define DIGIT1_PIN 16 // десятки (GPIO16, RTC)
#define DIGIT2_PIN 15 // единицы (GPIO15)
#define DIGIT3_PIN 3 // третий (RX, UART только TX!)
// ==================== ТАЙМИНГИ ====================
#define PWM_PERIOD_MS 2000
#define PID_SAMPLE_MS 1000
#define DISPLAY_REFRESH_MS 5
#define BTN_POLL_MS 50
#define LONG_PRESS_TICKS 40 // 40 * 50 мс = 2 сек
#define DP_BLINK_TICKS 100 // 100 * 5 мс = 500 мс
#define SECONDS_PER_MINUTE 60
// ==================== ПИД ====================
#define PID_KP 2.0
#define PID_KI 5.0
#define PID_KD 1.0
// ==================== ГРАНИЦЫ ====================
#define TEMP_MIN 30
#define TEMP_MAX 99
#define TEMP_STEP 1
#define TIMER_MIN_MIN 1
#define TIMER_MAX_MIN 999
#define TIMER_STEP_MIN 1
// ==================== FLASH ====================
#define FLASH_SECTOR 0x3C
#define FLASH_MAGIC 0xA5C3
// ==================== WI-FI ====================
#define AP_SSID "Dryer"
#define AP_PASS "12345678"
// ==================== РЕЖИМЫ ====================
enum { MODE_EDIT_TEMP = 0, MODE_EDIT_TIMER, MODE_RUNNING };
// ==================== ГЛОБАЛЬНЫЕ ====================
static os_timer_t pwm_timer, pid_timer, display_timer, btn_timer;
// ШИМ
static uint32_t pwm_counter = 0;
static uint32_t pwm_duty = 0;
// ПИД
static double pid_input = 0.0;
static double pid_output = 0.0;
static double pid_integral = 0.0;
static double pid_last_error = 0.0;
// Уставки
static int temp_setpoint = 50;
static uint32_t timer_setpoint_min = 5;
static uint32_t timer_remaining_min = 0;
static uint8_t sec_counter = 0;
// Режим
static uint8_t current_mode = MODE_EDIT_TEMP;
// Дисплей
static uint8_t disp_digits[3] = {0, 0, 0};
static uint8_t blank_digit[3] = {0, 0, 0};
static uint8_t display_idx = 0;
static bool dp_blink_state = true;
static uint32_t dp_blink_cnt = 0;
static uint8_t display_phase = 0;
// Кнопки
static uint8_t btn_up_last = 1;
static uint8_t btn_down_last = 1;
static uint8_t btn_mode_last = 1;
static uint32_t btn_mode_hold = 0;
// HTTP
static struct espconn http_conn;
static esp_tcp http_tcp;
// Таблица сегментов (a..g в битах 0..6)
static const uint8_t seg_table[10] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};
// ==================== ПРОТОТИПЫ ====================
static void ICACHE_FLASH_ATTR delay_us(uint32_t us);
static void ICACHE_FLASH_ATTR gpio16_set(uint8_t val);
static void ICACHE_FLASH_ATTR i2c_delay(void);
static void ICACHE_FLASH_ATTR i2c_start(void);
static void ICACHE_FLASH_ATTR i2c_stop(void);
static uint8_t ICACHE_FLASH_ATTR i2c_write_byte(uint8_t data);
static void ICACHE_FLASH_ATTR pcf8574_write(uint8_t data);
static void ICACHE_FLASH_ATTR ow_init(void);
static uint8_t ICACHE_FLASH_ATTR ow_reset(void);
static void ICACHE_FLASH_ATTR ow_write_bit(uint8_t b);
static uint8_t ICACHE_FLASH_ATTR ow_read_bit(void);
static void ICACHE_FLASH_ATTR ow_write_byte(uint8_t b);
static uint8_t ICACHE_FLASH_ATTR ow_read_byte(void);
static double ICACHE_FLASH_ATTR read_temperature(void);
static void ICACHE_FLASH_ATTR all_digits_off(void);
static void ICACHE_FLASH_ATTR select_digit(uint8_t idx);
static void ICACHE_FLASH_ATTR update_display(void);
static void ICACHE_FLASH_ATTR save_params(void);
static void ICACHE_FLASH_ATTR load_params(void);
static void ICACHE_FLASH_ATTR start_timer(void);
static void ICACHE_FLASH_ATTR stop_timer(void);
static void ICACHE_FLASH_ATTR pwm_timer_cb(void *arg);
static void ICACHE_FLASH_ATTR pid_timer_cb(void *arg);
static void ICACHE_FLASH_ATTR display_timer_cb(void *arg);
static void ICACHE_FLASH_ATTR btn_timer_cb(void *arg);
static void ICACHE_FLASH_ATTR http_recv_cb(void *arg, char *data, unsigned short len);
static void ICACHE_FLASH_ATTR http_sent_cb(void *arg);
static void ICACHE_FLASH_ATTR http_discon_cb(void *arg);
static void ICACHE_FLASH_ATTR http_recon_cb(void *arg, sint8 err);
static void ICACHE_FLASH_ATTR wifi_init_ap(void);
static void ICACHE_FLASH_ATTR http_init(void);
// ==================== ЗАДЕРЖКА ====================
static void ICACHE_FLASH_ATTR delay_us(uint32_t us) {
uint32_t start = system_get_time();
while (system_get_time() - start < us);
}
// ==================== GPIO16 ====================
static void ICACHE_FLASH_ATTR gpio16_set(uint8_t val) {
WRITE_PERI_REG(PAD_XPD_DCDC_CONF,
(READ_PERI_REG(PAD_XPD_DCDC_CONF) & 0xFFFFFFBC) | 0x01);
WRITE_PERI_REG(RTC_GPIO_CONF,
(READ_PERI_REG(RTC_GPIO_CONF) & 0xFFFFFFFE) | 0x00);
WRITE_PERI_REG(RTC_GPIO_ENABLE,
(READ_PERI_REG(RTC_GPIO_ENABLE) & 0xFFFFFFFE) | 0x01);
uint32_t out = READ_PERI_REG(RTC_GPIO_OUT);
if (val) out &= ~(1u << 0); // инверсная логика GPIO16
else out |= (1u << 0);
WRITE_PERI_REG(RTC_GPIO_OUT, out);
}
// ==================== I2C (программный) ====================
static void ICACHE_FLASH_ATTR i2c_delay(void) {
delay_us(4);
}
static void ICACHE_FLASH_ATTR i2c_start(void) {
GPIO_OUTPUT_SET(I2C_SDA_PIN, 1);
GPIO_OUTPUT_SET(I2C_SCL_PIN, 1);
i2c_delay();
GPIO_OUTPUT_SET(I2C_SDA_PIN, 0);
i2c_delay();
GPIO_OUTPUT_SET(I2C_SCL_PIN, 0);
i2c_delay();
}
static void ICACHE_FLASH_ATTR i2c_stop(void) {
GPIO_OUTPUT_SET(I2C_SDA_PIN, 0);
GPIO_OUTPUT_SET(I2C_SCL_PIN, 1);
i2c_delay();
GPIO_OUTPUT_SET(I2C_SDA_PIN, 1);
i2c_delay();
}
static uint8_t ICACHE_FLASH_ATTR i2c_write_byte(uint8_t data) {
for (uint8_t i = 0; i < 8; i++) {
GPIO_OUTPUT_SET(I2C_SDA_PIN, (data & 0x80) ? 1 : 0);
data <<= 1;
i2c_delay();
GPIO_OUTPUT_SET(I2C_SCL_PIN, 1);
i2c_delay();
GPIO_OUTPUT_SET(I2C_SCL_PIN, 0);
i2c_delay();
}
// ACK
GPIO_DIS_OUTPUT(I2C_SDA_PIN);
GPIO_OUTPUT_SET(I2C_SDA_PIN, 1);
i2c_delay();
GPIO_OUTPUT_SET(I2C_SCL_PIN, 1);
i2c_delay();
uint8_t ack = !GPIO_INPUT_GET(I2C_SDA_PIN);
GPIO_OUTPUT_SET(I2C_SCL_PIN, 0);
i2c_delay();
gpio_output_set(0, 0, I2C_SDA_PIN, 0);
return ack;
}
static void ICACHE_FLASH_ATTR pcf8574_write(uint8_t data) {
i2c_start();
i2c_write_byte((PCF8574_ADDR << 1) | 0);
i2c_write_byte(data);
i2c_stop();
}
// ==================== 1-WIRE ====================
static void ICACHE_FLASH_ATTR ow_init(void) {
GPIO_DIS_OUTPUT(DS18B20_PIN);
GPIO_OUTPUT_SET(DS18B20_PIN, 1);
}
static uint8_t ICACHE_FLASH_ATTR ow_reset(void) {
GPIO_OUTPUT_SET(DS18B20_PIN, 0);
delay_us(490);
GPIO_DIS_OUTPUT(DS18B20_PIN);
GPIO_OUTPUT_SET(DS18B20_PIN, 1);
delay_us(70);
uint8_t presence = !GPIO_INPUT_GET(DS18B20_PIN);
delay_us(410);
return presence;
}
static void ICACHE_FLASH_ATTR ow_write_bit(uint8_t b) {
GPIO_OUTPUT_SET(DS18B20_PIN, 0);
if (b) {
delay_us(6);
GPIO_DIS_OUTPUT(DS18B20_PIN);
GPIO_OUTPUT_SET(DS18B20_PIN, 1);
delay_us(64);
} else {
delay_us(60);
GPIO_DIS_OUTPUT(DS18B20_PIN);
GPIO_OUTPUT_SET(DS18B20_PIN, 1);
delay_us(10);
}
}
static uint8_t ICACHE_FLASH_ATTR ow_read_bit(void) {
GPIO_OUTPUT_SET(DS18B20_PIN, 0);
delay_us(6);
GPIO_DIS_OUTPUT(DS18B20_PIN);
GPIO_OUTPUT_SET(DS18B20_PIN, 1);
delay_us(9);
uint8_t b = GPIO_INPUT_GET(DS18B20_PIN);
delay_us(55);
return b;
}
static void ICACHE_FLASH_ATTR ow_write_byte(uint8_t b) {
for (uint8_t i = 0; i < 8; i++) { ow_write_bit(b & 1); b >>= 1; }
}
static uint8_t ICACHE_FLASH_ATTR ow_read_byte(void) {
uint8_t b = 0;
for (uint8_t i = 0; i < 8; i++) { b >>= 1; if (ow_read_bit()) b |= 0x80; }
return b;
}
static double ICACHE_FLASH_ATTR read_temperature(void) {
if (!ow_reset()) return -127.0;
ow_write_byte(0xCC);
ow_write_byte(0x44);
delay_us(750000);
if (!ow_reset()) return -127.0;
ow_write_byte(0xCC);
ow_write_byte(0xBE);
uint8_t lsb = ow_read_byte();
uint8_t msb = ow_read_byte();
int16_t raw = (msb << 8) | lsb;
return raw / 16.0;
}
// ==================== ПИД ====================
static double ICACHE_FLASH_ATTR pid_compute(double input, double setpoint) {
double error = setpoint - input;
double p = PID_KP * error;
pid_integral += error * (PID_SAMPLE_MS / 1000.0);
double i = PID_KI * pid_integral;
double d = PID_KD * (error - pid_last_error) / (PID_SAMPLE_MS / 1000.0);
pid_last_error = error;
double out = p + i + d;
if (out > 255) { out = 255; pid_integral -= error * (PID_SAMPLE_MS / 1000.0); }
if (out < 0) { out = 0; pid_integral -= error * (PID_SAMPLE_MS / 1000.0); }
return out;
}
// ==================== FLASH ====================
static void ICACHE_FLASH_ATTR save_params(void) {
uint32_t buf[4];
buf[0] = FLASH_MAGIC;
buf[1] = (uint32_t)temp_setpoint;
buf[2] = timer_setpoint_min;
buf[3] = 0xFFFFFFFF;
spi_flash_erase_sector(FLASH_SECTOR);
spi_flash_write(FLASH_SECTOR * 4096, buf, sizeof(buf));
}
static void ICACHE_FLASH_ATTR load_params(void) {
uint32_t buf[4] = {0};
spi_flash_read(FLASH_SECTOR * 4096, buf, sizeof(buf));
if (buf[0] != FLASH_MAGIC) return;
uint32_t t = buf[1];
if (t >= TEMP_MIN && t <= TEMP_MAX) temp_setpoint = (int)t;
uint32_t m = buf[2];
if (m >= TIMER_MIN_MIN && m <= TIMER_MAX_MIN) timer_setpoint_min = m;
}
// ==================== ТАЙМЕР ====================
static void ICACHE_FLASH_ATTR start_timer(void) {
timer_remaining_min = timer_setpoint_min;
sec_counter = 0;
display_phase = 0;
pid_integral = 0;
pid_last_error = 0;
os_printf("[RUN] Start: %.1f C, %u min\n", (double)temp_setpoint, timer_setpoint_min);
}
static void ICACHE_FLASH_ATTR stop_timer(void) {
timer_remaining_min = 0;
sec_counter = 0;
pwm_duty = 0;
GPIO_OUTPUT_SET(SSR_PIN, 0);
os_printf("[RUN] Stop\n");
}
// ==================== ДИСПЛЕЙ ====================
static void ICACHE_FLASH_ATTR update_display(void) {
blank_digit[0] = blank_digit[1] = blank_digit[2] = 0;
if (current_mode == MODE_EDIT_TEMP) {
int t = temp_setpoint;
if (t < 0) t = 0;
if (t > 99) t = 99;
disp_digits[0] = (t / 10) % 10;
disp_digits[1] = t % 10;
disp_digits[2] = 0;
blank_digit[2] = 1; // ГАСИМ ТРЕТИЙ РАЗРЯД
}
else if (current_mode == MODE_EDIT_TIMER) {
uint32_t m = timer_setpoint_min;
if (m > 999) m = 999;
disp_digits[0] = (m / 100) % 10;
disp_digits[1] = (m / 10) % 10;
disp_digits[2] = m % 10;
}
else { // MODE_RUNNING
if (display_phase < 5) {
int t10 = (int)(pid_input * 10.0 + 0.5);
if (t10 < 0) t10 = 0;
if (t10 > 999) t10 = 999;
disp_digits[0] = (t10 / 100) % 10;
disp_digits[1] = (t10 / 10) % 10;
disp_digits[2] = t10 % 10;
} else {
uint32_t m = timer_remaining_min;
if (m > 999) m = 999;
disp_digits[0] = (m / 100) % 10;
disp_digits[1] = (m / 10) % 10;
disp_digits[2] = m % 10;
}
}
}
static void ICACHE_FLASH_ATTR all_digits_off(void) {
gpio16_set(0);
GPIO_OUTPUT_SET(DIGIT2_PIN, 0);
GPIO_OUTPUT_SET(DIGIT3_PIN, 0);
}
static void ICACHE_FLASH_ATTR select_digit(uint8_t idx) {
switch (idx) {
case 0: gpio16_set(1); break;
case 1: GPIO_OUTPUT_SET(DIGIT2_PIN, 1); break;
case 2: GPIO_OUTPUT_SET(DIGIT3_PIN, 1); break;
}
}
static void ICACHE_FLASH_ATTR display_timer_cb(void *arg) {
all_digits_off();
dp_blink_cnt++;
if (dp_blink_cnt >= DP_BLINK_TICKS) {
dp_blink_cnt = 0;
dp_blink_state = !dp_blink_state;
}
update_display();
if (blank_digit[display_idx]) {
// Разряд погашен — сегменты не выводим, транзистор не открываем
pcf8574_write(dp_blink_state ? 0x80 : 0x00); // только точка (по желанию)
} else {
uint8_t seg = seg_table[disp_digits[display_idx]];
if (dp_blink_state) seg |= 0x80;
pcf8574_write(seg);
select_digit(display_idx);
}
display_idx = (display_idx + 1) % 3;
}
// ==================== КНОПКИ ====================
static void ICACHE_FLASH_ATTR btn_timer_cb(void *arg) {
uint8_t up = GPIO_INPUT_GET(BTN_UP_PIN);
uint8_t down = GPIO_INPUT_GET(BTN_DOWN_PIN);
uint8_t mode = GPIO_INPUT_GET(BTN_MODE_PIN);
// UP
if (btn_up_last == 1 && up == 0) {
if (current_mode == MODE_EDIT_TEMP) {
temp_setpoint += TEMP_STEP;
if (temp_setpoint > TEMP_MAX) temp_setpoint = TEMP_MAX;
save_params();
os_printf("[EDIT] Temp: %d C\n", temp_setpoint);
} else if (current_mode == MODE_EDIT_TIMER) {
if (timer_setpoint_min + TIMER_STEP_MIN <= TIMER_MAX_MIN)
timer_setpoint_min += TIMER_STEP_MIN;
save_params();
os_printf("[EDIT] Timer: %u min\n", timer_setpoint_min);
}
}
btn_up_last = up;
// DOWN
if (btn_down_last == 1 && down == 0) {
if (current_mode == MODE_EDIT_TEMP) {
temp_setpoint -= TEMP_STEP;
if (temp_setpoint < TEMP_MIN) temp_setpoint = TEMP_MIN;
save_params();
os_printf("[EDIT] Temp: %d C\n", temp_setpoint);
} else if (current_mode == MODE_EDIT_TIMER) {
if (timer_setpoint_min >= TIMER_MIN_MIN + TIMER_STEP_MIN)
timer_setpoint_min -= TIMER_STEP_MIN;
save_params();
os_printf("[EDIT] Timer: %u min\n", timer_setpoint_min);
}
}
btn_down_last = down;
// MODE
if (btn_mode_last == 1 && mode == 0) {
btn_mode_hold = 0;
}
else if (btn_mode_last == 0 && mode == 0) {
btn_mode_hold++;
if (btn_mode_hold == LONG_PRESS_TICKS) {
if (current_mode == MODE_RUNNING) {
stop_timer();
current_mode = MODE_EDIT_TEMP;
} else if (timer_setpoint_min > 0) {
start_timer();
current_mode = MODE_RUNNING;
}
btn_mode_hold = 0xFFFF;
}
}
else if (btn_mode_last == 0 && mode == 1) {
if (btn_mode_hold < LONG_PRESS_TICKS) {
if (current_mode == MODE_EDIT_TEMP) {
current_mode = MODE_EDIT_TIMER;
os_printf("[MODE] EDIT_TIMER\n");
} else if (current_mode == MODE_EDIT_TIMER) {
current_mode = MODE_EDIT_TEMP;
os_printf("[MODE] EDIT_TEMP\n");
}
}
btn_mode_hold = 0;
}
btn_mode_last = mode;
}
// ==================== ШИМ / ПИД ====================
static void ICACHE_FLASH_ATTR pwm_timer_cb(void *arg) {
pwm_counter++;
if (pwm_counter >= PWM_PERIOD_MS) pwm_counter = 0;
uint32_t thr = (pwm_duty * PWM_PERIOD_MS) / 255;
GPIO_OUTPUT_SET(SSR_PIN, (pwm_counter < thr) ? 1 : 0);
}
static void ICACHE_FLASH_ATTR pid_timer_cb(void *arg) {
pid_input = read_temperature();
if (current_mode == MODE_RUNNING && timer_remaining_min > 0) {
if (pid_input > -100.0) {
pid_output = pid_compute(pid_input, (double)temp_setpoint);
pwm_duty = (uint32_t)pid_output;
} else {
pwm_duty = 0;
}
sec_counter++;
if (sec_counter >= SECONDS_PER_MINUTE) {
sec_counter = 0;
timer_remaining_min--;
os_printf("[RUN] T=%.1f C, left=%u min, duty=%u\n",
pid_input, timer_remaining_min, (unsigned)pwm_duty);
if (timer_remaining_min == 0) {
stop_timer();
current_mode = MODE_EDIT_TEMP;
}
}
display_phase = (display_phase + 1) % 10;
} else {
pwm_duty = 0;
}
}
// ==================== HTTP ====================
static const char *http_header =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Connection: close\r\n\r\n";
static void ICACHE_FLASH_ATTR http_recv_cb(void *arg, char *data, unsigned short len) {
struct espconn *conn = (struct espconn *)arg;
char *body, *resp;
if (os_strncmp(data, "GET /set?t=", 11) == 0) {
int t = atoi(data + 11);
if (t < TEMP_MIN) t = TEMP_MIN;
if (t > TEMP_MAX) t = TEMP_MAX;
temp_setpoint = t;
save_params();
os_printf("[WEB] Temp set: %d C\n", t);
}
else if (os_strncmp(data, "GET /time?m=", 12) == 0) {
int m = atoi(data + 12);
if (m < TIMER_MIN_MIN) m = TIMER_MIN_MIN;
if (m > TIMER_MAX_MIN) m = TIMER_MAX_MIN;
timer_setpoint_min = (uint32_t)m;
save_params();
os_printf("[WEB] Timer set: %d min\n", m);
}
else if (os_strncmp(data, "GET /start", 10) == 0) {
if (current_mode != MODE_RUNNING && timer_setpoint_min > 0) {
start_timer();
current_mode = MODE_RUNNING;
}
}
else if (os_strncmp(data, "GET /stop", 9) == 0) {
stop_timer();
current_mode = MODE_EDIT_TEMP;
}
body = (char *)os_zalloc(640);
if (!body) { espconn_disconnect(conn); return; }
const char *mode_str = (current_mode == MODE_RUNNING) ? "RUNNING" :
(current_mode == MODE_EDIT_TEMP) ? "EDIT_TEMP" :
"EDIT_TIMER";
os_sprintf(body,
"<!DOCTYPE html><html><head><meta charset='utf-8'>"
"<meta name='viewport' content='width=device-width,initial-scale=1'>"
"<title>Dryer</title></head><body>"
"<h2>Сушилка</h2>"
"<p>Режим: <b>%s</b></p>"
"<p>Температура: <b>%.1f °C</b></p>"
"<p>Уставка темп.: <b>%d °C</b></p>"
"<p>Уставка таймера: <b>%u мин</b></p>"
"<p>Осталось: <b>%u мин</b></p>"
"<p>Мощность: %u / 255</p>"
"<form action='/set' method='get'>"
"Температура (30..99): "
"<input type='number' name='t' min='30' max='99' step='1' value='%d'>"
"<input type='submit' value='OK'></form>"
"<form action='/time' method='get'>"
"Таймер (1..999 мин): "
"<input type='number' name='m' min='1' max='999' step='1' value='%u'>"
"<input type='submit' value='OK'></form>"
"<p><a href='/start'>▶ СТАРТ</a> "
"<a href='/stop'>■ СТОП</a></p>"
"</body></html>",
mode_str, pid_input, temp_setpoint,
(unsigned)timer_setpoint_min, (unsigned)timer_remaining_min,
(unsigned)pwm_duty, temp_setpoint, (unsigned)timer_setpoint_min);
resp = (char *)os_zalloc(os_strlen(body) + 256);
if (!resp) { os_free(body); espconn_disconnect(conn); return; }
os_sprintf(resp, "%s%s", http_header, body);
espconn_sent(conn, (uint8 *)resp, os_strlen(resp));
os_free(body);
os_free(resp);
(void)len;
}
static void ICACHE_FLASH_ATTR http_sent_cb(void *arg) {
espconn_disconnect((struct espconn *)arg);
}
static void ICACHE_FLASH_ATTR http_discon_cb(void *arg) { (void)arg; }
static void ICACHE_FLASH_ATTR http_recon_cb(void *arg, sint8 err) { (void)arg; (void)err; }
static void ICACHE_FLASH_ATTR http_init(void) {
http_conn.type = ESPCONN_TCP;
http_conn.state = ESPCONN_NONE;
http_tcp.local_port = 80;
http_conn.proto.tcp = &http_tcp;
http_conn.proto.tcp->local_ip[0] = 0;
http_conn.proto.tcp->local_ip[1] = 0;
http_conn.proto.tcp->local_ip[2] = 0;
http_conn.proto.tcp->local_ip[3] = 0;
espconn_regist_recvcb(&http_conn, http_recv_cb);
espconn_regist_sentcb(&http_conn, http_sent_cb);
espconn_regist_disconcb(&http_conn, http_discon_cb);
espconn_regist_reconcb(&http_conn, http_recon_cb);
espconn_accept(&http_conn);
}
// ==================== WI-FI AP ====================
static void ICACHE_FLASH_ATTR wifi_init_ap(void) {
struct softap_config ap;
wifi_softap_get_config(&ap);
os_memset(ap.ssid, 0, sizeof(ap.ssid));
os_memset(ap.password, 0, sizeof(ap.password));
os_memcpy(ap.ssid, AP_SSID, os_strlen(AP_SSID));
os_memcpy(ap.password, AP_PASS, os_strlen(AP_PASS));
ap.ssid_len = os_strlen(AP_SSID);
ap.channel = 6;
ap.authmode = AUTH_WPA_WPA2_PSK;
ap.max_connection = 2;
ap.beacon_interval = 100;
wifi_softap_set_config(&ap);
wifi_set_opmode(STATIONAP_MODE);
}
// ==================== INIT ====================
void ICACHE_FLASH_ATTR user_init(void) {
uart_div_modify(0, UART_CLK_FREQ / 115200);
os_printf("\n\n=== Dryer start ===\n");
// --- SSR ---
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12);
gpio_output_set(0, 0, SSR_PIN, 0);
GPIO_OUTPUT_SET(SSR_PIN, 0);
// --- DS18B20 ---
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
ow_init();
// --- Кнопки ---
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0);
GPIO_DIS_OUTPUT(BTN_UP_PIN);
GPIO_DIS_OUTPUT(BTN_DOWN_PIN);
GPIO_DIS_OUTPUT(BTN_MODE_PIN);
PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO4_U);
PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO5_U);
PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO0_U); // + ВНЕШНИЙ pullup 10 кОм!
// --- I2C (PCF8574) ---
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14); // SDA
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13); // SCL
gpio_output_set(0, 0, I2C_SDA_PIN, 0);
gpio_output_set(0, 0, I2C_SCL_PIN, 0);
GPIO_OUTPUT_SET(I2C_SDA_PIN, 1);
GPIO_OUTPUT_SET(I2C_SCL_PIN, 1);
pcf8574_write(0x00); // все сегменты погашены
// --- Разряды ---
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15); // DIGIT2
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_GPIO3); // DIGIT3 (RX)
gpio_output_set(0, 0, DIGIT2_PIN, 0);
gpio_output_set(0, 0, DIGIT3_PIN, 0);
GPIO_OUTPUT_SET(DIGIT2_PIN, 0);
GPIO_OUTPUT_SET(DIGIT3_PIN, 0);
all_digits_off();
// --- Загрузка параметров ---
load_params();
os_printf("Loaded: temp=%d C, timer=%u min\n",
temp_setpoint, timer_setpoint_min);
// --- Wi-Fi + HTTP ---
wifi_init_ap();
http_init();
// --- Таймеры ---
os_timer_disarm(&pwm_timer);
os_timer_setfn(&pwm_timer, pwm_timer_cb, NULL);
os_timer_arm(&pwm_timer, 1, 1);
os_timer_disarm(&pid_timer);
os_timer_setfn(&pid_timer, pid_timer_cb, NULL);
os_timer_arm(&pid_timer, PID_SAMPLE_MS, 1);
os_timer_disarm(&display_timer);
os_timer_setfn(&display_timer, display_timer_cb, NULL);
os_timer_arm(&display_timer, DISPLAY_REFRESH_MS, 1);
os_timer_disarm(&btn_timer);
os_timer_setfn(&btn_timer, btn_timer_cb, NULL);
os_timer_arm(&btn_timer, BTN_POLL_MS, 1);
os_printf("Ready. AP: %s / %s\n", AP_SSID, AP_PASS);
}
uint32 ICACHE_FLASH_ATTR
user_rf_cal_sector_set(void)
{
enum flash_size_map size_map = system_get_flash_size_map();
uint32 rf_cal_sec = 0;
switch (size_map) {
case FLASH_SIZE_4M_MAP_256_256:
rf_cal_sec = 128 - 5;
break;
case FLASH_SIZE_8M_MAP_512_512:
rf_cal_sec = 256 - 5;
break;
case FLASH_SIZE_16M_MAP_512_512:
case FLASH_SIZE_16M_MAP_1024_1024:
rf_cal_sec = 512 - 5;
break;
case FLASH_SIZE_32M_MAP_512_512:
case FLASH_SIZE_32M_MAP_1024_1024:
rf_cal_sec = 1024 - 5;
break;
case FLASH_SIZE_64M_MAP_1024_1024:
rf_cal_sec = 2048 - 5;
break;
case FLASH_SIZE_128M_MAP_1024_1024:
rf_cal_sec = 4096 - 5;
break;
default:
rf_cal_sec = 0;
break;
}
return rf_cal_sec;
}