скачать файл с SD по Ethernet

pastry777
Offline
Зарегистрирован: 16.01.2014

есть всем известный гуляющий по интернету скетч управления реле через интернет для ардуино+ethernet шилд...все работает безупречно.вот он:

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // Мак адрес
byte ip[] = { 192, 168, 1, 100 }; // IP адрес (В броузере вводим 192.168.0.2)
EthernetServer server(100);

int numPins = 4;
int pins[] = { 2, 3, 4, 5 };    // Пины для реле
int pinState[] = {0, 0, 0, 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;charset=UTF-8");
      client.println();
      client.println("<html>");
      client.println("<head>");
      client.println("<title>Удаленное управление</title>");
      client.println("</head>");
      client.println("<body>");
      client.println("<hr>");
      client.println("<h2><b><p align='center'>Удаленное управление</p></b></h2> ");
      client.println("<hr>");
      client.println("<form method='post'>");
      client.print("<div><h2><p align='center'>РЕЛЕ 1 <input type='checkbox' ");
      if (pinState[0] == 1)
        client.print("checked");
      client.println(" name='r0'></p></h2></div>");
      client.print("<div><h2><p align='center'>РЕЛЕ 2 <input type='checkbox' ");
      if (pinState[1] == 1)
        client.print("checked");
      client.println(" name='r1'></p></h2></div>");
      client.print("<div><h2><p align='center'>РЕЛЕ 3 <input type='checkbox' ");
      if (pinState[2] == 1)
        client.print("checked");
      client.println(" name='r2'></p></h2></div>");
      client.print("<div><h2><p align='center'>РЕЛЕ 4 <input type='checkbox' ");
      if (pinState[3] == 1)
        client.print("checked");
      client.println(" name='r3'></p></h2></div>");
      client.println("<p align='center'><input type='submit' value='ПРИМЕНИТЬ'></p>");
      client.println("</form>");
      client.println("</body>");
      client.println("</html>");
      client.stop(); 
    }
  }
}

 

на его базе я сделал еще мониторинг температур на ds18b20,мониторинг напряжения в сети 220,все это отображается на этой же веб-страничке и собственно веду логи значений датчиков на карту памяти...весь скетч свой не выкладываю,смысла нет,там все элементарно...ОЧЕНЬ надо скачивать удаленно файлы логов с карты...я уже столько времени убил на это,не передать,скоро слезы пойдут...

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

File volt = SD.open("/volt.csv");
         if (volt){
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Disposition: attachment;filename=volt.csv;");
          client.println();
              while (volt.available()) {
                      client.write(volt.read());}
           volt.close();
         }
           
          client.stop();

 

тобишь по отдельности скетчи функционируют,я как обычно не могу их слепить воедино...

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

из сотни моих экспериментов,я вычислил,что вся суть ошибки исходит из парсинга,так как для включения пинов тоже используется запрос только POST,а для стандартного парсинга GET...короче вот тут и накладочка выходит....

я пришел к выводу что вариант с парсингом не пройдет..

я хотел бы попросить у вас помощи:возможно ли сделать в html страничке кнопку,при нажатии которой выполнялся бы вот тот кусок кода,который запускает скачивание файла?или может какие другие идеи.спасибо...

 

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

вы сделали неправильные выводы...

Тип запроса (ГЕТ, ПОСТ) ни на что не влияет.

pastry777
Offline
Зарегистрирован: 16.01.2014

