Tplink mr3020 + arduino

Белое на черном.

Выложу по тихому пару вкусностей, которые у меня имеются. На их основе могут быть построены (и надеюсь я когда-нибудь это сделаю) робототехнические DIY проекты. Осталось придумать в какую именно сторону заточить эти универсальные средства.

Первый DIY набор весьма универсален, прост и позволяет обойтись готовыми модулями, без (о чудо) паяльных дел. Широкому кругу ограниченных людей, близких к DIY, он до боли знаком.  Представляет из себя связку роутера с arduino.  в моем случае это:

Состав таков: 1. Роутер tplink mr3020  с прошивкой openwrt, позволяющей получить доступ к ресурсам роутера и сделать из него полноценный линукс компьютер — мозг системы, обеспечивающий подключения по средствам wi-fi, lan, usb. 2. USB флешка для расширения внутренней памяти роутера. 3. USB хаб. USB порт в моем роутере всего один, а хочется подключить к нему еще и arduino кроме флешки. 4. Arduino. В моем случае — это nano. Своеобразный мозжечок, связующее звено с низкоуровневой периферией — датчиками, светодиодами/моторами и т.п. 5. Источник питания и патч корд для первоначального коннекта с роутером и его настройки.

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

За рамками данного материала оставим следующие моменты: 1. Зачем все это нужно, когда есть raspberry pi, black swift etc? Да, есть и такие мощные, функциональные изделия. Но и представленное решение имеет право на жизнь, благодаря простоте, дешевизне. И еще одно — все составляющие у меня есть:) Поэтому о них рассказ сей. 2. Установка openwrt на роутер, монтирование загрузочной флешки, установка драйверов arduino, поднятие http сервера и прочие вспомогательные вещи, касающиеся настройки роутера, остаются за рамками этого поста. В сети они многократно описаны.

Итак, роутер настроен и arduino подключена к usb порту. Как водится у пользователей сего продукта — подергаем 13-м пином и помигаем тем самым встроенным светодиодом. Для начала, зальем в ардуину скетч:

int led = 13;
 
void setup() {
  pinMode(led, OUTPUT);
  Serial.begin(9600);
}
 
void loop() {
  while (Serial.available()) {
    switch (Serial.read()) {
      case '1': {
        digitalWrite(led, HIGH);
        Serial.write("Led on!");
        break;
      }
      case '0': {
        digitalWrite(led, LOW);
        Serial.write("Led off!");
        break;
      }
      default: break;
    }
  }
}
int led = 13;

void setup() {
  pinMode(led, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  while (Serial.available()) {
    switch (Serial.read()) {
      case '1': {
        digitalWrite(led, HIGH);
        Serial.write("Led on!");
        break;
      }
      case '0': {
        digitalWrite(led, LOW);
        Serial.write("Led off!");
        break;
      }
      default: break;
    }
  }
}

Как видно из кода, по приходу «1» в UART, на 13-м пине установится высокий уровень и светодиод загорится, при этом в порт будет отправлено «Led on!». «0» же, напротив, погасит светодиод, путем установки на пине 13 низкого уровня, а в порт ардуина пошлет «Led off!».

Итак, теперь время проделать это с помощью связки роутера и ардуины. Для связи с роутером я использую программу «PuTTY». Arduino определяется у мена в ls /dev как ttyUSB0. Для начала настроим порт роутера на нашу скорость и запретим перезагружать контроллер после каждой отправки (по умолчанию arduino будет перезагружаться). Для этого в окне терминала выполним команду: stty -F /dev/ttyUSB0 cs8 9600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl  Для отправки «1» в порт используем команду: echo 1 > /dev/ttyUSB0 При этом светодиод загорится. Для отправки «0» в порт используем команду: echo 0 > /dev/ttyUSB0 Светодиод погаснет. Если при этом открыть еще одно окно PuTTY и выполнить команду: cat /dev/ttyUSB0 Что означает — «слушать порт». То увидим сообщения от arduino.

Уже кое-что, не правда ли?) Теперь нужно как-то все автоматизировать. Имеется в linux системах такой мощный инструмент как командный интерпретатор bash и возможность работы с ним посредством скриптов. Для нашей вводной задачи будет достаточно одного-двух скриптов. Первый — скрипт настройки порта. Второй — собственно, скрипт посылки данных в порт. Я их разделил, но это не принципиально. У мена на роутере поднят http сервер и все скрипты лежат в папке /srv/www/cgi-bin/.

Итак, первый скрипт (port.sh) настройки порта будет содержать всю ту же нашу одну команду, приведенную выше:

!#/bin/sh
stty -F /dev/ttyUSB0 cs8 9600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl
!#/bin/sh
stty -F /dev/ttyUSB0 cs8 9600 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts -hupcl

