Zyxel Keenetic 4G, arduino и датчики температуры ds18b20

Современные роутеры как небольшие компьютеры, которые выполняют узкоспециализированную задачу для раздачи сетевого трафика. На борту такого небольшого устройства установлена ОС Linux, только без графического интерфейса и с урезанной программной частью. Передо мной встал вопрос, а почему бы не подключить arduino к своему роутеру — Zyxel Keenetic 4G? Немного погуглив я наткнулся на замечательный форум, где энтузиасты собирают модифицированные прошивки с поддержкой установки дополнительных,  пакетов для серии Keenetic.

В нашем случае управление Arduino происходит через библиотеку php_serial.class.php и легкого веб сервера на основе Lighttpd + php. С помощью этих инструментов будем подавать команды ардуинке на чтение показаний с двух температурных датчиков (дом, улица) , записывать ответ в базу данных Sqlite3 и выводить на нашу веб-страничку.

Подготовка маршрутизатора

Первым делом необходимо настроить наш маршрутизатор. У Zyxel Keenetic 4G очень маленькая внутренняя память, поэтому приобретете простенький usb-хаб и флешку не большого объема. Все тонкости настройки я не буду расписывать, а лишь поделюсь необходимыми ссылками. Сразу приготовитесь к долгой и утомительной процедуре.

  1. Альтернативная прошивка. Система opkg для установки дополнительных пакетов. (читаем внимательно WIKI, там все расписано)
  2. Устанавливаем Sqlite3 командой opkg install <имя пакета>, где <имя пакета> полный путь к пакету. Все необходимые пакеты берем здесь.
  3. Устанавливаем и настраиваем Lighttpd и php.

Подключение датчиков

Подключаем температурные датчики ds18b20 к arduino. Номинал резисторов — 4,7 кОм

Схема подключения температурных датчиков

Скетч
Заливаем скетч. За основу был взят код из этого урока и немного доработан под нашу задачу.

[php]
#include <OneWire.h>

int nc;
OneWire ds(10); // Первый датчик
OneWire ds2(11); // Второй датчик
byte addr[8];
byte addr2[8];

void setup(void)
{
Serial.begin(9600);
ds.search(addr);
ds2.search(addr2);
}

void loop(void)
{
byte i;
byte present = 0;
byte data[12];
int Temp;
int znak;

if (Serial.available() > 0) {
nc = Serial.read();
if (nc==1) {
ds.reset();
ds.select(addr);
ds.write(0x44,1);

present = ds.reset();
ds.select(addr);
ds.write(0xBE);

for ( i = 0; i < 9; i++) {
data[i] = ds.read();
}

Temp=(data[1]<<8)+data[0];
Temp=Temp;
znak=((Temp%16)*100)/16;
if (znak<0){
znak=znak*-1;
}
Serial.print(Temp/16);
Serial.print(".");
Serial.print(znak);
Serial.println();
}

if (nc==2) {
ds2.reset();
ds2.select(addr2);
ds2.write(0x44,1);
present = ds2.reset();
ds2.select(addr2);
ds2.write(0xBE);

for ( i = 0; i < 9; i++) {
data[i] = ds2.read();
}
Temp=(data[1]<<8)+data[0];
Temp=Temp;
znak=((Temp%16)*100)/16;
if (znak<0){
znak=znak*-1;
}
Serial.print(Temp/16);
Serial.print(".");
Serial.print(znak);
Serial.println();
}
}
}
[/php]

Передаем команды, принимаем ответы

Скетч принимает две команды — «1» и «2». Для того что бы отправлять команды в Arduino, я использовал обработку кнопок на java script при помощи известной библиотеки Jquery. На моей страничке находятся 2 кнопки и 2 табличных поля в которые выводятся значения с температурных датчиков(см. index.php). При помощь метода POST, скрипт обращается к arduino.php и передает значения наших команд. Здесь происходит передача команд непосредственно в Arduino и чтение ответа в виде температуры, а так же запись в базу данных Sqlite3 (см. arduino.php). Файл readbd.php нужен для первоначального занесения данных температуры в табличку. Красивое оформление кнопок было позаимствовано из статьи «3D кнопки с помощью CSS3».

index.php:

