Как сделать веб-хостинг, если весь код сайта хранится на SD-карте?

Я выполняю этот проект, в котором arduino выступает в качестве веб-сервера и размещает веб-сайт, на котором я показываю постоянно обновляемую температуру в лаборатории.

Проблема в том, что при запуске всего этого кода в arduino легко заканчивается память.

void loop() {
  int value= analogRead(PIN_LM35);
  float temperature= value/ 2.046;
  myFile = SD.open("temperature.txt");
  if (myFile) {
    Serial.println(temperature);
    }
    myFile.close();


   EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {   
      if (client.available()) {
        char c = client.read();
     
        //read char by char HTTP request
        if (readString.length() < 100) {
          //store characters to string
          readString += c;
          //Serial.print(c);
         }

         //if HTTP request has ended
         if (c == '\n') {          
           Serial.println(readString); //print to serial monitor for debuging
     
           client.println(F("HTTP/1.1 200 OK")); //send new page
           client.println(F("Content-Type: text/html"));
           client.println();  
           refreshcounter=refreshcounter+1;   
           client.println(F("<HTML>"));
           client.println(F("<HEAD>"));
           client.print(F("<meta http-equiv=\"refresh\" content=\"2\">"));
           client.println(F("<meta name='apple-mobile-web-app-capable' content='yes' />"));
           client.println(F("<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />"));
           client.println(F("<TITLE>TEMPERATURE SENSOR LAB01</TITLE>"));
           client.println(F("</HEAD>"));
           client.println(F("<BODY>"));
           client.println(F("<H1>TEMPERATURE SENSOR LAB01</H1>"));
           client.println(F("<hr />"));
           client.println(F("<br />"));  
           client.println(F("<H2>Arduino with Ethernet Shield</H2>"));
           client.println(F("<br />")); 
           if(temperatura<24){
              client.println("<p style=\"font-size:50px; color:#8eff59; font-weight:bold; font-style:italic;\">");
              client.println(temperature);
              client.println("</p>");
           }
           else if(temperature>=24 && temperature<=26){
             client.println("<p style=\"font-size:50px; color:#ffbc03; font-weight:bold; font-style:italic;\">");
             client.println(temperature);
             client.println("</p>");
           }
           else{
             client.println("<p style=\"font-size:50px; color:#ff0303; font-weight:bold; font-style:italic;\">");
             client.println(temperature);
             client.println(F("<H2>It is recommended to activate the air conditioner.</H2>"));
             client.println("</p>");
           }
           client.println(F("<p style=\"font-size:30px; color:#000000; font-weight:bold; ;\">Date/Time: <span id=\"datetime\"></span></p>"));
           client.println(F("<script>"));          
           client.println(F("var dt = new Date();"));
           client.println(F("document.getElementById(\"datetime\").innerHTML = ((\"0\"+dt.getDate()).slice(-2)) +\".\"+ ((\"0\"+(dt.getMonth()+1)).slice(-2)) +\".\"+ (dt.getFullYear()) +\" \"+ ((\"0\"+dt.getHours()).slice(-2)) +\":\"+ ((\"0\"+dt.getMinutes()).slice(-2));"));         
           client.println(F("</script>"));

           client.println("<svg width=\"1000\" height=\"250\">"); 
           client.println("<rect width=\"150\" height=\"5\" fill=\"gray\">"); 
           client.println("<animate attributeName=\"x\" from = \"0\" to =\"10000\" dur=\"10s\" fill=\"freeze\" />");
           client.println("</rect"); 
           client.println("</svg>");  
           
           client.println("<br />");     
           client.println("</BODY>");
           client.println("</HTML>");
     
           delay(1);
           //stopping client
           client.stop();
            //clearing string for next read
            readString="";  
           
         }
       }
    }
}
}
</p>")); client.println(F("<скрипт>")); client.println(F("var dt = новая дата();")); client.println(F("document.getElementById(\"datetime\").innerHTML = ((\"0\"+dt.getDate()).срез(-2)) +\".\"+ ((\"0\"+( дт.getMonth()+1)).нарезать ломтиками(-2)) +\".\"+ ( dt.Получить полностью год()) +\" \"+ ((\"0\"+ dt.getHours()).нарезать(-2)) +\":\"+ ((\"0\"+dt.getMinutes()).slice(-2));")); client.println(F("</script>")); client.println("<svg width=\"1000\" height=\"250\">"); клиент.println("<ширина прямоугольника = \"150 \" высота = \"5 \" заливка= \"серый \">"); client.println("<имя атрибута анимации = \"x \" от = \"0 \" до = \"10000 \" dur= \"10s \" fill=\"freeze \" />"); клиент.println("</rect"); клиент.println("</svg>"); client.println("<br />"); client.println("</BODY>"); client.println("</HTML>") клиент.println("</HTML>"); задержка (1); //остановка клиента клиент.стоп(); //очистка строки для следующего чтения Строка чтения=""; } } } } }

Я подумал сэкономить память, чтобы поместить весь код веб-страницы на внешнюю SD-карту. Однако я действительно не знаю, как использовать его с SD-карты.

Проблема, которая меня останавливает, заключается в том факте, что сайт не обязательно должен быть статичным, но постоянно обновляться новыми данными о температуре.

Как я могу это сделать?

, 👍2

Обсуждение

почему вы не используете F() для каждой строки? С Arduino Uno, если вы используете библиотеку Ethernet и библиотеку SD, у вас очень быстро заканчиваются flash и SRAM., @Juraj