Теперь для настройки порта arduino достаточно выполнить в терминале PuTTY команду вида: /srv/www/cgi-bin/port.sh Второй скрипт (led.sh) сложнее:

!#/bin/sh
#берем команду  из "переменной" command GET-запроса
#если эапроса нет - берем команду из первого аргумента
if [ -z "$QUERY_STRING" ]; then COMMAND=$1;
else
COMMAND=`echo "$QUERY_STRING" | sed -n 's/command=//p'`
fi
#отправка данных в порт
case $COMMAND in
        on)
           echo 1 > /dev/ttyUSB0;;
        off)
           echo 0 > /dev/ttyUSB0;;
esac
!#/bin/sh
#берем команду  из "переменной" command GET-запроса
#если эапроса нет - берем команду из первого аргумента
if [ -z "$QUERY_STRING" ]; then COMMAND=$1;
else
COMMAND=`echo "$QUERY_STRING" | sed -n 's/command=//p'`
fi
#отправка данных в порт
case $COMMAND in
        on)
           echo 1 > /dev/ttyUSB0;;
        off)
           echo 0 > /dev/ttyUSB0;;
esac

Скрипт имеет возможности большие чем нам нужно для мигания светодиодом из командной строки, но они пригодятся потом. Для нашей же задачи, достаточно просто ввести в терминале команду вида: /srv/www/cgi-bin/led.sh on для того, чтобы светодиод загорелся, или /srv/www/cgi-bin/led.sh off чтобы потух. Скрипт содержит два важных момента. На первом остановимся здесь, второй раскроем потом. Первое — это передача в скрипт при выполнении параметра (или аргумента). Они передаются после имени скрипта, отделенные от него и друг друга пробелами. В моем случае параметр всего один. Он должен быть в значении либо «on» или «off» для того чтобы результатом выполнения скрипта была отправка того или иного значения в порт. Мигать будет так:)

Все конечно гуд, но возится с терминалом и командной строкой для такого простого действия не совсем то, к чему нужно стремится. Поэтому перейдем к более привычным способам управления устройствами: для начала, с помощью браузера. Я создал простейшую web страницу (led_control_02.html) и разместил ее в каталоге www моего http сервера.

<html>
<head>
    <title>Relay control</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <script type="text/javascript" src="jquery1112min.js"></script>
    <script type="text/javascript">
        function command(action)
                {
                   url="/cgi-bin/led.sh?command="+action;
                   var xmlhttp=new XMLHttpRequest();
                   xmlhttp.open("GET",url,false);
                   xmlhttp.send();
                }
    </script>
</head>
<body>
<div id="content"></div>
    <script>
        function show()
        {
            $.ajax({
                url: "date.php",
                cache: false,
                success: function(html){
                    $("#content").html(html);
                }
            });
        }
        $(document).ready(function(){
            show();
            setInterval('show()',1000);
        });
    </script> 
 
<div class="control">
<label for="num">Num Lock LED</label>
<button id="num" type="button" onclick="command('on')">Led on</button>
</div>
<div class="control">
<label for="num">Num Lock LED</label>
<button id="num" type="button" onclick="command('off')">Led off</button>
</div>
</div>
</body>
</html>
<html>
<head>
    <title>Relay control</title>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <script type="text/javascript" src="jquery1112min.js"></script>
    <script type="text/javascript">
        function command(action)
                {
             	   url="/cgi-bin/led.sh?command="+action;
                   var xmlhttp=new XMLHttpRequest();
                   xmlhttp.open("GET",url,false);
                   xmlhttp.send();
                }
    </script>
</head>
<body>
<div id="content"></div>
    <script>
        function show()
        {
            $.ajax({
                url: "date.php",
                cache: false,
                success: function(html){
                    $("#content").html(html);
                }
            });
        }
        $(document).ready(function(){
            show();
            setInterval('show()',1000);
        });
    </script> 

<div class="control">
<label for="num">Num Lock LED</label>
<button id="num" type="button" onclick="command('on')">Led on</button>
</div>
<div class="control">
<label for="num">Num Lock LED</label>
<button id="num" type="button" onclick="command('off')">Led off</button>
</div>
</div>
</body>
</html>

Страница простая (всего две кнопки и текстовая строка), да не очень. Тут есть и ajax (обновляю им дату для информации) и javascript (запускаю скрипт и передаю ему параметр).  То есть перед использованием, нужно не забыть забросить в каталог сервера файл библиотеки jquery.  Выглядит в браузере так:

