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

Kuiper
Kuiper аватар
Offline
Зарегистрирован: 11.01.2014

Всем привет!

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

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

<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-скрипт, можно как-то это сделать?

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

com
Offline
Зарегистрирован: 06.09.2013

навскидку такая идея:

непосредственно в функции makeRequest вызов php-скрипта поместить в бесконечный цикл, который выполнять в зависимости от флага, а флаг устанавливать в true по событию onMouseDown и в false по onMouseUp

 

m0rjjj
m0rjjj аватар
Offline
Зарегистрирован: 02.09.2013

Я не могу понять: когда вы посылаете w контроллеру, что происходит? Пойдет питание контроллеру? На сколько оно подастся? Установится ли флаг или что? Просто даже если у вас нету проблем и браузер посылает состояние нажатой кнопки, посмотрите как будет работать ваше "устройство" если просто запустить цикл на php например

for ($i=0;$i<100;$i++){
    //тут ваша функция посылающая "w" контроллеру

    //потом задержка, так как иначе весь цикл пролетит за такт. 
    usleep(200000);// 200 000 микросекунд = 200 милисекунд.
}

Я почти уверен, что ваше устройство будет дергаться.

Попробуйте сделать так:

Посылайте в МК вашу заветную "w" скриптом php выше, а в нем уже отлавливая w начинайте движение.

В МК нужно проверять если в течении 201 милисекунды не пришло команд, то останавливаемся.

201 утрированно, я бы скорей взял 300. Вобщем поэкспериментируйте с цифрами, добиваясь плавного движения.

Насчет дальнейшей реализации, я бы взглянул на WEB Sockets.

 

 

m0rjjj
m0rjjj аватар
Offline
Зарегистрирован: 02.09.2013

com пишет:

навскидку такая идея:

непосредственно в функции makeRequest вызов php-скрипта поместить в бесконечный цикл, который выполнять в зависимости от флага, а флаг устанавливать в true по событию onMouseDown и в false по onMouseUp

Что произойдет, если флаг остановиться не придет?

com
Offline
Зарегистрирован: 06.09.2013

так это на совести ТС, что у него там происходит.

в техзадании просто сказано, что обязательно непрерывное действие, а уж он роботом по кухне ездит или у него реально экскаватор траншеи копает - хз. сие мне неведомо и последствия выходят за рамки вопроса

может у него наоборот, если все остановится - рванет чего-нибудь? :)

Kuiper
Kuiper аватар
Offline
Зарегистрирован: 11.01.2014

com, m0rjjj, большое спасибо за ответы!

C циклом в makeRequest попробую, интересная идея, спасибо!

morjjj, php-скрипт просто пишет в порт chr() символа, fwrite($fp, chr($symbol)). Ардуино в loop'е читает порт и если есть символ, то включает заданный пин, у меня самый простой вариант.
А js на HTML-странице интерфейса вызывает php-скрипт для управления через AJAX.

Но, как я уже писал в первом сообщении, у меня не получается делать "непрерывное нажатие", т.е. непрерывно вызывать php-скрипт через события JavaScript, пока кнопка нажата.
Возможно, есть какие-то типовые, применяемые ардуинщиками решения для управления через браузер, а я тут Америку пытаюсь открыть.

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

Код HTML-страницы:

<!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" onMouseOver="makeRequest('send.php?symbol=VPERED')"><br>
<img src="img/left.png" title="Влево" value="LEFT" onMouseOver="makeRequest('send.php?symbol=LEFT')">
<img src="img/back.png" title="Назад" value="NAZAD" onMouseDown="makeRequest('send.php?symbol=NAZAD')">
<img src="img/right.png" title="Вправо" value="RIGHT" onMouseDown="makeRequest('send.php?symbol=RIGHT')"><br><br><br>

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

</body>
<html>

Код php-скрипта:


<?php

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

