Управление движением через браузер, что применить?

Тема в разделе "Arduino & Shields", создана пользователем Kuiper, 11 янв 2014.

  1. Kuiper

    Kuiper Нуб

    Всем привет!

    Пожалуйста подскажите, каким образом можно управлять Ардуиной через браузер, с учетом непрерывного действия?
    То есть, чтобы при нажатой кнопке "Вперед" непрерывно шла отправка нужного символа на последовательный порт.

    Поясню, как я сейчас делаю:
    1. Есть php-скрипт, пишущий через fwrite на COM3-порт символ "w", получая который, Ардуино включает нужный пин (драйвер мотора).
    2. Скрипт вызывается через AJAX с HTML-страницы, таким образом:

    Код (Text):
    <script type="text/javascript" language="javascript">
    function makeRequest(url) {
    var http_request = false;
    if (window.XMLHttpRequest) { // Mozilla, Safari, ...
    http_request = new XMLHttpRequest();
    if (http_request.overrideMimeType) {
    http_request.overrideMimeType('text/xml');
    // Читайте ниже об этой строке
    }
    } else if (window.ActiveXObject) { // IE
    try {
    http_request = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
    try {
    http_request = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (e) {}
    }
    }

    http_request.onreadystatechange = function() { alertContents(http_request); };
    http_request.open('GET', url, true);
    http_request.send(null);
    }

    </script>

    <!-- ================= -->
    <!-- ТУТ КНОПКА ВПЕРЕД -->

    <div style="width:250px;" align="center">
    <img src="img/forward.png" title="Вперед" value="VPERED" onclick="makeRequest('send.php?symbol=VPERED')">

    <!-- ТУТ КНОПКА ВПЕРЕД -->
    <!-- ================= -->

    </div>
    До этого тестил с обычной <form></form>, где после нажатия кнопки браузер перекидывал на скрипт.

    Но текущий AJAX-скрипт также не может решить задачу непрерывного управления, то есть, нажав кнопку (картинку), страница отправляет один раз "w" на COM3 и все, ждет, пока кнопку\картинку вновь не нажмут.

    Аналогичная ситуация при использовании событий onMouseDown и onMouseOver - также, одинарное срабатывание. Если зажать кнопку\картинку, то событие идет один раз.



    Скажите, возможно ли через браузер сделать что-то вроде события onPressed, чтобы браузер циклично, непрерывно исполнял код, пока нажата кнопка\картинка?
    Если я правильно понимаю, только средствами JS отправку данных на последовательный порт не сделать, т.к. JS очень изолирован от железа компьютера, поэтому стоит задача непрерывно вызывать php-скрипт, можно как-то это сделать?

    Заранее большое спасибо!
     
  2. Recoshet

    Recoshet Нерд

    Используйте jquery
    Легко можно отловить события "начала нажатия" http://api.jquery.com/mousedown/
    Событие когда кнопка мыши "отожмётся" http://api.jquery.com/mouseup/

    И этим двум событиям присвоить ajax отправку данных.
    P.S. Не надо спамить php запросами. Достаточно отправить начало и конец. И тут понадобиться добавить ещё команду для остановки двигателя.

    UPD: Вот это то что вам нужно:
    Код (HTML5):

    <html lang="en">
    <head>
      <meta charset="utf-8">
      <title>mousedown demo</title>
      <script src="http://code.jquery.com/jquery-1.10.2.js"></script>
    </head>
    <body>
     
    <p>Press mouse and release here.</p>
     
    <script>
    $( "p" )
      .mouseup(function() {
        $( this ).append( "<span style='color:#f00;'>Mouse up.</span>" );
      })
      .mousedown(function() {
        $( this ).append( "<span style='color:#00f;'>Mouse down.</span>" );
      });
    </script>
     
    </body>
    </html>
     
     
    Последнее редактирование: 11 янв 2014
    Kuiper нравится это.
  3. rav_75

    rav_75 Гик

    аналогично keydown() и keyup(), читаете код нажатой/отпущеной клавиши, выполняете соответствующее действие
     
    Kuiper нравится это.
  4. Kuiper

    Kuiper Нуб

    Recoshet, rav_75, большое спасибо!
    Очень интересное решение с началом-остановкой. Действительно, это лучше, чем непрерывные запросы.
     
  5. Kuiper

    Kuiper Нуб

    Сделал вариант с двумя запросами (onMouseDown = действие, onMouseUp = сброс всех пинов в LOW), отличное работает, Recoshet, rav_75, еще раз спасибо! :)

    Может кому-то тоже пригодится:
    Код HTML-страницы:
    Код (Text):
    <!DOCTYPE html>
    <html dir="ltr" lang="ru-RU">
        <head>
            <meta charset="UTF-8" />
            <title>Управление Ардуино через браузер</title>
            <script type="text/javascript" src="javascript/jquery-1.7.2.min.js"></script>
        </head>
    <body>

    <script type="text/javascript" language="javascript">
    function makeRequest(url) {
    var http_request = false;
    if (window.XMLHttpRequest) { // Mozilla, Safari, ...
    http_request = new XMLHttpRequest();
    if (http_request.overrideMimeType) {
    http_request.overrideMimeType('text/xml');
    // Читайте ниже об этой строке
    }
    } else if (window.ActiveXObject) { // IE
    try {
    http_request = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
    try {
    http_request = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (e) {}
    }
    }

    http_request.onreadystatechange = function() { alertContents(http_request); };
    http_request.open('GET', url, true);
    http_request.send(null);
    }

    </script>

    <div style="width:250px;" align="center">
    <img src="img/forward.png" title="Вперед" value="VPERED" onMouseDown="makeRequest('send.php?symbol=VPERED')" onMouseUp="makeRequest('send.php?symbol=CLS')"><br>
    <img src="img/left.png" title="Влево" value="LEFT" onMouseDown="makeRequest('send.php?symbol=LEFT')" onMouseUp="makeRequest('send.php?symbol=CLS')">
    <img src="img/back.png" title="Назад" value="NAZAD" onMouseDown="makeRequest('send.php?symbol=NAZAD')" onMouseUp="makeRequest('send.php?symbol=CLS')">
    <img src="img/right.png" title="Вправо" value="RIGHT" onMouseDown="makeRequest('send.php?symbol=RIGHT')" onMouseUp="makeRequest('send.php?symbol=CLS')"><br><br><br>

    <img src="img/action.png" title="Действие" value="ACTION" onMouseDown="makeRequest('send.php?symbol=ACTION')" onMouseUp="makeRequest('send.php?symbol=CLS')">
    </div>

    </body>
    <html>
    Кнопки (закинуть в папку /img и переименовать):
    [​IMG] [​IMG] [​IMG] [​IMG] [​IMG]
    Код php-скрипта:
    Код (Text):

    <?php

    // Считываем полученный символ
    $curr = $_GET['symbol'];

    // Преобразовываем в ASCII-код
    switch ($curr) {
        // Буква Q (CLS)
        case 'CLS':
            $symbol = '81';
            break;
        // Буква w
        case 'VPERED':
            $symbol = '119';
            break;
        // Буква s
        case 'NAZAD':
            $symbol = '115';
            break;
        // Буква a
        case 'LEFT':
            $symbol = '97';
            break;
        // Буква d
        case 'RIGHT':
            $symbol = '100';
            break;
        // Буква z
        case 'ACTION':
            $symbol = '122';
            break;
        // По-умолчанию пустой символ
        default:
            break;
    }

    // Открываем порт на запись
    $fp = fopen ("COM3", "w");
    if (!$fp) {
      echo "<span style='color:red;'>Not open COM3 port!</span><br>";
    } else {
        echo "<span style='color:green;'>COM3 port successfully opened!</span><br>";
     
        echo "CURRENT SYMBOL IS: ".chr($symbol)."<br><br><br>";
     
        // Отправляем в COM-порт символ
        fwrite($fp, chr($symbol));
    }

    // Закрываем COM-порт
    fclose($fp);

    ?>
    Код скетча (вместо реле на пинах светодиоды, для тестирования):
    Код (Text):
    // Управление Ардуино через браузер

    int blue = 8;
    int red = 9;
    int green = 6;
    int tblue = 7;
    char p;

    void setup() {
      Serial.begin(9600);
      pinMode(blue, OUTPUT);
      pinMode(red, OUTPUT);
      pinMode(green, OUTPUT);
      pinMode(tblue, OUTPUT);
    }

    void loop() {
      // Если есть данные в последовательном порту
      if (Serial.available()) {
        // Считываем данные в переменную
        p = Serial.read();
     
        // Если буква Q (CLS)
        if (p == 'Q') {
          digitalWrite(blue, LOW);
          digitalWrite(red, LOW);
          digitalWrite(green, LOW);
          digitalWrite(tblue, LOW);
        }
     
        // Если буква w
        if (p == 'w') {
          digitalWrite(blue, HIGH);
        }
     
        // Если буква s
        else if (p == 's'){
          digitalWrite(red, HIGH);
        }
     
        // Если буква a
        else if (p == 'a'){
          digitalWrite(green, HIGH);
        }
     
        // Если буква d
        else if (p == 'd'){
          digitalWrite(tblue, HIGH);
        }
     
        // Если буква z
        else if (p == 'z'){
          digitalWrite(blue, HIGH);
          digitalWrite(green, HIGH);
        }
      }
    }

    Схема рабочая, но хочется немного улучшить эту схему управления, здесь два момента:

    1. Одновременная работа двух и более кнопок. То есть, к примеру, двигаясь вперед (нажата кнопка "ВПЕРЕД"), мы начинаем стрелять (нажимаем кнопку "ДЕЙСТВИЕ"). Постреляв, отпускаем кнопку "ДЕЙСТВИЕ" и при этом она отправляет запрос CLS (сброс), робот останавливается, .т.к. CLS отправил LOW для всех пинов.
    Можно сделать отдельный сброс для каждой кнопки, завязав, к примеру, на ту же букву, но в верхнем регистре (w = HIGH, W = LOW, s = HIGH, S = LOW и т.д.), но может есть более простой путь?

    2. Защита от потери связи. Если вдруг потеряется связь с роботом и при этом не успеет поступить сигнал сброса (CLS), то робот будет выполнять действие, пока не кончится заряд батареи.
    Можно просто сделать действие HIGH на какое-то время, например, так:
    Код (Text):
    if (p == "w") {
    digitalWrite(relay1, HIGH);
    delay(60000); // работает 1 минуту
    digitalWrite(relay1, LOW);
    }
    Но, если мы держим кнопку нажатой более 1 минуты, то Ардуино переведет пин в LOW и нужно будет нажимать кнопку вновь.
    Можно ли каким-то другим способом сделать защиту по времени на такой случай?

    Большое спасибо!
     
  6. Kuiper

    Kuiper Нуб

    Вариант с отдельными отключающими командами (w=>W, s=>S и т.д.):
    Интерфейс:
    Код (Text):
    <!DOCTYPE html>
    <html dir="ltr" lang="ru-RU">
        <head>
            <meta charset="UTF-8" />
            <title>Управление Ардуино через браузер</title>
            <script type="text/javascript" src="javascript/jquery-1.7.2.min.js"></script>
        </head>
    <body>

    <script type="text/javascript" language="javascript">
    function makeRequest(url) {
    var http_request = false;
    if (window.XMLHttpRequest) { // Mozilla, Safari, ...
    http_request = new XMLHttpRequest();
    if (http_request.overrideMimeType) {
    http_request.overrideMimeType('text/xml');
    // Читайте ниже об этой строке
    }
    } else if (window.ActiveXObject) { // IE
    try {
    http_request = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
    try {
    http_request = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (e) {}
    }
    }

    http_request.onreadystatechange = function() { alertContents(http_request); };
    http_request.open('GET', url, true);
    http_request.send(null);
    }

    </script>

    <div style="width:250px;" align="center">
    <img src="img/forward.png" title="Вперед" value="VPERED" onMouseDown="makeRequest('send.php?symbol=w')" onMouseUp="makeRequest('send.php?symbol=w&cls=1')"><br>
    <img src="img/left.png" title="Влево" value="LEFT" onMouseDown="makeRequest('send.php?symbol=a')" onMouseUp="makeRequest('send.php?symbol=a&cls=1')">
    <img src="img/back.png" title="Назад" value="NAZAD" onMouseDown="makeRequest('send.php?symbol=s')" onMouseUp="makeRequest('send.php?symbol=s&cls=1')">
    <img src="img/right.png" title="Вправо" value="RIGHT" onMouseDown="makeRequest('send.php?symbol=d')" onMouseUp="makeRequest('send.php?symbol=d&cls=1')"><br><br><br>

    <img src="img/action.png" title="Действие" value="ACTION" onMouseDown="makeRequest('send.php?symbol=z')" onMouseUp="makeRequest('send.php?symbol=z&cls=1')">
    </div>

    </body>
    <html>
    PHP-скрипт:
    Код (Text):
    <?php

    // Считываем полученный символ и преобразуем в ASCII-код
    $curr = ord($_GET['symbol']);

    // Если есть параметр cls=1, то отнимаем от ASCII-кода 32 (буква в большом регистре, сброс пина в LOW)
    $cls = $_GET['cls'];
    if ($cls == 1) {
        $curr = $curr-32;
    }

    // Открываем порт на запись
    $fp = fopen ("COM3", "w");
    if (!$fp) {
      echo "<span style='color:red;'>Not open COM3 port!</span><br>";
    } else {
        echo "<span style='color:green;'>COM3 port successfully opened!</span><br>";
       
        // Отправляем в COM-порт символ
        fwrite($fp, chr($curr));
    }

    // Закрываем COM-порт
    fclose($fp);

    ?>
    Скетч:
    Код (Text):
    // Управление Ардуино через браузер

    int blue = 8;
    int red = 9;
    int green = 6;
    int tblue = 7;
    char p;

    void setup() {
      Serial.begin(9600);
      pinMode(blue, OUTPUT);
      pinMode(red, OUTPUT);
      pinMode(green, OUTPUT);
      pinMode(tblue, OUTPUT);
    }

    void loop() {
      // Если есть данные в последовательном порту
      if (Serial.available()) {
        // Считываем данные в переменную
        p = Serial.read();
       
        // Если буква w
        if (p == 'w') {
          digitalWrite(blue, HIGH);
        }
        // Если буква W
        if (p == 'W') {
          digitalWrite(blue, LOW);
        }
       
        // Если буква s
        else if (p == 's'){
          digitalWrite(red, HIGH);
        }
        // Если буква S
        else if (p == 'S'){
          digitalWrite(red, LOW);
        }
       
        // Если буква a
        else if (p == 'a'){
          digitalWrite(green, HIGH);
        }
        // Если буква A
        else if (p == 'A'){
          digitalWrite(green, LOW);
        }
       
        // Если буква d
        else if (p == 'd'){
          digitalWrite(tblue, HIGH);
        }
        // Если буква D
        else if (p == 'D'){
          digitalWrite(tblue, LOW);
        }
       
        // Если буква z
        else if (p == 'z'){
          digitalWrite(blue, HIGH);
          digitalWrite(green, HIGH);
        }
        // Если буква Z
        else if (p == 'Z'){
          digitalWrite(blue, LOW);
          digitalWrite(green, LOW);
        }
     
      }
    }
     
  7. rav_75

    rav_75 Гик

    А как Вы собираетесь мышкой нажимать на 2 кнопки одновременно? Если речь идет о тачскрине, то, вероятно, там придется использовать jquery mobile либо jqtouch, впрочем я в этом не силен, никогда не писал под мобилы. Если речь об обычном компе, то я бы заморочился на события нажатий клавиш. Что конкретно посылать ардуино (w-W или 11-10 или w1-w0) - не важно, главное чтоб обработчик понимал, чего Вы от него хотите.
    По поводу контроля связи. Мне представляется такая штука: комп раз в, скажем, 5 сек отсылает ардуино контрольный байт, а ардуино ждет его 5+n сек (пусть 7 или 10 всего). Нет контрольного байта - все пины low, есть - сбрасываем таймер, ждем дальше. Может есть другие варианты, кто знает, подскажите.
     
    Kuiper нравится это.
  8. Kuiper

    Kuiper Нуб

    rav_75, хороший вопрос, спасибо!
    На компе можно делать так - нажимаем на кнопку и не отпуская клавишу, отводим курсор с кнопки, тогда событие onMouseUp не происходит и Ардуино продолжает выполнять действие кнопки.
    Аналогично и другие кнопки можно нажать, а для "отжима" нужно как обычно кликнуть, чтобы событие onMouseUp выполнилось.

    По контрольному байту, интересный вариант, спасибо!
    Думал над таким вариантом - сделать как писал выше, т.е.
    Код (Text):
    if (p == "w") {
    digitalWrite(relay1, HIGH);
    delay(60000); // работает 1 минуту
    digitalWrite(relay1, LOW);
    }
    , но delay() делать, например, 10 минут или больше обычного времени нажатия. Если кнопка будет "отжата" до 10 минут, то работать будет как обычно, а если сигнал потеряется, то через 10 минут минус уже отработанное время робот прекратит выполнение команды.
     
  9. rav_75

    rav_75 Гик

    Эко, батенька, Вас занесло... Попробуйте загрузить этот код, Вы неожиданно удивитесь результату. Особенно если delay() делать 10 минут или больше. Посмотрите код, почитайте про delay(), узнаете в чем фишка.
    Предлагаю сделать так...
    Код (Text):
    unsigned long lastTime=0;    //контрольная точка
    unsigned long timeOut=10*60*1000;      //время между сеансами связи - 10 мин
    ..............
    void loop(){
    ..............                //получаем контрольный символ с компа, пусть это будет "t"
    else if(p=='t'){
      lastTime=millis();
    };                              //завершаем проверку полученного
    if(millis()-lastTime>timeOut){      // если время истекло
    .........................        //здесь все пины в low
    }
    Как-то так, мне кажется. Ни разу не сложно и, вроде, должно работать. Но, ИМХО, 10 минут - очень много. Ну и заставить jquery раз в какое-то время пихать пхп, чтоб отсылать символ t на ардуино - не проблема
    Да, и попробуйте вместо if - else if использовать switch case. Поинтересней будет смотреться :)
     
    Последнее редактирование: 14 янв 2014
    Kuiper нравится это.
  10. Kuiper

    Kuiper Нуб

    rav_75, большое спасибо!
     
  11. rav_75

    rav_75 Гик

    Не за что. Только сделайте, чтоб комп отсылал контроль раньше, чем наступит таймаут. Т.е. на ардуино если ждем 10 мин, то комп отсылает каждые, к примеру, 9,5 минут. А то мало ли че ))
     
    Kuiper нравится это.
  12. slavalex

    slavalex Нуб

    Использую нижеприведенный код:
    Скетч:
    Код (Text):
    int blue = 13;

    char p;

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

    void loop() {
      if (Serial.available()) {
        p = Serial.read();

        if (p == 'Q') {
          digitalWrite(blue, LOW);
        }

        if (p == 'w') {
          digitalWrite(blue, HIGH);
        }
      }
    }
    php-скрипт:
    PHP:
    <?php
    $fp =fopen("COM4", "w+");
    if (!$fp) {
      echo "Not open COM4 port!";
    } else {
      echo "COM4 port successfully opened!";
      fwrite($fp, "w");
    }
    fclose($fp);
    ?>
    Возникает не понятная мне ситуация. Все работает как надо (т.е. загорается led на 13 пине при запуске php файла), только если я запускаю php файл сразу после того как залью скетч в МК .
    А если я вытащу ардуину из com порта и вставлю обратно, то при запуске php-скрипта led мигнет 1 раз и тухнет. Запускаю SerialMonitor, посылаю w и Q - все работает. Закрываем SerialMonitor, пробую запскать php-скрипт - не работает.

    Или если я залью скетч в ардуино, потом побалуюсь с SerialMonitor (посылаю w и Q), php-скрипт уже не работает.
    В чем проблема подскажите.
     
  13. slavalex

    slavalex Нуб

    Ни у кого никаких мыслей? Помогите, пожалуйста.