microAJAX для Ардуино
- Войдите на сайт для отправки комментариев
К написанию этого этюда меня подвиг очередной пост-вопрос о том, как организовать показ в браузере изменяющихся данных на странице, которая генерируется из Arduino.
Этот вопрос на форуме всплывает регулярно. Обычно, человеку нужно показать одно, два, ну пусть десяток значений, но он хочет, чтобы значения менялись сами, без перезагрузки страницы. В большинстве тем человеку немедленно советуют использовать ajax, что совершенно правильно – эта библиотека как раз для таких дел и предназначена. Беда лишь в том, что если нужно показать пару чисел типа температуру на кухне и влажность в ванной, то ajax – это такая пушка для этого воробья … К тому же эта пушка ещё и весит за 70 килобайт и ни в какую ардуину не полезет. В итоге, ради пары чисел на экране, человеку советуют ещё и прикрутить к ардуине SD карту. Оно стоит того? Я Вас умоляю!
Давайте разберёмся как это можно сделать по простому, без SD, без 70КБ – просто в несколько строк. Кстати, техника, которую мы будем использовать, используется и в ajax – ничего нового мы не изобретаем. Просто вместо пушки применим для воробья пневматическое ружьё – это более адекватно.
Немного теории. Как это всё работает
Допустим у Вас на сервере (например, на localhost) есть некий файл, a.htm. Тогда, загрузка этого файла в браузер будет выглядеть так: http://localhost/a.htm. Когда браузер загрузил файл (и показал его) больше на экране ничего не меняется. Всё статично.
Однако, в Вашем файле a.htm может присутствовать т.н. «активный сценарий» (скрипт) – это программа обычно (но не обязательно) на языке JavaScript, которую Браузер может исполнять.
Так вот, если эта программа будет периодически (скажем, раз в секунду или чаще) запрашивать у сервера необходимые данные и аккуратно вставлять полученные с сервера значения в нужные места в документе – то мы и получим то, чего хотим – значения на экране браузера будут меняться сами – без перезагрузки страницы. Меняться он будут, конечно, по мере того, как они изменяются на сервере.
Т.е., здесь Вы должны чётко понимать: сервер должен уметь обрабатывать два вида запросов. Запрос на файл a.htm Вы посылаете руками, когда набираете его адрес в браузере. Этот запрос выполняется один раз. А запрос на данные выполняет скрипт, размещённый в файле a.htm. В ответ сервер возвращает нужные данные, скрипт размещает данные в документ и снова исполняет тот же запрос к серверу. Снова получает данные, размещает их в документе и так до бесконечности.
Скрипт microAJAX
Я написал для Вас компактный, но достаточно универсальный скрипт, который как раз этим и занимается – запрашивает с сервера данные и вставляет их в документ с нужной Вам периодичностью. Скрипт никак не привязан к конкретным данным и конкретной странице и может без изменений использоваться для показа самых разных данных.
К сожалению, в жертву компактности принесена обработка ошибок, что затрудняет отладку, но в конце этого материала я приведу «отладочную версию» скрипта – чуть менее компактную, но снабжённую механизмом контроля ошибок и выдачи сообщений.
Скрипт собственно вот он:
<script language="JavaScript"> var o=new XMLHttpRequest(); function dR() { o.open("GET","data.txt?r="+Math.random(),1); o.onload=function(){ var a=this.responseText.replace(/\s*[\r\n=]+\s*/g, "=").replace(/^\s+/,"").replace(/\s+$/,"").match( /([^=]+)/g); for (var i=0, len=a.length; i< len; i+=2) try { document.getElementById(a[i]).innerText=a[i+1]; } catch(e) {}; setTimeout("dR()", 10); } o.send(); } </script>
Для того чтобы вставить его в Вашу Web-страницу необходимо сделать две вещи:
1. вставить приведённые 12 строк внутрь тэга HEAD (т.е. между <HEAD> и </HEAD>);
2. В тэг BODY вставить обработчик события onload=”dR”. Тэг может выглядеть, например, так:
<body onload="dR()">
В строке 8 имеется константа 10 – это задержка в миллисекундах между вставкой полученных с сервера данных в документ и выполнением нового запроса к серверу. Можете поставить число побольше, чтобы не гонять дурной траффик. Чем меньше это число, тем быстрее будут меняться данные на странице при изменении данных на сервере. И наоборот, чем задержка больше, тем реже будут обновляться данные. Если, например, вместо 10 поставить 1000, данные будут обновляться только раз в секунду.
В строке 8 имеется константа 10 – это задержка в миллисекундах между вставкой полученных с сервера данных в документ и выполнением нового запроса к серверу. Можете поставить число побольше, чтобы не гонять дурной траффик. Чем меньше это число, тем быстрее будут меняться данные на странице при изменении данных на сервере. И наоборот, чем задержка больше, тем реже будут обновляться данные. Если, например, вместо 10 поставить 1000, данные будут обновляться только раз в секунду.
Ну, работает это всё достаточно просто.
- cразу же по загрузке страницы, вызывается функция dR() (для этого мы писали обработчик события в тэге BODY);
- функция dR в строке 4 формирует запрос к серверу на файл data.txt. параметр запроса (случайное число) сам по себе не нужен, но служит для того, чтобы браузер реально запросил data.txt у сервера, а не выдал нам старую, кешированную версию.
- в строка 5-9 браузеру говорится, что когда данные с сервера будут получены, следует вызвать для их обработки безымянную функцию (строки 6-8), мы к ней ещё вернёмся.
- наконец, в строке 10 сформированный запрос отправляется на сервер.
Как только данные с сервера получены, вызывается безымянная функция, заданная в строка 6-8. Она делает следующее
- разбирает полученные данные (строка 6). Это делается с помощью регулярных выражений. Объяснение их работы несколько выходит за рамки этой статьи. Просто скажу, что если данные сформированы правильно (см. формат данных ниже), то в результате получается массив а, который в чётных элементах содержит названия данных, а в нечётных – значения;
- строка 7. Раскладывает значения по элементам HTML документа, ID которых совпадают с названиями данных;
- строка 8. Указывает браузеры на необходимость вызвать функцию dR ещё раз через 10 миллисекунд (можете поменять 10 на что-либо другое).
Формат данных в файле data.txt
Данные в файле data.txt записываются построчно в формате:
<имя> = <значение>
при этом в начале, в конце, а также слева и справа от знака = допускается любое (включая и нулевое) количество пробелов и/или знаков табуляции. Если же пробелы встречаются внутри имени или данных, они остаются на месте и попадают в документ. Имя должно записываться по правилам записи ID в HTML (латинские буквы, цифры, пробелы допускаются)
Например, вполне правильный файл:
temperature kitchen = 25 temperature kidsroom = 27 temperature bathroom = 30
Связывание данных с элементами документа
Для того, чтобы скрипт знал куда вставить данные, в HTML документе должны быть элементы (SPAN, DIV, TD и т.п.) с ID точно (регистр букв, пробелы - точно!) совпадающими с именами данных.
Например, для файла данных, приведённого в предыдущем разделе, HTM файл может выглядеть так:
<html> <head> <title>Cool dynamic page!!!</title> <script language="JavaScript"> var o=new XMLHttpRequest(); function dR() { o.open("GET","data.txt?r="+Math.random(),1); o.onload=function(){ var a=this.responseText.replace(/\s*[\r\n=]+\s*/g, "=").replace(/^\s+/,"").replace(/\s+$/,"").match( /([^=]+)/g); for (var i=0, len=a.length; i< len; i+=2) try { document.getElementById(a[i]).innerText=a[i+1]; } catch(e) {}; setTimeout("dR()", 10); } o.send(); } </script> </head> <body onload="dR()"> <h1>Temperatures:</h1> <p> Kitchen: <span id="temperature kitchen"></span>. </p> <p> Kidsroom: <span id="temperature kidsroom"></span>. </p> <p> Bathroom: <span id="temperature bathroom"></span>. </p> </body> </html>
Как видите, ID элементов в строках 20б 23 и 26 точно совпадают с именами данных из приведённого в предыдущем разделе файла data.txt. И наш скрипт вставит значение 25 в элемент в строке 20, 27 – в элемент в строке 23 и 30 – в элемент в строке 26.
Работающий пример
Для того чтобы убедиться, что всё это работает, возьмите файл данных из пред-предыдущего размера и поместите его на сервер с именем data.txt. Также возьмите HTML файл из предыдущего раздела и поместите его рядышком (в одной директории с файлом данных) под именем test.htm.
Откройте браузер и загрузите http://localhost/test.htm (если у Вас не localhost – укажите правильный сервер).
Когда файл загрузится, данные должны отобразиться! Это должно выглядеть примерно так:
Temperatures:
Kitchen: 25.
Kidsroom: 27.
Bathroom: 30.
Теперь, откройте в текстовом редакторе файл data.txt и поменяйте какое-нибудь значение (только имена не обижайте!). Сохраните файл и как только Вы это сделаете, значение тут же поменяется и в браузере!
Если у Вас всё получилось так, как я описал, значит у Вас всё отлично работает, можете себя поздравить.
Попробуйте со своими файлами. Ещё раз – Вы можете сделать любой HTML файл и показывать в нём любые (и любое количество) изменяемых значений. Всё, что Вам нужно соблюсти:
1. вставить скрипт внутрь тэга HEAD (т.е. между <HEAD> и </HEAD>);
2. В тэг BODY вставить обработчик события onload=”dR”.
3. Подготовить файл с данными в котором имена данных точно совпадают с ID элементов html-файла
И всё у Вас получится. Попробуйте, пощупайте, освойтесь.
Как всё это использовать в Ардуино
Этот раздел предполагает, что Вы уже умеете принять на ардуино запрос к серверу и ответить на него (например, чтобы в браузере появилось слово «Hello!»). Если Вы этого не умеете, то смотрите примеры к используемой Вами библиотеке Internet модуля. Там есть простейшие серверы.
Для того, чтобы сделать Web-страницу с использованием microAJAX я Вам советую, сначала сделать её (страницу) и необходимый файл данных на сервере (как мы делали в предыдущем примере). Отладить её в браузере и только тогда, когда она Вас полностью устраивает, переходить к Ардуино.
Скетч для сервера Ардуино должен работать так:
1.
Если к нему пришёл запрос на файл «как он там у Вас называется.htm» - необходимо вертуть Ваш html файл как строку ничего в нём не меняя.
2.
Если к серверу пришёл запрос на файл data.txt, надо вернуть Ваш файл данных, только вместо константных значений скетч должен подставить реальные (измеренные или рассчитанные) значения. При это Context-type следует указать text/plain.
Вот собственно и всё. Если Вы это сделаете, то у Вас получится самообновляемая из Ардуино странница.
Сложного ничего нет. Если Вы умеете выдать хоть что-нибудь (как я писал в начале этого раздела), то Вы сумеете выдать то, что нужно. Успехов!
К огромному сожалению у меня сейчас нет под рукой никакого интернетного модуля и я не могу дать Вам пример готового скетча (не выкладывать же непроверенный код!) Большая просьба к тем, у кого есть интернетный модуль и кто будет пробовать мой скрипт с ним: пожалуйста, как отладитесь, выложите здесь скетч – это многим поможет. Мог бы, я бы сам сделал.
Приложение: Обещанная отладочная версия
Ошибки в моём microAJAX не обрабатываются. Ну, ошибка - ошибке рознь, например потерю связи с сервером мы обрабатывать не будем - она и так очевидна.
Но есть одна ошибка, которая может попортить кровь разработчику - это неправильное написание имени. Когда в данных имя есть, а в html элемента с таким ID нет. Эту ошибку мы обработаем.
Приведённый ниже скрипт такой же, как и microAJAX, но с двумя отличиями:
1) он не обновляет дааных. Вернее, обновляет их один раз при загрузке страницы
2) он протоколирует всю работу посредством сообщений браузера.
Вот текст отладочного скрипта:
<script language="JavaScript"> var o=new XMLHttpRequest(); function dR() { o.open("GET","data.txt?r="+Math.random(),1); o.onload=function(){ alert("RECEIVED:\n" + this.responseText); var a=this.responseText.replace(/\s*[\r\n=]+\s*/g, "=").replace(/^\s+/,"").replace(/\s+$/,"").match( /([^=]+)/g); for (var i=0, len=a.length; i< len; i+=2) { try { document.getElementById(a[i]).innerText=a[i+1]; } catch(e) { alert("ERROR: ID '" + a[i] + "' not found in the document"); } } } o.send(); } </script>
при загрузке данных он покажет сообщение со словом "RECEIVED" и после него будет идти файл данных КАК СКРИПТ ЕГО ПОЛУЧИЛ! Вы сможете визуально проконтролировать всё ли нормально передаётся и, если что, поправить свой скетч.
В случае, если в данных обнаружится имя не имеющее соответствующего ID в html файле, будет выдано сообщение с этим именем и Вы легко сможете поправить ошибку.
ajax это хорошо ), но меня интересует вот что, я с ардуино посылаю ГЕТ запрос на сервер, ну там тип температуру, а сервер выдает в ответ инфо, тип имя=значение, вот тип так, нашел на странице о есп,я похожее делать буду:
Посоветуйте как принимать данные от сервера, что-бы управлять ардуино? Я думаю делать свой парсер, есть что-то в ардуино или все таки делать свой велосипед, мне можно как то полученные данные от сервера передавать в ардуино и в ардуино ими пользоватся тип как имя_переменной=значение ?
Вообще этот топик про случай, когда Ардуино является сервером, а не когда она запрашивает у сервера что-то.
Парсить на ардуино ... я бы парсил вручную. Где-то была библиотека регулярных выражений, но тоже ведь пушка по воробью. Проще распарсить руками, тем более формат-то тривиальный.
думаю без регулярок обойтись, у меня уно, он и так слабенький.
ну а после этого я возьмусь за АЯКС, так как буду выводить инфо на страницу, ну распарсить это еще ладно, а как мне сделать так -> имя=значение, в массив загнать ? в ардуино вроде нельзя такой массив создавать "ассоциативный" arr['name'] = value; ?
думаю без регулярок обойтись, у меня уно, он и так слабенький.
Вот и я ж про то же.
а как мне сделать так -> имя=значение, в массив загнать ? в ардуино вроде нельзя такой массив создавать "ассоциативный" arr['name'] = value; ?
Ну, стопиццот способов есть. Сделайте структуру с двумя полями: имя и значение, или два массива - в одном миена, а в другом значения. А, кстати, можно и в лоб - сделать класс с хитро определённой операцией взятия индекса, тогда вообще всё красиво будет.
я думал двумерный массив сделать тип так, char arr[3][10]; , в первом - это номер переменной, а во вотром значение, тип так "0""value", но вот насчет struct - это вариант. )
хотя )) это можно и без структуры делать, просто записывать значения в переменные тип так char* tmp = arr[0] и т.д.
Вобще подход хороший.
Имена переменным ниразу не надо. Именами будут ID жестко прописанніе в ардуине, в форме намболее простой и удобной для ардуин. А в скриптах к ним приспособится не сложно.
Я давно о таком подходе думал, и останавливает только вот что. Внешний облик страницы, тексты и сам скрипт у нас лежит в a.htm. А сам a.htm в ардуино, где нет места. Любая правка a.htm это перезаливка ардуины. Плохо.
В идеале в ардуино надо держать только некую ссылку на глубины инета, где выложен a.htm. А броузер пойдет по этой ссылке, загрузит и выполнив скрипт из a.htm поймет что за data.txt надо пойти по указаному адресу, вероятно статическому IP используя собственно предложенный механизм.
А возможно и ссылку в ардуино на a.htm не обязательно, просто разнести a.htm где-то на серваке, чтоб и этим ардуину не беспокоить, а за data.txt уже на ардуину.
я так и делаю, ардуино посылает гет или пост запрос на сторонний сервер, настройки и всякие параметры для обработки ардуино сервер отсылает, вот и все, а всю красоту АЯКСом выводим, застопорился только как ответ принимать, но походу ВЕЛОСИПЕДОМ ))
Всех приветствую.
Евгений. Благодарю, всё грамотно, ясно и доходчиво. Статья хорошая.
Значит захожу по адресу http://192.168.1.100/index.htm, получаю:
Знчит захожу по адресу http://192.168.1.100/data.txt, получаю:
Здесь вышеизложенный скрипт, т.к. ардуинка ругаеться на строчку
var a=this.responseText.replace(/\s*[\r\n=]+\s*/g, "=").replace(/^\s+/,"").replace(/\s+$/,"").match( /([^=]+)/g);
32 строка, это сайт изменил
Logic, ну, конечно, так лучше, кто бы спорил. Я просто думаю, что сервера на радуино делают тогда, когда нет возможности держать хозяйство на внешнем сервере. А если возможность есть есть, так понятно, что лучше там.
32 строка, это сайт изменил
В 32 строке Вы не все кавычки предварили \. Посмотрите внимательно.
Заработало )))
Вместо
if
(strstr(data,
"index.htm"
)){
if
(strstr(data,
"data.txt"
)){
сделал
if (strstr(data, "GET /index.htm")){
if (strstr(data, "GET /data.txt")){
Попробую разобраться со своим скриптом графического термометра
Ну, заработало и слава Богу. Только, думается вы что-то ещё сделали. Экспериментируйте дальше. Привыкните и поймёте, что там нет ничего сложного.
В коде адрес до скрипта не указал полностью, редактор на сайте ругается и кракозябы выдаёт.
Возникла пара вопросов:
Это я указал вручную, а как узнать сколько нужно? Или так оставить, прибавляя по необходимости?
44 строка
"gauge.setValue(<span id=\"komnata1\"></span>);"
не работает
gauge.setValue(значение);
По-моему, все браузеры будут активно препятствовать работе такого сценария. Ради безопасности запрещают странице полученной с сайта 1 общаться с сайтом 2.
Будут, но в разумных пределах это обходится.
"gauge.setValue(<span id=\"komnata1\"></span>);"
не работает
gauge.setValue(значение);
Сергей, вот честное слово, я не знаю что такое gauge. Вы чем-то пользуетесь, но я не знаю чем.
"gauge.setValue(<span id=\"komnata1\"></span>);"
не работает
gauge.setValue(значение);
Сергей, вот честное слово, я не знаю что такое gauge. Вы чем-то пользуетесь, но я не знаю чем.
Сдается мне что это аналоговый показометр, напрмер как здесь http://startingelectronics.org/projects/arduino-projects/web-server-two-temperature-gauges/
Сдается мне что это аналоговый показометр, напрмер как здесь
Да, именно
При запросе 192.168.1.100/index.htm в порт начинает посимвольно выводиться текст из index.htm очень медленноооо
А как мне этот файл поставить в ответ 192.168.1.100/index.htm
Или по другому, чтоб понять
Если <span id=\"komnata1\"></span> = 100
то
Как организовать это условие?
Никак. Если Вы делаете с моим скриптом, то в html файле у Вас должно быть
<span id=\"komnata1\"></span>
и всё!
А в файле данных должно быть
komnata1 = 100
Тогда всё само подставится. Скрипт загрузит данные, вытащит эту 100 и вставит её в нужный span.
А если при очередном обращении к серверу за данными тот вернёт
komnata1 = 101
то это снова подставится и, таким образом, само поменяется в браузере.
Это я понимаю.
"gauge.setValue(<span id=\"komnata1\"></span>);"
не воспнинимает значение komnata1
"gauge.setValue(130);"
"Комната 1:<span id=\"komnata1\"></span>градусов<br>"
Закомментировал скрипт
Закомментировал первую строку и скрипт (нельзя использовать два раза одно и тоже <span id=\"komnata1\"></span>)
Работает. Но решил попробовать сделать три термометра.
1-2-3 термометр работает
p><b>Температура в зале = <span id=
"zal"
></span> &#
176
;C</b></p>
не отображает температуру в зале
Это я понимаю.
"gauge.setValue(<span id=\"komnata1\"></span>);"
не воспнинимает значение komnata1
Стоп.
gauge исполняется на клиентской стороне.
Ну, тогда так и пишите
gauge.setValue(document.komnata1.innerText);
По идее, должно сработать. Если нет, попробуйте
gauge.setValue(document.komnata1.innerText);
gauge.setValue(parseInt(document.getElementById("komnata1").innerText));
Евгений, не работает ни один из вариантов
Так стоп, а как Вы его вызывать собираетесь? Строчка
gauge.setValue(document.komnata.innerText);
должна быть не сама по себе, она должна вызываться каждый раз после обновления span. А сейчас она вызывается в начале программы один раз. Вставьте её в мой скрипт после строки
setTimeout(
"dR()"
, 10);
Ага, Спасибо. Вот так работает.
Но
<span id="komnata"></span>
в тексте странички обязательна
На какой символ ругается ардуино? Добавляю в код такую строку, и сервер не отвечает.
Sergeyevd, а откуда Вы брали библиотеку gauge? Поделитесь ссылочкой ?
ЕвгенийП было бы здорово, если бы вдогоночку Вы рассказали про авторизацию для ардуино-сервера.
Моя проба пера на AJAX правда давняя, с год уже как. Страницу гружу с SD. Там же лог складывается.
http://startingelectronics.org/projects/arduino-projects/web-server-two-temperature-gauges/
Веб - термометр
https://github.com/Mikhus/canv-gauge
Библиотека с примерами
На какой символ ругается ардуино? Добавляю в код такую строку, и сервер не отвечает.
Не в ардуинке работает прекрасно
Но
<span id="komnata"></span>
в тексте странички обязательна
Совсем от него избавиться, это надо мой скрипт переписывать и он перестанет быть универсальны. Если хотите, чтоы его не было видно, добавьте в него style="display:none"
На какой символ ругается ардуино? Добавляю в код такую строку, и сервер не отвечает.
Не в ардуинке работает прекрасно
Я не понимаю какой там баланс кавычек. Вы его куда и как добавляете? Кусок скетча покажите.
Такое ощущение, что у Вас начинаются проблемы с памятью. Строка очень большая.
С памятью тоно всё в порядке?
Бросил это дело. Всё сохранил в скрипте на localhost, а то памяти в ардуино много ест.
Есть ли смысл запускать мою htm и скрипты с флешки? Скорость будет маленькой?
Пока не попробуешь не поймёшь.
А с паиятью я бы на Вашем месте поборолся. там у Вас много расзвесистых конструкций - можно прилично сэкономить
Пробую, но не выходит.
Вместо того,чтобы показать мне текст index.htm показывает в браузере "62"
Появилась ещё одна задача.
Теперь мне необходимо передать данные с html в ардуинку
К примеру
http://stanciya-skorpom.ru/reguliator/index.htm
Имею вот такой регулятор. Передвигая стрелку мышкой хочу изменять яркость светодиода.
Как организовать такую передачу?
а это уже надо парсер делать ))
Как организовать такую передачу?
Как два пальца.
Вы видели как я делаю запрос на файл data.txt?
Делаете ТОЧНО также только имя файла другое, например, kaka.htm и после имени файла ставьте знак вопроса и пишите имя параметра и значение. например:
var yarkost = 321;
var fileName = "kaka.htm?y="+yarkost;
В результате в Вашу ардуины свалится запрос такого вида: GET kaka.htm?y=321
И делайте на ардуине с ним всё, что захотите.
Ооо, сейчас попробую.
Евгений, вот что получается.
Умею делать любую яркость, но только зная конкретный гет запрос, т.е. условие если придёт /kaka.htm?y=10 HTTP/1.1 то ярклсть установлю 10, 20 - 20 и т.д....
Допустим сделал random(255) и гет запрос будет "xhr.open('GET', 'kaka.htm?y=случайное число', 1);"
Как мне при запросе kaka.htm получить только это самое случайное число, разобрать строку