Идея заключалась в том, чтобы сделать контроллер исполнительных устройств с некоторой степенью автоматизации и настраиваемый полностью через web интерфейс. Логика работы контроллера хранится в файле на SD карте, а не в коде, что позволяет очень просто модифицировать логику работы, без изменения кода и перепрошивки контроллера.
Поскольку формировать web странички в коде очень не хотелось, то пришлось порыться на просторах сети и откопать такой интересный проект — TinyWebServer. С помощью этой библиотеки логика работы контроллера теперь полностью отделена от отображения страниц. Arduino не формирует страницы полностью. Интерактивная часть формируется в браузере.
Все странички, скрипты и другая информация (в моем случае еще и все настройки контроллера) хранятся на SD карте.
Состав
1. Mega 2560
2. Ethernet+SD shield
3. Сдвиговые регистры 74HC595 (контроллер может поддерживать большое количество регистров, но есть ограничение по объему ОЗУ, так как имена и настройки загружаются в ОЗУ).
4. Для логической обработки возможно использовать в качестве входных сигналов:
a) свободные цифровые входы Arduino
b) аналоговые входы
c) цифровые входы, подключенные через сдвиговые регистры 74HC165
d) также можно добавить любые типы датчиков непосредственно в коде под свои нужды (далее я опишу как это можно сделать).
Что умеет получившийся контроллер.
1. Ручной режим управления выходами, реализованный через WEB интерфейс (Включить/Выключить/Подать импульс).
2. Задержку включения после подачи питания. Если установлена задержка включения, то логические правила для этого выхода не действуют и реле управляется только в ручном режиме.
3. Управление выходами с помощью логических правил, в которых можно использовать любые типы датчиков, логические операции NOT/AND/OR и скобки. В формулах используются ID датчиков, которые также настраиваются через WEB интерфейс. Например, можно написать такую формулу (0|1)&!2. Для приведения значения аналоговых датчиков к логическому значению используются два граничных значения для обеспечения гистерезиса. Если значение больше или равно верхнему граничному значению, то значению сигнала будет true, а если меньше или равно нижнему, то false. Для всех датчиков возможно использовать инверсию значения. Например, при подключении кнопки, если она не нажата, на входе будет логическая 1 (true), а при инвертировании сигнала true будет обрабатываться при нажатой кнопке, т.е. при логическом 0 на входе.
4. Посылка SMS через SMS провайдера при включении и при переключении реле. К сожалению работает не очень хорошо, так что лучше не использовать.
5. Возможна работа без Ethernet shild, только с SD. Для этого необходимо закомментировать объявление #define WEB_INTERFACE. Изменять все настройки и логику работы в этом случае можно редактированием файлов на SD карте.
Как это все работает.
Работа с выходными сдвиговыми регистрами 74HC595 организована через мою библиотеку SPI595. Управление регистрами осуществляется по шине SPI с выбором устройства на пине 7 (#define SPI595_CS 7).
Работа с входными сдвиговыми регистрами 74HC165 организована через мою библиотеку SPI165. Управление регистрами осуществляется по шине SPI с выбором устройства на пине 6 (#define SPI165_CS 6).
Ethernet shield W5100 работает со стандартной библиотекой (#define ETHER_CS 10).
SD карта работает с библиотекой SdFat (#define SD_CS 4).
При включении контроллера считывается информация из файла sett.txt. На основании этих данных инициализируются Ethernet shield, сдвиговые регистры и глобальные переменные. Если SD карта не инициализировалась при старте, то LED 13 будет быстро моргать.
Затем считываются файлы настроек реле и датчиков. При загрузки правил они автоматом переводятся в обратную польскую нотацию, для быстрого вычисления void prepareRPN(byte idx, char* formula)
Вычисление значения логического выражения после этого становится очень простым. Ниже приведена функция вычисления формулы, представленной в виде обратной польской нотации.
bool evaluateRPN(byte relayIdx){ bool res[20]; byte i = 0; byte idx = 0; while(relayArr[relayIdx].rule[i] != OPERATIONS_END){ switch(relayArr[relayIdx].rule[i]){ case OPERATIONS_NOT:{ res[idx-1] = ! res[idx-1]; break; } case OPERATIONS_AND:{ res[idx-2] = res[idx-2] && res[idx-1]; idx--; break; } case OPERATIONS_OR:{ res[idx-2] = res[idx-2] || res[idx-1]; idx--; break; } default:{ res[idx] = getSensor(relayArr[relayIdx].rule[i]); idx++; break; } } i++; } return(res[0]); }
Добавление своего типа датчика
Определить его тип и признак в константах:
#define SENSOR_DS18S20 4 код типа #define SENSOR_SETT_DS18S20 ‘T’ символ для настройки
Реализовать его код в функции
bool getSensor(byte index). switch(sensorArr[index].type){ … case SENSOR_DS18S20:{ return(GETDS18S20(sensorArr[index].number)); } … } return(false); }
Произвести загрузку его свойств и проинициализировать в функции void loadSettings(bool all)
Подправить sensett.js для правильного отображения в браузере.
WEB интерфейс
Собственно, за основу с самого начала я взял пример из TinyWebServer.
Скриншоты
Периодичность опроса контроллера 0,4 секунды.
После изменения настроек реле или сенсоров и нажатии кнопки Save изменения вступают в силу сразу же. Общие настройки применяются только при инициализации контроллера.
На будущее
1. Поскольку я с web программированием практически никак, то делал как можно проще, без изысков. Каждый может для себя поправить файлы. В самом коде скетча ничего править не нужно. Собственно ради этого все это и затевалось.
2. При загрузке данных браузер постоянно запрашивает все данные. А поскольку используется jquery.js, а она достаточно большая, то хотелось бы заставить браузер закэшировать ее (а еще лучше все htm и js файлы) на все время сессии. Тогда arduino передавал бы только файлы настроек и состояние реле и датчиков. Но пока, увы, не получилось. Если кто-нибудь подскажет как это сделать — буду признателен.
3. В разборе правил проверок нет, так что, если формула будет некорректной, то последствия непредсказуемы. Возможно когда-то появится проверка правил.
4. Сделать командное управление через com-порт. В принципе класс для этого у меня есть, но пока я его в этот проект не подключал. Что то типа:
relay 1 on
OK
get relay state
relay 1 ON
relay 99 OFF
5. Добавить переменные для сохранения логических выражений, для того чтобы в логических правилах можно было бы использовать не только идентификаторы датчиков, но и переменные, например, (0|A)&!2, где A это переменная, значение которой присваивается другой формулой.
Сейчас конечно можно выйти из положения подключив выход 74HC595 к цифровому входу Arduino или в входному регистру 74HC165.
6. Сделать входы счетчики…
7. Сделать несколько типов выходов. Не только через сдвиговый регистр, но и напрямую управлять цифровыми портами Arduino (в Mega2560 их достаточно).
8. Сделать не только Init state для выходов, но и init pulse.
9. Возможно реализовать MODBUS, но это в последнюю очередь.
10. Реализовать вход как логическую функция от часов реального времени, и как функцию таймера.
Баги
При включении выходные регистры включаются хаотически. А поскольку контроллер инициализируется некоторое время, то выходные реле могут быть включены. Пока схема сброса регистров не реализована.
После заливке скетча иногда почему-то не инициализируется SdFat. Приходится сбрасывать контроллер кнопкой reset.
Один важный момент.
Всё подключено через аппаратный SPI.
Собственно подключение Ethenet shield, SD shield и 74HC595 никаких трудностей не представляет.
Проблемой было подключить 74HC165. Выход у этого регистра не может переходить в Z-состояние (кто-то сэкономил на паре транзисторов 🙂 ), и при подключении его напрямую к MISO делает невозможным работу с другими устройствами. Для решения этой проблемы был использован буфер 74HC125. Управляется он выходом, определенным константой SPI165_CS.
Схема подключения регистра
Итого
В принципе уже сейчас на базе этого контроллера можно реализовать некоторую достаточно сложную автоматизацию. А при возможность править web интерфейс не вмешиваясь в код, можно реализовать удобное взаимодействие с пользователем. Единственная проблема — при работе с web клиентом контроллер полностью занят и не может реагировать на изменения состояний датчиков (для этого я и хотел заставить браузер кешировать данные на все время сессии), поэтому иногда появляются задержки. Вынос обработки логики в таймер пока не планирую, все таки это не real time device.
Ссылки
Проект
https://drive.google.com/folderview?id=0B7NuO_z3PX3NUTNncGw0VzQwRTQ&usp=sharing
В каталоге LIB лежат библиотеки, которые позволяют упростить работу с регистрами.
В HTML лежит web интерфейс.
TinyWebSerwer
https://github.com/ovidiucp/TinyWebServer
0 комментариев на «“Программируемый логический контроллер (ПЛК) на базе Arduino + Ethernet shield + 74HC165 + 74HC595”»
Очень, ОЧЕНЬ полезная работа. Но для подавляющего большинства начинающих пользователей всетаки не нужно считывать показания 100 датчиков и переключать 100 реле, а нужно быстро и с минимальными затратами (как денег так и усилий) запустить то же самое, но ограничившись 16-тью реле в виде стандартного блока реле ардуино, без сдвиговых регистров, просто плата и изернет щит 🙂
Просто без использования аппаратного SPI мне этот проект был не особо интересен.
В ближайшее время думаю сделать условную компиляцию для отключения 74HC165, 74HC595.
И сделаю прямые выходы.
А можно посмотреть, как Ваш проект реализован аппаратно? Фотографии, может быть разводка печатной платы? Может быть, всё не так страшно как звучит, если посмотреть глазами на готовый девайс? И кстати, не вырастет ли общая стабильность системы если отказаться от использования регистров?
Вот тут добавил фотографию :))))
Странно. Ссылка пропала.
В папке проекта в папке Doc
https://drive.google.com/folderview?id=0B7NuO_z3PX3NcUJIdGFvZlJNY00&usp=sharing&tid=0B7NuO_z3PX3NUTNncGw0VzQwRTQ
Ох, чтото мне страшно стало от вида этого клубка проводов :)возможно, что описанный Вами баг(что при включении состояния реле могут быть произвольными) исчезнет, если развести аккуратную печатную плату на двустороннем текстолите, и убрать этот клубок проводов… нет, я пожалуй предпочту сперва испытать без регистровый вариант, с бутербродом из главной платы и изернет щита 🙂
Нет. Не исчезнет. Дело в самих регистрах. Я это могу побороть. Но мне лень.
В вашем случае при управлении реле через выходы ардуино напрямую такого не будет.
По кэшированию. Надо правильные http-заголовки отдавать, при чём с правильным временем. Сам в своё время намучился, когда писал свой МакроСервер (ныне заброшенный).
Тот самый заголовок выглядит так:
Last-Modified: Sat, 19 Sep 2015 18:00:00 GMT
Кроме того, сервер должен реагировать на заголовок If-Modified-Since в реквесте. Если он там есть, то надо проверять дату-время страницы, и если текущая дата изменения страницы меньше или равна дате из запроса, тогда сервер должен выдать ответ 304 Not Modified.
Например, при втором заходе на ту же страницу браузер может прислать в числе прочего:
If-Modified-Since: Sat, 19 Sep 2015 18:00:00 GMT
Тут указываются дата-время, которые были получены в прошлый раз в Last-Modified.
В ответ (если данные не изменились) надо вместо 200 Ok выдать 304 Not Modified, заголовок Last-Modified передаётся и в этом случае:
Last-Modified: Sat, 19 Sep 2015 18:00:00 GMT
Время, как видно, везде в GMT.
Ещё крайне рекомендую обеспечить «Connection: keep-alive», чтобы все запросы шли пачкой, и ответы лились один за другим без разрывов и установки соединений.
Можно ещё Cache-Control/Expires использовать, но для моих целей (в частности, при отладке сайта) это было неудобно, при изменении на сервере «статического» файла в браузере приходилось нажимать Ctrl-F5, в общем, я не пользовался.
Например:
Cache-Control: private, max-age=3600
Должно кэшировать на один час. Но при отладке сайта из-за каждого мельчайшего изменения каждый раз придётся принудительно перезагружать вообще всё.
Возможно, в самом сервере есть какие-то опции, которые всё это включают. Если же нет, вроде несложно добавить.
Спасибо. Как будет время попробую.
Добрый день.
Пытаюсь все запустить и тут ошибка. Не могу импортировать библиотеки. Может версия Ардуины не та? Не подскажите где копать.
Добрый день!
Вы какую версию пробуете?
Лучше пробуйте из папки V 2.0
И какая ошибка?
После открытия файла ардуино он не может найти SPI595.h, SPI165.h, TimerOne.h, Flash.h, TinyWebServer.h
Думаю может из-за того что версия ардуины не та?
А вы библиотеки скачали из папки libs с google disk и установили?
Да скачал распокавал. Забросил в папку libraries.
Скопируйте сюда текст ошибки
Скопируйте библиотеки в эту папку
arduino\hardware\arduino\avr\libraries
И сюда скопировал все равно не видет.
А можите вы создать архив самого ардуино с библиотеками и выложить.
Если нет может тогда версию ардуино скажите.
У меня все дома. Вечером подготовлю.
Если не трудно. Буду очень благодарен.
В папке V2.0 архивы
ArduinoIDE_and_LanPLC.zip — IDE с библиотеками
LanPLC.zip — скетч со всеми вспомогательными файлами
Файл HTML\server.cmd запускает маленький веб сервер для отладки HTML
Только нужно пути подправить.
В скетче отключены SPI регистры
//#define SPI595_ENABLE // comment for disabling SP595
//#define SPI165_ENABLE // comment for disabling SP165
С этим #define SPI165_ENABLE компилироваться не будет.
Я переделывал скетч и что-то накосячил. Надо разбираться…
С #define SPI595_ENABLE работать вроде будет, но не уверен, так как последний раз с регистрами не отлаживал.