Сеть собирается кнопками прямо на устройстве, затем через сервисное меню контроллер переводится в Run
Сеть собирается кнопками прямо на устройстве, затем через сервисное меню контроллер переводится в Run

Проект называется AtsPLC. Это мой пет-проект: я начал его в 2024 году и делал неспешно. В этой статье я расскажу об идее и о том, как с контроллером работать. А во второй части - о том, как все устроено внутри.

Идея: ladder не нужен большой экран

Мне хотелось, чтобы логику можно было увидеть и поменять прямо на устройстве. И чтобы для этого хватило маленького и дешевого экрана.

Первопричина

Все началось с аквариума. Надоело каждый день включать свет, а днем еще и следить за солнцем: от избытка света вода в аквариуме зеленела. Компрессор тоже приходилось время от времени выключать. В ящике как раз лежало несколько модулей “WeMos ESP8266 board with OLED and 18650”, и один из них стал контроллером. Сначала программа была жестко зашита в код, без всяких настроек. Но скоро стали видны ее недостатки: в выходные хотелось, чтобы свет включался позже, а пороги фотодатчика приходилось то и дело подстраивать. И каждый раз ради этого нужно было править код и перепрошивать контроллер. Тогда вспомнилась Ladder Diagram, и появилась идея: пусть программу можно будет менять прямо на устройстве.

Для этой задачи хорошо подходит ladder. Программа на ladder - это просто список сетей. Каждая сеть читается как одна строка слева направо: от левой шины через контакты к катушке у правой шины. Такую строку легко показать даже на узком экране, а список строк удобно листать двумя кнопками.

Идея не новая: писать программу прямо на устройстве давно позволяют умные реле и даже конструкторы LEGO:

Устройство

Экран

Как пишется программа на устройстве

Schneider Zelio Logic SR2

символьный

ladder из символов, в строке 5 контактов и катушка

Eaton easyE4

символьный

ladder из символов, ввод курсорными кнопками

Siemens LOGO! 8

символьный

FBD, не ladder

LEGO Mindstorms NXT/EV3

графический

цепочка блоков-иконок

В AtsPLC на графическом экране 128x64 пишется именно ladder. Каждый элемент нарисован как настоящий элемент схемы и сразу показывает свои параметры прямо в сети: адрес контакта, уставку компаратора, время таймера, значение индикатора.

Программа и есть экран оператора

В AtsPLC программа сама показывает, что происходит:

  • Активный участок цепи нарисован сплошной линией, а пассивный - пунктиром. Сразу видно, где проходит сигнал, а где цепь разорвана.

  • Сверху экрана есть строка состояния с 8 маленькими шкалами: AI, DI, O1, O2 и V1…V4. Так значения всех входов, выходов и переменных видны всегда.

  • Элемент Indicator показывает значение адреса крупным шрифтом. Его можно масштабировать и поставить десятичную точку. Например, сырое значение АЦП можно показать сразу в вольтах.

  • В режиме Stop правая шина каждой сети пунктирная, а у отключенной сети пунктирная левая шина.

Режим Run: пока вход DI замкнут, сеть с INC выше на экране увеличивает V1, а цепь с DEC рисуется пунктиром. Когда DI разомкнут, V1 уменьшается. Indicator показывает значение V1
Режим Run: пока вход DI замкнут, сеть с INC выше на экране увеличивает V1, а цепь с DEC рисуется пунктиром. Когда DI разомкнут, V1 уменьшается. Indicator показывает значение V1

Получается, что отдельный HMI делать не нужно - он уже есть в самой программе. Нужно показать температуру? Достаточно добавить в программу сеть с одним элементом Indicator.

Железо

Фото собранного устройства

Сразу предупрежу: это макет, собранный на скорую руку. Задача была проверить идею, а не сделать красиво, поэтому здесь много термоклея и проводов поверх платы.