[php]
<?php
include("readbd.php");
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ru">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<head>
<link href="style.css" rel="stylesheet" media="all" />
<title>Температура в доме и на улице</title>
<link href="flot/examples/layout.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="jquery.js"></script>
</head>
<body class="body">
<br>
<div class="container">
<header>
<h1><span>Температура</span></h1>
</header>
</div>
<div class="ul1" >
<br>
<table width="60px" border="0"><tr>
<td><a href="#" id="led6" class="button black" onMouseDown="command(1);">Улица</a></td>
<td><a href="#" id="led6" class="button yellow" onMouseDown="command(2);">Дом</a></td>
</tr><tr>
<td class="c1"><div id="content1" ><?php echo ».$temp1.""; ?></div></td>
<td class="c1"><div id="content2" ><?php echo ».$temp2.""; ?></div></td>
</tr>
</table>
</div>
<br>
<script>
function command(id)
{
$.ajax({
type:’POST’,
url:’arduino.php’,
data:{msg:id},
cache: false,
success: function(html){
$("#content"+id).html(html);
}
})
}
</script>

</center>
</body>
</html>
[/php]

arduino.php:

[php]
<?php
include "php_serial.class.php";
$serial = new phpSerial;
//Задаем путь к Arduino (У вас может быть совсем по другому)
$serial->deviceSet("/../../../../dev/ttyACM0");
//Это стандарт
$serial->confBaudRate(9600);
$serial->confParity("none");
$serial->confCharacterLength(8);
$serial->confStopBits(1);
$serial->confFlowControl("none");
$serial->deviceOpen();
//Отправляем команду
$serial->sendMessage(chr($_POST[‘msg’]));
//Читаем ответ Arduino
$read = $serial->readPort();
//Зыкрываем соединение
$serial->deviceClose();
try {
// Создаем или открываем созданную ранее базу данных
$db = new PDO(‘sqlite:’.dirname(__FILE__).DIRECTORY_SEPARATOR.’arduino.db’);
// Создаем таблицу temp, если не найдена
$db->exec(‘CREATE TABLE IF NOT EXISTS temp (
idtemp INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
temperature VARCHAR(255) NOT NULL,
datetime VARCHAR(255) NOT NULL
)’);
$db->exec(‘CREATE TABLE IF NOT EXISTS temp2 (
idtemp INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
temperature VARCHAR(255) NOT NULL,
datetime VARCHAR(255) NOT NULL
)’);
//определяем текущцю дату и время
$d=date("d.m.y, G:i:s");
//Обновляем
//$db->exec(‘UPDATE temp SET temperature="’.$read.’", datetime="’.$d.’" WHERE idtemp=’.$_POST[‘msg’].»);

if ($_POST[‘msg’]==1){
//Добавляем новую строку в БД
$db->exec(‘INSERT INTO temp (temperature, datetime) VALUES ("’.$read.’","’.$d.’" )’);
//Получаем последнюю запись
$st = $db->query(‘SELECT MAX(idtemp) as id, temperature FROM temp’);
$results = $st->fetchAll();
foreach ($results as $row) {
echo ».$row[‘temperature’]."\n";
}
}
if ($_POST[‘msg’]==2){
$db->exec(‘INSERT INTO temp2 (temperature, datetime) VALUES ("’.$read.’","’.$d.’" )’);
$st = $db->query(‘SELECT MAX(idtemp) as id, temperature as tempe FROM temp2’);
$results = $st->fetchAll();
foreach ($results as $row) {
echo ».$row[‘tempe’]."\n";
}
}
} catch (PDOException $e) {
die($e->getMessage());
}
?>
[/php]

Проблемы с которыми я столкнулся:

  1. Arduino в маршрутизаторе определялся как /dev/ttyACM0, а не /dev/ttyUSB0.
  2. После того как php был написан, постоянно выскакивала ошибка — No stty availible, unable to run.», E_USER_ERROR.
  3. При отсылке команд Arduino постоянно перезагружался.

Решения:

  1. Так как Веб сервер у меня установлен на флешке в своем php скрипте нужно правильно указать путь к устройству. Так же установите права 777 на /dev/ttyACM0 или /dev/ttyUSB0 (зависит от Arduino).[php]deviceSet(/../../../../dev/ttyACM0);[/php]
  2. В php_serial.class.php убрал кусок кода:[php]
    if($this->_exec("stty —version") === 0)
    {
    register_shutdown_function(array($this, "deviceClose"));
    }
    else
    {
    trigger_error("No stty availible, unable to run.", E_USER_ERROR);
    }
    [/php]
  3. После заливки скетча в Arduino между пинами GND и Reset ставим конденсатор на 220 мФ. Это предотвращает перезагрузку во время приема команд.

