Удалённый доступ к контроллеру Siemens S7-1200 через веб-интерфейс
Контроллеры Siemens серии S7-1200 имеют встроенный веб-сервер, на котором крутится встроенный веб-интерфейс. Штука очень полезная, можно даже писать свои странички, использовать CSS и JavaScript, но особенно радует возможность чтения и изменения переменных.
Однако по умолчанию переменные необходимо вносить каждый раз при новом посещении страницы, для изменения переменных необходимо авторизоваться, что жутко неудобно, ибо сессия слетает достаточно быстро. Исправим это.
Рассказывать буду на своём примере, контроллер Siemens используется у меня для управления освещением в частном доме. Всего 40 групп освещения, т.е. участвует 40 входов и 40 выходов контроллера. Каждому входу соответствует выход. В самом контроллере написан простой код на SCL; при подаче кратковременного сигнала на вход выход замыкает/размыкает реле:
1 2 3 4 5 6 7 8 9 10 |
FOR #i:=0 TO 5 DO FOR #j:=0 TO 7 DO #bit_in:=PEEK_BOOL(area:=16#81,dbNumber:=0,byteOffset:=#i,bitOffset:=#j); #bit_out:=PEEK_BOOL(area:=16#82,dbNumber:=0,byteOffset:=#i,bitOffset:=#j); IF #bit_in AND NOT #bit_in_buffer[#i,#j] THEN POKE_BOOL(area:=16#82,dbNumber:=0,byteOffset:=#i,bitOffset:=#j,value:=NOT #bit_out); END_IF; #bit_in_buffer[#i,#j]:=#bit_in; END_FOR; END_FOR; |
Переменные такие:
В контроллер загружена одна единственная страничка get.html, которая возвращает состояние всех 40 выходов в формате JSON.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
{ "0.0"::=Q0.0:, "0.1"::=Q0.1:, "0.2"::=Q0.2:, "0.3"::=Q0.3:, "0.4"::=Q0.4:, "0.5"::=Q0.5:, "0.6"::=Q0.6:, "0.7"::=Q0.7:, "1.0"::=Q1.0:, "1.1"::=Q1.1:, "2.0"::=Q2.0:, "2.1"::=Q2.1:, "2.2"::=Q2.2:, "2.3"::=Q2.3:, "2.4"::=Q2.4:, "2.5"::=Q2.5:, "2.6"::=Q2.6:, "2.7"::=Q2.7:, "3.0"::=Q3.0:, "3.1"::=Q3.1:, "3.2"::=Q3.2:, "3.3"::=Q3.3:, "3.4"::=Q3.4:, "3.5"::=Q3.5:, "3.6"::=Q3.6:, "3.7"::=Q3.7:, "4.0"::=Q4.0:, "4.1"::=Q4.1:, "4.2"::=Q4.2:, "4.3"::=Q4.3:, "4.4"::=Q4.4:, "4.5"::=Q4.5:, "4.6"::=Q4.6:, "4.7"::=Q4.7:, "5.0"::=Q5.0:, "5.1"::=Q5.1:, "5.2"::=Q5.2:, "5.3"::=Q5.3:, "5.4"::=Q5.4:, "5.5"::=Q5.5:, "5.6"::=Q5.6:, "5.7"::=Q5.7: } |
Больше страничек в памяти контроллера у меня нет.
Про настройку и написание страниц для веб-сервера контроллера можно почитать здесь (на английском).
Для удобства хочется иметь под рукой приложение, через которое легким движением руки можно включать/выключать любую лампочку в доме, а также иметь возможность управлять группами света, например, выключать свет поэтажно.
Так как встроенный веб-сервер не позволяет без авторизации изменять переменные даже самописным страничкам, я разместил всё на отдельном сервере. Имея небольшие познания программирования на PHP, писать буду на нём.
Для авторизации на контроллере напишем функцию:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * @param string $ip - IP адрес контроллера * @param string $login - Логин пользователя контроллера, по умолчанию admin * @param string $password - Пароль пользователя, по умолчанию пустой */ function auth ($ip, $login, $password) { $ch = curl_init(); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_COOKIEFILE, "/tmp/cookieSiemens"); curl_setopt($ch, CURLOPT_COOKIEJAR, "/tmp/cookieSiemens"); curl_setopt($ch, CURLOPT_REFERER, "http://".$ip."/Portal/Portal.mwsl?PriNav=Varstate"); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_URL, "https://".$ip."/FormLogin"); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, "Redirection=&Login=".$login."&Password=".$password); curl_exec ($ch); curl_close ($ch); } |
Необходимо знать состояние выходов, как определнной группы, так и одного:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
/** * @param string $ip * @param string $programName - Название программы для личных страничек в контроллере * @param string $options - Строка или массив, содержащая либо названия выходов, состояние которых необходимо узнать, либо ключевое слово "enabled", которое возвращает перечень включенных выходов. Если оставлена пустой, то возвращает состояние всех выходов * @return array - массив, содержащий печерень выходов с их состоянием */ function get ($ip,$programName,$options = '') { $ch = curl_init(); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_REFERER, "http://".$ip."/Portal/Portal.mwsl?PriNav=Varstate"); curl_setopt($ch, CURLOPT_COOKIEFILE, "/tmp/cookieSiemens"); curl_setopt($ch, CURLOPT_COOKIEJAR, "/tmp/cookieSiemens"); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Это как раз обращение к той единственной страничке, которую мы загрузили в контроллер curl_setopt($ch, CURLOPT_URL, "http://".$ip."/awp/".$programName."/get.html"); $jsonRaw = curl_exec ($ch); curl_close($ch); $exitStatus = json_decode($jsonRaw, true); // Состояние всех выходов if ($options == '') { return $exitStatus; // Состояние выходов, указанных в массиве вида array('1.0','3.4') } elseif (is_array($options) == TRUE) { $output = array(); foreach ($options as $value) { $output[$value] = $exitStatus[$value]; } return $output; // Состояние включенных выходов } elseif ($options == 'enabled') { $output = array(); foreach ($exitStatus as $key => $value) { if ($value == 1) { $output[$key] = $value; } } return $output; // Состояние отдельно взятого выхода, например, $options = '1.0' } elseif (preg_match('#\d{1}\.\d{1}#', $options)) { return $exitStatus[$options]; } } |
Cчитывать состояние выходов через встроенную страничку также можно, но проще будет воспользоваться своей. Чего не скажешь об изменении их состояния. Осталась последняя функция, которая как раз будет изменять состояние выходов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
/** * @param string $ip * @param string $options - Массив вида array('1.0'=>1,'2.0'=>0). 1 - если хотим включить выход, 0 - если выключить. Ключевое слово "enable" - включает все выхода, "disable" - выключает все включенные выхода * @return string - Возвращает строку запроса для отладки */ function set ($ip, $options = '') { // Переменная, в которой у нас хранятся названия всех выходов, например array('1.0'=>'Свет на кухне') global $names $enabledList = array(); if ($options == 'disable') { $enabled = >get($ip, 'enabled'); foreach ($enabled as $key => $value) { $enabledList[$key] = 0; } } elseif ($options == "enable") { foreach ($names as $key => $value) { $enabledList[$key] = 1; } } elseif (is_array($options)) { foreach ($options as $key => $value) { if (preg_match('#\d{1}\.\d{1}#', $key)) { $enabledList[$key] = $value; } } } if ($enabledList) { $queryString = ''; $i = 1; foreach ($enabledList as $key => $value) { $setValue = (($value == 1) ? 'true' : 'false'); $queryString .= "&v".$i."=q".$key."&modifyvalue_t".$i."=".$setValue."&gobutton_t".$i."=Go"; $i++; } $ch = curl_init(); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_REFERER, "http://".$ip."/Portal/Portal.mwsl?PriNav=Varstate"); curl_setopt($ch, CURLOPT_COOKIEFILE, "/tmp/cookieSiemens"); curl_setopt($ch, CURLOPT_COOKIEJAR, "/tmp/cookieSiemens"); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_URL, "http://".$ip."/VarStateRedirect.mwsl?PriNav=PriNav".$queryString); curl_exec($ch); curl_close($ch); unset($enabledList); unset($i); return $queryString; } } |
Строка запроса на страничке изменения переменных у контроллера имеет следующий вид:
http://192.168.1.2/VarStateRedirect.mwsl?PriNav=PriNav&v1=q1.0&modifyvalue_t1=true&gobutton_t1=Go
Она может быть сколь угодно длинной, чем мы и воспользовались. Вот и всё, при помощи данных функций можно управлять выходами контроллера:
1 2 3 |
auth('192.168.1.2','admin',''); get('192.168.1.2', array('1.0','4.2','4.4','2.3'); set('192.168.1.2', array('0.1'=>1)); |
Можно пойти дальше и написать удобный интерфейс:
Теперь свой ленивый зад можно не отрывать от дивана :). Такой же подход можно использовать для управления другим оборудованием при помощи контроллера, а не только светом. Автополив, насосы, откатные ворота и прочее.
Спасибо за внимание.
8 комментариев
Theodore
about 6 лет назадДоброго времени суток! Подскажите пожалуйста как удаленно можно задавать переменные? К примеру указать температуру в помещение
Ответитьmemtew
about 6 лет назадЕсли в скриптах, то при помощи данной ссылки, предварительно авторизовавшись как Администратор: http://192.168.1.2/VarStateRedirect.mwsl?PriNav=PriNav&v1=q1.0&modifyvalue_t1=true&gobutton_t1=Go Выхода по сути те же самые переменные, поэтому вместо q1.0 вам необходимо указать переменную, которую вы используется в DB-шке. modifyvalue_t1 - это значение, которое вам необходимо задать. Через веб-браузер страница доступна по адресу: http://192.168.1.2/Portal/Portal.mwsl?PriNav=Varstate Также можно написать свою страничку, ссылку на статью об этом я давал в статье.
ОтветитьTheodore
about 6 лет назадПодскажите пожалуйста! Как к примеру реализовать для переменных длина и количество? Пример: есть длина "$length" и количество "$number", тогда чтобы мне передать переменные нужена такая ссылка: http://192.168.1.2/VarStateRedirect.mwsl?PriNav=PriNav&length1=$length&number1=$number&modifyvalue_t1=true&gobutton_t1=Go Правильно я Вас понял?
Ответитьmemtew
about 6 лет назадНе совсем верно. Разберем ссылку: http://192.168.1.2/VarStateRedirect.mwsl?PriNav=PriNav&v1=q1.0&modifyvalue_t1=true&gobutton_t1=Go Здесь: v1=q1.0 - это переменная q1.0 modifyvalue_t1=true - это значение переменной q1.0 gobutton_t1=Go - это подтверждение изменения переменной q1.0. Для нескольких переменных необходимо изменять цифру у v1, modifyvalue_t1, gobutton_t1. Например, с вашими переменными ссылка будет такой (допустим $length=60, $number=20): http://192.168.1.2/VarStateRedirect.mwsl?PriNav=PriNav&v1=length&modifyvalue_t1=60&gobutton_t1=Go&v2=number&modifyvalue_t2=20&gobutton_t2=Go
Tomas
about 6 лет назадДобрый день! А что находится в get.html файле? http://".$ip."/awp/".$programName."/get.html Исходный файлы не могли бы выложить?
Ответитьmemtew
about 6 лет назадИсходный код данной страницы я выложил в статье, но забыл подписать, что это за страница. Сейчас поправил.
ОтветитьАнтон
about 4 года назадПривет! Не получается отобразить полученные данные в формате JSON ни в каком виде, не в виде строки ни в виде элементов массива. Пытается перебросить на страницу http://localhost/Portal/Portal.mwsl?PriNav=Awp Тестирую на локальном хосте.
Ответитьmemtew
about 4 года назадПривет, а необходимые страницы были загружены в контроллер? DB-шки сгенерировал? Нужную строку для того, чтобы загруженные странички работали, в Cyclic interrupt записал?
Ответить