Ajax web-server (w5500)
- Войдите на сайт для отправки комментариев
Здравствуйте.
В перспективе создать web-сервер с выводом показаний датчиков и возможностью вкл/выкл нагрузок.
В статике вывод аналоговых и цифровых показателей работает, кноки управления нагрузки тоже. Изучаю Ajax. Победить его на ENC28J60 без SD не смог. Приобрел w5500.
Скетч из примера в сети с подставленным PIR-сенсором:
[spoiler]
#include <SPI.h>
#include <Ethernet.h>
// MAC address from Ethernet shield sticker under board
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 177); // IP address, may need to change depending on network
EthernetServer server(80); // create a server at port 80
String HTTP_req; // stores the HTTP request
int pirPin = 2; // инициализируем пин для получения сигнала от пироэлектрического датчика движения
void setup()
{
Ethernet.begin(mac, ip); // initialize Ethernet device
server.begin(); // start to listen for clients
Serial.begin(9600); // for diagnostics
pinMode(2, INPUT); // switch is attached to Arduino pin 3
}
void loop()
{
EthernetClient client = server.available(); // опрашиваем канал в поисках клиента
if (client) { // got client?
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) { // client data available to read
char c = client.read(); // read 1 byte (character) from client
HTTP_req += c; // save the HTTP request 1 char at a time
// last line of client request is blank and ends with \n
// respond to client only after last line received
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: keep-alive");
client.println();
if (HTTP_req.indexOf("ajax_switch") > -1) { // AJAX request for switch state
// read switch state and send appropriate paragraph text
GetSwitchState(client);
}
else { // HTTP request for web page
// send web page - contains JavaScript with AJAX calls
client.println("<!DOCTYPE html>");
client.println("<html>");
client.println("<head>");
client.println("<title>Arduino Web Page</title>");
client.println("<script>");
client.println("function GetSwitchState() {");
client.println("nocache = \"&nocache=\" + Math.random() * 1000000;"); // чтобы браузер не обращался к кэш
client.println("var request = new XMLHttpRequest();");
client.println("request.onreadystatechange = function() {");
client.println("if (this.readyState == 4) {");
client.println("if (this.status == 200) {");
client.println("if (this.responseText != null) {");
client.println("document.getElementById(\"data_to_update\").innerHTML = this.responseText;");
client.println("}}}}");
client.println("request.open(\"GET\", \"ajax_switch\" + nocache, true);");
client.println("request.send(null);");
client.println("setTimeout('GetSwitchState()', 1000);"); // интервал запросов данных (вызова функции "GetSwitchState()") (мсек)
client.println("}");
client.println("</script>");
client.println("</head>");
client.println("<body>");
client.println("<body onload=\"GetSwitchState()\">");
client.println("<h1>Arduino AJAX Switch Status</h1>");
client.println("<div id=\"data_to_update\">");
client.print("<p id=\"switch_txt\">Switch state: Not requested...</p>");
// switch state and button code will be inserted here by the JavaScript after AJAX call
client.println("</div>");
client.println("</body>");
client.println("</html>");
}
Serial.print(HTTP_req); // display received HTTP request on serial port
HTTP_req = ""; // finished with request, empty string
break;
}
//каждая строка полученная от клиента заканчивается \r\n
if (c == '\n') {
// последний символ принятого запроса
// начало новой строки для чтения
currentLineIsBlank = true;
}
else if (c != '\r') {
// получение текстового символа от клиента
currentLineIsBlank = false;
}
} // end if (client.available())
} // end while (client.connected())
delay(1); // даем время для браузера получить данные
client.stop(); // закрываем соединение
} // end if (client)
}
// send the state of the switch to the web browser
void GetSwitchState(EthernetClient cl)
{
if (digitalRead(2)) {
cl.println("<p>Switch state: ON</p><button type=\"button\" style=\"color: green;\" onclick=\"GetSwitchState()\">On</button>");
}
else {
cl.println("<p>Switch state: OFF</p><button type=\"button\" style=\"color: red;\" onclick=\"GetSwitchState()\">Off</button>");
}
}
[/spoiler]
Вопрос: После загрузки скетча страница отображает/обновляет статус датчика, xml ходят. Но стоит только перезагрузить страницу в браузере, статус показаний меняется на "Not requested...". Тоже самое и в других браузерах. Грубо говоря всё работает только при первой загрузке страницы сразу после заливки скетча. Выглядит это примерно так:

