Arduino+Ethernet shield+SD карта: Не могу подгрузить внешние .js файлы для веб страницы.
- Войдите на сайт для отправки комментариев
Если коротко, то суть вопроса в том, что если на Ардуине запущен Веб сервер и файлы Веб приложения находятся на SD карте, то файлы .htm, .css, .jpg загружаются без проблем, а файлы .js (javascript) – никак. В конце поста привожу примеры простейшей страницы и скетча с подробными комментариями. Если кто-нибудь сможет указать причину проблем с загрузкой .js, буду очень признателен.
На всякий случай опишу свой вопрос более детально:
Простое Веб приложение (в том числе и по управлению роботизированными устройствами через И-нет) состоит, как правило, из html кода, который наполняет страницу информацией; css стилей, которые говорят что и как располагать и разукрашивать на странице; и javaScript скрипта, который описывает функциональность страницы. Все эти части можно описать в одном файле, но гораздо удобней разделять на отдельные html, css, js файлы, в каждом из которых находится своя часть. К тому же, jQuery библиотека в принципе идет, как отдельный .js файл.
Если подготовить такой комплект файлов некоего Веб приложения, то его можно размещать где угодно: и на Веб сервере локальной машины Appache, и на сервере И-нета, и на сервере, запущенном на Raspberry или intel Galileo платах. При этом ничего не нужно менять, а только копировать папку с файлами.
Что касается Ардуино плат, то в большинстве примеров работы Веб сервера, html страница выводится прямо из скетча. Но это неудобно, ненаглядно и трудоемко. Гораздо удобней записать на SD карточку файлы веб приложения, подготовленные для любых платформ, и работать с ними. К тому же, при таком подходе, в Ардуину может быть залит один универсальный скетч, а информация с Веб сервера будет выводится в зависимости от того, какую карточку (с каким набором файлов) воткнуть.
При работе с внешними файлами (с файлами с sd карты) процесс выглядит примерно так: На сервер, запущенный на Ардуине, приходит запрос на открытие некой заглавной страницы. Ардуина вытаскивает нужный файл с карточки и шлет его клиенту, приславшему запрос. Когда браузер начинает открывать этот файл и натыкается на ссылку на любой другой файл, то он формирует и шлет новый запрос серверу (Ардуине), та вытаскивает новый файл и т.д., пока вся страница не будет открыта. Т.е. если в html странице указаны ссылки на .js, .scc файлы и на пару картинок, то на каждый из этих файлов Ардуина получит запрос от клиента и должна будет вытащить необходимый файл и отправить его по назначению.
Так вот, суть моей проблемы заключается в том, что Ардуина отлично справляется с отсылкой htm, css и jpg файлов. А файлы скрипта не отсылает. Но я не могу утверждать, что она их не отсылает, поскольку не знаю, как это проверить. Может быть она не досылает какого-то заголовка и браузер не может открыть эти файлы? Но js скрипт на загружаемой странице не работает.
В сети нашел 2 поста с аналогичной проблемой.
http://stackoverflow.com/questions/26856223/how-to-retrieve-css-js-files-on-arduino
http://forum.arduino.cc/index.php?topic=252757.0
Судя по всему, проблема решена на была. Даваемые там советы мне не помогли.
Ниже привожу все части элементарного Веб приложения. По нажатии на кнопку должно появляться сообщение «ОК». Если кто-то сможет подсказать, где ошибка, и как загрузить необходимую функциональность на страницу, в том числе и .js файл библиотеки jQuery, буду очень признателен.
Веб страница:
<!DOCTYPE html> <html> <head> <title>Arduino Web Page</title> <link rel="stylesheet" type="text/css" href="css/style.css" /> <script src="scripts/myscript.jsp" type="text/javascript"> </script> </head> <body> <h1>Hello from Arduino!</h1> <p>A web page from the Arduino server</p> <img src="img03.jpg" /> <button id="myButton">Test</button> </body> </html>
css файл:
/*Цвет шрифта и фона для заголовка H1*/ h1 { color:#374896; padding: 0 0 0 25px; background: rgb(218, 245, 196); }
js файл:
//После загрузки страницы запускаем функцию initPage. window.onload = initPage; function initPage() { //Записываем элемент с определенным ID (кнопку) в переменную. var myDomElement = document.getElementById("myButton"); //При событии клика по кнопке запускаем функцию... myDomElement.onclick = function () { //Выводим сообщение ОК. alert("OK"); } }
Скетч Ардуино:
//Подключение библиотек. #include <SPI.h> #include <Ethernet.h> #include <SD.h> // размер буфера для записи HTTP запроса. #define REQ_BUF_SZ 20 // Переметры для Веб сервера (MAC, IP, порт) byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(192, 168, 1, 177); // IP address. EthernetServer server(80); // port 80 //Переменная, типа File для работы с файлами на SD карте. File webFile; char HTTP_req[REQ_BUF_SZ] = {0}; // HTTP запрос записывается в эту строку (массив символов) char req_index = 0; // index строки запроса. void setup() { // Инициализация порта для SD карты на пине 4. SPI.begin(4); // Инициализация порта для Веб сервера на пине 10 SPI.begin(10); Serial.begin(9600); // Сериал порт для вывода сообщений. // Инициализация SD карты Serial.println("Initializing SD card..."); if (!SD.begin(4)) { Serial.println("ERROR - SD card initialization failed!"); return; } Serial.println("SUCCESS - SD card initialized."); // Проверка существования index.htm файла в указанном месте на карте. // И вывод соответствующего сообщения if (!SD.exists("proba/index.htm")) { Serial.println("ERROR - Can't find proba/index.htm file!"); return; } Serial.println("SUCCESS - Found proba/index.htm file."); // Проверка существования img03.jpg файла в указанном месте на карте. // И вывод соответствующего сообщения if (!SD.exists("proba/img03.jpg")) { Serial.println("ERROR - Can't find proba/img03.jpg file!"); return; } Serial.println("SUCCESS - Found proba/img03.jpg file."); // Проверка существования style.css файла в указанном месте на карте. // И вывод соответствующего сообщения if (!SD.exists("proba/css/style.css")) { Serial.println("ERROR - Can't find proba/css/style.css file!"); return; } Serial.println("SUCCESS - Found proba/scripts/myscript.js file."); // Проверка существования img03.jpg файла в указанном месте на карте. // И вывод соответствующего сообщения if (!SD.exists("proba/scripts/myscript.js")) { Serial.println("ERROR - Can't find proba/scripts/myscript.js file!"); return; // can't find index file } Serial.println("SUCCESS - Found proba/scripts/myscript.js file."); Ethernet.begin(mac, ip); // Инициализация Ethernet шилда. server.begin(); // Запуск Веб сервера. } void loop() { EthernetClient client = server.available(); // Ожидаем запроса от клиента, пока заботает сервер. if (client) { // Если запрос пришел... boolean currentLineIsBlank = true; while (client.connected()) { //Пока клиент на связи... if (client.available()) { //Если в запросе от клиента еще есть, что прочесть... char c = client.read(); // читаем 1 байт из клиентского запроса и записываем его в переменную. //Записываем этот прочитанный байт в общую строку запроса от клиента. //Т.е. записываем одну букву в последний индекс строчного массива. if (req_index < (REQ_BUF_SZ - 1)) { HTTP_req[req_index] = c; req_index++; } Serial.print(c); // выводим прочитанный символ в сериал монитор //Если достигнут конец запроса... // (последняя строка пустая и заканчивается на \n) if (c == '\n' && currentLineIsBlank) { // Если в запросе содержатся подстроки: "GET / " или "GET /proba/index.htm" или "GET /index.htm" // Т.е. если пришел запрос на открытие заглавной страницы... if (StrContains(HTTP_req, "GET / ") || StrContains(HTTP_req, "GET /proba/index.htm") || StrContains(HTTP_req, "GET /index.htm")) { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connnection: close"); client.println(); webFile = SD.open("proba/index.htm"); // Открываем файл с заглавной страницей с SD карты. } // Если в запросе содержатся подстроки: "GET /img03.jpg" // Т.е. если пришел запрос на открытие картинки... else if (StrContains(HTTP_req, "GET /img03.jpg")) { webFile = SD.open("proba/img03.jpg"); // открываем файл с картинкой с SD карты. if (webFile) { client.println("HTTP/1.1 200 OK"); client.println(); } } // Если в запросе содержатся подстрока: "GET /css/style.css" // Т.е. если пришел запрос на открытие css файла... else if (StrContains(HTTP_req, "GET /css/style.css")) { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/css"); client.println("Connnection: close"); client.println(); webFile = SD.open("proba/css/style.css"); // открываем файл css с SD карты. } // Если в запросе содержатся подстрока: "GET /scripts/myscript.js" // Т.е. если пришел запрос на открытие js файла... else if (StrContains(HTTP_req, "GET /scripts/myscript.js")) { client.println("HTTP/1.1 200 OK"); //client.println("Content-Type: application/x-javascript"); client.println("Content-Type: text/javascript"); client.println("Connnection: close"); client.println(); webFile = SD.open("proba/scripts/myscript.js"); } // Если нужный файл открылся, отсылаем страницу клиенту. if (webFile) { while(webFile.available()) { client.write(webFile.read()); } webFile.close(); } // устанавливаем индекс строки запроса в 0 и очищаем саму строку. req_index = 0; StrClear(HTTP_req, REQ_BUF_SZ); break; } // every line of text received from the client ends with \r\n if (c == '\n') { // last character on line of received text // starting new line with next character read currentLineIsBlank = true; } else if (c != '\r') { // a text character was received from client currentLineIsBlank = false; } } // end if (client.available()) } // end while (client.connected()) delay(1); // give the web browser time to receive the data client.stop(); // close the connection } // end if (client) } // Функция очищает строку, содержащую запрос. void StrClear(char *str, char length) { for (int i = 0; i < length; i++) { str[i] = 0; } } // Функция ищет подстроку в строке // Возвращает 1, если подстрока найдена. // Возвращает 0, если нет. char StrContains(char *str, char *sfind) { char found = 0; char index = 0; char len; len = strlen(str); if (strlen(sfind) > len) { return 0; } while (index < len) { if (str[index] == sfind[found]) { found++; if (strlen(sfind) == found) { return 1; } } else { found = 0; } index++; } return 0; }
Так трудно разве, расставить Serial.print в сомнительных местах - и сразу всё будет понятно.
Сам файл, который js, открывается нормально? Строчка "SUCCESS - Found proba/scripts/myscript.js file" в serial monitor'е появляется?
Так трудно разве, расставить Serial.print в сомнительных местах - и сразу всё будет понятно.
Сам файл, который js, открывается нормально? Строчка "SUCCESS - Found proba/scripts/myscript.js file" в serial monitor'е появляется?
Все элементарные вещи я проверил. Например, в setup-е делается проверка наличия всех необходимых файлов на карточке. Конечно же, при запуске скетча выводится инфа, что все файлы присутствуют.
Затем, в сериале выводится полный текст запроса, который приходит от сервера. В нем тоже все в порядке - приходит именно та подстрочка (на js файл), которая и проверяется в условии.
А вот что можно сделать еще? Serial.print какого параметра мне надо указать, чтобы в сериал мониторе увидеть построчно, какой файл (с каким содержимым) отправляется клиенту?
Но и это не все. Я не могу найти никакой инфы на счет того, а как js файлы отправляются клиенту с обычного Веб сервера на стационарном компе. Может быть в заголовках запроса какие-то отличия и Ардуиновский ответ не понимается браузерами?
В догонку: Пробовал работу на 3-х браузерах: IE, Фаерфокс и Хром. Файлы на карточке пробовал размещать все в корень. js файлу пробовал давать расширение из 3 букв. Различий нет.
там народ и не видит ни какой проблемы... обычный сервер из примера выдает любые файлы с карточки... вопрос в том как и в какой последовательности у вас запрашивается.. и как у вас построена передача всей этой ботвы на ардуино... берете пример сервера и запрашиваете с негог любые файлы с карточки...
А вот что можно сделать еще? Serial.print какого параметра мне надо указать, чтобы в сериал мониторе увидеть построчно, какой файл (с каким содержимым) отправляется клиенту?
Не люблю программировать "в уме", а попробовать сейчас не на чем.
Но, наверно, приблизительно так должно выглядеть:
Но и это не все. Я не могу найти никакой инфы на счет того, а как js файлы отправляются клиенту с обычного Веб сервера на стационарном компе. Может быть в заголовках запроса какие-то отличия и Ардуиновский ответ не понимается браузерами?
А для этого советую установить на стороне клиента какой-нибудь http-сниффер.
У меня, например, установлен Lucid Edge Illumination. Все http-запросы и ответы, вместе с заголовками, видны как на ладони. Остаётся только сравнить ответ ардуины с ответом какого-нибудь другого сервера.
Программа платная, но по-моему работает и "за так" - правда, жалуется на это, при каждом запуске. :)
Если тема все еще актуальна, попробуйте этот скетч, но у мен я отлично читает SD карту, только джаву исполнять не хочет (видимо у него нет такой задачи).
Ваш скетч у меня почему-то не проходит компиляцию по строке 023
SPI.begin(10);
Попробуй перенести все файлы из папок в корень карты памяти.
И соответственно исправь обращения к файлам в коде. Так же в библиотеке SD.h действуют ограничения к имени файла 8.3 символов
пример
ну зачем поднимать старые темы?....
Vadim111,
1. html содержит ссылку на myscript.jsp - тут ошибка
2. файл со скриптом на карте как называется? js?
3. какой content type?
4. в строке 057 непроверенное заявление выводите.
>> ... http сниффер ...
в 2017 году в любом браузере нажать F12 -network
Это прям совет из будущего :)