Парсинг XML. Прогноз погоды
- Войдите на сайт для отправки комментариев
Пт, 19/08/2016 - 10:27
Добрый день! Решил сделать метеостанцию с прогнозом погоды.
Для получения данных был выбран сайт http://www.eurometeo.ru/. На данном сайте есть мой город, а также самое главное бесплатный экспорт в XML.
Для реализации у меня есть Arduino Mega и enc28j60.
Для реализации была выбрана библиотека: https://github.com/ntruchsess/arduino_uip
Данный пример отлично работает с штатной библиотекой и модулем W5100, для использования заменить строку в коде: #include <UIPEthernet.h> на #include <Ethernet.h>.
Данные кроме ком порта решил визуализировать на LCD1602(что было по рукой).
Результат творчества.
#include <SPI.h>
#include <UIPEthernet.h>
//#include <string.h>
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
char server[] = "www.eurometeo.ru";
//IPAddress ip(192, 168, 0, 2);
EthernetClient client;
String HTTP_req;
long previousMillis_1,previousMillis;//переменная для хранения значений таймера
boolean stopclient=true;
#include <LiquidCrystal.h> // Лобавляем необходимую библиотеку
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // (RS, E, DB4, DB5, DB6, DB7)
int str=0;//
void setup() {
Serial.begin(9600);
Ethernet.begin(mac);
// Дадим время шилду на инициализацию
delay(1000);
Serial.print("Free Ram - ");
Serial.println( freeRam ());
Serial.println("connecting...");
for(byte a=1;a<10;a++ ){
if (client.connect(server, 80)) {
Serial.println("---------------");
// Создаем HTTP-запрос
client.println("GET /ukraina/cherkaska-oblast/cherkasi/export/xml/data/ HTTP/1.1");
client.println("Host: www.eurometeo.ru");
client.println("User-Agent: arduino-ethernet");
client.println("Connection: close");
client.println();
break;
}
else {
// if you didn't get a connection to the server:
Serial.println("connection failed");
delay(5000);
}
}
pinMode(13,OUTPUT);//инициализация портов
lcd.begin(16, 2); // Задаем размерность экрана
lcd.clear();
previousMillis_1=0; previousMillis=0;
}
void loop() {
// Если есть доступные биты, читаем их и выводим на экран
if (client.available()) {
Serial.println("Loading.....");
lcd.setCursor(0, 0);
lcd.print("Loading....."); // Выводим текст
while (client.available()) {
char c = client.read(); // read 1 byte (character) from client
HTTP_req += c; // save the HTTP request 1 char at a time
// Serial.print(c);
delay(1);
}
lcd.setCursor(0, 1);
Serial.println("End Loading.....");
lcd.print("End Loading....."); // Выводим текст
}
if (HTTP_req.indexOf("</weather>") > 0){//Точно получили все данные
if(stopclient){ //остановливаем клиент
if (!client.connected()) {
Serial.println();
Serial.println("---------------");
Serial.println("disconnecting");
client.stop();
}
Serial.println("==============================");
Serial.print("Free Ram - ");
Serial.println( freeRam ());
Serial.println("==============================");
//delay(5000);
stopclient=false;//
}
if (millis() - previousMillis_1 >2000) //меняем по кругу
{
previousMillis_1 = millis(); //запучкаем таймер
if(HTTP_req.indexOf("<datetime>",str) > 0)
{
lcd.clear(); //
int a,new_str;
a =HTTP_req.indexOf("<datetime>",str);
Serial.print(HTTP_req.substring(a+10, a+29)+", t = ");
lcd.setCursor(0, 0); // Устанавливаем курсор в начало 1 строки
lcd.print(HTTP_req.substring(a+10, a+29)); // Выводим текст
new_str=a+29;
a = HTTP_req.indexOf("<temperature>",a+29);
Serial.print(HTTP_req.substring(a+13, a+18));
lcd.setCursor(0, 1); // Устанавливаем курсор в начало 2 строки
lcd.print("T ="+HTTP_req.substring(a+13, a+18));
Serial.print(", h = ");
a = HTTP_req.indexOf("<humidity>",a+18);
Serial.println(HTTP_req.substring(a+10, a+12));
lcd.print(" H ="+HTTP_req.substring(a+10, a+12));
str =new_str;
}else{str=0;}//if
}//if
}//if
//Проверка, не зависла ли ардуино?
if (millis() - previousMillis >500)
{
previousMillis = millis(); //запучкаем таймер
digitalWrite(13, !digitalRead(13));//меняем значение порта каждые 0.5секунд
}
}
int freeRam () {
extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); }
Для получения данных был выбран сайт http://www.eurometeo.ru/. На данном сайте есть мой город, а также самое главное бесплатный экспорт в XML.
Ай спасибо !
Вот резултат роботы, жрет дофига озу. Может как-то оптимизировать?
Сейчас скетч построен так, что сначала весь ответ сайта полностью принимается в String HTTP_req;, а потом парсится и выводится на экран.
Оптимизировать можно, если парсить прямо в процессе приёма, тогда размеры HTTP_req (и объём памяти, ей занимаемый) можно будет значительно сократить.
В идеале стоило бы отказаться от использования String в пользу char-массивов.
Что финня, постоянно в ребут уходит: упростил код:
#include <SPI.h> #include <UIPEthernet.h> #include <string.h> byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; char server[] = "www.eurometeo.ru"; //IPAddress ip(192, 168, 0, 2); EthernetClient client; char HTTP_req[]= ""; int req_index = 0; // index into HTTP_req buffer long previousMillis_1,previousMillis;//переменная для хранения значений таймера boolean stopclient=true; int str=0;// void setup() { Serial.begin(9600); Ethernet.begin(mac); // Дадим время шилду на инициализацию delay(1000); Serial.print("Free Ram - "); Serial.println( freeRam ()); Serial.println("connecting..."); for(byte a=1;a<10;a++ ){ if (client.connect(server, 80)) { Serial.println("---------------"); // Создаем HTTP-запрос client.println("GET /ukraina/cherkaska-oblast/cherkasi/export/xml/data/ HTTP/1.1"); client.println("Host: www.eurometeo.ru"); client.println("User-Agent: arduino-ethernet"); client.println("Connection: close"); client.println(); break; } else { // if you didn't get a connection to the server: Serial.println("connection failed"); delay(5000); } } } void loop() { // Если есть доступные биты, читаем их и выводим на экран if (client.available()) { char c = client.read(); // read 1 byte (character) from client HTTP_req[req_index] = c; // save HTTP request character req_index++; Serial.print(c); delay(1); } if (millis() - previousMillis >500) { previousMillis = millis(); //запучкаем таймер digitalWrite(13, !digitalRead(13));//меняем значение порта каждые 0.5секунд } } int freeRam () { extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); }Результат роботы:
Разобрался, надо размер буфера указать
charHTTP_req[4230]="";Это размер пакета данных, буду дробить на 500, и анализировать, посмотрим что с этого получится.
char-массивов пока еще не разобрался, точнее, блоками выводить получилось, а вот с поиском в массиве не очень. Решил оптимизировать тока роботу со строками. Убрав все лишнее, вывод только в консоль:
#include <SD.h> #include <SPI.h> #include <UIPEthernet.h> //#include <string.h> byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; char server[] = "www.eurometeo.ru"; //IPAddress ip(192, 168, 0, 2); EthernetClient client; String HTTP_req; long previousMillis;//переменная для хранения значений таймера boolean stopclient=true; void setup() { Serial.begin(9600); Ethernet.begin(mac); // Дадим время шилду на инициализацию delay(1000); Serial.print("Free Ram - "); Serial.println( freeRam ()); Serial.println("connecting..."); for(byte a=1;a<10;a++ ){ if (client.connect(server, 80)) { Serial.println("---------------"); // Создаем HTTP-запрос client.println("GET /ukraina/cherkaska-oblast/cherkasi/export/xml/data/ HTTP/1.1"); client.println("Host: www.eurometeo.ru"); client.println("User-Agent: arduino-ethernet"); client.println("Connection: close"); client.println(); break; } else { // if you didn't get a connection to the server: Serial.println("connection failed"); delay(5000); } } pinMode(13,OUTPUT);//инициализация портов previousMillis=0; } void loop() { // Если есть доступные биты, читаем их и выводим на экран if (client.available()) { Serial.println("Loading....."); while (client.available()) { char c = client.read(); // read 1 byte (character) from client HTTP_req += c; // save the HTTP request 1 char at a time // Serial.print(c); if (HTTP_req.indexOf("</step>") > 0){ int a; a =HTTP_req.indexOf("<datetime>"); Serial.print(HTTP_req.substring(a+10, a+29)+", t = "); a = HTTP_req.indexOf("<temperature>",a+29); Serial.print(HTTP_req.substring(a+13, a+18)); Serial.print(", h = "); a = HTTP_req.indexOf("<humidity>",a+18); Serial.println(HTTP_req.substring(a+10, a+12)); HTTP_req=""; } delay(1); } if (HTTP_req.indexOf("</weather>") > 0){//Точно получили все данные Serial.println("End Loading....."); if(stopclient){ //остановливаем клиент if (!client.connected()) { Serial.println(); Serial.println("---------------"); Serial.println("disconnecting"); client.stop(); } Serial.println("=============================="); Serial.print("Free Ram - "); Serial.println( freeRam ()); Serial.println("=============================="); //delay(5000); stopclient=false;// }//if }//if } //Проверка, не зависла ли ардуино? if (millis() - previousMillis >500) { previousMillis = millis(); //запучкаем таймер digitalWrite(13, !digitalRead(13));//меняем значение порта каждые 0.5секунд } } int freeRam () { extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); }Резултат роботы:
Следал хранение данных в структуре:
struct meteo { String Date_time; float t; int h; } add_meteo[12];Также подробно описана структура файла для парсинга:
Пока проект заканчиваю, возможно попозже добавлю визуализацию данных(1602 маловат для этого):
#include <SD.h> #include <SPI.h> #include <UIPEthernet.h> //#include <string.h> byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; char server[] = "www.eurometeo.ru"; //IPAddress ip(192, 168, 0, 2); EthernetClient client; String HTTP_req; long previousMillis;//переменная для хранения значений таймера boolean stopclient=true; //Структура, где будем хранитиь данные о погоде //Параметром можно и по больше, незабываем память не резиновая. struct meteo { String Date_time; float t; int h; } add_meteo[12]; byte index=0; void setup() { Serial.begin(9600); Ethernet.begin(mac); // Дадим время шилду на инициализацию delay(1000); Serial.print("Free Ram - "); Serial.println( freeRam ()); Serial.println("connecting..."); for(byte a=1;a<10;a++ ){ if (client.connect(server, 80)) { Serial.println("---------------"); // Создаем HTTP-запрос //Сдесь данные свого города: client.println("GET /ukraina/cherkaska-oblast/cherkasi/export/xml/data/ HTTP/1.1"); client.println("Host: www.eurometeo.ru"); client.println("User-Agent: arduino-ethernet"); client.println("Connection: close"); client.println(); break; } else { // if you didn't get a connection to the server: Serial.println("connection failed"); delay(5000); } } pinMode(13,OUTPUT);//инициализация портов previousMillis=0; } void loop() { // Если есть доступные биты, читаем их и выводим на экран if (client.available()) { Serial.println("Loading....."); while (client.available()) { char c = client.read(); // read 1 byte (character) from client HTTP_req += c; // save the HTTP request 1 char at a time // Serial.print(c); if (HTTP_req.indexOf("</step>") > 0){//12 блоков начало <step> конец </step> /* <step> <datetime>2016-08-08 05:00:00</datetime> <pressure>754.24</pressure> <temperature>17.43</temperature> <humidity>28</humidity> <cloudcover>0</cloudcover> <windspeed>5.30</windspeed> <windgust>9.78</windgust> <winddir>258</winddir> <precipitation>0.00</precipitation> </step> <step> Шаг прогноза, содержит в себе данные на определенное время <datetime> Время прогноза погоды <pressure> Атмосферное давление в мм рт.столба <temperature> Температура воздуха в °C <humidity> Относительная влажность <cloudcover> Облачность в % (100% облака без просветов, 0% - чистое небо) <windspeed> Скорость ветра в м/с <windgust> Скорость порывов ветра в м/с <winddir> Направление ветра в градусах от 0 до 359. 0 - южный ветер, ветер дует по направлению с юга, 90 - ветер с запада, 180 - ветер с севера, 270 - ветер с востока <precipitation> Осадки в мм за 3 часов */ //Беру только 2 параметра: темепературу и влажность. int a; a =HTTP_req.indexOf("<datetime>"); Serial.print(HTTP_req.substring(a+10, a+29)+", temperature = "); add_meteo[index].Date_time = HTTP_req.substring(a+10, a+29); a = HTTP_req.indexOf("<temperature>",a+29); Serial.print(HTTP_req.substring(a+13, a+18)); char floatbufVar[32]; String stringVar = HTTP_req.substring(a+13, a+18); stringVar.toCharArray(floatbufVar,sizeof(floatbufVar)); add_meteo[index].t=atof(floatbufVar); Serial.print(", humidity = "); a = HTTP_req.indexOf("<humidity>",a+18); Serial.println(HTTP_req.substring(a+10, a+12)); add_meteo[index].h=HTTP_req.substring(a+10, a+12).toInt(); //Ощищаем для экономии озу. HTTP_req=""; index++; } delay(1); } if (HTTP_req.indexOf("</weather>") > 0){//Точно получили все данные Serial.println("End Loading....."); if(stopclient){ //остановливаем клиент if (!client.connected()) { Serial.println(); Serial.println("---------------"); Serial.println("disconnecting"); client.stop(); } Serial.println("=============================="); Serial.print("Free Ram - "); Serial.println( freeRam ()); Serial.println("=============================="); //delay(5000); stopclient=false;// HTTP_req=""; //проверка Массива, потом можно будет убрать for(index=0;index<12;index++) { Serial.print(add_meteo[index].Date_time +", t = " ); Serial.print(add_meteo[index].t); Serial.print(", h = "); Serial.println(add_meteo[index].h); } Serial.println("=============================="); index=0; }//if }//if } //Проверка, не зависла ли ардуино? //Крутю по кругу массив if (millis() - previousMillis >500) { previousMillis = millis(); //запучкаем таймер digitalWrite(13, !digitalRead(13));//меняем значение порта каждые 0.5секунд if(stopclient==false){ //Крутим если скачали полный файл, иногда качает частями Serial.print(add_meteo[index].Date_time+", t = " ); Serial.print(add_meteo[index].t); Serial.print(", h = "); Serial.println(add_meteo[index].h); index++; //Обнуляем счетчик if(index==12){ index=0;} } }//if }//loop int freeRam () { extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); }Результат:
Итак идем дальше, есть 2.4 tft lcd shield 0x9325, и библиотека MCUFRIEND_kbv. Проверил данный shield на Меге и Уно. Работает без проблем, и без правки библиотек. СД карта на Меге не завелась, так как не соответствуют пины, ну мне пока и не нужно. Пока только визуализировано температуру, в дальнейшем задействую тач, и возможность строить гистограмму не только по температуре, а также и по другим параметрам (влажность, давление, осадки, скорость ветра, облачность).
Видео : https://yadi.sk/i/HZXLU4QCurniS
#include <SD.h> #include <SPI.h> #include <UIPEthernet.h> //#include <string.h> byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; char server[] = "www.eurometeo.ru"; //IPAddress ip(192, 168, 0, 2); EthernetClient client; String HTTP_req; long previousMillis;//переменная для хранения значений таймера boolean stopclient=true; //Структура, где будем хранитиь данные о погоде //Параметром можно и по больше, незабываем память не резиновая. struct meteo { String Date_time; float t; int h; } add_meteo[12]; //Блок роботы с TFT #include <Adafruit_GFX.h> // Hardware-specific library #include <MCUFRIEND_kbv.h> MCUFRIEND_kbv tft; #define BLACK 0x0000 #define BLUE 0x001F #define RED 0xF800 #define GREEN 0x07E0 #define CYAN 0x07FF #define MAGENTA 0xF81F #define YELLOW 0xFFE0 #define WHITE 0xFFFF byte index=0; void setup() { // Serial.begin(9600); Ethernet.begin(mac); // Дадим время шилду на инициализацию delay(1000); tft.reset(); uint16_t id = tft.readID(); tft.begin(id); tft.setRotation(1); tft.fillScreen(BLACK); tft.setTextColor(WHITE, BLACK); tft.setTextSize(2); // System font is 8 pixels. ht = 8*2=16 tft.print("Free Ram - "); tft.println( freeRam ()); tft.println("connecting..."); for(byte a=1;a<10;a++ ){ if (client.connect(server, 80)) { // Serial.println("---------------"); // Создаем HTTP-запрос //Сдесь данные свого города: client.println("GET /ukraina/cherkaska-oblast/cherkasi/export/xml/data/ HTTP/1.1"); client.println("Host: www.eurometeo.ru"); client.println("User-Agent: arduino-ethernet"); client.println("Connection: close"); client.println(); break; } else { // if you didn't get a connection to the server: tft.println("connection failed"); delay(5000); } } pinMode(13,OUTPUT);//инициализация портов previousMillis=0; } void loop() { // Если есть доступные биты, читаем их и выводим на экран if (client.available()) { tft.println("Loading....."); while (client.available()) { char c = client.read(); // read 1 byte (character) from client HTTP_req += c; // save the HTTP request 1 char at a time // Serial.print(c); if (HTTP_req.indexOf("</step>") > 0){//12 блоков начало <step> конец </step> /* <step> <datetime>2016-08-08 05:00:00</datetime> <pressure>754.24</pressure> <temperature>17.43</temperature> <humidity>28</humidity> <cloudcover>0</cloudcover> <windspeed>5.30</windspeed> <windgust>9.78</windgust> <winddir>258</winddir> <precipitation>0.00</precipitation> </step> <step> Шаг прогноза, содержит в себе данные на определенное время <datetime> Время прогноза погоды <pressure> Атмосферное давление в мм рт.столба <temperature> Температура воздуха в °C <humidity> Относительная влажность <cloudcover> Облачность в % (100% облака без просветов, 0% - чистое небо) <windspeed> Скорость ветра в м/с <windgust> Скорость порывов ветра в м/с <winddir> Направление ветра в градусах от 0 до 359. 0 - южный ветер, ветер дует по направлению с юга, 90 - ветер с запада, 180 - ветер с севера, 270 - ветер с востока <precipitation> Осадки в мм за 3 часов */ //Беру только 2 параметра: темепературу и влажность. int a; a =HTTP_req.indexOf("<datetime>"); // Serial.print(HTTP_req.substring(a+10, a+29)+", temperature = "); add_meteo[index].Date_time = HTTP_req.substring(a+10, a+29); a = HTTP_req.indexOf("<temperature>",a+29); // Serial.print(HTTP_req.substring(a+13, a+18)); char floatbufVar[32]; String stringVar = HTTP_req.substring(a+13, a+18); stringVar.toCharArray(floatbufVar,sizeof(floatbufVar)); add_meteo[index].t=atof(floatbufVar); // Serial.print(", humidity = "); a = HTTP_req.indexOf("<humidity>",a+18); // Serial.println(HTTP_req.substring(a+10, a+12)); add_meteo[index].h=HTTP_req.substring(a+10, a+12).toInt(); //Ощищаем для экономии озу. HTTP_req=""; index++; } delay(1); } if (HTTP_req.indexOf("</weather>") > 0){//Точно получили все данные tft.println("End Loading....."); if(stopclient){ //остановливаем клиент if (!client.connected()) { // Serial.println(); // Serial.println("---------------"); tft.println("disconnecting"); client.stop(); } // Serial.println("=============================="); tft.print("Free Ram - "); tft.println( freeRam ()); // Serial.println("=============================="); //delay(5000); stopclient=false;// HTTP_req=""; delay(2000); //проверка Массива, потом можно будет убрать tft.fillScreen(BLACK); tft.setCursor(0, 0); tft.setTextSize(2); for(index=0;index<12;index++) { tft.print(add_meteo[index].Date_time.substring(2,16) +"t=" ); tft.print(add_meteo[index].t); tft.print(",h="); tft.print(add_meteo[index].h); } delay(2000); //Grid tft.fillScreen(BLACK); int line; tft.drawFastVLine(0, 0, 100, GREEN); for (line = 0; line < 13; line++) tft.drawFastVLine(25*line, 0, 100, GREEN); ; tft.drawFastVLine(319, 0, 100, GREEN); tft.drawFastHLine(0, 100, 320, GREEN); for (line = 0; line < 11; line++) tft.drawFastHLine(0, 10*line, 320, GREEN); ; //end Grid //min,max float min_t,max_t; min_t=100; max_t=-100; for (line = 0; line < 12; line++) { if(min_t>add_meteo[line].t) min_t=add_meteo[line].t; if(max_t<add_meteo[line].t) max_t=add_meteo[line].t; } //end min,max tft.setTextSize(1); tft.setTextColor(WHITE,BLACK); //tft.setCursor(0, 120); //tft.println(min_t); //tft.println(max_t); //Добавим 10% для красивой сетки min_t=min_t-min_t*0.2; max_t=max_t+max_t*0.1; for (line = 0; line < 12; line++) { // tft.drawLine(13+25*line, 100,13+25*line,100-map(add_meteo[line].t,min_t,max_t,0,100), RED); tft.fillRect(13+25*line, 100,5,-map(add_meteo[line].t,min_t,max_t,0,100), RED); tft.setCursor(13+25*line, 100-map(add_meteo[line].t,min_t,max_t,0,100)); tft.print(add_meteo[line].t); } tft.setCursor(0, 120); tft.setTextSize(1); for(index=0;index<12;index++) { tft.print(add_meteo[index].Date_time +", t = " ); tft.print(add_meteo[index].t); tft.print(" , h = "); tft.println(add_meteo[index].h); } // Serial.println("=============================="); index=0; }//if }//if } //Проверка, не зависла ли ардуино? if (millis() - previousMillis >500) { previousMillis = millis(); //запучкаем таймер digitalWrite(13, !digitalRead(13));//меняем значение порта каждые 0.5секунд }//if }//loop int freeRam () { extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); }почему у меня ошибка в
client.println("Host: <a href="http://www.eurometeo.ru" title="www.eurometeo.ru"" rel="nofollow">www.eurometeo.ru"</a>)unable to find string literal operator 'operator""http' with 'const char [15]', 'unsigned int' arguments
помогите если можете
почему у меня ошибка в
client.println("Host: <a href="http://www.eurometeo.ru" title="www.eurometeo.ru"" rel="nofollow">www.eurometeo.ru"</a>)unable to find string literal operator 'operator""http' with 'const char [15]', 'unsigned int' arguments
помогите если можете
Кавычки внутри кавычек необходимо экранировать символом \
client.println("Host: <a href="http://www.eurometeo.ru" title="www.eurometeo.ru"" rel="nofollow">www.eurometeo.ru"</a>);заменить "client.println("Host: www.eurometeo.ru");"
тут можно найти свой город.
https://eurometeo.ru/ukraina/cherkaska-oblast/cherkasi/export/
Мой город уже в статистику не попадает, а свой проверяйте.
добавлено: По ходу сайт уже давно статистику не обновляет по всем городам