Пару фоток


[stextbox id=»custom» caption=»Скачать все необходимые файлы»]

[download id=»55″]
[/stextbox]
P.S.
Это лишь небольшой пример того как можно использовать arduino в связке с роутером. Можно написать более серьезные вещи. Управлять любыми приборами в своем доме при помощи веб-интерфейса и Arduino.

16 комментариев к “Zyxel Keenetic 4G, arduino и датчики температуры ds18b20”

  1. Не получается, беда прям. Вот что выдает php:
    Warning: Unable to open the device in /opt/lampp/htdocs/ard/php_serial.class.php on line 157
    Warning: Device must be opened in /opt/lampp/htdocs/ard/php_serial.class.php on line 541

    Конденсатор спасает от перезагрузки. У меня мигает TX на Arduino и все, но если Arduino реагирует, это означает, что связь с портом есть, но сбрасывается что ли. То есть скрипт открывает порт, а Arduino тут же сбрасывает соединение, получается так. Пробую на OpenSuse.

    • Возможно какие то проблемы с настройками самого веб-сервера. Я пробовал на роутере и на Ubuntu server 11.10, нигде не было проблем, кроме выше описанных.

  2. Заработало. Удалось передать и прочитать данные.
    Проблема заключалось в настройках php — open_basedir. Пришлось поставить там корневой каталог.

  3. Добрый день, смысл использовать два ds18d на разных пинах дуины? Смысл 1wire в универсальности подключения.
    А вот про альтернативу интернет шилда — это хорошо, полезно.

    • К сожалению я не разобрался как по одной линии работать с этими датчиками. Знаю что можно получить уникальный номер устройства. Поэтому пошел более простым для себя путем.

  4. Очень интересные проекты. Разместил ссылки на Ваши проекты с коротеньким описанием на arduino.ru. Очень надеюсь, что Вы не против.

  5. Сделал себе управление ардуинкой через вебморду на кинетике 4г по вашему описанию, только попроще задача и соответственно код 🙂 Спасибо за статью 🙂

  6. Через пару дней использования ардуинка начала перегружаться при передаче данных, конденсатор как описано выше проблему решил частично (данные отправлялись нормально, но от ардуино не приходили). После конфигурирования порта командой (написал ниже) все работает стабильно.

    stty -F /dev/usb/tts/0 cs8 9600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl

  7. имеется zyxel keenetic, ваш пример, почему-то отрабатывается через раз, также периодичесиу выдает ошибку -127

  8. Здравствуйте! Весьма познавательная статья, спасибо! :good:
    Есть вопрос по пресловутому /dev/ttyACM0. Работаю под Убунту 10.10, ардуина замечательно определилась как /dev/ttyACM0. Php скрипт

    призван писать в порт
    1.»1″ — номер устройства
    2.»1″ или «0» т.е. вкл или выкл.

    Скетч вида:
    int cmd = 0;
    int newOLStatus = 0;
    int prevOLStatus = 0;
    void setup()
    {
    Serial.begin(9600);
    pinMode(OLPin, OUTPUT);
    }
    void loop()
    if (Serial.available() > 0)
    {
    if (isExecuting == false)
    {
    cmd = Serial.read() — ‘0’;
    isExecuting = true;
    }
    if (cmd == 1) //управление светодиодом
    {
    newOLStatus = (int) Serial.read();
    if (newOLStatus != prevOLStatus)
    {
    digitalWrite(13,newOLStatus);
    prevOLStatus = newOLStatus;
    }
    }
    }

    читает порт и исполняет команды вкл./выкл диод.

    Проблема: в не зависимости от значения второго отправленного бита (1 или 0) устройство 1 включается и выключаться не хочет. В виндовс на процессинге запись в порт работет на ура — скетч без ошибок.. Может ли такой глюк быть связан с настройками порта? :pleasantry:
    :pleasantry:

  9. php скрипт использовал такой (ниже) и тот, что предлагали Вы — результат одинаковый:
    «$fp = fopen(«/dev/ttyACM0», «w»);
    flock($fp,LOCK_EX);
    fwrite($fp, 1);
    fwrite($fp, 0);»

  10. А чем отличается альтернативная прошивка от оригинальной? Просто у меня Zyxel Keenetic и хочется попробовать так сделать!!

Обсуждение закрыто.