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 какого параметра мне надо указать, чтобы в сериал мониторе увидеть построчно, какой файл (с каким содержимым) отправляется клиенту?
Не люблю программировать "в уме", а попробовать сейчас не на чем.
Но, наверно, приблизительно так должно выглядеть:
// Если нужный файл открылся, отсылаем страницу клиенту. if (webFile) { Serial.println( "---------- start of file ----------" ); while(webFile.available()) { char Ch = webFile.read( ); client.write( Ch ); Serial.print( Ch ); } Serial.println( "----------- end of file -----------" ); webFile.close(); }Но и это не все. Я не могу найти никакой инфы на счет того, а как js файлы отправляются клиенту с обычного Веб сервера на стационарном компе. Может быть в заголовках запроса какие-то отличия и Ардуиновский ответ не понимается браузерами?
А для этого советую установить на стороне клиента какой-нибудь http-сниффер.
У меня, например, установлен Lucid Edge Illumination. Все http-запросы и ответы, вместе с заголовками, видны как на ладони. Остаётся только сравнить ответ ардуины с ответом какого-нибудь другого сервера.
Программа платная, но по-моему работает и "за так" - правда, жалуется на это, при каждом запуске. :)
Если тема все еще актуальна, попробуйте этот скетч, но у мен я отлично читает SD карту, только джаву исполнять не хочет (видимо у него нет такой задачи).
#include <SPI.h> #include <Ethernet.h> #include <SD.h> byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(192,168,1,100); char rootFileName[] = "index.htm"; EthernetServer server(80); File myFile; void setup() { SD.begin(4); Serial.begin(9600); Serial.println("Free RAM: "); Serial.println(FreeRam()); pinMode(10, OUTPUT); // установить SS вывод как выходящий digitalWrite(10, HIGH); // Выключить чип w5100 // Запускаем сервер. По умолчанию шлюз выбран 192.168.1.1, маска 255.255.255.0 Ethernet.begin(mac, ip); server.begin(); } // Выбираем размер буфера 100 символов, где находится имя файла. #define BUFSIZ 100 void loop() { char clientline[BUFSIZ]; char *filename; int index = 0; EthernetClient client = server.available(); if (client) { // an http request ends with a blank line boolean current_line_is_blank = true; // reset the input buffer index = 0; while (client.connected()) { if (client.available()) { char c = client.read(); // Сбросить соединение, если пришел непонятный символ от клиента. //Например, наблюдались зависания от браузера // Safary (IPad 2), который посылал "непонятные" символы if ( c==0x0A || c==0x0D ) goto aa; if ( c<0x20 || c>0x7E ) break; aa: // Если символ от клиента правильный, записываем его в буфер // Если идет чтение не новой строки, то продолжаем ее символы записывать в буфер. if (c != '\n' && c != '\r') { clientline[index] = c; index++; // Идем на продолжение считывать новый символ. continue; } // Заканчиваем строку символом 0, если следующая строка новая (получили \n или \r ) clientline[index] = 0; filename = 0; // Распечатываем прочитанную строку. Serial.println(clientline); // Look for substring such as a request to get the root file if (strstr(clientline, "GET / ") != 0) { filename = rootFileName; } if (strstr(clientline, "GET /") != 0) { // this time no space after the /, so a sub-file if (!filename) filename = clientline + 5; // look after the "GET /" (5 chars) // a little trick, look for the " HTTP/1.1" string and // turn the first character of the substring into a 0 to clear it out. (strstr(clientline, " HTTP"))[0] = 0; // print the file we want Serial.println(filename); myFile = SD.open(filename); if (!myFile ) { client.println("HTTP/1.1 404 Not Found"); client.println("Content-Type: text/html"); client.println(); client.println("<h2>File Not Found!</h2>"); break; } Serial.println("Opened!"); client.println("HTTP/1.1 200 OK"); if (strstr(filename, ".htm") != 0) client.println("Content-Type: text/html"); else if (strstr(filename, ".css") != 0) client.println("Content-Type: text/css"); else if (strstr(filename, ".png") != 0) client.println("Content-Type: image/png"); else if (strstr(filename, ".jpg") != 0) client.println("Content-Type: image/jpeg"); else if (strstr(filename, ".gif") != 0) client.println("Content-Type: image/gif"); else if (strstr(filename, ".3gp") != 0) client.println("Content-Type: video/mpeg"); else if (strstr(filename, ".pdf") != 0) client.println("Content-Type: application/pdf"); else if (strstr(filename, ".js") != 0) client.println("Content-Type: application/x-javascript"); else if (strstr(filename, ".xml") != 0) client.println("Content-Type: application/xml"); else client.println("Content-Type: text"); client.println(); byte cB[64]; int cC=0; while (myFile.available()) { cB[cC]=myFile.read(); cC++; if(cC > 63) { client.write(cB,64); cC=0; } } if(cC > 0) client.write(cB,cC); myFile.close(); } else { // everything else is a 404 client.println("HTTP/1.1 404 Not Found"); client.println("Content-Type: text/html"); client.println(); client.println("<h2>File Not Found!</h2>"); } break; } } // give the web browser time to receive the data delay(1); client.stop(); } }Ваш скетч у меня почему-то не проходит компиляцию по строке 023
SPI.begin(10);Попробуй перенести все файлы из папок в корень карты памяти.
И соответственно исправь обращения к файлам в коде. Так же в библиотеке SD.h действуют ограничения к имени файла 8.3 символов
пример
ну зачем поднимать старые темы?....
Vadim111,
1. html содержит ссылку на myscript.jsp - тут ошибка
2. файл со скриптом на карте как называется? js?
3. какой content type?
4. в строке 057 непроверенное заявление выводите.
>> ... http сниффер ...
в 2017 году в любом браузере нажать F12 -network
Это прям совет из будущего :)