Подскажите как мне получать статус с датчиков после перезагрузки страницы?
А что терминал прилетает? есть там строка "ajax_switch"?
Похоже не весь ответ считывает.
и замени в строке 28,
ifнаwhile.Можно в консоль браузера статусы хттп-реквестов повыводить - мошт ошибка возникает при загрузке. А весь html вынести в PGM и выплёвывать одной строкой, не ломая мозг...
1. Гонять по TCP/IP HTML код тупо и небезопасно. Конечно, в домашней сети его не подменить, но привыкать к этому не следует. Не всяк тот хакер. Хак - код дебила, индусский и т. п., существуют правила. Любой хак однажды аукается. Код должен предусматривать все состояния, и не быть полиморфным. И нельзя писать код который работает, надо писать код который понятен. Иначе есть риск стать индусом, спать на улице, начать поклоняться Шиве и подцепить паразитов. Прекратите в ответах выдавать HTML. Для состояний есть JSON. И короче, BTW.
2. Раз уж такой праздник (Wiznet), можно же править заголовки. То есть ещё раз тыкаю носом: перепишите JS так чтобы ему был нужен только JSON: "[0]" выключено, "[1]" включено, без кавычек (в строке), через запятую если несколько. На ENC28J60 заголовки тоже можно изменять, держу в курсе (правда, треба уже лезть в библиотеку, там это спрятано).
3. Мне лень предусматривать различные запросы на дуине, я просто отдаю полезный JSON, добавив заголовок "Access-Control-Allow-Origin: *" (без кавычек), а сам вебинтерфейс (HTML+JS) держу на клиенте. От флешки смартфона, если записать туда полуторакилобайтный HTML файл, не убавится. А писанины меньше.
4. Недоступность ресурса после перезагрузки значит только одно: цикл обслуживания Eth - кривой. Сверяйте с примером.
5. Убедиться в наличии заголовка "Connection: close" (без кавычек). Соединения keep-alive чреваты вывертами.
Верно. Но чем боротся с багами от применения старого громоздкого барахла из кладовки вебдизайна просто применить протокол, адекватный для реалтайм данных - https://ru.wikipedia.org/wiki/WebSocket
Правильно ли я понимаю, что Voodoo предлагает сам код страницы с интерфейсом помещать вручную на каждый смартфон или планшет (предварительно произведя Jailbreak для определённых), JSON применять в виде "...{"states":"0,1,1,1,1,0"}..." для дополнительно написанного JS-парсера, бояться keep-alive соединений и править некие тайные заголовки, зачем-то спрятанные внутри библиотеки? В противном случае же Шива наградит пейсателя вшами, а Ганеш заставит петь и танцевать до изнеможения.
По поводу кривизны обслуживания запросов - согласен.
Не, неправильно. Не надо никаких парсеров, всё есть в V8.
Насчёт вставить HTML в progmem и отдавать вместе со статусом: слушаю и записываю. А то любят такие "ой да ты нуб, вот есть такая то магия, я ваще фаерболы суммоню". А как кастануть эту магию попросишь - так все врассыпную, как боты от перса, когда на паузу поставишь, читы подрубишь и левел апнешь.
Я на дваче и пр. не сижу, поэтому жаргон мне этот непонятен. А ролики с кликбейтными названиям вообще не смотрю.
Но, если действительно интересует, как в PGM держать исходник страницы, не размазывая его по куче print() то изволь:
const char html_page[] PROGMEM="<html><head><meta charset=\"utf-8\"><title>...</title>\n\ <style>\n\ body{font-family:Geneva,Arial,Helvetica,sans-serif;-webkit-text-size-adjust:100%}\n\ @media (min-width:1024px){.sctw{width:24.69%}.scts{width:49%}.sm{display:none}#box{max-width:1024px}}\n\ @media (min-width:426px) and (max-width:1023px){.sctw{width:49.5%}.scts{width:100%}.sm{display:none}}\n\ ... </style>\n\ </head><body><div id=\"box\"class=\"rb\">\n\ ... <div>IP Address</div><div><input id=\"na\" name=\"na\" type=\"text\" size=\"15\"/></div></div>\n\ <div><div>Subnet</div><div><input id=\"nm\" name=\"nm\" type=\"text\" size=\"15\"/></div></div>\n\ <div><div>Gateway</div><div><input id=\"ng\" name=\"ng\" type=\"text\" size=\"15\"/></div></div>\n\ <div><div>DHCP</div><div><input id=\"nd\" name=\"nd\" type=\"checkbox\"/></div></div>\n\ </div>\n\ ... <script>\n\ var h=new XMLHttpRequest(),oUpd=document.getElementById('upds'),oUpw=document.getElementById('updw')\n\ var cUpw,tUpd,e,j;\n\ function sQry(){h.open('GET',dUrl+'?t='+Math.random(),true);h.onreadystatechange=oRSC;h.send()}\n\ function oRSC(){\n\ if(this.readyState!=4){return;}\n\ try{\n\ ... </script></body></html>"; #define FSH_P(p) (reinterpret_cast<const __FlashStringHelper *>(p)) ... EthernetClient ethClient; ... ethClient.print(FSH_P(html_page));После этого достаточно просто на ПК всё отладить и копипастнуть исходник страницы прямо в .ino файл.
Что же касается V8... Тебе, конечно, это может показаться странным, но я из Safari смотрю интерфейс, а то и из Firefox. Поэтому предпочитаю использовать функционал объекта JSON, который есть там и сям и кормить его вполне легальным (хотя и избыточным) JSON-форматом без извращений с псевдокомпрессией. И при этом мне не приходится ломать голову над тем, как на iPad засунуть html-файл, потом его загрузить из локального хранилища в единственный браузер, который имеет механику, чтобы раскрутить нечто, заточенное под V8.
Спасибо за ответы.
ifнаwhile.После этого действия web-страница в браузерах не загружается.
Стало намного стабильнее после замены библиотеки с Ethernet на Ethernet2 при том же скетче.
Logik Спасибо за наводку, начал изучать web сокет.
А давайте сначала по RS232 погоняем, а уж ПОТОМ...