Собранное устройство: сверху модуль WeMos с OLED и джойстиком, ниже на макетной плате два реле, повышающий преобразователь на 12 В для их катушек и блок питания 220 В -> 5 В
Собранное устройство: сверху модуль WeMos с OLED и джойстиком, ниже на макетной плате два реле, повышающий преобразователь на 12 В для их катушек и блок питания 220 В -> 5 В

Устройство состоит из двух частей: платы контроллера и периферии. Плата контроллера - это готовый модуль “WeMos ESP8266 board with OLED and 18650”, а периферию я собрал на макетной плате. Для моих задач этого вполне хватает. Схема лежит в репозитории, так что при желании ее можно перенести на свою печатную плату.

Что есть на плате контроллера:

  • ESP8266;

  • OLED 0.96" 128x64 на контроллере SSD1306, шина I2C;

  • джойстик на 5 положений: вверх, вниз и нажатие работают как кнопки Up, Down и Select;

  • на этом модуле джойстик вправо замыкает вход DI (GPIO0), поэтому DI удобно проверять без внешнего датчика, а влево сбрасывает устройство;

  • светодиод.

Что есть в периферии:

  • 1 дискретный вход (DI);

  • 1 аналоговый вход (AI), это АЦП ESP8266, 0…1 В;

  • 2 выхода (O1, O2);

  • питание от сети 220 В через модуль AC/DC на 5 В.

Выходы я собирал в двух вариантах: с электромагнитными реле с катушкой на 12 В и переключающим контактом, и с твердотельными оптореле K293KP11BP. Входы защищены TVS-диодами и RC-фильтрами.

Схема. Деталей там немного, и все они легко находятся.

Так что если у вас в ящике лежит пара ESP8266 без дела, это хороший повод дать им работу.

Как писать программу тремя кнопками

Экран программы в веб-интерфейсе: на OLED видны две сети на желтом фоне, а браузер показывает и соседние
Экран программы в веб-интерфейсе: на OLED видны две сети на желтом фоне, а браузер показывает и соседние

На экране помещаются две сети, а кнопки Up и Down листают программу.

Менять программу можно только в режимах Stop и Debug, о них чуть ниже. В режиме Run программа защищена от случайных правок, и кнопки только листают ее.

Чтобы изменить сеть, нужно сделать так:

  1. Select выбирает сеть.

  2. Второй Select открывает ее.

  3. Up и Down выбирают место в сети.

  4. На выбранном месте Up и Down перебирают элементы. При этом контроллер предлагает только те элементы, которые помещаются на оставшееся место. Параметры переходят к новому элементу: например, если поменять нормально открытый контакт на нормально закрытый, адрес останется прежним.

  5. Select подтверждает элемент, и дальше Up и Down меняют его параметры. Если держать Up или Down нажатой, значение меняется большим шагом.

А если долго держать Select на выбранной сети, откроются действия со всей сетью: переместить, копировать, удалить или отключить.

Как оказалось, тремя кнопками собирать программу на ladder довольно просто. Сначала я относился к этой идее скептически и собирался подключить больше кнопок. Но лень творит чудеса: новые кнопки я так и не подключил, а трех оказалось вполне достаточно.

Через сервисное меню включается Debug, затем во второй сети контакт DI заменяется на компаратор, и настраивается его уставка
Через сервисное меню включается Debug, затем во второй сети контакт DI заменяется на компаратор, и настраивается его уставка

Режимы работы

Режим

Сети выполняются

Выходы

Редактирование

Stop

нет

сброшены в 0

можно

Run

да

работают по программе

нельзя

Debug

да

работают по программе

можно

Debug нужен при наладке: можно поменять уставку и сразу увидеть результат на живом объекте. Но это опасное сочетание, поэтому флаг Debug не переживает пропадание питания: после него устройство стартует в Run.

Сервисный режим

Если включить питание с нажатой кнопкой Up, откроется сервисное меню. В нем можно выбрать режим работы, настроить WiFi через приложение ESP-Touch, сохранить программу в один из 4 слотов, восстановить ее или сбросить настройки.

Какие элементы есть