Жмем кнопку и в скрипт запускается в виде 192.168.2.1:81/cgi-bin/led.sh?comand=on либо off. Вернемся теперь к телу скрипта. Все что после знака «?» передается зарезервированной строковой переменной «$QUERY_STRING» В первом условии скрипта проверяем эту строку на нулевую длину. Если условие истинно то передаем в обработку первый параметр (так делаем когда управляем с командной строки). Если ложно, то работаем с этой строкой и с помощью поточного текстового редактора sed отделяем мух от котлет параметр от «comand=». Полученный результат используем для выбора зажигать или гасить светодиод.

Браузер освоили, теперь дело за приложением. Для простоты, взаимодействовать оно будет с роутером так же как браузер, то есть по средствам http запросов. Для написания тестовой программы я использовал C++ и библиотеку Qt (нравится она мне почему-то). Приложение состоит из формы с текстовым полем, для вывода ответа от сервера, и двумя кнопками on и off. Для общения с сервером используется класс QNetworkAccessManager. Исходный код главного окна программы:

#include "mainwindow.h"
#include "ui_mainwindow.h"
 
QNetworkAccessManager manager;
QNetworkReply *reply;
QUrl apiUrl;
QByteArray requestString;
 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    nam = new QNetworkAccessManager(this);
    QObject::connect(nam, SIGNAL(finished(QNetworkReply*)),
    this, SLOT(finishedSlot(QNetworkReply*)));
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
 
void MainWindow::on_pushButton_clicked()
{
    QUrl url("http://192.168.2.1:81/cgi-bin/led.sh?command=on");
    QNetworkReply* reply = nam->get(QNetworkRequest(url));
}
 
void MainWindow::finishedSlot(QNetworkReply* reply)
{
// Не произошло-ли ошибки?
    if (reply->error() == QNetworkReply::NoError) {
        // Читаем ответ от сервера
        QByteArray bytes = reply->readAll();
        QString string(bytes);
        // Выводим ответ н экран
        ui->textEdit->append(string);
        //ui->textEdit->append("Yes");
        qDebug() << string;
    } else {
        // обрабатываем ошибку
        ui->textEdit->append(reply->errorString());
       // ui->textEdit->append("No");
        qDebug() << reply->errorString();
    }
    delete reply;
}
 
void MainWindow::on_pushButton_2_clicked()
{
    QUrl url("http://192.168.2.1:81/cgi-bin/led.sh?command=off");
    QNetworkReply* reply = nam->get(QNetworkRequest(url));
}
#include "mainwindow.h"
#include "ui_mainwindow.h"

QNetworkAccessManager manager;
QNetworkReply *reply;
QUrl apiUrl;
QByteArray requestString;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    nam = new QNetworkAccessManager(this);
    QObject::connect(nam, SIGNAL(finished(QNetworkReply*)),
    this, SLOT(finishedSlot(QNetworkReply*)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    QUrl url("http://192.168.2.1:81/cgi-bin/led.sh?command=on");
    QNetworkReply* reply = nam->get(QNetworkRequest(url));
}

void MainWindow::finishedSlot(QNetworkReply* reply)
{
// Не произошло-ли ошибки?
    if (reply->error() == QNetworkReply::NoError) {
        // Читаем ответ от сервера
        QByteArray bytes = reply->readAll();
        QString string(bytes);
        // Выводим ответ н экран
        ui->textEdit->append(string);
        //ui->textEdit->append("Yes");
        qDebug() << string;
    } else {
        // обрабатываем ошибку
        ui->textEdit->append(reply->errorString());
       // ui->textEdit->append("No");
        qDebug() << reply->errorString();
    }
    delete reply;
}

void MainWindow::on_pushButton_2_clicked()
{
    QUrl url("http://192.168.2.1:81/cgi-bin/led.sh?command=off");
    QNetworkReply* reply = nam->get(QNetworkRequest(url));
}

Заголовочный файл.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include <QtNetwork>
#include <QNetworkAccessManager>
 
namespace Ui {
class MainWindow;
}
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
 
    QNetworkAccessManager * nam;
 
private slots:
    void on_pushButton_clicked();
    void finishedSlot(QNetworkReply* reply);
 
    void on_pushButton_2_clicked();
 
private:
    Ui::MainWindow *ui;
};
 
#endif // MAINWINDOW_H
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtNetwork>
#include <QNetworkAccessManager>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    QNetworkAccessManager * nam;

private slots:
    void on_pushButton_clicked();
    void finishedSlot(QNetworkReply* reply);

    void on_pushButton_2_clicked();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

Остальное могу выложить, но смысла в этом не много. Выглядит форма так:

Да, уже добавил еще кнопок на будущее.) Но они не должны смущать.

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

Запись опубликована в рубрике AVR, openwrt, Разработки. Добавьте в закладки постоянную ссылку.

         
Подписаться на новые статьи блога:

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

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.