WebServer на ардуино 2560 и ethernet контроллере W5100
- Войдите на сайт для отправки комментариев
Здравствуйте, помогите пожалуйста разобраться. Задача сделать удаленное управление периферией, через web интерфейс (эта часть с небольшим трудом сделана). Но управление должно происходить в автоматическом или ручном режиме, в зависимости от первоначального выбора. Т.е. если я выбираю автоматический режим, то периферия управляется по определенному алгоритму. Если выбрал ручной режим - то управляется каждый элемент периферии отдельно.
За основу взял http://zelectro.cc/Ethernet_shield_W5100.
Убрал кнопку отправить, сделал отправление команды после установки галочки в checkbox.
Для красоты запихнул все в табличку и выровнял по левому краю.
Добавил checkbox для выбора автоматического режима. И столкнулся с проблемой того, что состояние выходов парсится и укладывается в массив, из которого потом считывается. Пробовал добавлять такое условие
char automan = client.read(); if (automan == 'm') pinState[0] = 1
но результата не дало.
#include <SPI.h> #include <Ethernet.h> byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // Мак адрес byte ip[] = { 192, 168, 1, 198 }; // IP адрес (В броузере вводим 192.168.0.2) EthernetServer server(80); int numPins = 4; int pins[] = { 3, 5, 7, 10 }; // Пины для реле int pinState[] = {0, 0, 0, 0}; // Состояние пинов bool mode= 0; void setup() { // Изначально выключаем все реле for (int i = 0; i < numPins; i++) { pinMode(pins[i], OUTPUT); digitalWrite(pins[i], 1); } Serial.begin(9600); Ethernet.begin(mac, ip); server.begin(); } void loop() { EthernetClient client = server.available(); if (client) { // Проверяем подключен ли клиент к серверу while (client.connected()) { // Проверяем идет ли запрос к серверу int dataCount = client.available(); if (dataCount > 0) { // Считываем данные передаваемые серверу с клиента (браузера) for (int i = 0; i < dataCount; i++) { char ch = client.read(); // Если данные передаются, то они будут переданы POST запросом, который начинается с символа 'P' if (i == 0 && ch != 'P') break; if (ch == '\n' && i < dataCount - 1) { // Находим строку, в которой содержатся передаваемые данные char chNext = client.read(); // Формат строки r2=on&r3=on&r4=on (Пример если нужно включены 2,3,4 реле) if (chNext == 'r') { // Выключаем все реле pinState[0] = 0; pinState[1] = 0; pinState[2] = 0; pinState[3] = 0; // Считываем первый номер реле, который нужно включить char relayNum = client.read(); pinState[relayNum-'0'] = 1; Serial.write(relayNum); // Считываем вспомогательную информацию (=on&) relayNum = client.read(); relayNum = client.read(); relayNum = client.read(); relayNum = client.read(); // Пока есть данные об остальных реле, считываем и заносим в массив pinState while (relayNum != -1) { relayNum = client.read(); relayNum = client.read(); pinState[relayNum-'0'] = 1; Serial.write(relayNum); relayNum = client.read(); relayNum = client.read(); relayNum = client.read(); relayNum = client.read(); } } else { // Если не было передано данных, то выключаем все реле pinState[0] = 0; pinState[1] = 0; pinState[2] = 0; pinState[3] = 0; } } } } // В соответствие с переданными данными включаем реле for (int i = 0; i < 4; i++) { digitalWrite(pins[i], pinState[i]); } // Выводим HTML страницу, на которой пользователь может включить или выключить нужные ему реле client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); client.println("<html>"); client.println("<head>"); //client.println("<meta http-equiv= Refresh content=15; >"); client.println("<title>Управление аквариумом</title>"); client.println("<meta http-equiv='Content-Type' content='text/html; charset=utf-8' /> "); client.println("</head>"); client.println("<body>"); //client.println("<h3>Zelectro. Relay + Ethernet shield.</h3>"); client.println("<table align='left' border=0 cellpadding=1 cellspacing=1 style='width:190px'>"); client.println("<tbody>"); client.println("<tr>"); client.println("<td>"); client.println("<form method='post'>"); client.print("<div> Авто/Ручной режим"); client.println("</td>"); client.println("<td>"); client.println("<input type='checkbox' onClick='this.form.submit()' "); //<input type="checkbox" onClick="this.form.submit()" name="test" value="test" /> if (mode == true) client.print("checked"); //client.print("onClick='this.form.submit()'"); client.println(" name='m0'></div>"); client.println("</td>"); client.println("</tr>"); client.println("<tr>"); client.println("<td>"); client.println("<form method='post'>"); client.print("<div> Свет"); client.println("</td>"); client.println("<td>"); client.println("<input type='checkbox' onClick='this.form.submit()' "); //<input type="checkbox" onClick="this.form.submit()" name="test" value="test" /> if (pinState[0] == 1) client.print("checked"); //client.print("onClick='this.form.submit()'"); client.println(" name='r0'></div>"); client.println("</td>"); client.println("</tr>"); client.println("<tr>"); client.println("<td>"); client.print("<div>Фильтр"); client.println("</td>"); client.println("<td>"); client.println(" <input type='checkbox' onClick='this.form.submit()' "); if (pinState[1] == 1) // client.print("onClick='this.form.submit()'"); client.print("checked"); client.println(" name='r1'></div>"); client.println("</td>"); client.println("<tr>"); client.println("<td>"); client.print("<div>Компрессор"); client.println("</td>"); client.println("<td>"); client.println(" <input type='checkbox' onClick='this.form.submit()' "); if (pinState[2] == 1) // client.print("onClick='this.form.submit()'"); client.print("checked"); client.println(" name='r2'></div>"); client.println("</td>"); client.println("<tr>"); client.println("<td>"); client.print("<div>Обогрев"); client.println("</td>"); client.println("<td>"); client.println(" <input type='checkbox' onClick='this.form.submit()' "); if (pinState[3] == 1) // client.print("onClick='this.form.submit()'"); client.print("checked"); client.println(" name='r3'></div>"); client.println("</td>"); client.println("</tbody>"); client.println("</table>"); //client.println("<input type='submit' value='Refresh'>"); client.println("</form>"); client.println("</body>"); client.println("</html>"); client.stop(); } } }
Создайте еще одну переменную, которая будет переключать режимы и все... Вы должны понимать, что, к примеру, в строках 145, 157, 168, 179 содержатся имена отправляемых релюшек... Тобишь поставив галочку напротив всех реле получите строку вида r1=on&r2=on&r3=on&r4=on... Я сам начинал разбираться с этого примера и могу сказать, что посимвольный парсинг для новичков не самый простой вариант... Проще использовать String и парсить через indexOf(), хоть это и съедает побольше ресурсов..
Спасибо за ответ, все что вы написали я прекрасно понимаю, вы процитировали 51 строку. Если не сложно, ткните в инфу, где можно по подробнее почитать про post запрос и посимвольный парсинг именно в ардуино. Или если не сложно, объясните пожалуйста на пальцах.
Фух... ща попытаюсь...Вам нужно понять принцип работы всей этой штуковины... строка 047 подходит как раз к переданным командам POST-запроса...После нее можно создать строку типа String и зафигачить в нее все передаваемые данные... К примеру так в 049:
int index_r1=X.indexOf("r1=");
if (index_r1!=-1) { //если такая совокупность символов найдена в строке Х
//делаем любое нам нужное действие... Почитайте тут про indexOf();
}
Ужасная реализация генератора html кода. Я бы сделал так:
Спасибо за ответы, попробую переварить и покажу на ваш суд, что получилось.
Сделал как вы говорили, но проблема в том, что X - string, а chNext - char. И одно в пихнуть другое напрямую нельзя.
Нужен какой-то костыль.
Попробовал сделать так:
Та же проблема в string нельзя запихнуть int
Нужен какой-то костыль.
int однозначно нужно превращать в строку itoa, print и т.п. функции в помощь.
нашел вот такой вариант с переводом char в String, но все ровно считать не получилось ничего. Судя по монитору порта, я подозреваю, что вхожу в цикл while и из него уже не выхожу, т.к. после установки галочки, страница уходит в долгую перезагрузку и в ответ "m" не приходит.
Цикл while(chNext != -1); обречен быть бесконечным из-за ; после while. По логике вещей цикл должен выглядеть так:
Или я что-то не понимаю или одно из двух. Этот вариант то же не работает.
По поводу ; после цикла - мой промах. Но что-то не пойму. Сначала мы определили типы данных для indexmode и chNext, проверили что соединение есть и все в порядке, записали в chNext символ, проверили что строка не кончилась и переписали тип данных char в String?
Слушай, ты замутил цикл for от 0 до количества символов в буфере, а пытаешься прочитать больше чем есть.
Этот код приведет к тому, что в буфере не останется принятых символов и очередная функция client.read() будет ждать пока не поступит сообщение в приемник. Функцию cient.read() надо вызывать столько раз сколько там реально есть символов.
А вообще я тебе изначально предлагал уйти от этой пурги:
Но ты меня на разбор POST запросов подписал :)
POST запрос выглядит таким образом:
Размер заголовка может меняться, твоя задача убедиться что это POST запрос, найти в запросе пустую строку \r\n\r\n и после неё уже разбирать твой запрос. название_поля1=значение&название_поля2=значение&
Цикл может выглядеть так
Если вы мне поможете, то попробую все переделать:
Не вопрос, только давай так, идеи за мной, отладка за тобой. Насколько я понимаю за основу взят библиотечный пример WebServer. Теперь к нему надо прикрутить парсер запросов и нормальный генератор html кода. Моя идея хранить в буфере html страничку целиком, и выводить её целиком в цикле
, а не кусочками, как в примере при помощи тыщи команд
Но поскольку страничка должна меняться время от времени, я предлагаю в нужных местах поместить свои теги, а во время вывода странички эти теги подменять значениями переменных.
Есть одна засада, компиляторы не умеют инициализировать буфер из файла, поэтому предлагаю такой выход из ситуации: весь html код вынести в файл html.h, а в проект вставить при помощи директивы
В файле html.h мы просто объявим буфер и проинициализируем чистым html кодом твоей странички. Давай сначала эту фишку реализуем. С тебя нужен чистый html код твоей странички.
Или я что-то не понимаю или одно из двух. Этот вариант то же не работает.
Изучите этот материал http://startingelectronics.org/tutorials/arduino/ethernet-shield-web-server-tutorial/
Тут все подробно изложено, причем от простого к сложному.
Изучите этот материал http://startingelectronics.org/tutorials/arduino/ethernet-shield-web-server-tutorial/
Тут все подробно изложено, причем от простого к сложному.
Тогда, по вашему, проект известный как Arduino Mega Server http://majordomo.smartliving.ru/forum/viewtopic.php?f=4&t=2347&sid=941635f1d7a7edbd3938e3e51bab724e , за основу которого взят тот самый цикл статей - это говносервер? Тогда покажите миру что-то свое, гораздо более эффективное. Или расскажите, почему это говнопример?
alexvs, не указывай, что мне делать, и я не стану указывать, куда тебе идти.
Я не указываю, а прошу объяснить, почему цикл статей, на который я дал ссылку - это говнопример.
Этот генератор html кода хороший пример "как не надо делать".
А это не парсер запросов, а кусок овна
Взамен этого я и предлагаю написать нормальный генератор html кода (без SD карты) и по человечески разобрать пару запросов.
Про первое - согласен, не очень правильно, но на этапе изучения возможностей - вполне сойдет, только надо в каждой строке добавить F,
client.println(F(
"HTTP/1.1 200 OK")
);
, что бы зря не забивать оперативку. Далее, в статьях, предлагается исходный код HTML вообще хранить в файлах на SD Card и загружать странички прямо из файлов - что вообще самое правильное, на мой взгляд.Ну и если не трудно, можно пример адекватного парсера?
http://arduino.ru/forum/proekty/web-server-na-arduino-s-kontrollerom-temperatury
Посмотрите там мой 40 пост. Может это то что нужно?
Работает и в ручном режиме и в автоматическом.
Можно свои датчики подключить.
Вся web морда на sd карте
GraninDm спасибо за ссылку. Но хотелось бы поочередно разобарться как что формируется, особенно запросы и как это парсится.
Andy вот html странички, для начала. Приношу извинения за долгий ответ, но работа прежде всего.
Получилось вот так:
Это HTML.h
Это request.h
Давай отладим это. Модификацию html и разбор данных POST запроса добавим позже.
Ошибок нет, но страничка не грузится. "Во время загрузки страницы соединение с сервером было сброшено."
Нашел одну ошибку, в request.h между строками 30 и 31 нужно вставить return false;
исправил - ничего не изменилось
подключай Serial.print();
Для PROGMEM рекомендуют #include <avr/pgmspace.h>, попробуй добавить.
Попробуй вместо client.print(ok); client.print(html); вызвать функцию:
В общем надо отлаживать код. Если в коде что-то непонятно спрашивай.
client.print(html) - не выводит. Подозреваю, что нужно каким-то другим методом выводить содержимое html.
Судя по этой статье http://easyelectronics.ru/avr-uchebnyj-kurs-programmirovanie-na-si-rabota-s-pamyatyu-adresa-i-ukazateli.html мы не так выводим из памяти html. Наверное нужно что-то типа pgm_read_word, а потом уже запихивать это в printline.
Давай попробуем так. Добавь к массивам Ок и html символ \0 в самом конце.
В html:
Замени вызовы: client.print(Ok); на send(Ok); а clien.print(html); на send(html);
добавь функцию send
Эта функция нам потом все равно понадобится, при выводе htm кода будем налету подменять значения.
подключай Serial.print();
Для PROGMEM рекомендуют #include <avr/pgmspace.h>, попробуй добавить.
Попробуй вместо client.print(ok); client.print(html); вызвать функцию:
В общем надо отлаживать код. Если в коде что-то непонятно спрашивай.
А на несколько постов ранее......
Этот генератор html кода хороший пример "как не надо делать".
Для alexvs. Функция func() предлагалась мне временно, для вычисления места до которого у меня работает код. Это пока для меня единственный меттод для определения места возникновения проблемы. Правда сегодня пол ночи потратил, что бы нормально прикрутить к atmes studia 6.2 Visual Micro, но уйти от ошибки при компиляции об отсутствии cor.a так и не смог.
Давай попробуем так. Добавь к массивам Ок и html символ \0 в самом конце.
В html:
Ничего не изменилось.
Ругается на 32 строчку exit status 1'client' was not declared in this scope
Ставил void send(char *ptr) после void loop(), ругался на 47 строчку exit status 1'Ok' was not declared in this scope
Зачем ты полез в такие дебри если ты путаешься с областью видимости переменных?
Где у тебя объявлена переменная client? Правильно, в loop(), так с какого перепугу ты ее пытаешься заюзать в send()? Или объявляй ее как глобальную, или передавай в send().
Я тебе дал ссылку на прекрасный материал, котрый проведет тебя от простого к сложному в области создания веб-сервера на Ардуино, но ты упорно бьешься над своими ошибками, не понимя, что делаешь.
Вот так без ошибок
Все заработало!
Отлично! Теперь нам надо, что бы html страничка отображала значения переменных. Есть несколько вариантов:
1. Менять html код налету, во время вывода. В браузере требуется обновление всей странички, решается при помощи refresh. Можно обойтись без скриптов. В html коде размещаем ключевые символы, которые при выводе подменяем значениями переменных. С точки зрения повторения проекта другими - это не очень наглядный способ.
2. Использовать скрипты: <script type=" text/javascript" src="URL"></script> где URL будет указывать на наш скрипт с переменными. Скрипт достаточно простой, можно создавать во время вывода. В браузере требуется обновление всей странички, решается при помощи refresh или скриптов. В коде html размещаем идентификаторы id='sensor1'. В скрипте создаем запись типа document.getElementById('sensor1').value = 123; Потребуется разбор GET запроса. С точки зрения повторения проекта другими - это более наглядный способ.
3. Использовать iframe. В браузере обновлять можно только iframe при помощи скриптов. Потребуется разбор GET запросов. С точки зрения повторения проекта другими - достаточно наглядный способ.
Какой вариант тебе больше нравится?
2 вариант
Вот как это будет выглядеть
Это твой html с небольшими изменениями:
Это скрипт, который мы будем создавать во время вывода:
чего-то я ничего не понял.
1. Мы уже не используем PROGMEM html[]?
2. script.js - это function set()?
3. onload="setVars() - у нас вроде нигде не используется setVars?
1.Используем, это я выложил чистый код html, его можно в браузере открыть. Код для ардуины я выложу чуть позже.
2.Опечаточка произошла: script.js - это function setVars(). Она же вызывается по onload в body.
Вопрос такой: у тебя 5 переменных. В html коде нет ни одной формы. Будем делать 5 форм (по одной на каждую переменную) или одну форму на все?
Хмм.. А про Ajax почитать ?
http://www.ibm.com/developerworks/ru/views/xml/libraryview.jsp?search_by=Ajax+master
и куча ссылок по запросу "arduino ajax" с работающими примерами
Хмм.. А про Ajax почитать ?
http://www.ibm.com/developerworks/ru/views/xml/libraryview.jsp?search_by=Ajax+master
и куча ссылок по запросу "arduino ajax" с работающими примерами
Да я предлогал ТС данный материал для изучения, но Andy отговорил его, сказав, что это говнопример.
У меня все в табличку впихнуто. По логике, если будет всю страничку обновлять, то смысл тогда делать отдельные формы. Но я в дальнейшем планирую еще температуру выводить, обратную связь от реле.
Хмм.. А про Ajax почитать ?
http://www.ibm.com/developerworks/ru/views/xml/libraryview.jsp?search_by=Ajax+master
и куча ссылок по запросу "arduino ajax" с работающими примерами
Да я предлогал ТС данный материал для изучения, но Andy отговорил его, сказав, что это говнопример.
Я по мере наличия времени изучаю любезно скинутые вами ссылки и возможно в дальнейшем где-нибудь применю.