// Преобразовываем в ASCII-код
switch ($curr) {
	// Буква 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);

?>

Код скетча Ардуино:

// Управление светодиодиодами компьютера

int relay1 = 8;
int relay2 = 9;
int relay3 = 6;
int relay4 = 7;
int relay5 = 10;
char p;

void setup() {
  Serial.begin(9600);
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode(relay5, OUTPUT);
}

void loop() {
  // Если есть данные в последовательном порту
  if (Serial.available()) {
    // Считываем данные в переменную
    p = Serial.read();
    
    // Если буква w
    if (p == 'w') {
      digitalWrite(relay1, HIGH);
      delay(500);
      digitalWrite(relay1, LOW);
    }
    
    // Если буква s
    else if (p == 's'){
      digitalWrite(relay2, HIGH);
      delay(500);
      digitalWrite(relay2, LOW);
    }
    
    // Если буква a
    else if (p == 'a'){
      digitalWrite(relay3, HIGH);
      delay(500);
      digitalWrite(relay3, LOW);
    }
    
    // Если буква d
    else if (p == 'd'){
      digitalWrite(relay4, HIGH);
      delay(500);
      digitalWrite(relay4, LOW);
    }
    
    // Если буква z
    else if (p == 'z'){
      digitalWrite(relay5, HIGH);
      delay(500);
      digitalWrite(relay5, LOW);
    }
  }
}
com
Offline
Зарегистрирован: 06.09.2013

отпишитесь потом, получилось или нет, мне самому интересно стало

m0rjjj
m0rjjj аватар
Offline
Зарегистрирован: 02.09.2013

У вас в скетче delay, а это паразит. Объясню почему в вашем случае. Допустим вы зажали кнопку в браузере, и с интервалом в 500мс аяксом будет вызываться php скрипт, который пошлет в ком МК какой-то символ, на первый раз все сработает, а вот со вторым символом если он придет раньше чем ~500 мс в МК то эти 500мс МК будет простаивать, а если позже, то возможно реле дернется на 1~5мс.

В вашем случае я порекомендую попробывать такой алгорит. Если вы уже знакомы, как поменять deleay на millis то проблем у вас не возникнет. Делаем так:
1. заводим отдельный счетчик для каждой команды: например millisFront, millisBack, millisLeft и т.д.
2. Когда приходит символ с ком порта то вы как бы инкрементируете millisFront на 500 мс, а уже ниже проверяете если millisFront больше текущих millis то включаем реле, тем самым зная, что ему еще работать те самые 500мс, иначе - отключаем, т.к. он свое время просрочил. 

Плюс возможно в том, что можно слать с браузера сразу несколько зажатых кнопок, и тем самым сразу ехать прямо и в лево. Но думаю это проблематично, лучше не стоит.

Kuiper
Kuiper аватар
Offline
Зарегистрирован: 11.01.2014

com, на Амперке посоветовали делать не непрерывно, а в два запроса - первый, на событие onMouseDown, непрерывная работа (переводим пин в HIGH), второй, на событие onMouseUp, сброс пина в LOW.

Получился такой рабочий вариант:

Интерфейс:

<!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-скрипт:

<?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);
?>

Скетч:

// Управление Ардуино через браузер
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);
    }
  }
}
Kuiper
Kuiper аватар
Offline
Зарегистрирован: 11.01.2014

m0rjjj, Вы правы про delay().
В варианте с непрерывной работой по onMouseDown (мое сообщение выше) это решается и одновременно могут работать несколько кнопок (хоть все).

Теперь осталась только такая проблема - если вдруг потеряется связь, а сигнала на LOW не поступит, то робот будет выполнять действие, пока не потратит всю батарейку.
Можно сделать через:

if (p == "w") {
digitalWrite(relay1, HIGH);
delay(60000); // работает 1 минуту
digitalWrite(relay1, LOW);
}

, но если кнопка будет нажата более, чем минуту, то Ардуино переведет пин в LOW и нужно будет вновь нажимать кнопку.

m0rjjj
m0rjjj аватар
Offline
Зарегистрирован: 02.09.2013

Kuiper пишет:

com, на Амперке посоветовали делать не непрерывно, а в два запроса - первый, на событие onMouseDown, непрерывная работа (переводим пин в HIGH), второй, на событие onMouseUp, сброс пина в LOW.

Я вам указал про возможную проблему, в отсутствии сигнала от mouseUp. Решать все-равно вам, оба варианта рабочие, отпишитесь потом что получилось.

Kuiper
Kuiper аватар
Offline
Зарегистрирован: 11.01.2014

m0rjjj, с двумя запросами на onMouseDown, onMouseUp работает, его взял для робота :)

Теперь с блютусом буду пробовать беспроводное управление.