Я пытался, но у меня тоже заканчивалась флэш-память, @Leo

Возможно, вам захочется подробно описать, какую библиотеку, экран и плату вы используете. Это один из стандартных AVR Arduino? Какое у него есть оборудование и библиотека, которые позволяют запускать его в первую очередь как сервер?, @RDragonrydr

Я бы посоветовал ознакомиться с примерами библиотеки экранов wifi / ethernet или примерами библиотеки SD-карт. Возможно, вы найдете тот, который уже делает то, что вам нужно. В самом худшем случае вы можете написать цикл, который считывает символы или строки с карты и сервера.печатает их. Создайте на карте два файла, примыкающих к линии печати, чтобы вставить значение температуры, затем распечатайте файл 1, Температура, файл 2., @RDragonrydr


1 ответ


Лучший ответ:

6

Вы написали:

сайт не обязательно должен быть статичным, но постоянно обновляться новыми данными о температуре.

Хорошим вариантом является использование Ajax. Основная идея состоит в том, чтобы разделить сайт на две части:

  • статичная часть, потенциально большая, которая содержит весь пользовательский интерфейс, стиль, навороты и навороты...
  • крошечная динамическая часть, содержащая данные, которые постоянно обновляются и ничего больше.

Статическая часть будет передаваться с SD-карты, когда клиент отправляет GET / request. Динамическая часть будет обслуживаться с другой конечной точки, например, в качестве ответа на GET / temperature. Обслуживание динамической части должно быть очень простым, что-то вроде:

client.println(temperature);

Да, просто отправьте номер в формате ASCII, без форматирования HTML. Если вам когда-нибудь понадобится отправить более одного числа (например, несколько значений температуры или температуры и влажности), отформатируйте их в формате JSON. Для этого не нужно использовать библиотеку , обычная функция print() должна быть дешевле и достаточно хороша:

client.print(F("{\"temperature\":"));
client.print(temperature);
client.print(F(",\"humidity\":"));
client.print(humidity);
client.print('}');

На стороне клиента JSON.parse() возвращает данные в виде простой в использовании структуры данных.

Вот минимальный пример того, как может выглядеть статическая часть. Обратите внимание на код JavaScript, отправленный клиенту. Этот код отвечает за запрос температуры каждые 1000 миллисекунд и обновление Веб-страница с новыми данными;

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Ajax test</title>
</head>
<body>

<h1>Ajax test</h1>

<p>Temperature: <span id="temperature">---</span> °C</p>

<script>
var data_field = document.getElementById("temperature");
setInterval(function() {
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
        if (request.readyState != 4) return;
        data_field.innerText = JSON.parse(request.responseText);
    };
    request.open("GET", "/temperature");
    request.send();
}, 1000);
</script>
</body>
</html>
°C</p> <сценарий> var data_field = document.getElementById("температура"); setInterval(функция() { var запрос = новый XMLHttpRequest(); request.onreadystatechange = функция() { if (request.readyState != 4) возвращает; data_field.innerText = JSON.parse(запрос.responseText); }; запрос.открыть ("ПОЛУЧИТЬ", "/температура"); запрос.отправить(); }, 1000); </скрипт> </тело> </html>

Несколько замечаний:
  • Этот пример намеренно минимален. Возможно, вы захотите добавить некоторую проверку ошибок, некоторые украшения, некоторые CSS и тому подобное. Обработка цвета, зависящего от температуры, также относится к клиентскому коду.
  • В примере используется старый добрый XMLHttpRequest. Возможно, вместо этого вы захотите попробовать более современный fetch API

    :
    setInterval(function() {
        fetch("/temperature")
            .then(response => response.json())
            .then(data => { data_field.innerText = data; });
    }, 1000);
    
    setInterval(функция() { выборка ("/ температура") .затем(response => response.json()) .затем(data => { data_field.innerText = данные; }); }, 1000);
  • Если вы отправляете обновления достаточно часто, чтобы значительно загрузить свой Arduino, вы можете посмотреть на события, отправляемые сервером

    . Это метод, который позволяет значительно сократить накладные расходы на отправку обновлений данных. Он не так популярен, как веб-сокеты, но имеет то преимущество, что его проще реализовать на стороне сервера.
  • Если вы можете установить обратный прокси-сервер между вашим Arduino и Интернетом, прокси-сервер может обрабатывать статический контент. Тогда ваш Arduino будет обрабатывать только динамические данные и не будет нуждаться в SD-карте. Если вы отменяете события, отправленные прокси-сервером, убедитесь, что прокси-сервер не буферизует ответ сервера. См., например, эти советы по настройке Nginx

    для этой цели.
  • Статические данные также могут быть переданы с совершенно другого веб -сайта. Чтобы это сработало, вы должны выполнить два условия:
    1. Сервер Arduino должен добавить заголовок Access-Control-Allow-Origin: *

      к его ответам.
    2. В клиентском коде вы должны указать полный URL-адрес Arduino в качестве аргумента request.open(), fetch() или new EventSource()

      .
  • Предыдущий трюк также позволяет вам открывать веб-страницу непосредственно из вашей локальной файловой системы, что может быть очень удобно для разработки.
  • Как только вы освоите базовую схему, веб-технологии позволят вам делать все, что вам заблагорассудится. Вы могли бы создать аналоговый датчик (с помощью SVG transform rotate). Вы даже можете добавить график

    , обновляемый в режиме реального времени, который показывает изменение температуры во времени и полностью обрабатывается клиентом.
,