я исходил из того что получил по факту...в основном скетче мы парсим ПОСТ...если я над ним ставил парсинг ГЕТ по которому я попадал либо на эту самую страницу  с реле(которая генерирует ПОСТ в зависимости от  расставленных галочек) либо на страницу скачивания файла,то страница отображалась,но абсолютно не реагировала на расставленные галочки..значит либо запрос не формировался,либо мы его не анализировали в итоге..как никак накладка выходила,отсюда мои и выводы=(

я предполагал что проблема могла быть в этом:

к примеру в первом основном скетче у меня путь к странице был такой 192.168.1.100..все работает..я делаю разветвление,теперь страница реле 192.168.1.100/$1,а скачивание файла 192.168.1.100/$2..страница открывается,я ставлю галочки,нажимаю кнопку применить(значок загрузки страницы начинает крутиться,причем вечно) ,у нас формируется запрос но в адресе ведь добавилось $1 может поэтому ничего не происходит..в скетче не увидел зависимости парсинга ПОСТ к адресу,поэтому не знаю куда и что дописать..сижу курю сайты по html,может что найду...

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

pastry777 пишет:

...

к примеру в первом основном скетче у меня путь к странице был такой 192.168.1.100..все работает..я делаю разветвление,теперь страница реле 192.168.1.100/$1,а скачивание файла 192.168.1.100/$2..страница открывается,я ставлю галочки,нажимаю кнопку применить(значок загрузки страницы начинает крутиться,причем вечно) ,у нас формируется запрос но в адресе ведь добавилось $1 может поэтому ничего не происходит..в скетче не увидел зависимости парсинга ПОСТ к адресу,поэтому не знаю куда и что дописать..сижу курю сайты по html,может что найду...

Или я вас не понимаю, или вы меня не понимаете.

Во первых - вам нужно решить, с каким именно запросом вы работаете. Тут или мальчик или девочка.

Во вторых - в теле запроса есть строка адреса (192.168.1.100/$1), вам нужно ее распарсить (узнать, какое число стоит после "$") - по сути узнать значение константы, и в зависимости от этого выполнять действия. Никакиз лишних страниц создавать не нужно.

В реальной жизни (на настоящем веб-сервере) - вы обратились бы к серверному скрипту (например "relay.php") и передали бы ему номер реле, с которым нужно работать (например так - "relay.php?1=OFF"). Этот скрипт произвел бы какие-либо действия на сервере (например сохранил бы состояние реле в файл). А клиент (Ардуино) прочитал бы этот файл и изменил бы состояние реле.

Но если в роли сервера выступант Ардуино - нам нужно, по сути взять на себя функции интерпретатора скриптов и самим отлавливать команды, переданные с запросом (в данном случае - GET).

 

pastry777
Offline
Зарегистрирован: 16.01.2014

спасибо!в целом вы правильно меня поняли=)я пытался работать и с пост(на нем организовано управление реле) и с гет(так как с этим запросом связаны все примеры парсинга и он легко формируется в адресной строке)вот тут и накладка получается,вы это подтвердили...я думал что с ними можно независимо работать...лишнюю страницу не делал,я условно выразился,вторым действием была отдача файла браузеру..

01 File volt = SD.open("/volt.csv");
02          if (volt){
03           client.println("HTTP/1.1 200 OK");
04           client.println("Content-Disposition: attachment;filename=volt.csv;");
05           client.println();
06               while (volt.available()) {
07                       client.write(volt.read());}
08            volt.close();
09          }
10            
11           client.stop();

в общем я понял что мне нужно глубже  копать в сторону ПОСТ,сделать на страничке кнопку,которая будет формировать очередной запрос и корректировать парсинг на отлов этого запроса....просто там в основном скетче с управлением реле парсинг так заточен под эти самые реле не знаю как туда всунуться еще со своими дополнительными символами...сегодня вот снова до трех ночи читал и про парсинг и про все остальное,очень скудные примеры,тяжелые для восприятия новичку,мало коментариев...спасибо,буду дальше пытаться разобраться..

pastry777
Offline
Зарегистрирован: 16.01.2014

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

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

pastry777 пишет:

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

Блин...

Парсинг - суть синтаксический анализ. В вашем случае - это нахождение в строке (в HTTP-запросе) необходимого символа или последовательности символов. Смотрите работу со строками.

Вам собственно, даже не нужно разбирать весь запрос.

Вот, например:

GET 192.168.1.100/index.htm?relay1OFF HTTP/1.1

Между "GET /" и "?" находится адрес запрашиваемой страницы. Если не хотите реализовывать подгрузку файлов - можете проигнорировать.

Далее, между "?" и " HTTP/1.1" находятся, собственно параметры запроса. Простыми словами - то что находится в этом месте вы можете использовать для передачи значений.

Вам по сути нужны несколько функций работы со строками:

нахождение подстроки в строке http://arduino.cc/en/Reference/StringIndexOf

выделение подстроки http://arduino.cc/en/Reference/StringSubstring