Программируемый логический контроллер (ПЛК) на базе Arduino + Ethernet shield + 74HC165 + 74HC595


Идея заключалась в том, чтобы сделать контроллер исполнительных устройств с некоторой степенью автоматизации и настраиваемый полностью через 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

SDFat
https://code.google.com/p/sdfatlib/


0 комментариев на «“Программируемый логический контроллер (ПЛК) на базе Arduino + Ethernet shield + 74HC165 + 74HC595”»

  1. Очень, ОЧЕНЬ полезная работа. Но для подавляющего большинства начинающих пользователей всетаки не нужно считывать показания 100 датчиков и переключать 100 реле, а нужно быстро и с минимальными затратами (как денег так и усилий) запустить то же самое, но ограничившись 16-тью реле в виде стандартного блока реле ардуино, без сдвиговых регистров, просто плата и изернет щит 🙂

    • Просто без использования аппаратного SPI мне этот проект был не особо интересен.
      В ближайшее время думаю сделать условную компиляцию для отключения 74HC165, 74HC595.
      И сделаю прямые выходы.

  2. А можно посмотреть, как Ваш проект реализован аппаратно? Фотографии, может быть разводка печатной платы? Может быть, всё не так страшно как звучит, если посмотреть глазами на готовый девайс? И кстати, не вырастет ли общая стабильность системы если отказаться от использования регистров?

    • Ох, чтото мне страшно стало от вида этого клубка проводов :)возможно, что описанный Вами баг(что при включении состояния реле могут быть произвольными) исчезнет, если развести аккуратную печатную плату на двустороннем текстолите, и убрать этот клубок проводов… нет, я пожалуй предпочту сперва испытать без регистровый вариант, с бутербродом из главной платы и изернет щита 🙂

    • Нет. Не исчезнет. Дело в самих регистрах. Я это могу побороть. Но мне лень.
      В вашем случае при управлении реле через выходы ардуино напрямую такого не будет.

  3. По кэшированию. Надо правильные 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

    Должно кэшировать на один час. Но при отладке сайта из-за каждого мельчайшего изменения каждый раз придётся принудительно перезагружать вообще всё.

    Возможно, в самом сервере есть какие-то опции, которые всё это включают. Если же нет, вроде несложно добавить.

  4. Добрый день.

    Пытаюсь все запустить и тут ошибка. Не могу импортировать библиотеки. Может версия Ардуины не та? Не подскажите где копать.

    • Добрый день!
      Вы какую версию пробуете?
      Лучше пробуйте из папки 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 работать вроде будет, но не уверен, так как последний раз с регистрами не отлаживал.

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Arduino Bluetooth CraftDuino DIY Google IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение