Instructions: Press Ctrl + P to print. In the print dialog, set Scale to 100% (or "Actual Size") and disable margins/headers. Cut along the outer solid box if needed.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index abbf8de..2067e30 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,25 @@
-# autoclav
+# Таймер автоклава для работы с терморегулятором
-Автоматика таймера для автоклава
\ No newline at end of file
+Легковесное решение для автоматизации процессов стерилизации в автоклаве на базе микроконтроллера Wemos D1 Mini (ESP8266) под управлением прошивки NodeMCU Lua (версия integer-only, без плавающей точки).
+
+Система управляет циклом нагрева и выдержки времени по сигналу от внешнего термостата. Настройка времени работы осуществляется локально с помощью ползункового фейдера, а удаленный мониторинг доступен через автономный веб-интерфейс по WiFi.
+
+## Особенности системы
+- **Защита при старте:** Встроенная программная пауза 5 секунд при включении устройства в розетку для предотвращения ложных срабатываний до стабилизации питания.
+- **Аппаратная фиксация таймера:** Уставка времени жестко блокируется в памяти в момент первого достижения рабочей температуры. Случайное смещение ползунка во время стерилизации не повлияет на обратный отсчет.
+- **Фильтрация шумов АЦП:** Сглаживание показаний потенциометра методом скользящего среднего по 10 замерам для устранения дребезга значений.
+- **Нелинейная шкала времени:** Дискретность шага составляет 5 минут в диапазоне до 1 часа и 10 минут в диапазоне от 1 до 3 часов для удобной физической разметки панели.
+- **Автономный Веб-интерфейс:** Точка доступа WiFi (`192.168.4.1`) работает без пароля и выводит адаптивную страницу со статусом системы и поминутным округлением оставшегося времени (с автообновлением каждые 5 секунд).
+- **Ориентированность на отказоустойчивость:** Код разработан с учетом совместной работы с независимой механической защитой по питанию.
+
+## Коммутация и распиновка (Pinout)
+
+| Компонент | Пин Wemos D1 | ID пина в NodeMCU | Логический уровень / Питание | Описание |
+| :--- | :--- | :--- | :--- | :--- |
+| **Реле ТЭНа** | `D1` (GPIO5) | `1` | Выход 3.3V | Управляет силовым реле или контактором ТЭНов. |
+| **Сигнал термостата** | `D2` (GPIO4) | `2` | Вход 3.3V (Pull-up) | Подключается к сухим контактам внешнего термостата. Пока идет нагрев, контакты замкнуты на GND. При достижении уставки размыкаются (переход в HIGH). |
+| **Фейдер времени** | `A0` (ADC0) | `0` | Аналоговый 0V - 3.3V | Ползунковый потенциометр 10 кОм. Подключается строго к линиям `3V3` и `GND`. |
+| **Аварийный термостат** | *Внешний* | *Отсутствует* | Силовая линия ~220В | Биметаллический выключатель KSD301 (130°C/140°C NC с кнопкой ручного сброса) на корпусе. Включается последовательно в разрыв питания ТЭНа. |
+
+### ⚠️ Предупреждение по питанию потенциометра
+Несмотря на то, что периферийные датчики могут питаться от 5V, ползунковый резистор времени **ОБЯЗАТЕЛЬНО** должен быть запитан от пина **`3V3`** платы Wemos. Это гарантирует, что аналоговый сигнал не превысит допустимый предел встроенного делителя напряжения ESP8266 (3.2V).
diff --git a/flash.sh b/flash.sh
new file mode 100644
index 0000000..48a105c
--- /dev/null
+++ b/flash.sh
@@ -0,0 +1,2 @@
+python3 -m esptool --port /dev/ttyUSB0 erase_flash
+python3 -m esptool --port /dev/ttyUSB0 write_flash -fm dio 0x00000 nodemcu-release-10-modules-2026-08-18-07-13-33-integer.bin
diff --git a/init.lua b/init.lua
new file mode 100644
index 0000000..ca3675d
--- /dev/null
+++ b/init.lua
@@ -0,0 +1,151 @@
+main_timer = tmr.create()
+cycle_timer = tmr.create()
+adc_timer = tmr.create()
+srv = net.createServer(net.TCP)
+ap_config = {}
+
+local pin_relay = 1
+local pin_thermostat = 2
+local START_DELAY_MS = 5000
+
+local time_table = {}
+local table_size = 0
+
+local function init_time_table()
+ local index = 0
+ for m = 5, 60, 5 do
+ time_table[index] = m * 60
+ index = index + 1
+ end
+ for m = 70, 180, 10 do
+ time_table[index] = m * 60
+ index = index + 1
+ end
+ table_size = index - 1
+end
+init_time_table()
+
+local adc_history = {}
+local adc_index = 1
+local max_samples = 10
+local adc_average = 0
+local set_time_sec = 300
+local remaining_time_sec = 300
+local process_started = false
+local process_finished = false
+
+gpio.mode(pin_relay, gpio.OUTPUT)
+gpio.write(pin_relay, gpio.LOW)
+gpio.mode(pin_thermostat, gpio.INPUT, gpio.PULLUP)
+
+print("System init delay...")
+
+local function read_averaged_adc()
+ local raw = adc.read(0)
+ adc_history[adc_index] = raw
+ adc_index = adc_index + 1
+ if adc_index > max_samples then adc_index = 1 end
+
+ local sum = 0
+ local count = 0
+ for i = 1, max_samples do
+ if adc_history[i] then
+ sum = sum + adc_history[i]
+ count = count + 1
+ end
+ end
+ adc_average = sum / count
+
+ local tbl_index = (adc_average * table_size) / 1023
+ if tbl_index < 0 then tbl_index = 0 end
+ if tbl_index > table_size then tbl_index = table_size end
+
+ set_time_sec = time_table[tbl_index]
+
+ if not process_started then
+ remaining_time_sec = set_time_sec
+ end
+end
+
+adc_timer:register(200, tmr.ALARM_AUTO, read_averaged_adc)
+
+main_timer:register(START_DELAY_MS, tmr.ALARM_SINGLE, function()
+ print("Heater ON. Waiting thermostat...")
+ gpio.write(pin_relay, gpio.HIGH)
+ adc_timer:start()
+
+ cycle_timer:register(1000, tmr.ALARM_AUTO, function()
+ local thermo_state = gpio.read(pin_thermostat)
+
+ local left_min = (remaining_time_sec + 59) / 60
+ local set_min = set_time_sec / 60
+ print("TMR: Left=" .. left_min .. "m | Set=" .. set_min .. "m | Input D2=" .. thermo_state)
+
+ if not process_started and not process_finished then
+ if thermo_state == gpio.HIGH then
+ process_started = true
+ adc_timer:stop()
+ print("Target reached! Timer active: " .. set_min .. "m")
+ end
+
+ elseif process_started and not process_finished then
+ if remaining_time_sec > 0 then
+ remaining_time_sec = remaining_time_sec - 1
+ else
+ process_started = false
+ process_finished = true
+ gpio.write(pin_relay, gpio.LOW)
+ print("Process finished. Heater OFF.")
+ cycle_timer:stop()
+ end
+ end
+ end)
+ cycle_timer:start()
+end)
+main_timer:start()
+
+wifi.setmode(wifi.SOFTAP)
+ap_config.ssid = "Autoclave_Control"
+ap_config.auth = wifi.OPEN
+wifi.ap.config(ap_config)
+print("AP Ready. IP: " .. wifi.ap.getip())
+
+srv:listen(80, function(conn)
+ conn:on("receive", function(client, request)
+
+ local function format_time_min(sec, is_set)
+ local total_min = 0
+ if is_set then
+ total_min = sec / 60
+ else
+ total_min = (sec + 59) / 60
+ end
+ local h = total_min / 60
+ local m = total_min % 60
+ return string.format("%02d:%02d", h, m)
+ end
+
+ local status_text = "Heating"
+ if process_started then status_text = "Sterilization" end
+ if process_finished then status_text = "Done" end
+
+ local html = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
+ html = html .. ""
+ html = html .. ""
+ html = html .. ""
+ html = html .. "Autoclave"
+ html = html .. ""
+ html = html .. "
"
+ html = html .. "
Autoclave Control
"
+ html = html .. "
Status: " .. status_text .. "
"
+ html = html .. "
Set Time: " .. format_time_min(set_time_sec, true) .. "