Что может делать программа, зависит от набора элементов, поэтому перечислю их все. Но сначала про адреса. Доступны DI, AI, O1, O2 и переменные V1…V4, и все они хранят значения 0…255.

Входные элементы:

Элемент

Что делает

Нормально открытый контакт

пропускает сигнал, когда значение адреса не 0

Нормально закрытый контакт

пропускает сигнал, когда значение адреса 0

Таймер, секунды

задержка 1…99999 с

Таймер, миллисекунды

задержка 50…99950 мс, шаг 50 мс

Генератор

импульсы, длительность паузы и импульса настраивается

Компараторы

==, <>, >, >=, <=, < со значением уставки

Indicator

показывает значение адреса с масштабом и десятичной точкой

Выходные элементы:

Элемент

Что делает

Прямой выход

пишет 255, пока цепь активна, и 0, когда пассивна

Set

пишет 255, когда цепь стала активной. Если цепь потом стала пассивной, значение остается 255, пока его не сбросит Reset

Reset

пишет 0, когда цепь стала активной

Inc

прибавляет 1, когда цепь стала активной, максимум 255

Dec

вычитает 1, когда цепь стала активной, минимум 0

Связь между сетями:

Элемент

Что делает

ContinuationIn

ставится в конце сети и передает ее состояние дальше

ContinuationOut

ставится в начале сети и берет это состояние

В одну сеть помещается до 5 элементов. Если этого мало, выручает пара ContinuationIn и ContinuationOut: цепь просто продолжается в следующих сетях. Причем одна ContinuationIn может питать сразу несколько сетей ниже.

Привязки к WiFi, времени и настройкам:

Элемент

Что делает

WiFiBinding

ищет сеть WiFi с заданным именем и пишет в переменную уровень ее сигнала

WiFiStaBinding

подключается к роутеру и пишет в переменную уровень сигнала

WiFiApBinding

поднимает свою точку доступа и пишет в переменную число подключенных клиентов. Маска по MAC-адресу задает, какие клиенты считаются: так можно учитывать только свой телефон

DateTimeBinding

пишет в переменную секунды, минуты, часы, день, день недели, месяц или год

Settings

показывает и правит одну настройку устройства прямо в сети

Как элементы выглядят на экране
Все элементы на экране 128x64, по одному в сети, с подписями и группами
Все элементы на экране 128x64, по одному в сети, с подписями и группами

Пример: сигнализация, где телефон - это ключ

Чтобы было понятнее, как это выглядит в деле, разберу одну задачу.

Контроллер поднимает свою точку доступа WiFi. Если к ней подключен телефон хозяина, то значит, хозяин дома. Посторонний телефон к точке доступа не подключится: для этого нужно знать пароль. Но пароль могут знать и другие устройства, которые когда-то подключались: старый телефон, планшет или телефон гостя. Чтобы их не считать, в маске MAC-адреса указывается MAC телефона хозяина, и тогда хозяином считается только он. А если телефона нет и при этом открылась дверь, контроллер ждет 30 секунд и включает сирену на 10 минут. Красный светодиод показывает, в каком состоянии охрана. Линия датчика двери тоже проверяется: обрыв или короткое замыкание видно на светодиоде, а если дома никого нет, то неисправность сразу включает тревогу.

Подключение:

Вход или выход

Что подключено

AI

датчик двери через делитель

O1

сирена

O2

красный светодиод

Датчик двери включен в делитель на аналоговом входе. Благодаря этому по одному входу можно различить сразу четыре состояния:

AI

Состояние

0…29

неисправность, например линия замкнута

30…129

дверь закрыта

130…229

дверь открыта

230…255

неисправность, например оборван резистор

Переменные:

  • V1 - сколько телефонов подключено к точке доступа;

  • V2 - состояние охраны: 0 - тишина, 1…30 - ожидание 30 секунд, 31…180 - тревога, 181 - тревога закончилась.

