Парсинг строки адреса Arduino в режиме веб-сервера

R13
Offline
Зарегистрирован: 11.04.2013

Имею arduino nano и enc28j60 модуль к нему.  Мой проект - это очередная ethernet метеостанция, но не суть )

В процедуре Loop идет обработа запросов и выдача результатов:

void loop()

{
 char* httpurl;
 
  if (httpurl = e.serviceRequest())
  {
     if (strcmp(httpurl, "") == 0)
  {
  
    e.print("<A HREF='/'><H1>nano v3</H1></A>");
    e.print("<br>");
    e.print("<A HREF='?=temp'>Show Temperature</A>");
    e.print("<br>");
    e.print("<A HREF='?=ip'>IP Settings</A>");
    e.print("<br>");
    e.print("<A HREF='?=relay'>Relay Switch</A>");
  }
 
 if (strcmp(httpurl, "?=ip") == 0)
  { 
    e.print("<html>");
    e.print("<head>");
    e.print("</head>");
    e.print("<body>");
    e.print("<A HREF='/'><H1>nano v3</H1></A>");
    e.print("<br> IP (loaded from ROM): <br>");
    e.print("<form name = 'form1' action = 'setip' method = 'get'>");
    e.print("ip: <input type = 'a' name = 'aaa' value = ");
    e.print(EEPROM.read(0));
    e.print(">");
    e.print(". <input type = 'b'; name = 'bbb' value = ");
    e.print(EEPROM.read(1));
    e.print(">");
    e.print(". <input type = 'c'; name = 'ccc' value = ");
    e.print(EEPROM.read(2));
    e.print(">");
    e.print(". <input type = 'd'; name = 'ddd' value = ");
    e.print(EEPROM.read(3));
    e.print(">");
    e.print("<br>");
     e.print("<input type ='submit'; value = Apply >");
    e.print("</form>");
    e.print("</body>");
    e.print("</html>");
  }
 e.respond();
 }

При запросе http://ip-адрес/?=ip выдается страничка с 4 полями и загруженными в нее значениями из eeprom. Если вписать свои значения нажать на кнопку Apply, то сформируется ссылка вида:

http://192.168.0.33/setip?aaa=192&bbb=168&ccc=0&ddd=34

Соответственно в httpurl передается значение:

setip?aaa=192&bbb=168&ccc=0&ddd=34

Задача вытянуть числа 192, 168, 0 и 34 в arduino для внутренней обработки - сохранения в eeprom. Есть библиотека TextFinder, но она не работает с моей библиотекой ethernet-модуля.

R13
Offline
Зарегистрирован: 11.04.2013

Также не совсем понимаю, как из char* httpurl преобразовать в String.

Логику работы парсинга себе представляю себе так:

1. поиск по маске, например, "aaa="

2. вычленение значения после выражения до символа "&" или окончания строки или можно что-то в стиле 3 символа после найденного значения+применение функции atoi

leshak
Offline
Зарегистрирован: 29.09.2011

Если бы вы первые слова своей темы "Парсинг строки" вбили в поиск в верхенм правом углу, то второй же ссылкой вам выдало-бы тему где обсуждалось ровно то же самое. Причем буквально пару дней назад (а это значит что даже без поиска, просто посмотрев темы последние - можно было найти).

Разбор тестовой строки | Аппаратная платформа Arduino

R13
Offline
Зарегистрирован: 11.04.2013

Извините, подобную тему искал раньше даты создания темы от 4 апреля. Спасибо!

leshak
Offline
Зарегистрирован: 29.09.2011

Единственный коммент - в той теме параметры в урле топикстартер разделяет запятыми и дветочиями. А у вас - амперсанды и знаки равно. "По стандартам" - ваш вариант более правильный/традиционный. Его легче понять просто посмотрев на урл любому веб-разработчику без доп. объяснений. 

Так что там вы посмотрите только "принцип", но используйте свои разделители & и =.

>преобразовать в String.

Да просто передайте в конструктор

String myString=String(httpurl)

Но... лично я вообще предпочитаю не использовать класс String. Во первых "место", во вторых он "только в ардуине существует" и то используется редко в серьезном коде.

Так что лучше изначально привыкать работать с char*  (обычные сишные строки) string

Все встроенные обычные функции сишные atoi, stkok и т.п. - работают именно с такими строками, а не String объектом.

R13
Offline
Зарегистрирован: 11.04.2013

void parseParams(char* inputString){
  
  parsedParams=0;
 char* buffer=strtok(inputString,"?");
  if(buffer!=NULL){
    for(buffer=strtok(NULL,"&"); buffer!=NULL;     buffer=strtok(NULL,"&") ) 
    {
        params[parsedParams].value=buffer;
         parsedParams++; 
         if(parsedParams>MAX_PARAMS-1)return;
     } 
  }
}

Кручу-верчу функцию из найденой темы. При таком варианте возвращаются значения:

 

aaa=192
bbb=168
ccc=1
ddd=33
 
Не могу понять, как отделить из каждой строки значение после "=".
Пытался дописать 
params[parsedParams].value=strtok(buffer, "=");

но возвращает только:

 

aaa
192
 
А если делать преобразование atoi(buffer), то выдает четыре нуля.
Видимо, я что-то не допонимаю.
 
И еще насчет char* и char . Как первое во второе преобразовать? В чем отличие друг от друга?  

 

leshak
Offline
Зарегистрирован: 29.09.2011

Ну во первых, не правильно "аналогию провели".  Двоеточия из того кода - соотвествуют вашим знакам равно. То есть в for-ре у вас strtok(NULL,"="

Вот вам и будет парсинг имен параметров. А не захват "имя+значение" сразу. Но, у вас начнется потеря значений.

Вы же код из строк 41-42 выкинули, а именно он отвечает за парсинг значений. Вам он тоже нужен. Только там он ищется по запятой, а у вас амперсанд.

Вообщем из того кода в ваш переход делается чере замену  ":"-->"="  и ","-->"&"

 

Цитата:

 

Видимо, я что-то не допонимаю.
 
И еще насчет char* и char . Как первое во второе преобразовать? В чем отличие друг от друга?  

Верно. Не понимаете. Но в двух словах тут не расскажешь.  загуглите какой-нибудь учебник по C/C++ и почитайте разделы "указатели/ссылки" и "строки". Возможно даже несколько учебников прийдется прочитать. Тема действительна сложная для понимания (но когда понял - все просто) не каждый автор наглядно может объяснить. А тема важна. Без нее что-то сложней "мигаем диодом" - трудно. А в работе со строками - вообще никак.

P.S. И наверное луче было продолжать дисскуссию в той ветке. Что-бы "не размазывать". Тем более что я планировал на выходных туда дописать альтернативу atoi. Да и топикстартер той ветки - тоже мог бы вам подсказать. Он то понял "как оно работает". Да еще говорил что "разобрался с указателями", может подскажет где почитал (я-то уже естественно не помню где про это нормально написано).

R13
Offline
Зарегистрирован: 11.04.2013

В первоначальном виде я в функцию наоборот запихал свои & и = . Теперь все работает.

Оставлю для истории правильный вариант:

void parseParams(char* inputString){
  
  parsedParams=0; // пока ничего не напарсили
 char* buffer=strtok(inputString,"?"); // лучше так проверять/пропускать вопросилово
 
  if(buffer!=NULL){
    for(buffer=strtok(NULL,"="); buffer!=NULL;     buffer=strtok(NULL,"=") ) 
    {
     if( (buffer=strtok(NULL,"&")) !=NULL)   params[parsedParams].value=atoi(buffer);
         else return ;  
         parsedParams++; // отмечаем сколько удалось распарсить
         if(parsedParams>MAX_PARAMS-1)return; // больше нет места куда сохранять парсенное.
     } 
  }
}

Огромное спасибо.

leshak
Offline
Зарегистрирован: 29.09.2011

А где в этом правильном варианте парсинг имен параметров? или они вам не нужны?

R13
Offline
Зарегистрирован: 11.04.2013

В моем случае это не особо принципиально, но по-хорошему проверка нужна (действительно ли, к примеру передается параметр 'aaa', а не 'cdf'). Только я опять затрудняюсь с решением данной задачи.

leshak
Offline
Зарегистрирован: 29.09.2011

Ну, проверять - это уже потом. Вначале грамотно выпарсить нужно. И даже умея работать со строками - есть там с чем повозится.

Вообщем в любом случае "вначали читать книжку про указатели/ссылки и строки", в любом случае это пригодится (а без понимания - даже если дадут вам готовый код - будет все рушится при малейшей попытке что-то изменить).

R13
Offline
Зарегистрирован: 11.04.2013
int strtoint(String str) // Процедура переобразования строки в число
{
  int tempInt;
  char rez[str.length()+1];
  str.toCharArray(rez, sizeof(rez));
  tempInt = atoi(rez);
  return tempInt;
}

void parseParams(char* inputString){
  
  parsedParams=0; // пока ничего не напарсили
 char* buffer=strtok(inputString,"?"); // лучше так проверять/пропускать вопросилово
 
  if(buffer!=NULL){
    for(buffer=strtok(NULL,"&"); buffer!=NULL;     buffer=strtok(NULL,"&") ) 
    {
  String buffer1= String(buffer);
    params[parsedParams].name= buffer1.substring(0,buffer1.indexOf('='));   //достаем имя
    params[parsedParams].value=strtoint(buffer1.substring(buffer1.indexOf('=')+1)); //достаем значение в integer
  
    parsedParams++; // отмечаем сколько удалось распарсить
     }
         if(parsedParams>MAX_PARAMS-1)return; // больше нет места куда сохранять парсенное.
     } 
  
}

Пока сделал такой костыль. Выдает структуру struct port_param_t {String name;int value;};

 

aaa -> 192
bbb -> 168
ccc -> 1
ddd -> 33
 
Нерационально? )
Полный листинг для понимания дела.
// структра описывающие наш параметр

struct port_param_t{String name; int value;};

#define MAX_PARAMS 20 // сколько параметров максимально мы умеем парсить

port_param_t params[MAX_PARAMS]; // в этот массис будем сохранять наши парсенные параметры

byte parsedParams=0; // сколько параметров нам удалось напарсить

void setup(){

  Serial.begin(9600);
  char* inputString="h ttp://192.168.3.5/setip?aaa=192&bbb=168&ccc=1&ddd=33"; // наши тестовые данные
  // выводим что собираемся парсить

  Serial.print("input '");Serial.print(inputString);  Serial.println("'");
  // парсим
  parseParams(inputString);
  // выводим что получилось
  printParams();
}

// парсид входящую строку в массив params[] и устанавливает parsedParam в количество прочитанных элементов

void parseParams(char* inputString){
  
  parsedParams=0; // пока ничего не напарсили
 char* buffer=strtok(inputString,"?"); // лучше так проверять/пропускать вопросилово
 
  if(buffer!=NULL){
    for(buffer=strtok(NULL,"&"); buffer!=NULL;     buffer=strtok(NULL,"&") ) 
    {
  String buffer1= String(buffer);
    params[parsedParams].name= buffer1.substring(0,buffer1.indexOf('='));   
    params[parsedParams].value=strtoint(buffer1.substring(buffer1.indexOf('=')+1)); 
  
    parsedParams++; // отмечаем сколько удалось распарсить
     }
         if(parsedParams>MAX_PARAMS-1)return; // больше нет места куда сохранять парсенное.
     } 
  
}
// Выводит в Serial массив parsedParams[]
void printParams(){
  for(byte i=0;i<parsedParams;i++){
      // TODO: всю эту кучу принтов можно заменить одним sprintf
      
      Serial.print(params[i].name);
      Serial.print(" -> ");
      Serial.println(params[i].value);
    //  Serial.print("=");
    //  Serial.println(params[i].value,DEC);
  }
}

int strtoint(String str) // Процедура переобразования строки в число
{
  int tempInt;
  char rez[str.length()+1];
  str.toCharArray(rez, sizeof(rez));
  tempInt = atoi(rez);
  return tempInt;
}

void loop(){
}