Всем привет! Пожалуйста подскажите, каким образом можно управлять Ардуиной через браузер, с учетом непрерывного действия? То есть, чтобы при нажатой кнопке "Вперед" непрерывно шла отправка нужного символа на последовательный порт. Поясню, как я сейчас делаю: 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-скрипт, можно как-то это сделать? Заранее большое спасибо!
Используйте 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>
аналогично keydown() и keyup(), читаете код нажатой/отпущеной клавиши, выполняете соответствующее действие
Recoshet, rav_75, большое спасибо! Очень интересное решение с началом-остановкой. Действительно, это лучше, чем непрерывные запросы.
Сделал вариант с двумя запросами (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 и переименовать): Код 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 и нужно будет нажимать кнопку вновь. Можно ли каким-то другим способом сделать защиту по времени на такой случай? Большое спасибо!
Вариант с отдельными отключающими командами (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); } } }
А как Вы собираетесь мышкой нажимать на 2 кнопки одновременно? Если речь идет о тачскрине, то, вероятно, там придется использовать jquery mobile либо jqtouch, впрочем я в этом не силен, никогда не писал под мобилы. Если речь об обычном компе, то я бы заморочился на события нажатий клавиш. Что конкретно посылать ардуино (w-W или 11-10 или w1-w0) - не важно, главное чтоб обработчик понимал, чего Вы от него хотите. По поводу контроля связи. Мне представляется такая штука: комп раз в, скажем, 5 сек отсылает ардуино контрольный байт, а ардуино ждет его 5+n сек (пусть 7 или 10 всего). Нет контрольного байта - все пины low, есть - сбрасываем таймер, ждем дальше. Может есть другие варианты, кто знает, подскажите.
rav_75, хороший вопрос, спасибо! На компе можно делать так - нажимаем на кнопку и не отпуская клавишу, отводим курсор с кнопки, тогда событие onMouseUp не происходит и Ардуино продолжает выполнять действие кнопки. Аналогично и другие кнопки можно нажать, а для "отжима" нужно как обычно кликнуть, чтобы событие onMouseUp выполнилось. По контрольному байту, интересный вариант, спасибо! Думал над таким вариантом - сделать как писал выше, т.е. Код (Text): if (p == "w") { digitalWrite(relay1, HIGH); delay(60000); // работает 1 минуту digitalWrite(relay1, LOW); } , но delay() делать, например, 10 минут или больше обычного времени нажатия. Если кнопка будет "отжата" до 10 минут, то работать будет как обычно, а если сигнал потеряется, то через 10 минут минус уже отработанное время робот прекратит выполнение команды.
Эко, батенька, Вас занесло... Попробуйте загрузить этот код, Вы неожиданно удивитесь результату. Особенно если 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. Поинтересней будет смотреться
Не за что. Только сделайте, чтоб комп отсылал контроль раньше, чем наступит таймаут. Т.е. на ардуино если ждем 10 мин, то комп отсылает каждые, к примеру, 9,5 минут. А то мало ли че ))
Использую нижеприведенный код: Скетч: Код (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-скрипт уже не работает. В чем проблема подскажите.