V2 тут работает сразу и как состояние, и как счетчик времени. Генератор перед элементом Inc прибавляет 1 на каждом импульсе, и поэтому значение V2 растет вместе со временем.

Начнем с начала программы. Первая сеть поднимает точку доступа, а следующие три выводят значения на экран:

 1  ||--[WiFiAp V1 "AtsPLC" "ats-PLC0" "************"]----||

 2  ||--[Indicator AI 0..255]-----------------------------||

 3  ||--[Indicator V1 0..255]-----------------------------||

 4  ||--[Indicator V2 0..255]-----------------------------||

Теперь запуск тревоги. Если телефона нет, дверь открыта, а охрана в покое, то V2 становится равной 1:

12  ||--[V1 == 0]--[AI >= 130]--[V2 == 0]----------------->>

                                                      V2
13  >>-----------------------------------------------(+)--||

Дальше ожидание 30 секунд. Пока V2 находится между 1 и 30, она растет на 1 каждую секунду:

16  ||--[V2 > 0]--[V2 <= 30]------------------------------>>

                                                      V2
17  >>--[SQW off 500ms on 500ms]---------------------(+)--||

И наконец, хозяин вернулся. Как только телефон подключился, все сбрасывается. Здесь одна ContinuationIn питает сразу три сети:

21  ||--[V1 > 0]------------------------------------------>>

                                                      V2
22  >>-----------------------------------------------(R)--||

                                                      O1
23  >>-----------------------------------------------(R)--||

                                                      O2
24  >>-----------------------------------------------(R)--||

Все ladder-схемы в статье сделаны скриптом Tools/ladder.py из файла программы.

Сигнализация в работе: при открытой двери AI растет, телефона нет (V1 = 0), и V2 отсчитывает 30 секунд ожидания. Зеленый светодиод (O2) показывает, что охрана сработала, а синий (O1) вместо сирены загорается, когда начинается тревога
Сигнализация в работе: при открытой двери AI растет, телефона нет (V1 = 0), и V2 отсчитывает 30 секунд ожидания. Зеленый светодиод (O2) показывает, что охрана сработала, а синий (O1) вместо сирены загорается, когда начинается тревога

Сети 2-4 здесь работают как экран оператора. При наладке на них видно значение AI при открытой и закрытой двери, так что пороги можно поправить прямо на месте.

Полное описание, схема делителя и вся программа лежат в Demo/security-alarm.md. Там же есть еще три готовые программы:

  • аквариум: свет и насос фильтра по расписанию, с датчиком освещенности;

  • WiFi watchdog: перезагружает роутер, когда его сигнал слабый или пропал;

  • термостат: держит температуру в ящике с помощью нагревателя и вентилятора, датчик - термистор.

Веб-интерфейс

Когда устройство подключено к роутеру, у него появляется веб-интерфейс. Браузер показывает ту же картинку, что и OLED, только крупнее, а кнопки можно нажимать прямо на странице или с клавиатуры. При этом в браузере видно больше сетей, чем на устройстве: он собирает их из кадров, которые уже видел.

Еще через веб-интерфейс можно скачать и загрузить программу и переключить режим работы. А прошивка обновляется по WiFi через тот же HTTP-сервер, об этом во второй части.

Веб-интерфейс и устройство: в браузере та же программа, что на OLED, и желтым выделены сети, которые сейчас видны на устройстве. Справа кнопки Up, Ok и Down, слева загрузка и выгрузка программы и выбор режима работы
Веб-интерфейс и устройство: в браузере та же программа, что на OLED, и желтым выделены сети, которые сейчас видны на устройстве. Справа кнопки Up, Ok и Down, слева загрузка и выгрузка программы и выбор режима работы

Своего редактора программы со страницами, формами и кнопками в браузере нет, и это сделано специально. Браузер просто показывает копию экрана устройства, поэтому умеет ровно то же, что и само устройство. Как это устроено, я расскажу во второй части.

Ограничения

  • Это самодельный ПЛК. Язык программы - подобие ladder из IEC 61131-3. Контроллер подходит для несложных процессов, где не нужна высокая надежность: ESP8266 с включенным WiFi на нее и не рассчитан, а на входах нет гальванической развязки.

  • Мало входов и выходов. Сейчас их всего 1 DI, 1 AI, 2 выхода и 4 переменные. Но можно добавить расширитель GPIO: исходный код доступен, и лицензия разрешает его менять. Если сделаете это как отдельную конфигурацию, буду рад pull request.

  • Значения только 0…255. Знаковых чисел, дробей и арифметики нет.

  • Каждая сеть - одна цепочка. Так ее удобно показывать на маленьком экране. Логическое ИЛИ собирается из нескольких сетей: например, каждое условие в своей сети ставит общую переменную через Set, а отдельная сеть сбрасывает ее через Reset, когда все условия ложны.

  • Размер программы. До 80 сетей, до 5 элементов в сети, файл до 4 КБ.

  • Одно радио. ESP8266 не может одновременно держать свою точку доступа и подключение к роутеру. Поэтому, например, в программе сигнализации веб-интерфейс работает только в режиме Stop.

  • Веб-интерфейс без пароля. Устройство рассчитано на домашнюю сеть, которой вы доверяете.

Лицензия

Бесплатно для хобби, обучения и для работы внутри своей компании. А если вы продаете устройства с AtsPLC или ставите его клиентам за деньги - нужна коммерческая лицензия.

Исходный код доступен всем, но в строгом смысле это не open source, правильнее называть это source available.

Что дальше

Идея оказалась удачной, и на ее основе я начал новый проект - SuPLC. Он пока еще в разработке.

SuPLC работает на нескольких платформах из одного кода: ESP32, ESP32-S3, ESP32-P4, ESP8266, LPC4088 и Linux. Дисплеи тоже разные: E-Ink 2.13", AMOLED 2.41" с тачем, LCD 3.5" с тачем, TFT 5.6". В нем уже есть Modbus TCP и RTU, сохраняемые переменные и значения до 65535.

Фото SuPLC на трех дисплеях
SuPLC на трех дисплеях: AMOLED 2.41" (ESP32-S3), E-Ink 2.13" (ESP32) и TFT 5.6" (LPC4088). В программе привязки Modbus TCP, а ползунки и кнопки стоят прямо в сетях
SuPLC на трех дисплеях: AMOLED 2.41" (ESP32-S3), E-Ink 2.13" (ESP32) и TFT 5.6" (LPC4088). В программе привязки Modbus TCP, а ползунки и кнопки стоят прямо в сетях

А во второй части я расскажу, как AtsPLC устроен внутри: как работает цикл контроллера без постоянного опроса, как рисуется картинка на 128x64, почему веб-интерфейс - это просто копия экрана, как устройство перезапускается без потери состояния и как все это тестируется.

Ссылки

Комментарии (4)


  1. NutsUnderline
    05.11.2026 08:48

    mqtt прикрутить надо, думаю несложно,тогда очередь будет на этот девайс. из тех кто мечтает домашнюю автоматизацию на "понятных" iec языках


    1. viordash Автор
      05.11.2026 08:48

      я пробовал, но у esp8266 не хватает ресурсов на нормальную работу с mqtt.


  1. NutsUnderline
    05.11.2026 08:48

    AMOLED 2.41" (ESP32-S3),

    в такой размер amoled не видел, с ними довольно мелкие платки, а можно название?


    1. viordash Автор
      05.11.2026 08:48

      я покупал этот девайс "Плата разработки Waveshare ESP32-S3-Touch-AMOLED-2.41 с 2.4” сенсорным AMOLED дисплеем 600x450, встроенный акселерометр и гироскоп (Без корпуса)"
      по ссылке https://amperkot.ru/products/plata_razrabotki_waveshare_esp32s3touchamoled241_s_24_sensornyim_amoled_displeem_600x450_vstroennyiy_akselerometr_i_giroskop__bez_korpusa/42745417.html